diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9fcb69e2e9..1280e6a6e5 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.15.12-nightly.4 - 3.15.12-nightly.3 - 3.15.12-nightly.2 - 3.15.12-nightly.1 @@ -134,7 +135,6 @@ body: - 3.14.5-nightly.1 - 3.14.4 - 3.14.4-nightly.4 - - 3.14.4-nightly.3 validations: required: true - type: dropdown diff --git a/openpype/hosts/houdini/plugins/create/create_pointcache.py b/openpype/hosts/houdini/plugins/create/create_pointcache.py index df74070fee..554d5f2016 100644 --- a/openpype/hosts/houdini/plugins/create/create_pointcache.py +++ b/openpype/hosts/houdini/plugins/create/create_pointcache.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Creator plugin for creating pointcache alembics.""" from openpype.hosts.houdini.api import plugin -from openpype.pipeline import CreatedInstance import hou @@ -14,15 +13,13 @@ class CreatePointCache(plugin.HoudiniCreator): icon = "gears" def create(self, subset_name, instance_data, pre_create_data): - import hou - instance_data.pop("active", None) instance_data.update({"node_type": "alembic"}) instance = super(CreatePointCache, self).create( subset_name, instance_data, - pre_create_data) # type: CreatedInstance + pre_create_data) instance_node = hou.node(instance.get("instance_node")) parms = { @@ -37,13 +34,44 @@ class CreatePointCache(plugin.HoudiniCreator): } if self.selected_nodes: - parms["sop_path"] = self.selected_nodes[0].path() + selected_node = self.selected_nodes[0] - # try to find output node - for child in self.selected_nodes[0].children(): - if child.type().name() == "output": - parms["sop_path"] = child.path() - break + # Although Houdini allows ObjNode path on `sop_path` for the + # the ROP node we prefer it set to the SopNode path explicitly + + # Allow sop level paths (e.g. /obj/geo1/box1) + if isinstance(selected_node, hou.SopNode): + parms["sop_path"] = selected_node.path() + self.log.debug( + "Valid SopNode selection, 'SOP Path' in ROP will be set to '%s'." + % selected_node.path() + ) + + # Allow object level paths to Geometry nodes (e.g. /obj/geo1) + # but do not allow other object level nodes types like cameras, etc. + elif isinstance(selected_node, hou.ObjNode) and \ + selected_node.type().name() in ["geo"]: + + # get the output node with the minimum + # 'outputidx' or the node with display flag + sop_path = self.get_obj_output(selected_node) + + if sop_path: + parms["sop_path"] = sop_path.path() + self.log.debug( + "Valid ObjNode selection, 'SOP Path' in ROP will be set to " + "the child path '%s'." + % sop_path.path() + ) + + if not parms.get("sop_path", None): + self.log.debug( + "Selection isn't valid. 'SOP Path' in ROP will be empty." + ) + else: + self.log.debug( + "No Selection. 'SOP Path' in ROP will be empty." + ) instance_node.setParms(parms) instance_node.parm("trange").set(1) @@ -57,3 +85,23 @@ class CreatePointCache(plugin.HoudiniCreator): hou.ropNodeTypeCategory(), hou.sopNodeTypeCategory() ] + + def get_obj_output(self, obj_node): + """Find output node with the smallest 'outputidx'.""" + + outputs = obj_node.subnetOutputs() + + # if obj_node is empty + if not outputs: + return + + # if obj_node has one output child whether its + # sop output node or a node with the render flag + elif len(outputs) == 1: + return outputs[0] + + # if there are more than one, then it have multiple ouput nodes + # return the one with the minimum 'outputidx' + else: + return min(outputs, + key=lambda node: node.evalParm('outputidx')) diff --git a/openpype/hosts/maya/plugins/load/load_look.py b/openpype/hosts/maya/plugins/load/load_look.py index 8f3e017658..b060ae2b05 100644 --- a/openpype/hosts/maya/plugins/load/load_look.py +++ b/openpype/hosts/maya/plugins/load/load_look.py @@ -29,7 +29,7 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): color = "orange" def process_reference(self, context, name, namespace, options): - import maya.cmds as cmds + from maya import cmds with lib.maintained_selection(): file_url = self.prepare_root_value(self.fname, @@ -113,8 +113,8 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # region compute lookup nodes_by_id = defaultdict(list) - for n in nodes: - nodes_by_id[lib.get_id(n)].append(n) + for node in nodes: + nodes_by_id[lib.get_id(node)].append(node) lib.apply_attributes(attributes, nodes_by_id) def _get_nodes_with_shader(self, shader_nodes): @@ -125,14 +125,16 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): Returns node names """ - import maya.cmds as cmds + from maya import cmds - nodes_list = [] for shader in shader_nodes: - connections = cmds.listConnections(cmds.listHistory(shader, f=1), + future = cmds.listHistory(shader, future=True) + connections = cmds.listConnections(future, type='mesh') if connections: - for connection in connections: - nodes_list.extend(cmds.listRelatives(connection, - shapes=True)) - return nodes_list + # Ensure unique entries only to optimize query and results + connections = list(set(connections)) + return cmds.listRelatives(connections, + shapes=True, + fullPath=True) or [] + return [] diff --git a/openpype/hosts/unreal/addon.py b/openpype/hosts/unreal/addon.py index ed23950b35..b5c978d98f 100644 --- a/openpype/hosts/unreal/addon.py +++ b/openpype/hosts/unreal/addon.py @@ -1,7 +1,6 @@ import os import re from openpype.modules import IHostAddon, OpenPypeModule -from openpype.widgets.message_window import Window UNREAL_ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -21,6 +20,8 @@ class UnrealAddon(OpenPypeModule, IHostAddon): from .lib import get_compatible_integration + from openpype.widgets.message_window import Window + pattern = re.compile(r'^\d+-\d+$') if not pattern.match(app.name):