From 09f215504b4f72020e96711b368a5e51bd541ecb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 29 Jan 2022 08:26:55 +0100 Subject: [PATCH 1/3] Load Arnold .ass procedurals into Houdini --- .../hosts/houdini/plugins/load/load_ass.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 openpype/hosts/houdini/plugins/load/load_ass.py diff --git a/openpype/hosts/houdini/plugins/load/load_ass.py b/openpype/hosts/houdini/plugins/load/load_ass.py new file mode 100644 index 0000000000..47fd9262d7 --- /dev/null +++ b/openpype/hosts/houdini/plugins/load/load_ass.py @@ -0,0 +1,89 @@ +import os + +from avalon import api +from avalon.houdini import pipeline +import clique + + +class AssLoader(api.Loader): + """Load .ass with Arnold Procedural""" + + families = ["ass"] + label = "Load Arnold Procedural" + representations = ["ass"] + order = -10 + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + + import hou + + # 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 + procedural = obj.createNode("arnold::procedural", node_name=node_name) + procedural.setParms({"ar_filename": self.get_path(self.fname)}) + + nodes = [procedural] + self[:] = nodes + + return pipeline.containerise( + node_name, + namespace, + nodes, + context, + self.__class__.__name__, + suffix="", + ) + + def get_path(self, path): + + # Find all frames in the folder + ext = ".ass.gz" if path.endswith(".ass.gz") else ".ass" + folder = os.path.dirname(path) + frames = [f for f in os.listdir(folder) if f.endswith(ext)] + + # Get the collection of frames to detect frame padding + patterns = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble(frames, + minimum_items=1, + patterns=patterns) + assert len(collections) == 1 + assert not remainder + collection = collections[0] + + num_frames = len(collection.indexes) + if num_frames == 1: + # Return the input path without dynamic $F variable + result = path + else: + fname = "{}$F{}{}".format(collection.head, + collection.padding, + collection.tail) + result = os.path.join(folder, fname) + + # Format file name, Houdini only wants forward slashes + return os.path.normpath(result).replace("\\", "/") + + def update(self, container, representation): + + # Update the file path + file_path = api.get_representation_path(representation) + file_path = file_path.replace("\\", "/") + + procedural = container["node"] + procedural.setParms({"ar_filename": file_path}) + + # Update attribute + node.setParms({"representation": str(representation["_id"])}) + + def remove(self, container): + + node = container["node"] + node.destroy() From 29f68f366b233cdeca919622070b36bea1e67018 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 29 Jan 2022 08:32:11 +0100 Subject: [PATCH 2/3] Fix update path and refactor invalid variable name --- openpype/hosts/houdini/plugins/load/load_ass.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/load/load_ass.py b/openpype/hosts/houdini/plugins/load/load_ass.py index 47fd9262d7..71c3535ca0 100644 --- a/openpype/hosts/houdini/plugins/load/load_ass.py +++ b/openpype/hosts/houdini/plugins/load/load_ass.py @@ -78,10 +78,10 @@ class AssLoader(api.Loader): file_path = file_path.replace("\\", "/") procedural = container["node"] - procedural.setParms({"ar_filename": file_path}) + procedural.setParms({"ar_filename": self.get_path(file_path)}) # Update attribute - node.setParms({"representation": str(representation["_id"])}) + procedural.setParms({"representation": str(representation["_id"])}) def remove(self, container): From b1850aac035f3aa73a034643bbd686e70bf36e44 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 29 Jan 2022 08:43:01 +0100 Subject: [PATCH 3/3] Fix support for published files without frame numbers in name --- .../hosts/houdini/plugins/load/load_ass.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/load/load_ass.py b/openpype/hosts/houdini/plugins/load/load_ass.py index 71c3535ca0..8c272044ec 100644 --- a/openpype/hosts/houdini/plugins/load/load_ass.py +++ b/openpype/hosts/houdini/plugins/load/load_ass.py @@ -54,8 +54,21 @@ class AssLoader(api.Loader): collections, remainder = clique.assemble(frames, minimum_items=1, patterns=patterns) - assert len(collections) == 1 - assert not remainder + self.log.debug("Detected collections: {}".format(collections)) + self.log.debug("Detected remainder: {}".format(remainder)) + + if not collections and remainder: + if len(remainder) != 1: + raise ValueError("Frames not correctly detected " + "in: {}".format(remainder)) + + # A single frame without frame range detected + filepath = remainder[0] + return os.path.normpath(filepath).replace("\\", "/") + + # Frames detected with a valid "frame" number pattern + # Then we don't want to have any remainder files found + assert len(collections) == 1 and not remainder collection = collections[0] num_frames = len(collection.indexes) @@ -63,6 +76,7 @@ class AssLoader(api.Loader): # Return the input path without dynamic $F variable result = path else: + # More than a single frame detected - use $F{padding} fname = "{}$F{}{}".format(collection.head, collection.padding, collection.tail)