diff --git a/pype/harmony/__init__.py b/pype/harmony/__init__.py new file mode 100644 index 0000000000..b3edca7d15 --- /dev/null +++ b/pype/harmony/__init__.py @@ -0,0 +1,39 @@ +import os + +from avalon import api, harmony +import pyblish.api + + +def install(): + print("Installing Pype config...") + + plugins_directory = os.path.join( + os.path.dirname(os.path.dirname(__file__)), "plugins", "harmony" + ) + + pyblish.api.register_plugin_path( + os.path.join(plugins_directory, "publish") + ) + api.register_plugin_path( + api.Loader, os.path.join(plugins_directory, "load") + ) + api.register_plugin_path( + api.Creator, os.path.join(plugins_directory, "create") + ) + + pyblish.api.register_callback( + "instanceToggled", on_pyblish_instance_toggled + ) + + +def on_pyblish_instance_toggled(instance, old_value, new_value): + """Toggle node enabling on instance toggles.""" + func = """function func(args) + { + node.setEnable(args[0], args[1]) + } + func + """ + harmony.send( + {"function": func, "args": [instance[0], new_value]} + ) diff --git a/pype/plugins/harmony/create/create_render.py b/pype/plugins/harmony/create/create_render.py new file mode 100644 index 0000000000..c416bb6fff --- /dev/null +++ b/pype/plugins/harmony/create/create_render.py @@ -0,0 +1,26 @@ +from avalon import harmony + + +class CreateRender(harmony.Creator): + """Composite node for publishing renders.""" + + name = "renderDefault" + label = "Render" + family = "render" + families = "imagesequence" + node_type = "WRITE" + + def __init__(self, *args, **kwargs): + super(CreateRender, self).__init__(*args, **kwargs) + + def setup_node(self, node): + func = """function func(args) + { + node.setTextAttr(args[0], "DRAWING_TYPE", 1, "PNG4"); + node.setTextAttr(args[0], "DRAWING_NAME", 1, args[1]); + node.setTextAttr(args[0], "MOVIE_PATH", 1, args[1]); + } + func + """ + path = "{0}/{0}".format(node.split("/")[-1]) + harmony.send({"function": func, "args": [node, path]}) diff --git a/pype/plugins/harmony/publish/collect_instances.py b/pype/plugins/harmony/publish/collect_instances.py new file mode 100644 index 0000000000..76d735c28c --- /dev/null +++ b/pype/plugins/harmony/publish/collect_instances.py @@ -0,0 +1,47 @@ +import pyblish.api +from avalon import harmony + + +class CollectInstances(pyblish.api.ContextPlugin): + """Gather instances by nodes metadata. + + This collector takes into account assets that are associated with + a composite node and marked with a unique identifier; + + Identifier: + id (str): "pyblish.avalon.instance" + """ + + label = "Instances" + order = pyblish.api.CollectorOrder + hosts = ["harmony"] + + def process(self, context): + nodes = harmony.send( + {"function": "node.subNodes", "args": ["Top"]} + )["result"] + + for node in nodes: + data = harmony.read(node) + + # Skip non-tagged nodes. + if not data: + continue + + # Skip containers. + if "container" in data["id"]: + continue + + # Adding families if missing. + data["families"] = data.get("families", []) + + instance = context.create_instance(node.split("/")[-1]) + instance.append(node) + instance.data.update(data) + instance.data["publish"] = harmony.send( + {"function": "node.getEnable", "args": [node]} + )["result"] + + # Produce diagnostic message for any graphical + # user interface interested in visualising it. + self.log.info("Found: \"%s\" " % instance.data["name"]) diff --git a/pype/plugins/harmony/publish/extract_render.py b/pype/plugins/harmony/publish/extract_render.py new file mode 100644 index 0000000000..d24110da19 --- /dev/null +++ b/pype/plugins/harmony/publish/extract_render.py @@ -0,0 +1,82 @@ +import os +import tempfile + +import pyblish.api +from avalon import harmony +import pype.lib + +import clique + + +class ExtractRender(pyblish.api.InstancePlugin): + """Produce a flattened image file from instance. + This plug-in only takes into account the nodes connected to the composite. + """ + + label = "Extract Render" + order = pyblish.api.ExtractorOrder + hosts = ["harmony"] + families = ["render"] + + def process(self, instance): + # Collect scene data. + func = """function func(write_node) + { + return [ + about.getApplicationPath(), + scene.currentProjectPath(), + scene.currentScene() + ] + } + func + """ + result = harmony.send( + {"function": func, "args": [instance[0]]} + )["result"] + application_path = result[0] + project_path = result[1] + scene_path = os.path.join(result[1], result[2] + ".xstage") + + # Set output path to temp folder. + path = tempfile.mkdtemp() + func = """function func(args) + { + node.setTextAttr(args[0], "DRAWING_NAME", 1, args[1]); + scene.saveAll(); + } + func + """ + result = harmony.send( + { + "function": func, + "args": [instance[0], path + "/" + instance.data["name"]] + } + ) + + # Execute rendering. + output = pype.lib._subprocess([application_path, "-batch", scene_path]) + self.log.info(output) + + # Collect rendered files. + files = os.listdir(path) + collections, remainder = clique.assemble(files, minimum_items=1) + assert not remainder, ( + "There shouldn't have been a remainder for '%s': " + "%s" % (instance[0], remainder) + ) + assert len(collections) == 1, ( + "There should only be one image sequence in {}. Found: {}".format( + path, len(collections) + ) + ) + + extension = os.path.splitext(list(collections[0])[0])[-1][1:] + representation = { + "name": extension, + "ext": extension, + "files": list(collections[0]), + "stagingDir": path, + } + instance.data["representations"] = [representation] + + self.log.info("Extracted {instance} to {path}".format(**locals())) diff --git a/pype/plugins/harmony/publish/extract_save_scene.py b/pype/plugins/harmony/publish/extract_save_scene.py new file mode 100644 index 0000000000..9f1195c058 --- /dev/null +++ b/pype/plugins/harmony/publish/extract_save_scene.py @@ -0,0 +1,14 @@ +import pyblish.api +from avalon import harmony + + +class ExtractSaveScene(pyblish.api.ContextPlugin): + """Save the scene.""" + + label = "Extract Save Scene" + order = pyblish.api.ExtractorOrder - 0.49 + hosts = ["harmony"] + families = ["render"] + + def process(self, instance): + harmony.send({"function": "scene.saveAll"})