From 9ce470faa8b24a95ad9241a9841f927a785cdbe3 Mon Sep 17 00:00:00 2001 From: wijnand Date: Thu, 28 Jun 2018 11:17:26 +0200 Subject: [PATCH 01/32] added houdini folder --- colorbleed/houdini/__init__.py | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 colorbleed/houdini/__init__.py diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py new file mode 100644 index 0000000000..91a6df3edd --- /dev/null +++ b/colorbleed/houdini/__init__.py @@ -0,0 +1,69 @@ +import os + +import logging + +from avalon import api as avalon, pipeline, houdini +from pyblish import api as pyblish + + +PARENT_DIR = os.path.dirname(__file__) +PACKAGE_DIR = os.path.dirname(PARENT_DIR) +PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins") + +PUBLISH_PATH = os.path.join(PLUGINS_DIR, "houdini", "publish") +LOAD_PATH = os.path.join(PLUGINS_DIR, "houdini", "load") +CREATE_PATH = os.path.join(PLUGINS_DIR, "houdini", "create") + +log = logging.getLogger("colorbleed.houdini") + + +def install(): + pyblish.register_plugin_path(PUBLISH_PATH) + avalon.register_plugin_path(avalon.Loader, LOAD_PATH) + avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + + log.info("Installing callbacks ... ") + avalon.on("init", on_init) + avalon.on("save", on_save) + avalon.on("open", on_open) + + log.info("Overriding existing event 'taskChanged'") + + log.info("Setting default family states for loader..") + avalon.data["familiesStateToggled"] = ["colorbleed.imagesequence"] + + + +def on_init(): + pass + + +def on_save(): + pass + + +def on_open(): + pass + + +def on_task_changed(*args): + """Wrapped function of app initialize and maya's on task changed""" + + # Inputs (from the switched session and running app) + session = avalon.Session.copy() + app_name = os.environ["AVALON_APP_NAME"] + + # Find the application definition + app_definition = pipeline.lib.get_application(app_name) + + App = type("app_%s" % app_name, + (avalon.Application,), + {"config": app_definition.copy()}) + + # Initialize within the new session's environment + app = App() + env = app.environ(session) + app.initialize(env) + + # Run + houdini.pipeline._on_task_changed() \ No newline at end of file From f8b7860d28df5474acd933f775ebeeaf1ab2789d Mon Sep 17 00:00:00 2001 From: wijnand Date: Fri, 6 Jul 2018 09:35:56 +0200 Subject: [PATCH 02/32] added houdini plugins --- colorbleed/houdini/__init__.py | 21 +---------- .../houdini/create/create_pointcache.py | 35 +++++++++++++++++++ .../plugins/houdini/load/load_alembic.py | 15 ++++++++ 3 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 colorbleed/plugins/houdini/create/create_pointcache.py create mode 100644 colorbleed/plugins/houdini/load/load_alembic.py diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py index 91a6df3edd..8272f8622b 100644 --- a/colorbleed/houdini/__init__.py +++ b/colorbleed/houdini/__init__.py @@ -33,7 +33,6 @@ def install(): avalon.data["familiesStateToggled"] = ["colorbleed.imagesequence"] - def on_init(): pass @@ -48,22 +47,4 @@ def on_open(): def on_task_changed(*args): """Wrapped function of app initialize and maya's on task changed""" - - # Inputs (from the switched session and running app) - session = avalon.Session.copy() - app_name = os.environ["AVALON_APP_NAME"] - - # Find the application definition - app_definition = pipeline.lib.get_application(app_name) - - App = type("app_%s" % app_name, - (avalon.Application,), - {"config": app_definition.copy()}) - - # Initialize within the new session's environment - app = App() - env = app.environ(session) - app.initialize(env) - - # Run - houdini.pipeline._on_task_changed() \ No newline at end of file + pass \ No newline at end of file diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py new file mode 100644 index 0000000000..eedddc5fb7 --- /dev/null +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -0,0 +1,35 @@ +from collections import OrderedDict + +import avalon.api + + +class CreatePointCache(avalon.api.Creator): + """Alembic pointcache for animated data""" + + name = "pointcache" + label = "Point Cache" + family = "colorbleed.pointcache" + icon = "gears" + + def __init__(self, *args, **kwargs): + super(CreatePointCache, self).__init__(*args, **kwargs) + + # create an ordered dict with the existing data first + data = OrderedDict(**self.data) + + # get basic animation data : start / end / handles / steps + for key, value in lib.collect_animation_data().items(): + data[key] = value + + # Write vertex colors with the geometry. + data["writeColorSets"] = False + + # Include only renderable visible shapes. + # Skips locators and empty transforms + data["renderableOnly"] = False + + # Include only nodes that are visible at least once during the + # frame range. + data["visibleOnly"] = False + + self.data = data \ No newline at end of file diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py new file mode 100644 index 0000000000..ed2258373b --- /dev/null +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -0,0 +1,15 @@ +import avalon.api + + +class AbcLoader(avalon.api.Loader): + """Specific loader of Alembic for the avalon.animation family""" + + families = ["colorbleed.animation", "colorbleed.pointcache"] + label = "Reference animation" + representations = ["abc"] + order = -10 + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + print("Not implemented") \ No newline at end of file From 6bcdc6de4752bfabd1b60a07b35ceb4b6a27992f Mon Sep 17 00:00:00 2001 From: wijnand Date: Thu, 12 Jul 2018 17:47:49 +0200 Subject: [PATCH 03/32] Temp storage --- colorbleed/houdini/__init__.py | 3 ++- res/houdini/123.py | 5 ++++ res/houdini/MainMenuCommon.XML | 46 ++++++++++++++++++++++++++++++++++ res/houdini/houdini.env | 22 ++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 res/houdini/123.py create mode 100644 res/houdini/MainMenuCommon.XML create mode 100644 res/houdini/houdini.env diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py index 8272f8622b..f12720b395 100644 --- a/colorbleed/houdini/__init__.py +++ b/colorbleed/houdini/__init__.py @@ -2,7 +2,7 @@ import os import logging -from avalon import api as avalon, pipeline, houdini +from avalon import api as avalon # pipeline, houdini from pyblish import api as pyblish @@ -18,6 +18,7 @@ log = logging.getLogger("colorbleed.houdini") def install(): + pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) avalon.register_plugin_path(avalon.Creator, CREATE_PATH) diff --git a/res/houdini/123.py b/res/houdini/123.py new file mode 100644 index 0000000000..f47f185604 --- /dev/null +++ b/res/houdini/123.py @@ -0,0 +1,5 @@ +from colorbleed import houdini + +houdini.install() + +print("BANANA") \ No newline at end of file diff --git a/res/houdini/MainMenuCommon.XML b/res/houdini/MainMenuCommon.XML new file mode 100644 index 0000000000..37a058635b --- /dev/null +++ b/res/houdini/MainMenuCommon.XML @@ -0,0 +1,46 @@ + + + + + + + + + $HOME/scripts/first_script.py + + + + + $HOME/scripts/generic_script.py + -q -n camera + + + + + $HOME/scripts/generic_script.py + -q -n camera + + + + + + import hou + from avalon.tools import publish + + parent = hou.qt.mainWindow() + run = publish.show(parent) + + return True + + + + + + + + $HOME/scripts/generic_script.py + -q -n light + + + + diff --git a/res/houdini/houdini.env b/res/houdini/houdini.env new file mode 100644 index 0000000000..3645ebea8c --- /dev/null +++ b/res/houdini/houdini.env @@ -0,0 +1,22 @@ +# +# Houdini Environment Settings +# +# The contents of this file are read into the environment +# at startup. They will override any existing entries in +# the environment. +# +# The syntax is one entry per line as follows: +# VAR = VALUE +# +# Values may be quoted +# VAR = "VALUE" +# +# Values may be empty +# VAR = +# + +# Example: +# +# HOUDINI_NO_SPLASH = 1 + +HOUDINI_PATH = $COLORBLEED_CONFIG/res/houdini From 94c1d5def687d3f869b3cf90ad0e83f09bfd346b Mon Sep 17 00:00:00 2001 From: wijnand Date: Tue, 17 Jul 2018 09:46:06 +0200 Subject: [PATCH 04/32] removed default startup setting to avalon core --- res/houdini/123.py | 5 ---- res/houdini/MainMenuCommon.XML | 46 ---------------------------------- res/houdini/houdini.env | 22 ---------------- 3 files changed, 73 deletions(-) delete mode 100644 res/houdini/123.py delete mode 100644 res/houdini/MainMenuCommon.XML delete mode 100644 res/houdini/houdini.env diff --git a/res/houdini/123.py b/res/houdini/123.py deleted file mode 100644 index f47f185604..0000000000 --- a/res/houdini/123.py +++ /dev/null @@ -1,5 +0,0 @@ -from colorbleed import houdini - -houdini.install() - -print("BANANA") \ No newline at end of file diff --git a/res/houdini/MainMenuCommon.XML b/res/houdini/MainMenuCommon.XML deleted file mode 100644 index 37a058635b..0000000000 --- a/res/houdini/MainMenuCommon.XML +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - $HOME/scripts/first_script.py - - - - - $HOME/scripts/generic_script.py - -q -n camera - - - - - $HOME/scripts/generic_script.py - -q -n camera - - - - - - import hou - from avalon.tools import publish - - parent = hou.qt.mainWindow() - run = publish.show(parent) - - return True - - - - - - - - $HOME/scripts/generic_script.py - -q -n light - - - - diff --git a/res/houdini/houdini.env b/res/houdini/houdini.env deleted file mode 100644 index 3645ebea8c..0000000000 --- a/res/houdini/houdini.env +++ /dev/null @@ -1,22 +0,0 @@ -# -# Houdini Environment Settings -# -# The contents of this file are read into the environment -# at startup. They will override any existing entries in -# the environment. -# -# The syntax is one entry per line as follows: -# VAR = VALUE -# -# Values may be quoted -# VAR = "VALUE" -# -# Values may be empty -# VAR = -# - -# Example: -# -# HOUDINI_NO_SPLASH = 1 - -HOUDINI_PATH = $COLORBLEED_CONFIG/res/houdini From 901f0d688d00e20cd823c150f0a0e0744ab81726 Mon Sep 17 00:00:00 2001 From: wijnand Date: Tue, 17 Jul 2018 09:46:54 +0200 Subject: [PATCH 05/32] added install logic for houdini --- colorbleed/houdini/__init__.py | 62 ++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py index f12720b395..f70b2d6198 100644 --- a/colorbleed/houdini/__init__.py +++ b/colorbleed/houdini/__init__.py @@ -1,10 +1,20 @@ import os - import logging -from avalon import api as avalon # pipeline, houdini +import hou + from pyblish import api as pyblish +from avalon import api as avalon +from avalon.houdini import pipeline as houdini + +from colorbleed.houdini import lib + +from colorbleed.lib import ( + any_outdated, + update_task_from_path +) + PARENT_DIR = os.path.dirname(__file__) PACKAGE_DIR = os.path.dirname(PARENT_DIR) @@ -19,6 +29,8 @@ log = logging.getLogger("colorbleed.houdini") def install(): + # Set + pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) avalon.register_plugin_path(avalon.Creator, CREATE_PATH) @@ -34,16 +46,52 @@ def install(): avalon.data["familiesStateToggled"] = ["colorbleed.imagesequence"] -def on_init(): - pass +def on_init(_): + houdini.on_houdini_initialize() -def on_save(): - pass +def on_save(_): + + avalon.logger.info("Running callback on save..") + + update_task_from_path(hou.hipFile.path()) + + nodes = lib.get_id_required_nodes() + for node, new_id in lib.generate_ids(nodes): + lib.set_id(node, new_id, overwrite=False) def on_open(): - pass + + update_task_from_path(hou.hipFile.path()) + + if any_outdated(): + from avalon.vendor.Qt import QtWidgets + from ..widgets import popup + + log.warning("Scene has outdated content.") + + # Find maya main window + top_level_widgets = {w.objectName(): w for w in + QtWidgets.QApplication.topLevelWidgets()} + parent = top_level_widgets.get("MayaWindow", None) + + if parent is None: + log.info("Skipping outdated content pop-up " + "because Maya window can't be found.") + else: + + # Show outdated pop-up + def _on_show_inventory(): + import avalon.tools.cbsceneinventory as tool + tool.show(parent=parent) + + dialog = popup.Popup(parent=parent) + dialog.setWindowTitle("Maya scene has outdated content") + dialog.setMessage("There are outdated containers in " + "your Maya scene.") + dialog.on_show.connect(_on_show_inventory) + dialog.show() def on_task_changed(*args): From d2580fc963338edd5291760ec3779ee4ae6e7649 Mon Sep 17 00:00:00 2001 From: wijnand Date: Tue, 17 Jul 2018 09:47:15 +0200 Subject: [PATCH 06/32] added alembic loader --- .../plugins/houdini/load/load_alembic.py | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index ed2258373b..cbf76ace07 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -1,5 +1,9 @@ import avalon.api +from avalon.houdini import pipeline, lib +reload(pipeline) +reload(lib) + class AbcLoader(avalon.api.Loader): """Specific loader of Alembic for the avalon.animation family""" @@ -12,4 +16,68 @@ class AbcLoader(avalon.api.Loader): color = "orange" def load(self, context, name=None, namespace=None, data=None): - print("Not implemented") \ No newline at end of file + + import os + import hou + + # Format file name, Houdini only wants forward slashes + file_path = os.path.normpath(self.fname) + file_path = file_path.replace("\\", "/") + + # Get the root node + obj = hou.node("/obj") + + # Create a unique name + counter = 1 + namespace = namespace if namespace else context["asset"]["name"] + formatted = "{}_{}".format(namespace, name) if namespace else name + node_name = "{0}_{1:03d}".format(formatted, counter) + + children = lib.children_as_string(hou.node("/obj")) + while node_name in children: + counter += 1 + node_name = "{0}_{1:03d}".format(formatted, counter) + + # Create a new geo node + container = obj.createNode("geo", node_name=node_name) + + # Remove the file node, it only loads static meshes + node_path = "/obj/{}/file1".format(node_name) + file_node = hou.node(node_path) + file_node.destroy() + + # Create an alembic node (supports animation) + alembic = container.createNode("alembic", node_name=node_name) + alembic.setParms({"fileName": file_path}) + + # Add unpack node + unpack = container.createNode("unpack") + unpack.setInput(0, alembic) + unpack.setParms({"transfer_attributes": "path"}) + + # Set new position for unpack node else it gets cluttered + unpack.setPosition([0, -1]) + + # set unpack as display node + unpack.setDisplayFlag(True) + + null_node = container.createNode("null", + node_name="OUT_{}".format(name)) + null_node.setPosition([0, -2]) + null_node.setInput(0, unpack) + + nodes = [container, alembic, unpack, null_node] + + self[:] = nodes + + return pipeline.containerise(node_name, + namespace, + nodes, + context, + self.__class__.__name__) + + def update(self, container, representation): + pass + + def remove(self, container): + print(">>>", container["objectName"]) From 3739de1f150207a2c0fdfcef82c8634fb5c563fc Mon Sep 17 00:00:00 2001 From: wijnand Date: Tue, 17 Jul 2018 09:47:54 +0200 Subject: [PATCH 07/32] added point cache creator plugin --- .../houdini/create/create_pointcache.py | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index eedddc5fb7..b94e7575ff 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -1,9 +1,11 @@ from collections import OrderedDict -import avalon.api +import hou + +from avalon import houdini -class CreatePointCache(avalon.api.Creator): +class CreatePointCache(houdini.Creator): """Alembic pointcache for animated data""" name = "pointcache" @@ -17,19 +19,9 @@ class CreatePointCache(avalon.api.Creator): # create an ordered dict with the existing data first data = OrderedDict(**self.data) - # get basic animation data : start / end / handles / steps - for key, value in lib.collect_animation_data().items(): - data[key] = value + # Collect animation data for point cache exporting + start, end = hou.playbar.timelineRange() + data["startFrame"] = start + data["endFrame"] = end - # Write vertex colors with the geometry. - data["writeColorSets"] = False - - # Include only renderable visible shapes. - # Skips locators and empty transforms - data["renderableOnly"] = False - - # Include only nodes that are visible at least once during the - # frame range. - data["visibleOnly"] = False - - self.data = data \ No newline at end of file + self.data = data From b825b3a775bf727b6699ff647c11c133da1592ab Mon Sep 17 00:00:00 2001 From: wijnand Date: Tue, 17 Jul 2018 09:48:27 +0200 Subject: [PATCH 08/32] added lib and pyblish plugins --- colorbleed/houdini/lib.py | 93 +++++++++++++++++++ .../houdini/publish/collect_alembic_nodes.py | 9 ++ .../houdini/publish/collect_instances.py | 34 +++++++ 3 files changed, 136 insertions(+) create mode 100644 colorbleed/houdini/lib.py create mode 100644 colorbleed/plugins/houdini/publish/collect_alembic_nodes.py create mode 100644 colorbleed/plugins/houdini/publish/collect_instances.py diff --git a/colorbleed/houdini/lib.py b/colorbleed/houdini/lib.py new file mode 100644 index 0000000000..fbd190731c --- /dev/null +++ b/colorbleed/houdini/lib.py @@ -0,0 +1,93 @@ +import uuid + +import hou + +from avalon import io +from avalon.houdini import lib + + +def set_id(node, unique_id, overwrite=False): + + exists = node.parm("id") + if not exists: + lib.imprint(node, {"id": unique_id}) + + if not exists and overwrite: + node.setParm("id", unique_id) + + +def get_id(node): + """ + Get the `cbId` attribute of the given node + Args: + node (hou.Node): the name of the node to retrieve the attribute from + + Returns: + str + + """ + + if node is None: + return + + id = node.parm("id") + if node is None: + return + return id + + +def generate_ids(nodes, asset_id=None): + """Returns new unique ids for the given nodes. + + Note: This does not assign the new ids, it only generates the values. + + To assign new ids using this method: + >>> nodes = ["a", "b", "c"] + >>> for node, id in generate_ids(nodes): + >>> set_id(node, id) + + To also override any existing values (and assign regenerated ids): + >>> nodes = ["a", "b", "c"] + >>> for node, id in generate_ids(nodes): + >>> set_id(node, id, overwrite=True) + + Args: + nodes (list): List of nodes. + asset_id (str or bson.ObjectId): The database id for the *asset* to + generate for. When None provided the current asset in the + active session is used. + + Returns: + list: A list of (node, id) tuples. + + """ + + if asset_id is None: + # Get the asset ID from the database for the asset of current context + asset_data = io.find_one({"type": "asset", + "name": api.Session["AVALON_ASSET"]}, + projection={"_id": True}) + assert asset_data, "No current asset found in Session" + asset_id = asset_data['_id'] + + node_ids = [] + for node in nodes: + _, uid = str(uuid.uuid4()).rsplit("-", 1) + unique_id = "{}:{}".format(asset_id, uid) + node_ids.append((node, unique_id)) + + return node_ids + + +def get_id_required_nodes(): + + valid_types = ["geometry"] + nodes = {n for n in hou.node("/out").children() if + n.type().name() in valid_types} + + return list(nodes) + + +def get_additional_data(container): + """Not implemented yet!""" + pass \ No newline at end of file diff --git a/colorbleed/plugins/houdini/publish/collect_alembic_nodes.py b/colorbleed/plugins/houdini/publish/collect_alembic_nodes.py new file mode 100644 index 0000000000..dc0de42126 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/collect_alembic_nodes.py @@ -0,0 +1,9 @@ +import pyblish.api + + +class CollectAlembicNodes(pyblish.api.InstancePlugin): + + label = "Collect Alembic Nodes" + + def process(self, instance): + pass \ No newline at end of file diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py new file mode 100644 index 0000000000..8912611005 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -0,0 +1,34 @@ +import hou + +import pyblish.api + +from avalon.houdini import lib + + +class CollectInstances(pyblish.api.ContextPlugin): + + label = "Collect Instances" + order = pyblish.api.CollectorOrder + hosts = ["houdini"] + + def process(self, context): + + instances = [] + + nodes = hou.node("/out").children() + for node in nodes: + if node.parm("id"): + continue + + if not node.parm("id") != "pyblish.avalon.instance": + continue + + has_family = node.parm("family") + assert has_family, "'%s' is missing 'family'" % node.name() + + # TODO: Ensure not all data passes through! + data = lib.read(node) + instance = context.create_instance(data.get("name", node.name())) + instance[:] = [node] + + From 4653cabb8acb6c1feea7e57cbc7eec008e46f314 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 18 Jul 2018 11:04:53 +0200 Subject: [PATCH 09/32] added current file check --- .../houdini/publish/collect_current_file.py | 15 +++++++++++++++ .../plugins/houdini/publish/extract_alembic.py | 0 2 files changed, 15 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/collect_current_file.py create mode 100644 colorbleed/plugins/houdini/publish/extract_alembic.py diff --git a/colorbleed/plugins/houdini/publish/collect_current_file.py b/colorbleed/plugins/houdini/publish/collect_current_file.py new file mode 100644 index 0000000000..e8612bdc12 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/collect_current_file.py @@ -0,0 +1,15 @@ +import hou + +import pyblish.api + + +class CollectMayaCurrentFile(pyblish.api.ContextPlugin): + """Inject the current working file into context""" + + order = pyblish.api.CollectorOrder - 0.5 + label = "Houdini Current File" + hosts = ['houdini'] + + def process(self, context): + """Inject the current working file""" + context.data['currentFile'] = hou.hipFile.path() diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py new file mode 100644 index 0000000000..e69de29bb2 From 03c111f58da2cbbc7ec121d5f7cb27e890087b84 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 18 Jul 2018 11:09:44 +0200 Subject: [PATCH 10/32] added context function for attributes --- colorbleed/houdini/lib.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/colorbleed/houdini/lib.py b/colorbleed/houdini/lib.py index fbd190731c..c9ce643751 100644 --- a/colorbleed/houdini/lib.py +++ b/colorbleed/houdini/lib.py @@ -1,8 +1,10 @@ import uuid +from contextlib import contextmanager + import hou -from avalon import io +from avalon import api, io from avalon.houdini import lib @@ -81,7 +83,7 @@ def generate_ids(nodes, asset_id=None): def get_id_required_nodes(): - valid_types = ["geometry"] + valid_types = ["geometry", "geometry"] nodes = {n for n in hou.node("/out").children() if n.type().name() in valid_types} @@ -90,4 +92,22 @@ def get_id_required_nodes(): def get_additional_data(container): """Not implemented yet!""" - pass \ No newline at end of file + pass + + +@contextmanager +def attribute_values(node, data): + + previous_attrs = {key: node.parm(key).eval() for key in data.keys()} + print("before", previous_attrs) + try: + node.setParms(data) + during_attrs = {key: node.parm(key).eval() for key in data.keys()} + print("during", during_attrs) + yield + except Exception as exc: + print(exc) + pass + finally: + print("reset") + node.setParms(previous_attrs) From 59fe720e8c2ab8467202e145164b74fd3d47bb25 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 18 Jul 2018 11:10:39 +0200 Subject: [PATCH 11/32] updated plugins --- .../houdini/create/create_pointcache.py | 3 ++ .../houdini/publish/collect_instances.py | 43 +++++++++++++++++-- .../houdini/publish/extract_alembic.py | 35 +++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index b94e7575ff..93cd83a6b3 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -19,6 +19,9 @@ class CreatePointCache(houdini.Creator): # create an ordered dict with the existing data first data = OrderedDict(**self.data) + # Set node type to create for output + data["node_type"] = "alembic" + # Collect animation data for point cache exporting start, end = hou.playbar.timelineRange() data["startFrame"] = start diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py index 8912611005..2a9edb5667 100644 --- a/colorbleed/plugins/houdini/publish/collect_instances.py +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -6,6 +6,23 @@ from avalon.houdini import lib class CollectInstances(pyblish.api.ContextPlugin): + """Gather instances by all node in out graph and pre-defined attributes + + This collector takes into account assets that are associated with + an specific node and marked with a unique identifier; + + Identifier: + id (str): "pyblish.avalon.instance + + Specific node: + The specific node is important because it dictates in which way the subset + is being exported. + + alembic: will export Alembic file which supports cascading attributes + like 'cbId' and 'path' + geometry: Can export a wide range of file types, default out + + """ label = "Collect Instances" order = pyblish.api.CollectorOrder @@ -14,21 +31,39 @@ class CollectInstances(pyblish.api.ContextPlugin): def process(self, context): instances = [] + keys = ["active", "id", "family", "asset", "subset"] nodes = hou.node("/out").children() for node in nodes: - if node.parm("id"): + + if not node.parm("id"): continue - if not node.parm("id") != "pyblish.avalon.instance": + if node.parm("id").eval() != "pyblish.avalon.instance": continue - has_family = node.parm("family") + has_family = node.parm("family").eval() assert has_family, "'%s' is missing 'family'" % node.name() - # TODO: Ensure not all data passes through! data = lib.read(node) + + # temporarily translation of `active` to `publish` till issue has + # been resolved, https://github.com/pyblish/pyblish-base/issues/307 + if "active" in data: + data["publish"] = data["active"] + instance = context.create_instance(data.get("name", node.name())) + instance[:] = [node] + instance.data.update(data) + instances.append(instance) + def sort_by_family(instance): + """Sort by family""" + return instance.data.get("families", instance.data.get("family")) + + # Sort/grouped by family (preserving local index) + context[:] = sorted(context, key=sort_by_family) + + return context diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py index e69de29bb2..d427a747fb 100644 --- a/colorbleed/plugins/houdini/publish/extract_alembic.py +++ b/colorbleed/plugins/houdini/publish/extract_alembic.py @@ -0,0 +1,35 @@ +import os + +import hou + +import pyblish.api +import colorbleed.api +from colorbleed.houdini import lib + + +class ExtractAlembic(colorbleed.api.Extractor): + + order = pyblish.api.ExtractorOrder + label = "Extract Pointcache (Alembic)" + hosts = ["houdini"] + families = ["colorbleed.pointcache"] + + def process(self, instance): + + staging_dir = self.staging_dir(instance) + + file_name = "{}.abc".format(instance.data["subset"]) + tmp_filepath = os.path.join(staging_dir, file_name) + + ropnode = instance[0] + attributes = {"trange": 1, + "f1": instance.data["startFrame"], + "f2": instance.data["endFrame"]} + + with lib.attribute_values(ropnode, attributes): + ropnode.execute() + + if "files" not in instance.data: + instance.data["files"] = [] + + instance.data["files"].append(tmp_filepath) From f658e3faac0f650f198c2a3dfef17fbd58f85e0c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 10 Sep 2018 16:19:29 +0200 Subject: [PATCH 12/32] Added get_project_data, update get_project_fps function --- colorbleed/lib.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index 1297aba606..45bb569af4 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -257,16 +257,33 @@ def get_project_fps(): Returns: int, float + + """ + + data = get_project_data() + fps = data.get("fps", 25.0) + + return fps + + +def get_project_data(): + """Get the data of the current project + + The data of the project can contain things like: + resolution + fps + renderer + + Returns: + dict: + """ project_name = io.active_project() project = io.find_one({"name": project_name, "type": "project"}, - projection={"config": True}) + projection={"data": True}) - config = project.get("config", None) - assert config, "This is a bug" + data = project.get("data", {}) - fps = config.get("fps", 25.0) - - return fps + return data From 9258d860dac295ed3bb0cd63bede7862925a1b62 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 10 Sep 2018 16:24:04 +0200 Subject: [PATCH 13/32] Added set_project_settings and set_scene_resolution functions --- colorbleed/maya/lib.py | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index bb41b4a738..5371f5abc6 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1371,6 +1371,7 @@ def get_id_from_history(node): return _id +# Project settings def set_scene_fps(fps, update=True): """Set FPS from project configuration @@ -1399,6 +1400,58 @@ def set_scene_fps(fps, update=True): cmds.file(modified=True) +def set_scene_resolution(resolution, renderer): + """Set the render resolution + + Args: + resolution(str): x, eg: '1920x1080' + renderer(str): name of the curren renderer; vray / redshift / arnold + + Returns: + bool: True if successful + + """ + + control_node = "defaultResolution" + cmds.setAttr("defaultRenderGlobals.currentRenderer", + renderer, + type="string") + + width, height = resolution.split("x") + + # Give VRay a helping hand as it is slightly different from the rest + if renderer == "vray": + control_node = "vraySettings" + if not cmds.objExists(type=control_node): + cmds.createNode("VRaySettingsNode", name=control_node) + + cmds.setAttr("%s.width" % control_node, int(width)) + cmds.setAttr("%s.height" % control_node, int(height)) + + log.info("Set project resolution to: %s" % resolution) + + return True + + +def set_project_settings(): + """Apply the project settings from the project definition + + Returns: + None + """ + + # Todo (Wijnand): apply renderer and resolution of project + + # Get project settings + data = lib.get_project_data() + fps = data.get("fps", None) + + if fps is None: + return + + set_scene_fps(fps) + + # Valid FPS def validate_fps(): """Validate current scene FPS and show pop-up when it is incorrect From 913f53116966b378297ef661a63c12d7d2aa40ca Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 10 Sep 2018 16:44:04 +0200 Subject: [PATCH 14/32] added on_new callback --- colorbleed/maya/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/colorbleed/maya/__init__.py b/colorbleed/maya/__init__.py index 74ecebba1f..1ce509f170 100644 --- a/colorbleed/maya/__init__.py +++ b/colorbleed/maya/__init__.py @@ -39,6 +39,8 @@ def install(): avalon.before("save", on_before_save) + avalon.on("new", on_new) + log.info("Overriding existing event 'taskChanged'") override_event("taskChanged", on_task_changed) @@ -158,6 +160,13 @@ def on_open(_): dialog.show() +def on_new(_): + """Set project resolution and fps when create a new file""" + + with maya.suspended_refresh(): + lib.set_project_settings() + + def on_task_changed(*args): """Wrapped function of app initialize and maya's on task changed""" From 2637a3ea1d938d5d6d9727195582b1516c1c0acc Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 10 Sep 2018 17:45:26 +0200 Subject: [PATCH 15/32] Refactored function, added asset override and fallback --- colorbleed/maya/lib.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 5371f5abc6..d1649fa977 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1433,9 +1433,17 @@ def set_scene_resolution(resolution, renderer): return True -def set_project_settings(): +def set_context_settings(): """Apply the project settings from the project definition + Settings can be overwritten by an asset if the asset.data contains + any information regarding those settings. + + Examples of settings: + fps + resolution + renderer + Returns: None """ @@ -1444,8 +1452,7 @@ def set_project_settings(): # Get project settings data = lib.get_project_data() - fps = data.get("fps", None) - + fps = lib.get_asset_fps() or data.get("fps", None) if fps is None: return From 3c2e536f22436d3f2c914684c86f939cb6a0f4c9 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 10 Sep 2018 17:45:50 +0200 Subject: [PATCH 16/32] Added get_asset_data and fps --- colorbleed/lib.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index 45bb569af4..ccc2369a86 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -287,3 +287,22 @@ def get_project_data(): data = project.get("data", {}) return data + + +def get_asset_data(): + + asset_name = avalon.api.Session["AVALON_ASSET"] + document = io.find_one({"name": asset_name, + "type": "asset"}) + + data = document.get("data", {}) + + return data + + +def get_asset_fps(): + + data = get_asset_data() + fps = data.get("fps", None) + + return fps From a70e3012b188afb0df6c0df65412dbb4ac82d34e Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 10 Sep 2018 17:46:23 +0200 Subject: [PATCH 17/32] Refactored function name --- colorbleed/maya/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/maya/__init__.py b/colorbleed/maya/__init__.py index 1ce509f170..5463cb0097 100644 --- a/colorbleed/maya/__init__.py +++ b/colorbleed/maya/__init__.py @@ -162,9 +162,9 @@ def on_open(_): def on_new(_): """Set project resolution and fps when create a new file""" - + avalon.logger.info("Running callback on new..") with maya.suspended_refresh(): - lib.set_project_settings() + lib.set_context_settings() def on_task_changed(*args): From 5ede6c44db88009720ada4f19460c3a9b0ea8889 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 11 Sep 2018 10:46:00 +0200 Subject: [PATCH 18/32] Removed create node for performance, cosmetics for readability --- colorbleed/lib.py | 18 ++++++++++++++++-- colorbleed/maya/lib.py | 9 ++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index ccc2369a86..e37a41eec3 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -289,9 +289,17 @@ def get_project_data(): return data -def get_asset_data(): +def get_asset_data(asset=None): + """Get the data from the current asset + + Args: + asset(str, Optional): name of the asset, eg: + + Returns: + dict + """ - asset_name = avalon.api.Session["AVALON_ASSET"] + asset_name = asset or avalon.api.Session["AVALON_ASSET"] document = io.find_one({"name": asset_name, "type": "asset"}) @@ -301,6 +309,12 @@ def get_asset_data(): def get_asset_fps(): + """Return the FPS from the asset data if found else None + + Returns: + int, float, None + + """ data = get_asset_data() fps = data.get("fps", None) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index d1649fa977..1acdf4092c 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1423,7 +1423,9 @@ def set_scene_resolution(resolution, renderer): if renderer == "vray": control_node = "vraySettings" if not cmds.objExists(type=control_node): - cmds.createNode("VRaySettingsNode", name=control_node) + log.error("Cannot set resolution because there is no node named:" + "`%s`" % control_node) + return cmds.setAttr("%s.width" % control_node, int(width)) cmds.setAttr("%s.height" % control_node, int(height)) @@ -1451,8 +1453,9 @@ def set_context_settings(): # Todo (Wijnand): apply renderer and resolution of project # Get project settings - data = lib.get_project_data() - fps = lib.get_asset_fps() or data.get("fps", None) + project_data = lib.get_project_data() + asset_data = lib.get_asset_data() + fps = asset_data.get("fps", project_data.get("fps", None)) if fps is None: return From 1c0ce2043363ddc47226c8e988efcaca42ac7165 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 11 Sep 2018 12:07:45 +0200 Subject: [PATCH 19/32] Removed double value, added rounding float, updated set_scene_resolution --- colorbleed/maya/lib.py | 54 +++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 1acdf4092c..b641ac8019 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -90,7 +90,7 @@ _alembic_options = { } INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000} -FLOAT_FPS = {23.976, 29.97, 29.97, 47.952, 59.94} +FLOAT_FPS = {23.976, 29.97, 47.952, 59.94} def matrix_equals(a, b, tolerance=1e-10): @@ -1385,7 +1385,8 @@ def set_scene_fps(fps, update=True): """ if fps in FLOAT_FPS: - unit = "{:f}fps".format(fps) + # :.2f means . + unit = "{:.2f}fps".format(fps) elif fps in INT_FPS: unit = "{:d}fps".format(int(fps)) @@ -1400,39 +1401,33 @@ def set_scene_fps(fps, update=True): cmds.file(modified=True) -def set_scene_resolution(resolution, renderer): +def set_scene_resolution(width, height): """Set the render resolution Args: - resolution(str): x, eg: '1920x1080' - renderer(str): name of the curren renderer; vray / redshift / arnold + width(int): value of the width + height(int): value of the height Returns: - bool: True if successful + None """ control_node = "defaultResolution" - cmds.setAttr("defaultRenderGlobals.currentRenderer", - renderer, - type="string") - - width, height = resolution.split("x") + current_renderer = cmds.getAttr("defaultRenderGlobals.currentRenderer") # Give VRay a helping hand as it is slightly different from the rest - if renderer == "vray": - control_node = "vraySettings" - if not cmds.objExists(type=control_node): - log.error("Cannot set resolution because there is no node named:" - "`%s`" % control_node) - return + if current_renderer == "vray": + vray_node = "vraySettings" + if cmds.objExists(vray_node): + control_node = vray_node + else: + log.error("Can't set VRay resolution because there is no node " + "named: `%s`" % vray_node) - cmds.setAttr("%s.width" % control_node, int(width)) - cmds.setAttr("%s.height" % control_node, int(height)) - - log.info("Set project resolution to: %s" % resolution) - - return True + log.info("Setting project resolution to: %s x %s" % (width, height)) + cmds.setAttr("%s.width" % control_node, width) + cmds.setAttr("%s.height" % control_node, height) def set_context_settings(): @@ -1452,15 +1447,24 @@ def set_context_settings(): # Todo (Wijnand): apply renderer and resolution of project - # Get project settings project_data = lib.get_project_data() asset_data = lib.get_asset_data() + + # Set project fps fps = asset_data.get("fps", project_data.get("fps", None)) if fps is None: return - set_scene_fps(fps) + # Set project resolution + width_key = "resolution_width" + height_key = "resolution_height" + + width = asset_data.get(width_key, project_data.get(width_key, 1920)) + height = asset_data.get(height_key, project_data.get(height_key, 1080)) + + set_scene_resolution(width, height) + # Valid FPS def validate_fps(): From 1779fccafcbef32e06962d31e96d8279af8643e2 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 11 Sep 2018 13:07:50 +0200 Subject: [PATCH 20/32] resolved feedback: simplified format --- colorbleed/maya/lib.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index b641ac8019..66f06172dc 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1385,11 +1385,10 @@ def set_scene_fps(fps, update=True): """ if fps in FLOAT_FPS: - # :.2f means . - unit = "{:.2f}fps".format(fps) + unit = "{}fps".format(fps) elif fps in INT_FPS: - unit = "{:d}fps".format(int(fps)) + unit = "{}fps".format(int(fps)) else: raise ValueError("Unsupported FPS value: `%s`" % fps) From a99873d6d37c3d5cae09ed0c281f44a703b34385 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 11 Sep 2018 13:58:13 +0200 Subject: [PATCH 21/32] Updated validate function to check asset fps too --- colorbleed/maya/lib.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 66f06172dc..59ea54c20d 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1450,9 +1450,7 @@ def set_context_settings(): asset_data = lib.get_asset_data() # Set project fps - fps = asset_data.get("fps", project_data.get("fps", None)) - if fps is None: - return + fps = asset_data.get("fps", project_data.get("fps", 25)) set_scene_fps(fps) # Set project resolution @@ -1474,7 +1472,8 @@ def validate_fps(): """ - fps = lib.get_project_fps() # can be int or float + asset_data = lib.get_asset_data() + fps = asset_data.get("fps", lib.get_project_fps()) # can be int or float current_fps = mel.eval('currentTimeUnitToFPS()') # returns float if current_fps != fps: From 389a9d9ab86cfd7bb673480c1e36e8aecbad504a Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 11 Sep 2018 13:58:28 +0200 Subject: [PATCH 22/32] Removed redundant function --- colorbleed/lib.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index e37a41eec3..119dcd4c25 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -306,17 +306,3 @@ def get_asset_data(asset=None): data = document.get("data", {}) return data - - -def get_asset_fps(): - """Return the FPS from the asset data if found else None - - Returns: - int, float, None - - """ - - data = get_asset_data() - fps = data.get("fps", None) - - return fps From 2234fcabd31b779b8e5651ad3edccf2279fdad9a Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 11:56:21 +0200 Subject: [PATCH 23/32] Added update and remove function --- .../plugins/houdini/load/load_alembic.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index cbf76ace07..d17ad692df 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -1,11 +1,11 @@ -import avalon.api +from avalon import api from avalon.houdini import pipeline, lib reload(pipeline) reload(lib) -class AbcLoader(avalon.api.Loader): +class AbcLoader(api.Loader): """Specific loader of Alembic for the avalon.animation family""" families = ["colorbleed.animation", "colorbleed.pointcache"] @@ -43,8 +43,7 @@ class AbcLoader(avalon.api.Loader): # Remove the file node, it only loads static meshes node_path = "/obj/{}/file1".format(node_name) - file_node = hou.node(node_path) - file_node.destroy() + hou.node(node_path) # Create an alembic node (supports animation) alembic = container.createNode("alembic", node_name=node_name) @@ -77,7 +76,25 @@ class AbcLoader(avalon.api.Loader): self.__class__.__name__) def update(self, container, representation): - pass + + node = container["node"] + try: + alembic_node = next(n for n in node.children() if + n.type().name() == "alembic") + except StopIteration: + self.log.error("Could not find node of type `alembic`") + return + + # Update the file path + file_path = api.get_representation_path(representation) + file_path = file_path.replace("\\", "/") + + alembic_node.setParms({"fileName": file_path}) + + # Update attribute + node.setParms({"representation": str(representation["_id"])}) def remove(self, container): - print(">>>", container["objectName"]) + + node = container["node"] + node.destroy() From 5aa46689af6895e061253137229158e12e78c917 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 11:57:25 +0200 Subject: [PATCH 24/32] removed redundant reloads --- colorbleed/plugins/houdini/load/load_alembic.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index d17ad692df..f3fcc3ad37 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -1,8 +1,6 @@ from avalon import api from avalon.houdini import pipeline, lib -reload(pipeline) -reload(lib) class AbcLoader(api.Loader): From f7d8642718b75d5e866a3cf4c873ecb99157d135 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 11:58:25 +0200 Subject: [PATCH 25/32] removed unused variable --- colorbleed/plugins/houdini/publish/collect_instances.py | 1 - 1 file changed, 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py index 2a9edb5667..5f77b9d805 100644 --- a/colorbleed/plugins/houdini/publish/collect_instances.py +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -31,7 +31,6 @@ class CollectInstances(pyblish.api.ContextPlugin): def process(self, context): instances = [] - keys = ["active", "id", "family", "asset", "subset"] nodes = hou.node("/out").children() for node in nodes: From 5da32ff95f67ea2b7f02e71b10c57d8e8e7a00f0 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:25:39 +0200 Subject: [PATCH 26/32] Added validator to check for create intermediate dir toggle --- .../publish/validate_mkpaths_toggled.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py diff --git a/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py b/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py new file mode 100644 index 0000000000..8dcc6e0509 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py @@ -0,0 +1,38 @@ +import pyblish.api +import colorbleed.api + + +class ValidatIntermediateDirectoriesChecked(pyblish.api.InstancePlugin): + """Validate if node attribute Create intermediate Directories is turned on + + Rules: + * The node must have Create intermediate Directories turned on to + ensure the output file will be created + + """ + + order = colorbleed.api.ValidateContentsOrder + families = ['colorbleed.pointcache'] + hosts = ['houdini'] + label = 'Create Intermediate Directories Checked' + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Found ROP nodes with Create Intermediate " + "Directories turned off") + + @classmethod + def get_invalid(cls, instance): + + result = [] + + for node in instance[:]: + if node.parm("mkpath").eval() != 1: + cls.log.error("Invalid settings found on `%s`" % node.path()) + result.append(node.path()) + + return result + + From 28067766d5f15f2c131aea124267bb0563ce5f32 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:26:14 +0200 Subject: [PATCH 27/32] Updated extract plugin --- .../plugins/houdini/publish/extract_alembic.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py index d427a747fb..098020b905 100644 --- a/colorbleed/plugins/houdini/publish/extract_alembic.py +++ b/colorbleed/plugins/houdini/publish/extract_alembic.py @@ -1,7 +1,5 @@ import os -import hou - import pyblish.api import colorbleed.api from colorbleed.houdini import lib @@ -21,15 +19,17 @@ class ExtractAlembic(colorbleed.api.Extractor): file_name = "{}.abc".format(instance.data["subset"]) tmp_filepath = os.path.join(staging_dir, file_name) + start_frame = float(instance.data["startFrame"]) + end_frame = float(instance.data["endFrame"]) + ropnode = instance[0] - attributes = {"trange": 1, - "f1": instance.data["startFrame"], - "f2": instance.data["endFrame"]} + attributes = {"filename": tmp_filepath, + "trange": 2} with lib.attribute_values(ropnode, attributes): - ropnode.execute() + ropnode.render(frame_range=(start_frame, end_frame, 1)) if "files" not in instance.data: instance.data["files"] = [] - instance.data["files"].append(tmp_filepath) + instance.data["files"].append(file_name) From 1edaad61b5b812618cad503b271ebd17e25e78f3 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:27:31 +0200 Subject: [PATCH 28/32] Added validator for outnode --- .../publish/validate_outnode_exists.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/validate_outnode_exists.py diff --git a/colorbleed/plugins/houdini/publish/validate_outnode_exists.py b/colorbleed/plugins/houdini/publish/validate_outnode_exists.py new file mode 100644 index 0000000000..479579a8f0 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/validate_outnode_exists.py @@ -0,0 +1,41 @@ +import pyblish.api +import colorbleed.api + + +class ValidatOutputNodeExists(pyblish.api.InstancePlugin): + """Validate if node attribute Create intermediate Directories is turned on + + Rules: + * The node must have Create intermediate Directories turned on to + ensure the output file will be created + + """ + + order = colorbleed.api.ValidateContentsOrder + families = ["*"] + hosts = ['houdini'] + label = "Output Node Exists" + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Could not find output node(s)!") + + @classmethod + def get_invalid(cls, instance): + + import hou + + result = set() + + node = instance[0] + sop_path = node.parm("sop_path").eval() + if not sop_path.endswith("OUT"): + cls.log.error("SOP Path does not end path at output node") + result.add(node.path()) + + if hou.node(sop_path) is None: + cls.log.error("Node at '%s' does not exist" % sop_path) + result.add(node.path()) + + return result From cd5c41942434ac5efba5301b04332ea178704355 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:28:30 +0200 Subject: [PATCH 29/32] Fix get_additional_data to make items visible in scene inventory --- colorbleed/houdini/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/houdini/lib.py b/colorbleed/houdini/lib.py index c9ce643751..0a0ba4b18d 100644 --- a/colorbleed/houdini/lib.py +++ b/colorbleed/houdini/lib.py @@ -92,7 +92,7 @@ def get_id_required_nodes(): def get_additional_data(container): """Not implemented yet!""" - pass + return container @contextmanager From 251f28190998340c7b683cea3acb24d244caf7a6 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:29:57 +0200 Subject: [PATCH 30/32] Removed debug prints, removed double value --- colorbleed/houdini/lib.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/colorbleed/houdini/lib.py b/colorbleed/houdini/lib.py index 0a0ba4b18d..cf526ba2b9 100644 --- a/colorbleed/houdini/lib.py +++ b/colorbleed/houdini/lib.py @@ -83,7 +83,7 @@ def generate_ids(nodes, asset_id=None): def get_id_required_nodes(): - valid_types = ["geometry", "geometry"] + valid_types = ["geometry"] nodes = {n for n in hou.node("/out").children() if n.type().name() in valid_types} @@ -99,15 +99,10 @@ def get_additional_data(container): def attribute_values(node, data): previous_attrs = {key: node.parm(key).eval() for key in data.keys()} - print("before", previous_attrs) try: node.setParms(data) - during_attrs = {key: node.parm(key).eval() for key in data.keys()} - print("during", during_attrs) yield except Exception as exc: - print(exc) pass finally: - print("reset") node.setParms(previous_attrs) From 7f55d394e468b33decd336d86426f8825799861c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:37:44 +0200 Subject: [PATCH 31/32] Remove print --- colorbleed/lib.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index 8e1d0becc6..73bd80f9ff 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -242,8 +242,6 @@ def collect_container_metadata(container): hostlib = importlib.import_module(package_name) if not hasattr(hostlib, "get_additional_data"): - print("{} has no function called " - "get_additional_data".format(package_name)) return {} return hostlib.get_additional_data(container) From 7616158dabf5ca2e03d5e2e1060767e315da5241 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 12 Sep 2018 15:38:17 +0200 Subject: [PATCH 32/32] Renamed plugin --- colorbleed/plugins/houdini/load/load_alembic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index f3fcc3ad37..5043ba5a0d 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -7,7 +7,7 @@ class AbcLoader(api.Loader): """Specific loader of Alembic for the avalon.animation family""" families = ["colorbleed.animation", "colorbleed.pointcache"] - label = "Reference animation" + label = "Load Animation" representations = ["abc"] order = -10 icon = "code-fork"