diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 83bb73c23f..ce851d2dbe 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3966,6 +3966,71 @@ def get_capture_preset(task_name, task_type, subset, project_settings, log): return capture_preset or {} +def get_reference_node(members, log=None): + """Get the reference node from the container members + Args: + members: list of node names + + Returns: + str: Reference node name. + + """ + + # Collect the references without .placeHolderList[] attributes as + # unique entries (objects only) and skipping the sharedReferenceNode. + references = set() + for ref in cmds.ls(members, exactType="reference", objectsOnly=True): + + # Ignore any `:sharedReferenceNode` + if ref.rsplit(":", 1)[-1].startswith("sharedReferenceNode"): + continue + + # Ignore _UNKNOWN_REF_NODE_ (PLN-160) + if ref.rsplit(":", 1)[-1].startswith("_UNKNOWN_REF_NODE_"): + continue + + references.add(ref) + + assert references, "No reference node found in container" + + # Get highest reference node (least parents) + highest = min(references, + key=lambda x: len(get_reference_node_parents(x))) + + # Warn the user when we're taking the highest reference node + if len(references) > 1: + if not log: + log = logging.getLogger(__name__) + + log.warning("More than one reference node found in " + "container, using highest reference node: " + "%s (in: %s)", highest, list(references)) + + return highest + + +def get_reference_node_parents(ref): + """Return all parent reference nodes of reference node + + Args: + ref (str): reference node. + + Returns: + list: The upstream parent reference nodes. + + """ + parent = cmds.referenceQuery(ref, + referenceNode=True, + parent=True) + parents = [] + while parent: + parents.append(parent) + parent = cmds.referenceQuery(parent, + referenceNode=True, + parent=True) + return parents + + def create_rig_animation_instance( nodes, context, namespace, options=None, log=None ): diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 604ff101db..967d39674c 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -19,69 +19,30 @@ from .pipeline import containerise from . import lib -def get_reference_node(members, log=None): - """Get the reference node from the container members - Args: - members: list of node names +log = Logger.get_logger() - Returns: - str: Reference node name. +# Backwards compatibility: these functions has been moved to lib. +def get_reference_node(*args, **kwargs): """ - - # Collect the references without .placeHolderList[] attributes as - # unique entries (objects only) and skipping the sharedReferenceNode. - references = set() - for ref in cmds.ls(members, exactType="reference", objectsOnly=True): - - # Ignore any `:sharedReferenceNode` - if ref.rsplit(":", 1)[-1].startswith("sharedReferenceNode"): - continue - - # Ignore _UNKNOWN_REF_NODE_ (PLN-160) - if ref.rsplit(":", 1)[-1].startswith("_UNKNOWN_REF_NODE_"): - continue - - references.add(ref) - - assert references, "No reference node found in container" - - # Get highest reference node (least parents) - highest = min(references, - key=lambda x: len(get_reference_node_parents(x))) - - # Warn the user when we're taking the highest reference node - if len(references) > 1: - if not log: - log = Logger.get_logger(__name__) - - log.warning("More than one reference node found in " - "container, using highest reference node: " - "%s (in: %s)", highest, list(references)) - - return highest - - -def get_reference_node_parents(ref): - """Return all parent reference nodes of reference node - - Args: - ref (str): reference node. - - Returns: - list: The upstream parent reference nodes. - + Deprecated: + This function was moved and will be removed in 3.16.x. """ - parent = cmds.referenceQuery(ref, - referenceNode=True, - parent=True) - parents = [] - while parent: - parents.append(parent) - parent = cmds.referenceQuery(parent, - referenceNode=True, - parent=True) - return parents + msg = "Function 'get_reference_node' has been moved." + log.warning(msg) + cmds.warning(msg) + return lib.get_reference_node(*args, **kwargs) + + +def get_reference_node_parents(*args, **kwargs): + """ + Deprecated: + This function was moved and will be removed in 3.16.x. + """ + msg = "Function 'get_reference_node_parents' has been moved." + log.warning(msg) + cmds.warning(msg) + return lib.get_reference_node_parents(*args, **kwargs) class Creator(LegacyCreator): @@ -205,7 +166,7 @@ class ReferenceLoader(Loader): if not nodes: return - ref_node = get_reference_node(nodes, self.log) + ref_node = lib.get_reference_node(nodes, self.log) container = containerise( name=name, namespace=namespace, @@ -234,7 +195,7 @@ class ReferenceLoader(Loader): # Get reference node from container members members = get_container_members(node) - reference_node = get_reference_node(members, self.log) + reference_node = lib.get_reference_node(members, self.log) namespace = cmds.referenceQuery(reference_node, namespace=True) file_type = { @@ -382,7 +343,7 @@ class ReferenceLoader(Loader): # Assume asset has been referenced members = cmds.sets(node, query=True) - reference_node = get_reference_node(members, self.log) + reference_node = lib.get_reference_node(members, self.log) assert reference_node, ("Imported container not supported; " "container must be referenced.") diff --git a/openpype/hosts/maya/api/workfile_template_builder.py b/openpype/hosts/maya/api/workfile_template_builder.py index b7e731bbb6..e2f30f46d0 100644 --- a/openpype/hosts/maya/api/workfile_template_builder.py +++ b/openpype/hosts/maya/api/workfile_template_builder.py @@ -14,7 +14,7 @@ from openpype.tools.workfile_template_build import ( WorkfileBuildPlaceholderDialog, ) -from .lib import read, imprint, get_main_window +from .lib import read, imprint, get_reference_node, get_main_window PLACEHOLDER_SET = "PLACEHOLDERS_SET" @@ -243,15 +243,19 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): def get_placeholder_options(self, options=None): return self.get_load_plugin_options(options) - def cleanup_placeholder(self, placeholder, failed): + def post_placeholder_process(self, placeholder, failed): """Hide placeholder, add them to placeholder set """ - node = placeholder._scene_identifier + node = placeholder.scene_identifier cmds.sets(node, addElement=PLACEHOLDER_SET) cmds.hide(node) cmds.setAttr(node + ".hiddenInOutliner", True) + def delete_placeholder(self, placeholder): + """Remove placeholder if building was successful""" + cmds.delete(placeholder.scene_identifier) + def load_succeed(self, placeholder, container): self._parent_in_hierarchy(placeholder, container) @@ -268,9 +272,19 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): return roots = cmds.sets(container, q=True) + ref_node = get_reference_node(roots) nodes_to_parent = [] for root in roots: + if ref_node: + ref_root = cmds.referenceQuery(root, nodes=True)[0] + ref_root = ( + cmds.listRelatives(ref_root, parent=True, path=True) or + [ref_root] + ) + nodes_to_parent.extend(ref_root) + continue if root.endswith("_RN"): + # Backwards compatibility for hardcoded reference names. refRoot = cmds.referenceQuery(root, n=True)[0] refRoot = cmds.listRelatives(refRoot, parent=True) or [refRoot] nodes_to_parent.extend(refRoot) @@ -287,10 +301,17 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin): matrix=True, worldSpace=True ) + scene_parent = cmds.listRelatives( + placeholder.scene_identifier, parent=True, fullPath=True + ) for node in set(nodes_to_parent): cmds.reorder(node, front=True) cmds.reorder(node, relative=placeholder.data["index"]) cmds.xform(node, matrix=placeholder_form, ws=True) + if scene_parent: + cmds.parent(node, scene_parent) + else: + cmds.parent(node, world=True) holding_sets = cmds.listSets(object=placeholder.scene_identifier) if not holding_sets: diff --git a/openpype/hosts/nuke/api/workfile_template_builder.py b/openpype/hosts/nuke/api/workfile_template_builder.py index 766fb0bc47..2384e8eca1 100644 --- a/openpype/hosts/nuke/api/workfile_template_builder.py +++ b/openpype/hosts/nuke/api/workfile_template_builder.py @@ -190,7 +190,7 @@ class NukePlaceholderLoadPlugin(NukePlaceholderPlugin, PlaceholderLoadMixin): def get_placeholder_options(self, options=None): return self.get_load_plugin_options(options) - def cleanup_placeholder(self, placeholder, failed): + def post_placeholder_process(self, placeholder, failed): # deselect all selected nodes placeholder_node = nuke.toNode(placeholder.scene_identifier) @@ -604,7 +604,7 @@ class NukePlaceholderCreatePlugin( def get_placeholder_options(self, options=None): return self.get_create_plugin_options(options) - def cleanup_placeholder(self, placeholder, failed): + def post_placeholder_process(self, placeholder, failed): # deselect all selected nodes placeholder_node = nuke.toNode(placeholder.scene_identifier) diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index e8519fbbc5..42483a302c 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -479,7 +479,9 @@ class AbstractTemplateBuilder(object): create_first_version = template_preset["create_first_version"] # check if first version is created - created_version_workfile = self.create_first_workfile_version() + created_version_workfile = False + if create_first_version: + created_version_workfile = self.create_first_workfile_version() # if first version is created, import template # and populate placeholders @@ -1567,7 +1569,16 @@ class PlaceholderLoadMixin(object): else: failed = False self.load_succeed(placeholder, container) - self.cleanup_placeholder(placeholder, failed) + self.post_placeholder_process(placeholder, failed) + + if failed: + self.log.debug( + "Placeholder cleanup skipped due to failed placeholder " + "population." + ) + return + if not placeholder.data.get("keep_placeholder", True): + self.delete_placeholder(placeholder) def load_failed(self, placeholder, representation): if hasattr(placeholder, "load_failed"): @@ -1577,7 +1588,7 @@ class PlaceholderLoadMixin(object): if hasattr(placeholder, "load_succeed"): placeholder.load_succeed(container) - def cleanup_placeholder(self, placeholder, failed): + def post_placeholder_process(self, placeholder, failed): """Cleanup placeholder after load of single representation. Can be called multiple times during placeholder item populating and is @@ -1591,6 +1602,10 @@ class PlaceholderLoadMixin(object): pass + def delete_placeholder(self, placeholder, failed): + """Called when all item population is done.""" + self.log.debug("Clean up of placeholder is not implemented.") + class PlaceholderCreateMixin(object): """Mixin prepared for creating placeholder plugins. @@ -1753,7 +1768,7 @@ class PlaceholderCreateMixin(object): failed = False self.create_succeed(placeholder, creator_instance) - self.cleanup_placeholder(placeholder, failed) + self.post_placeholder_process(placeholder, failed) def create_failed(self, placeholder, creator_data): if hasattr(placeholder, "create_failed"): @@ -1763,7 +1778,7 @@ class PlaceholderCreateMixin(object): if hasattr(placeholder, "create_succeed"): placeholder.create_succeed(creator_instance) - def cleanup_placeholder(self, placeholder, failed): + def post_placeholder_process(self, placeholder, failed): """Cleanup placeholder after load of single representation. Can be called multiple times during placeholder item populating and is