diff --git a/openpype/hosts/nuke/plugins/create/create_write_still.py b/openpype/hosts/nuke/plugins/create/create_write_still.py new file mode 100644 index 0000000000..1178928652 --- /dev/null +++ b/openpype/hosts/nuke/plugins/create/create_write_still.py @@ -0,0 +1,142 @@ +from collections import OrderedDict +from openpype.hosts.nuke.api import ( + plugin, + lib) +import nuke + + +class CreateWriteStill(plugin.PypeCreator): + # change this to template preset + name = "WriteStillFrame" + label = "Create Write Still Image" + hosts = ["nuke"] + n_class = "Write" + family = "still" + icon = "image" + defaults = [ + "ImageFrame{:0>4}".format(nuke.frame()), + "MPFrame{:0>4}".format(nuke.frame()), + "LayoutFrame{:0>4}".format(nuke.frame()) + ] + + def __init__(self, *args, **kwargs): + super(CreateWriteStill, self).__init__(*args, **kwargs) + + data = OrderedDict() + + data["family"] = self.family + data["families"] = self.n_class + + for k, v in self.data.items(): + if k not in data.keys(): + data.update({k: v}) + + self.data = data + self.nodes = nuke.selectedNodes() + self.log.debug("_ self.data: '{}'".format(self.data)) + + def process(self): + + inputs = [] + outputs = [] + instance = nuke.toNode(self.data["subset"]) + selected_node = None + + # use selection + if (self.options or {}).get("useSelection"): + nodes = self.nodes + + if not (len(nodes) < 2): + msg = ("Select only one node. " + "The node you want to connect to, " + "or tick off `Use selection`") + self.log.error(msg) + nuke.message(msg) + return + + if len(nodes) == 0: + msg = ( + "No nodes selected. Please select a single node to connect" + " to or tick off `Use selection`" + ) + self.log.error(msg) + nuke.message(msg) + return + + selected_node = nodes[0] + inputs = [selected_node] + outputs = selected_node.dependent() + + if instance: + if (instance.name() in selected_node.name()): + selected_node = instance.dependencies()[0] + + # if node already exist + if instance: + # collect input / outputs + inputs = instance.dependencies() + outputs = instance.dependent() + selected_node = inputs[0] + # remove old one + nuke.delete(instance) + + # recreate new + write_data = { + "nodeclass": self.n_class, + "families": [self.family], + "avalon": self.data + } + + # add creator data + creator_data = {"creator": self.__class__.__name__} + self.data.update(creator_data) + write_data.update(creator_data) + + + self.log.info("Adding template path from plugin") + write_data.update({ + "fpath_template": ("{work}/renders/nuke/{subset}" + "/{subset}.{ext}")}) + + _prenodes = [ + { + "name": "FrameHold01", + "class": "FrameHold", + "knobs": [ + ("first_frame", nuke.frame()) + ], + "dependent": None + } + ] + + write_node = lib.create_write_node( + self.name, + write_data, + input=selected_node, + review=False, + prenodes=_prenodes, + farm=False, + linked_knobs=["channels", "___", "first", "last", "use_limit"]) + + # relinking to collected connections + for i, input in enumerate(inputs): + write_node.setInput(i, input) + + write_node.autoplace() + + for output in outputs: + output.setInput(0, write_node) + + # link frame hold to group node + write_node.begin() + for n in nuke.allNodes(): + # get write node + if n.Class() in "Write": + w_node = n + write_node.end() + + w_node["use_limit"].setValue(True) + w_node["first"].setValue(nuke.frame()) + w_node["last"].setValue(nuke.frame()) + + return write_node diff --git a/openpype/hosts/nuke/plugins/load/load_image.py b/openpype/hosts/nuke/plugins/load/load_image.py index 8bc266f01b..afd1a173b6 100644 --- a/openpype/hosts/nuke/plugins/load/load_image.py +++ b/openpype/hosts/nuke/plugins/load/load_image.py @@ -12,8 +12,8 @@ from openpype.hosts.nuke.api.lib import ( class LoadImage(api.Loader): """Load still image into Nuke""" - families = ["render", "source", "plate", "review", "image"] - representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd"] + families = ["render", "source", "plate", "review", "image", "still"] + representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd", "tiff"] label = "Load Image" order = -10 diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 49609f70e0..253fc5e6a3 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -17,7 +17,7 @@ class NukeRenderLocal(openpype.api.Extractor): order = pyblish.api.ExtractorOrder label = "Render Local" hosts = ["nuke"] - families = ["render.local", "prerender.local"] + families = ["render.local", "prerender.local", "still.local"] def process(self, instance): families = instance.data["families"] @@ -66,13 +66,23 @@ class NukeRenderLocal(openpype.api.Extractor): instance.data["representations"] = [] collected_frames = os.listdir(out_dir) - repre = { - 'name': ext, - 'ext': ext, - 'frameStart': "%0{}d".format(len(str(last_frame))) % first_frame, - 'files': collected_frames, - "stagingDir": out_dir - } + + if len(collected_frames) == 1: + repre = { + 'name': ext, + 'ext': ext, + 'files': collected_frames.pop(), + "stagingDir": out_dir + } + else: + repre = { + 'name': ext, + 'ext': ext, + 'frameStart': "%0{}d".format( + len(str(last_frame))) % first_frame, + 'files': collected_frames, + "stagingDir": out_dir + } instance.data["representations"].append(repre) self.log.info("Extracted instance '{0}' to: {1}".format( @@ -89,6 +99,9 @@ class NukeRenderLocal(openpype.api.Extractor): instance.data['family'] = 'prerender' families.remove('prerender.local') families.insert(0, "prerender") + elif "still.local" in families: + instance.data['family'] = 'still' + families.remove('still.local') instance.data["families"] = families collections, remainder = clique.assemble(collected_frames) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_writes.py b/openpype/hosts/nuke/plugins/publish/precollect_writes.py index 47189c31fc..4d9bf26457 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_writes.py @@ -64,7 +64,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): ) if [fm for fm in _families_test - if fm in ["render", "prerender"]]: + if fm in ["render", "prerender", "still"]]: if "representations" not in instance.data: instance.data["representations"] = list() @@ -100,7 +100,10 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): frame_start_str, frame_slate_str) collected_frames.insert(0, slate_frame) - representation['files'] = collected_frames + if collected_frames_len == 1: + representation['files'] = collected_frames.pop() + else: + representation['files'] = collected_frames instance.data["representations"].append(representation) except Exception: instance.data["representations"].append(representation) diff --git a/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py b/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py index 0c88014649..29faf867d2 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py +++ b/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py @@ -55,7 +55,7 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin): """ Validates file output. """ order = pyblish.api.ValidatorOrder + 0.1 - families = ["render", "prerender"] + families = ["render", "prerender", "still"] label = "Validate rendered frame" hosts = ["nuke", "nukestudio"] @@ -71,6 +71,9 @@ class ValidateRenderedFrames(pyblish.api.InstancePlugin): self.log.error(msg) raise ValidationException(msg) + if isinstance(repre["files"], str): + return + collections, remainder = clique.assemble(repre["files"]) self.log.info("collections: {}".format(str(collections))) self.log.info("remainder: {}".format(str(remainder))) diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 3bff3ff79c..13815d5dd5 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -86,6 +86,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "source", "matchmove", "image", + "still", "source", "assembly", "fbx", diff --git a/openpype/settings/defaults/project_anatomy/imageio.json b/openpype/settings/defaults/project_anatomy/imageio.json index fcebc876f5..38313a3d84 100644 --- a/openpype/settings/defaults/project_anatomy/imageio.json +++ b/openpype/settings/defaults/project_anatomy/imageio.json @@ -124,9 +124,47 @@ "value": "True" } ] + }, + { + "plugins": [ + "CreateWriteStill" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "name": "file_type", + "value": "tiff" + }, + { + "name": "datatype", + "value": "16 bit" + }, + { + "name": "compression", + "value": "Deflate" + }, + { + "name": "tile_color", + "value": "0x23ff00ff" + }, + { + "name": "channels", + "value": "rgb" + }, + { + "name": "colorspace", + "value": "sRGB" + }, + { + "name": "create_directories", + "value": "True" + } + ] } ], - "customNodes": [] + "customNodes": [ + + ] }, "regexInputs": { "inputs": [ diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index ac35349415..0ea6c47027 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -119,7 +119,8 @@ "render", "prerender", "review", - "image" + "image", + "still" ], "representations": [ "exr", @@ -127,7 +128,8 @@ "jpg", "jpeg", "png", - "psd" + "psd", + "tiff" ], "node_name_template": "{class_name}_{ext}" },