mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
rename folder
This commit is contained in:
parent
483c930a68
commit
8432e94615
1511 changed files with 0 additions and 0 deletions
53
openpype/hosts/nuke/plugins/create/create_backdrop.py
Normal file
53
openpype/hosts/nuke/plugins/create/create_backdrop.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
|
||||
|
||||
class CreateBackdrop(plugin.PypeCreator):
|
||||
"""Add Publishable Backdrop"""
|
||||
|
||||
name = "nukenodes"
|
||||
label = "Create Backdrop"
|
||||
family = "nukenodes"
|
||||
icon = "file-archive-o"
|
||||
defaults = ["Main"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateBackdrop, self).__init__(*args, **kwargs)
|
||||
self.nodes = nuke.selectedNodes()
|
||||
self.node_color = "0xdfea5dff"
|
||||
return
|
||||
|
||||
def process(self):
|
||||
from nukescripts import autoBackdrop
|
||||
nodes = list()
|
||||
if (self.options or {}).get("useSelection"):
|
||||
nodes = self.nodes
|
||||
|
||||
if len(nodes) >= 1:
|
||||
anlib.select_nodes(nodes)
|
||||
bckd_node = autoBackdrop()
|
||||
bckd_node["name"].setValue("{}_BDN".format(self.name))
|
||||
bckd_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(bckd_node, self.data)
|
||||
|
||||
return instance
|
||||
else:
|
||||
msg = str("Please select nodes you "
|
||||
"wish to add to a container")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
else:
|
||||
bckd_node = autoBackdrop()
|
||||
bckd_node["name"].setValue("{}_BDN".format(self.name))
|
||||
bckd_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(bckd_node, self.data)
|
||||
|
||||
return instance
|
||||
53
openpype/hosts/nuke/plugins/create/create_camera.py
Normal file
53
openpype/hosts/nuke/plugins/create/create_camera.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
|
||||
|
||||
class CreateCamera(plugin.PypeCreator):
|
||||
"""Add Publishable Backdrop"""
|
||||
|
||||
name = "camera"
|
||||
label = "Create 3d Camera"
|
||||
family = "camera"
|
||||
icon = "camera"
|
||||
defaults = ["Main"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateCamera, self).__init__(*args, **kwargs)
|
||||
self.nodes = nuke.selectedNodes()
|
||||
self.node_color = "0xff9100ff"
|
||||
return
|
||||
|
||||
def process(self):
|
||||
nodes = list()
|
||||
if (self.options or {}).get("useSelection"):
|
||||
nodes = self.nodes
|
||||
|
||||
if len(nodes) >= 1:
|
||||
# loop selected nodes
|
||||
for n in nodes:
|
||||
data = self.data.copy()
|
||||
if len(nodes) > 1:
|
||||
# rename subset name only if more
|
||||
# then one node are selected
|
||||
subset = self.family + n["name"].value().capitalize()
|
||||
data["subset"] = subset
|
||||
|
||||
# change node color
|
||||
n["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
anlib.set_avalon_knob_data(n, data)
|
||||
return True
|
||||
else:
|
||||
msg = str("Please select nodes you "
|
||||
"wish to add to a container")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
else:
|
||||
# if selected is off then create one node
|
||||
camera_node = nuke.createNode("Camera2")
|
||||
camera_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(camera_node, self.data)
|
||||
return instance
|
||||
83
openpype/hosts/nuke/plugins/create/create_gizmo.py
Normal file
83
openpype/hosts/nuke/plugins/create/create_gizmo.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
|
||||
|
||||
class CreateGizmo(plugin.PypeCreator):
|
||||
"""Add Publishable "gizmo" group
|
||||
|
||||
The name is symbolically gizmo as presumably
|
||||
it is something familiar to nuke users as group of nodes
|
||||
distributed downstream in workflow
|
||||
"""
|
||||
|
||||
name = "gizmo"
|
||||
label = "Gizmo"
|
||||
family = "gizmo"
|
||||
icon = "file-archive-o"
|
||||
defaults = ["ViewerInput", "Lut", "Effect"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateGizmo, self).__init__(*args, **kwargs)
|
||||
self.nodes = nuke.selectedNodes()
|
||||
self.node_color = "0x7533c1ff"
|
||||
return
|
||||
|
||||
def process(self):
|
||||
if (self.options or {}).get("useSelection"):
|
||||
nodes = self.nodes
|
||||
self.log.info(len(nodes))
|
||||
if len(nodes) == 1:
|
||||
anlib.select_nodes(nodes)
|
||||
node = nodes[-1]
|
||||
# check if Group node
|
||||
if node.Class() in "Group":
|
||||
node["name"].setValue("{}_GZM".format(self.name))
|
||||
node["tile_color"].setValue(int(self.node_color, 16))
|
||||
return anlib.set_avalon_knob_data(node, self.data)
|
||||
else:
|
||||
msg = ("Please select a group node "
|
||||
"you wish to publish as the gizmo")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
|
||||
if len(nodes) >= 2:
|
||||
anlib.select_nodes(nodes)
|
||||
nuke.makeGroup()
|
||||
gizmo_node = nuke.selectedNode()
|
||||
gizmo_node["name"].setValue("{}_GZM".format(self.name))
|
||||
gizmo_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
# add sticky node wit guide
|
||||
with gizmo_node:
|
||||
sticky = nuke.createNode("StickyNote")
|
||||
sticky["label"].setValue(
|
||||
"Add following:\n- set Input"
|
||||
" nodes\n- set one Output1\n"
|
||||
"- create User knobs on the group")
|
||||
|
||||
# add avalon knobs
|
||||
return anlib.set_avalon_knob_data(gizmo_node, self.data)
|
||||
|
||||
else:
|
||||
msg = ("Please select nodes you "
|
||||
"wish to add to the gizmo")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
else:
|
||||
with anlib.maintained_selection():
|
||||
gizmo_node = nuke.createNode("Group")
|
||||
gizmo_node["name"].setValue("{}_GZM".format(self.name))
|
||||
gizmo_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
# add sticky node wit guide
|
||||
with gizmo_node:
|
||||
sticky = nuke.createNode("StickyNote")
|
||||
sticky["label"].setValue(
|
||||
"Add following:\n- add Input"
|
||||
" nodes\n- add one Output1\n"
|
||||
"- create User knobs on the group")
|
||||
|
||||
# add avalon knobs
|
||||
return anlib.set_avalon_knob_data(gizmo_node, self.data)
|
||||
56
openpype/hosts/nuke/plugins/create/create_read.py
Normal file
56
openpype/hosts/nuke/plugins/create/create_read.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
from collections import OrderedDict
|
||||
import avalon.api
|
||||
import avalon.nuke
|
||||
from openpype import api as pype
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
|
||||
import nuke
|
||||
|
||||
|
||||
class CrateRead(plugin.PypeCreator):
|
||||
# change this to template preset
|
||||
name = "ReadCopy"
|
||||
label = "Create Read Copy"
|
||||
hosts = ["nuke"]
|
||||
family = "source"
|
||||
families = family
|
||||
icon = "film"
|
||||
defaults = ["Effect", "Backplate", "Fire", "Smoke"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CrateRead, self).__init__(*args, **kwargs)
|
||||
self.nodes = nuke.selectedNodes()
|
||||
data = OrderedDict()
|
||||
data['family'] = self.family
|
||||
data['families'] = self.families
|
||||
|
||||
for k, v in self.data.items():
|
||||
if k not in data.keys():
|
||||
data.update({k: v})
|
||||
|
||||
self.data = data
|
||||
|
||||
def process(self):
|
||||
self.name = self.data["subset"]
|
||||
nodes = self.nodes
|
||||
|
||||
if not nodes or len(nodes) == 0:
|
||||
msg = "Please select Read node"
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
else:
|
||||
count_reads = 0
|
||||
for node in nodes:
|
||||
if node.Class() != 'Read':
|
||||
continue
|
||||
avalon_data = self.data
|
||||
avalon_data['subset'] = "{}".format(self.name)
|
||||
avalon.nuke.lib.set_avalon_knob_data(node, avalon_data)
|
||||
node['tile_color'].setValue(16744935)
|
||||
count_reads += 1
|
||||
|
||||
if count_reads < 1:
|
||||
msg = "Please select Read node"
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
136
openpype/hosts/nuke/plugins/create/create_write_prerender.py
Normal file
136
openpype/hosts/nuke/plugins/create/create_write_prerender.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
from collections import OrderedDict
|
||||
from openpype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib)
|
||||
import nuke
|
||||
|
||||
|
||||
class CreateWritePrerender(plugin.PypeCreator):
|
||||
# change this to template preset
|
||||
name = "WritePrerender"
|
||||
label = "Create Write Prerender"
|
||||
hosts = ["nuke"]
|
||||
n_class = "Write"
|
||||
family = "prerender"
|
||||
icon = "sign-out"
|
||||
defaults = ["Key01", "Bg01", "Fg01", "Branch01", "Part01"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateWritePrerender, 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)
|
||||
|
||||
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)
|
||||
|
||||
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,
|
||||
"creator": self.__class__.__name__
|
||||
}
|
||||
|
||||
if self.presets.get('fpath_template'):
|
||||
self.log.info("Adding template path from preset")
|
||||
write_data.update(
|
||||
{"fpath_template": self.presets["fpath_template"]}
|
||||
)
|
||||
else:
|
||||
self.log.info("Adding template path from plugin")
|
||||
write_data.update({
|
||||
"fpath_template": ("{work}/prerenders/nuke/{subset}"
|
||||
"/{subset}.{frame}.{ext}")})
|
||||
|
||||
self.log.info("write_data: {}".format(write_data))
|
||||
|
||||
write_node = lib.create_write_node(
|
||||
self.data["subset"],
|
||||
write_data,
|
||||
input=selected_node,
|
||||
prenodes=[],
|
||||
review=False)
|
||||
|
||||
# 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)
|
||||
|
||||
# open group node
|
||||
write_node.begin()
|
||||
for n in nuke.allNodes():
|
||||
# get write node
|
||||
if n.Class() in "Write":
|
||||
w_node = n
|
||||
write_node.end()
|
||||
|
||||
# add inner write node Tab
|
||||
write_node.addKnob(nuke.Tab_Knob("WriteLinkedKnobs"))
|
||||
|
||||
# linking knobs to group property panel
|
||||
linking_knobs = ["channels", "___", "first", "last", "use_limit"]
|
||||
for k in linking_knobs:
|
||||
if "___" in k:
|
||||
write_node.addKnob(nuke.Text_Knob(''))
|
||||
else:
|
||||
lnk = nuke.Link_Knob(k)
|
||||
lnk.makeLink(w_node.name(), k)
|
||||
lnk.setName(k.replace('_', ' ').capitalize())
|
||||
lnk.clearFlag(nuke.STARTLINE)
|
||||
write_node.addKnob(lnk)
|
||||
|
||||
return write_node
|
||||
112
openpype/hosts/nuke/plugins/create/create_write_render.py
Normal file
112
openpype/hosts/nuke/plugins/create/create_write_render.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from collections import OrderedDict
|
||||
from openpype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib)
|
||||
import nuke
|
||||
|
||||
|
||||
class CreateWriteRender(plugin.PypeCreator):
|
||||
# change this to template preset
|
||||
name = "WriteRender"
|
||||
label = "Create Write Render"
|
||||
hosts = ["nuke"]
|
||||
n_class = "Write"
|
||||
family = "render"
|
||||
icon = "sign-out"
|
||||
defaults = ["Main", "Mask"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateWriteRender, 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,
|
||||
"creator": self.__class__.__name__
|
||||
}
|
||||
|
||||
if self.presets.get('fpath_template'):
|
||||
self.log.info("Adding template path from preset")
|
||||
write_data.update(
|
||||
{"fpath_template": self.presets["fpath_template"]}
|
||||
)
|
||||
else:
|
||||
self.log.info("Adding template path from plugin")
|
||||
write_data.update({
|
||||
"fpath_template": ("{work}/renders/nuke/{subset}"
|
||||
"/{subset}.{frame}.{ext}")})
|
||||
|
||||
write_node = lib.create_write_node(
|
||||
self.data["subset"],
|
||||
write_data,
|
||||
input=selected_node)
|
||||
|
||||
# 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)
|
||||
|
||||
return write_node
|
||||
21
openpype/hosts/nuke/plugins/inventory/select_containers.py
Normal file
21
openpype/hosts/nuke/plugins/inventory/select_containers.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from avalon import api
|
||||
|
||||
|
||||
class SelectContainers(api.InventoryAction):
|
||||
|
||||
label = "Select Containers"
|
||||
icon = "mouse-pointer"
|
||||
color = "#d8d8d8"
|
||||
|
||||
def process(self, containers):
|
||||
|
||||
import avalon.nuke
|
||||
|
||||
nodes = [i["_node"] for i in containers]
|
||||
|
||||
with avalon.nuke.viewer_update_and_undo_stop():
|
||||
# clear previous_selection
|
||||
[n['selected'].setValue(False) for n in nodes]
|
||||
# Select tool
|
||||
for node in nodes:
|
||||
node["selected"].setValue(True)
|
||||
68
openpype/hosts/nuke/plugins/inventory/set_tool_color.py
Normal file
68
openpype/hosts/nuke/plugins/inventory/set_tool_color.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# from avalon import api, style
|
||||
# from avalon.vendor.Qt import QtGui, QtWidgets
|
||||
#
|
||||
# import avalon.fusion
|
||||
#
|
||||
#
|
||||
# class FusionSetToolColor(api.InventoryAction):
|
||||
# """Update the color of the selected tools"""
|
||||
#
|
||||
# label = "Set Tool Color"
|
||||
# icon = "plus"
|
||||
# color = "#d8d8d8"
|
||||
# _fallback_color = QtGui.QColor(1.0, 1.0, 1.0)
|
||||
#
|
||||
# def process(self, containers):
|
||||
# """Color all selected tools the selected colors"""
|
||||
#
|
||||
# result = []
|
||||
# comp = avalon.fusion.get_current_comp()
|
||||
#
|
||||
# # Get tool color
|
||||
# first = containers[0]
|
||||
# tool = first["_node"]
|
||||
# color = tool.TileColor
|
||||
#
|
||||
# if color is not None:
|
||||
# qcolor = QtGui.QColor().fromRgbF(color["R"], color["G"], color["B"])
|
||||
# else:
|
||||
# qcolor = self._fallback_color
|
||||
#
|
||||
# # Launch pick color
|
||||
# picked_color = self.get_color_picker(qcolor)
|
||||
# if not picked_color:
|
||||
# return
|
||||
#
|
||||
# with avalon.fusion.comp_lock_and_undo_chunk(comp):
|
||||
# for container in containers:
|
||||
# # Convert color to RGB 0-1 floats
|
||||
# rgb_f = picked_color.getRgbF()
|
||||
# rgb_f_table = {"R": rgb_f[0], "G": rgb_f[1], "B": rgb_f[2]}
|
||||
#
|
||||
# # Update tool
|
||||
# tool = container["_node"]
|
||||
# tool.TileColor = rgb_f_table
|
||||
#
|
||||
# result.append(container)
|
||||
#
|
||||
# return result
|
||||
#
|
||||
# def get_color_picker(self, color):
|
||||
# """Launch color picker and return chosen color
|
||||
#
|
||||
# Args:
|
||||
# color(QtGui.QColor): Start color to display
|
||||
#
|
||||
# Returns:
|
||||
# QtGui.QColor
|
||||
#
|
||||
# """
|
||||
#
|
||||
# color_dialog = QtWidgets.QColorDialog(color)
|
||||
# color_dialog.setStyleSheet(style.load_stylesheet())
|
||||
#
|
||||
# accepted = color_dialog.exec_()
|
||||
# if not accepted:
|
||||
# return
|
||||
#
|
||||
# return color_dialog.selectedColor()
|
||||
80
openpype/hosts/nuke/plugins/load/actions.py
Normal file
80
openpype/hosts/nuke/plugins/load/actions.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
"""A module containing generic loader actions that will display in the Loader.
|
||||
|
||||
"""
|
||||
|
||||
from avalon import api
|
||||
from openpype.api import Logger
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
class SetFrameRangeLoader(api.Loader):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["animation",
|
||||
"camera",
|
||||
"write",
|
||||
"yeticache",
|
||||
"pointcache"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Set frame range"
|
||||
order = 11
|
||||
icon = "clock-o"
|
||||
color = "white"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from openpype.hosts.nuke.api import lib
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
||||
start = version_data.get("frameStart", None)
|
||||
end = version_data.get("frameEnd", None)
|
||||
|
||||
log.info("start: {}, end: {}".format(start, end))
|
||||
if start is None or end is None:
|
||||
log.info("Skipping setting frame range because start or "
|
||||
"end frame data is missing..")
|
||||
return
|
||||
|
||||
lib.update_frame_range(start, end)
|
||||
|
||||
|
||||
class SetFrameRangeWithHandlesLoader(api.Loader):
|
||||
"""Specific loader of Alembic for the avalon.animation family"""
|
||||
|
||||
families = ["animation",
|
||||
"camera",
|
||||
"write",
|
||||
"yeticache",
|
||||
"pointcache"]
|
||||
representations = ["*"]
|
||||
|
||||
label = "Set frame range (with handles)"
|
||||
order = 12
|
||||
icon = "clock-o"
|
||||
color = "white"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from openpype.hosts.nuke.api import lib
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
||||
start = version_data.get("frameStart", None)
|
||||
end = version_data.get("frameEnd", None)
|
||||
|
||||
if start is None or end is None:
|
||||
print("Skipping setting frame range because start or "
|
||||
"end frame data is missing..")
|
||||
return
|
||||
|
||||
# Include handles
|
||||
handles = version_data.get("handles", 0)
|
||||
start -= handles
|
||||
end += handles
|
||||
|
||||
lib.update_frame_range(start, end)
|
||||
250
openpype/hosts/nuke/plugins/load/load_backdrop.py
Normal file
250
openpype/hosts/nuke/plugins/load/load_backdrop.py
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import nukescripts
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
reload(pnlib)
|
||||
|
||||
class LoadBackdropNodes(api.Loader):
|
||||
"""Loading Published Backdrop nodes (workfile, nukenodes)"""
|
||||
|
||||
representations = ["nk"]
|
||||
families = ["workfile", "nukenodes"]
|
||||
|
||||
label = "Iport Nuke Nodes"
|
||||
order = 0
|
||||
icon = "eye"
|
||||
color = style.colors.light
|
||||
node_color = "0x7533c1ff"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Loading function to import .nk file into script and wrap
|
||||
it on backdrop
|
||||
|
||||
Arguments:
|
||||
context (dict): context of version
|
||||
name (str): name of the version
|
||||
namespace (str): asset name
|
||||
data (dict): compulsory attribute > not used
|
||||
|
||||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
namespace = namespace or context['asset']['name']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
# Get mouse position
|
||||
n = nuke.createNode("NoOp")
|
||||
xcursor, ycursor = (n.xpos(), n.ypos())
|
||||
anlib.reset_selection()
|
||||
nuke.delete(n)
|
||||
|
||||
bdn_frame = 50
|
||||
|
||||
with anlib.maintained_selection():
|
||||
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
# get all pasted nodes
|
||||
new_nodes = list()
|
||||
nodes = nuke.selectedNodes()
|
||||
|
||||
# get pointer position in DAG
|
||||
xpointer, ypointer = pnlib.find_free_space_to_paste_nodes(nodes, direction="right", offset=200+bdn_frame)
|
||||
|
||||
# reset position to all nodes and replace inputs and output
|
||||
for n in nodes:
|
||||
anlib.reset_selection()
|
||||
xpos = (n.xpos() - xcursor) + xpointer
|
||||
ypos = (n.ypos() - ycursor) + ypointer
|
||||
n.setXYpos(xpos, ypos)
|
||||
|
||||
# replace Input nodes for dots
|
||||
if n.Class() in "Input":
|
||||
dot = nuke.createNode("Dot")
|
||||
new_name = n.name().replace("INP", "DOT")
|
||||
dot.setName(new_name)
|
||||
dot["label"].setValue(new_name)
|
||||
dot.setXYpos(xpos, ypos)
|
||||
new_nodes.append(dot)
|
||||
|
||||
# rewire
|
||||
dep = n.dependent()
|
||||
for d in dep:
|
||||
index = next((i for i, dpcy in enumerate(
|
||||
d.dependencies())
|
||||
if n is dpcy), 0)
|
||||
d.setInput(index, dot)
|
||||
|
||||
# remove Input node
|
||||
anlib.reset_selection()
|
||||
nuke.delete(n)
|
||||
continue
|
||||
|
||||
# replace Input nodes for dots
|
||||
elif n.Class() in "Output":
|
||||
dot = nuke.createNode("Dot")
|
||||
new_name = n.name() + "_DOT"
|
||||
dot.setName(new_name)
|
||||
dot["label"].setValue(new_name)
|
||||
dot.setXYpos(xpos, ypos)
|
||||
new_nodes.append(dot)
|
||||
|
||||
# rewire
|
||||
dep = next((d for d in n.dependencies()), None)
|
||||
if dep:
|
||||
dot.setInput(0, dep)
|
||||
|
||||
# remove Input node
|
||||
anlib.reset_selection()
|
||||
nuke.delete(n)
|
||||
continue
|
||||
else:
|
||||
new_nodes.append(n)
|
||||
|
||||
# reselect nodes with new Dot instead of Inputs and Output
|
||||
anlib.reset_selection()
|
||||
anlib.select_nodes(new_nodes)
|
||||
# place on backdrop
|
||||
bdn = nukescripts.autoBackdrop()
|
||||
|
||||
# add frame offset
|
||||
xpos = bdn.xpos() - bdn_frame
|
||||
ypos = bdn.ypos() - bdn_frame
|
||||
bdwidth = bdn["bdwidth"].value() + (bdn_frame*2)
|
||||
bdheight = bdn["bdheight"].value() + (bdn_frame*2)
|
||||
|
||||
bdn["xpos"].setValue(xpos)
|
||||
bdn["ypos"].setValue(ypos)
|
||||
bdn["bdwidth"].setValue(bdwidth)
|
||||
bdn["bdheight"].setValue(bdheight)
|
||||
|
||||
bdn["name"].setValue(object_name)
|
||||
bdn["label"].setValue("Version tracked frame: \n`{}`\n\nPLEASE DO NOT REMOVE OR MOVE \nANYTHING FROM THIS FRAME!".format(object_name))
|
||||
bdn["note_font_size"].setValue(20)
|
||||
|
||||
return containerise(
|
||||
node=bdn,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
# get corresponding node
|
||||
GN = nuke.toNode(container['objectName'])
|
||||
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
context = representation["context"]
|
||||
name = container['name']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
namespace = container['namespace']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
xpos = GN.xpos()
|
||||
ypos = GN.ypos()
|
||||
avalon_data = anlib.get_avalon_knob_data(GN)
|
||||
nuke.delete(GN)
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
anlib.set_avalon_knob_data(GN, avalon_data)
|
||||
GN.setXYpos(xpos, ypos)
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
GN["tile_color"].setValue(int("0xd88467ff", 16))
|
||||
else:
|
||||
GN["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
return update_container(GN, data_imprint)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
187
openpype/hosts/nuke/plugins/load/load_camera_abc.py
Normal file
187
openpype/hosts/nuke/plugins/load/load_camera_abc.py
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
from avalon import api, io
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
import nuke
|
||||
|
||||
|
||||
class AlembicCameraLoader(api.Loader):
|
||||
"""
|
||||
This will load alembic camera into script.
|
||||
"""
|
||||
|
||||
families = ["camera"]
|
||||
representations = ["abc"]
|
||||
|
||||
label = "Load Alembic Camera"
|
||||
icon = "camera"
|
||||
color = "orange"
|
||||
node_color = "0x3469ffff"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
fps = version_data.get("fps") or nuke.root()["fps"].getValue()
|
||||
namespace = namespace or context['asset']['name']
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
camera_node = nuke.createNode(
|
||||
"Camera2",
|
||||
"name {} file {} read_from_file True".format(
|
||||
object_name, file),
|
||||
inpanel=False
|
||||
)
|
||||
camera_node.forceValidate()
|
||||
camera_node["frame_rate"].setValue(float(fps))
|
||||
|
||||
# workaround because nuke's bug is not adding
|
||||
# animation keys properly
|
||||
xpos = camera_node.xpos()
|
||||
ypos = camera_node.ypos()
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
nuke.delete(camera_node)
|
||||
nuke.nodePaste("%clipboard%")
|
||||
camera_node = nuke.toNode(object_name)
|
||||
camera_node.setXYpos(xpos, ypos)
|
||||
|
||||
# color node by correct color by actual version
|
||||
self.node_version_color(version, camera_node)
|
||||
|
||||
return containerise(
|
||||
node=camera_node,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""
|
||||
Called by Scene Inventory when look should be updated to current
|
||||
version.
|
||||
If any reference edits cannot be applied, eg. shader renamed and
|
||||
material not present, reference is unloaded and cleaned.
|
||||
All failed edits are highlighted to the user via message box.
|
||||
|
||||
Args:
|
||||
container: object that has look to be updated
|
||||
representation: (dict): relationship data to get proper
|
||||
representation from DB and persisted
|
||||
data in .json
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
object_name = container['objectName']
|
||||
# get corresponding node
|
||||
camera_node = nuke.toNode(object_name)
|
||||
|
||||
# get main variables
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
fps = version_data.get("fps") or nuke.root()["fps"].getValue()
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
camera_node = nuke.toNode(object_name)
|
||||
camera_node['selected'].setValue(True)
|
||||
|
||||
# collect input output dependencies
|
||||
dependencies = camera_node.dependencies()
|
||||
dependent = camera_node.dependent()
|
||||
|
||||
camera_node["frame_rate"].setValue(float(fps))
|
||||
camera_node["file"].setValue(file)
|
||||
|
||||
# workaround because nuke's bug is
|
||||
# not adding animation keys properly
|
||||
xpos = camera_node.xpos()
|
||||
ypos = camera_node.ypos()
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
nuke.delete(camera_node)
|
||||
nuke.nodePaste("%clipboard%")
|
||||
camera_node = nuke.toNode(object_name)
|
||||
camera_node.setXYpos(xpos, ypos)
|
||||
|
||||
# link to original input nodes
|
||||
for i, input in enumerate(dependencies):
|
||||
camera_node.setInput(i, input)
|
||||
# link to original output nodes
|
||||
for d in dependent:
|
||||
index = next((i for i, dpcy in enumerate(
|
||||
d.dependencies())
|
||||
if camera_node is dpcy), 0)
|
||||
d.setInput(index, camera_node)
|
||||
|
||||
# color node by correct color by actual version
|
||||
self.node_version_color(version, camera_node)
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
return update_container(camera_node, data_imprint)
|
||||
|
||||
def node_version_color(self, version, node):
|
||||
""" Coloring a node by correct color by actual version
|
||||
"""
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd88467ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
163
openpype/hosts/nuke/plugins/load/load_gizmo.py
Normal file
163
openpype/hosts/nuke/plugins/load/load_gizmo.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
|
||||
|
||||
class LoadGizmo(api.Loader):
|
||||
"""Loading nuke Gizmo"""
|
||||
|
||||
representations = ["gizmo"]
|
||||
families = ["gizmo"]
|
||||
|
||||
label = "Load Gizmo"
|
||||
order = 0
|
||||
icon = "dropbox"
|
||||
color = style.colors.light
|
||||
node_color = "0x75338eff"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Loading function to get Gizmo into node graph
|
||||
|
||||
Arguments:
|
||||
context (dict): context of version
|
||||
name (str): name of the version
|
||||
namespace (str): asset name
|
||||
data (dict): compulsory attribute > not used
|
||||
|
||||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
namespace = namespace or context['asset']['name']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
return containerise(
|
||||
node=GN,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
# get corresponding node
|
||||
GN = nuke.toNode(container['objectName'])
|
||||
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
name = container['name']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
namespace = container['namespace']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
xpos = GN.xpos()
|
||||
ypos = GN.ypos()
|
||||
avalon_data = anlib.get_avalon_knob_data(GN)
|
||||
nuke.delete(GN)
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
anlib.set_avalon_knob_data(GN, avalon_data)
|
||||
GN.setXYpos(xpos, ypos)
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
GN["tile_color"].setValue(int("0xd88467ff", 16))
|
||||
else:
|
||||
GN["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
return update_container(GN, data_imprint)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
240
openpype/hosts/nuke/plugins/load/load_gizmo_ip.py
Normal file
240
openpype/hosts/nuke/plugins/load/load_gizmo_ip.py
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
|
||||
|
||||
class LoadGizmoInputProcess(api.Loader):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["gizmo"]
|
||||
families = ["gizmo"]
|
||||
|
||||
label = "Load Gizmo - Input Process"
|
||||
order = 0
|
||||
icon = "eye"
|
||||
color = style.colors.alert
|
||||
node_color = "0x7533c1ff"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Loading function to get Gizmo as Input Process on viewer
|
||||
|
||||
Arguments:
|
||||
context (dict): context of version
|
||||
name (str): name of the version
|
||||
namespace (str): asset name
|
||||
data (dict): compulsory attribute > not used
|
||||
|
||||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
namespace = namespace or context['asset']['name']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
# try to place it under Viewer1
|
||||
if not self.connect_active_viewer(GN):
|
||||
nuke.delete(GN)
|
||||
return
|
||||
|
||||
return containerise(
|
||||
node=GN,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
# get corresponding node
|
||||
GN = nuke.toNode(container['objectName'])
|
||||
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
name = container['name']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
namespace = container['namespace']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
with anlib.maintained_selection():
|
||||
xpos = GN.xpos()
|
||||
ypos = GN.ypos()
|
||||
avalon_data = anlib.get_avalon_knob_data(GN)
|
||||
nuke.delete(GN)
|
||||
# add group from nk
|
||||
nuke.nodePaste(file)
|
||||
|
||||
GN = nuke.selectedNode()
|
||||
anlib.set_avalon_knob_data(GN, avalon_data)
|
||||
GN.setXYpos(xpos, ypos)
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
GN["tile_color"].setValue(int("0xd88467ff", 16))
|
||||
else:
|
||||
GN["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
return update_container(GN, data_imprint)
|
||||
|
||||
def connect_active_viewer(self, group_node):
|
||||
"""
|
||||
Finds Active viewer and
|
||||
place the node under it, also adds
|
||||
name of group into Input Process of the viewer
|
||||
|
||||
Arguments:
|
||||
group_node (nuke node): nuke group node object
|
||||
|
||||
"""
|
||||
group_node_name = group_node["name"].value()
|
||||
|
||||
viewer = [n for n in nuke.allNodes() if "Viewer1" in n["name"].value()]
|
||||
if len(viewer) > 0:
|
||||
viewer = viewer[0]
|
||||
else:
|
||||
msg = str("Please create Viewer node before you "
|
||||
"run this action again")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return None
|
||||
|
||||
# get coordinates of Viewer1
|
||||
xpos = viewer["xpos"].value()
|
||||
ypos = viewer["ypos"].value()
|
||||
|
||||
ypos += 150
|
||||
|
||||
viewer["ypos"].setValue(ypos)
|
||||
|
||||
# set coordinates to group node
|
||||
group_node["xpos"].setValue(xpos)
|
||||
group_node["ypos"].setValue(ypos + 50)
|
||||
|
||||
# add group node name to Viewer Input Process
|
||||
viewer["input_process_node"].setValue(group_node_name)
|
||||
|
||||
# put backdrop under
|
||||
pnlib.create_backdrop(label="Input Process", layer=2,
|
||||
nodes=[viewer, group_node], color="0x7c7faaff")
|
||||
|
||||
return True
|
||||
|
||||
def get_item(self, data, trackIndex, subTrackIndex):
|
||||
return {key: val for key, val in data.items()
|
||||
if subTrackIndex == val["subTrackIndex"]
|
||||
if trackIndex == val["trackIndex"]}
|
||||
|
||||
def byteify(self, input):
|
||||
"""
|
||||
Converts unicode strings to strings
|
||||
It goes trought all dictionary
|
||||
|
||||
Arguments:
|
||||
input (dict/str): input
|
||||
|
||||
Returns:
|
||||
dict: with fixed values and keys
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(input, dict):
|
||||
return {self.byteify(key): self.byteify(value)
|
||||
for key, value in input.iteritems()}
|
||||
elif isinstance(input, list):
|
||||
return [self.byteify(element) for element in input]
|
||||
elif isinstance(input, unicode):
|
||||
return input.encode('utf-8')
|
||||
else:
|
||||
return input
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
233
openpype/hosts/nuke/plugins/load/load_image.py
Normal file
233
openpype/hosts/nuke/plugins/load/load_image.py
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
import re
|
||||
import nuke
|
||||
|
||||
from avalon.vendor import qargparse
|
||||
from avalon import api, io
|
||||
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
)
|
||||
|
||||
|
||||
class LoadImage(api.Loader):
|
||||
"""Load still image into Nuke"""
|
||||
|
||||
families = ["render", "source", "plate", "review", "image"]
|
||||
representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd"]
|
||||
|
||||
label = "Load Image"
|
||||
order = -10
|
||||
icon = "image"
|
||||
color = "white"
|
||||
|
||||
node_name_template = "{class_name}_{ext}"
|
||||
|
||||
options = [
|
||||
qargparse.Integer(
|
||||
"frame_number",
|
||||
label="Frame Number",
|
||||
default=int(nuke.root()["first_frame"].getValue()),
|
||||
min=1,
|
||||
max=999999,
|
||||
help="What frame is reading from?"
|
||||
)
|
||||
]
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
from avalon.nuke import (
|
||||
containerise,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
self.log.info("__ options: `{}`".format(options))
|
||||
frame_number = options.get("frame_number", 1)
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
repr_id = context["representation"]["_id"]
|
||||
|
||||
self.log.info("version_data: {}\n".format(version_data))
|
||||
self.log.debug(
|
||||
"Representation id `{}` ".format(repr_id))
|
||||
|
||||
last = first = int(frame_number)
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
||||
file = self.fname
|
||||
|
||||
if not file:
|
||||
repr_id = context["representation"]["_id"]
|
||||
self.log.warning(
|
||||
"Representation id `{}` is failing to load".format(repr_id))
|
||||
return
|
||||
|
||||
file = file.replace("\\", "/")
|
||||
|
||||
repr_cont = context["representation"]["context"]
|
||||
frame = repr_cont.get("frame")
|
||||
if frame:
|
||||
padding = len(frame)
|
||||
file = file.replace(
|
||||
frame,
|
||||
format(frame_number, "0{}".format(padding)))
|
||||
|
||||
name_data = {
|
||||
"asset": repr_cont["asset"],
|
||||
"subset": repr_cont["subset"],
|
||||
"representation": context["representation"]["name"],
|
||||
"ext": repr_cont["representation"],
|
||||
"id": context["representation"]["_id"],
|
||||
"class_name": self.__class__.__name__
|
||||
}
|
||||
|
||||
read_name = self.node_name_template.format(**name_data)
|
||||
|
||||
# Create the Loader with the filename path set
|
||||
with viewer_update_and_undo_stop():
|
||||
r = nuke.createNode(
|
||||
"Read",
|
||||
"name {}".format(read_name))
|
||||
r["file"].setValue(file)
|
||||
|
||||
# Set colorspace defined in version data
|
||||
colorspace = context["version"]["data"].get("colorspace")
|
||||
if colorspace:
|
||||
r["colorspace"].setValue(str(colorspace))
|
||||
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
if preset_clrsp is not None:
|
||||
r["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
r["origfirst"].setValue(first)
|
||||
r["first"].setValue(first)
|
||||
r["origlast"].setValue(last)
|
||||
r["last"].setValue(last)
|
||||
|
||||
# add additional metadata from the version to imprint Avalon knob
|
||||
add_keys = ["source", "colorspace", "author", "fps", "version"]
|
||||
|
||||
data_imprint = {
|
||||
"frameStart": first,
|
||||
"frameEnd": last
|
||||
}
|
||||
for k in add_keys:
|
||||
if k == 'version':
|
||||
data_imprint.update({k: context["version"]['name']})
|
||||
else:
|
||||
data_imprint.update(
|
||||
{k: context["version"]['data'].get(k, str(None))})
|
||||
|
||||
data_imprint.update({"objectName": read_name})
|
||||
|
||||
r["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
return containerise(r,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container["objectName"])
|
||||
frame_number = node["first"].value()
|
||||
|
||||
assert node.Class() == "Read", "Must be Read"
|
||||
|
||||
repr_cont = representation["context"]
|
||||
|
||||
file = api.get_representation_path(representation)
|
||||
|
||||
if not file:
|
||||
repr_id = representation["_id"]
|
||||
self.log.warning(
|
||||
"Representation id `{}` is failing to load".format(repr_id))
|
||||
return
|
||||
|
||||
file = file.replace("\\", "/")
|
||||
|
||||
frame = repr_cont.get("frame")
|
||||
if frame:
|
||||
padding = len(frame)
|
||||
file = file.replace(
|
||||
frame,
|
||||
format(frame_number, "0{}".format(padding)))
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
version_data = version.get("data", {})
|
||||
|
||||
last = first = int(frame_number)
|
||||
|
||||
# Set the global in to the start frame of the sequence
|
||||
node["file"].setValue(file)
|
||||
node["origfirst"].setValue(first)
|
||||
node["first"].setValue(first)
|
||||
node["origlast"].setValue(last)
|
||||
node["last"].setValue(last)
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
"representation": str(representation["_id"]),
|
||||
"frameStart": str(first),
|
||||
"frameEnd": str(last),
|
||||
"version": str(version.get("name")),
|
||||
"colorspace": version_data.get("colorspace"),
|
||||
"source": version_data.get("source"),
|
||||
"fps": str(version_data.get("fps")),
|
||||
"author": version_data.get("author"),
|
||||
"outputDir": version_data.get("outputDir"),
|
||||
})
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
node,
|
||||
updated_dict
|
||||
)
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
assert node.Class() == "Read", "Must be Read"
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
336
openpype/hosts/nuke/plugins/load/load_luts.py
Normal file
336
openpype/hosts/nuke/plugins/load/load_luts.py
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class LoadLuts(api.Loader):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["lutJson"]
|
||||
families = ["lut"]
|
||||
|
||||
label = "Load Luts - nodes"
|
||||
order = 0
|
||||
icon = "cc"
|
||||
color = style.colors.light
|
||||
ignore_attr = ["useLifetime"]
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Loading function to get the soft effects to particular read node
|
||||
|
||||
Arguments:
|
||||
context (dict): context of version
|
||||
name (str): name of the version
|
||||
namespace (str): asset name
|
||||
data (dict): compulsory attribute > not used
|
||||
|
||||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
# import dependencies
|
||||
from avalon.nuke import containerise
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
workfile_first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
namespace = namespace or context['asset']['name']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
# getting data from json file with unicode conversion
|
||||
with open(file, "r") as f:
|
||||
json_f = {self.byteify(key): self.byteify(value)
|
||||
for key, value in json.load(f).iteritems()}
|
||||
|
||||
# get correct order of nodes by positions on track and subtrack
|
||||
nodes_order = self.reorder_nodes(json_f["effects"])
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
GN = nuke.createNode("Group")
|
||||
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
# adding content to the group node
|
||||
with GN:
|
||||
pre_node = nuke.createNode("Input")
|
||||
pre_node["name"].setValue("rgb")
|
||||
|
||||
for ef_name, ef_val in nodes_order.items():
|
||||
node = nuke.createNode(ef_val["class"])
|
||||
for k, v in ef_val["node"].items():
|
||||
if k in self.ignore_attr:
|
||||
continue
|
||||
|
||||
try:
|
||||
node[k].value()
|
||||
except NameError as e:
|
||||
self.log.warning(e)
|
||||
continue
|
||||
|
||||
if isinstance(v, list) and len(v) > 4:
|
||||
node[k].setAnimated()
|
||||
for i, value in enumerate(v):
|
||||
if isinstance(value, list):
|
||||
for ci, cv in enumerate(value):
|
||||
node[k].setValueAt(
|
||||
cv,
|
||||
(workfile_first_frame + i),
|
||||
ci)
|
||||
else:
|
||||
node[k].setValueAt(
|
||||
value,
|
||||
(workfile_first_frame + i))
|
||||
else:
|
||||
node[k].setValue(v)
|
||||
node.setInput(0, pre_node)
|
||||
pre_node = node
|
||||
|
||||
output = nuke.createNode("Output")
|
||||
output.setInput(0, pre_node)
|
||||
|
||||
# try to find parent read node
|
||||
self.connect_read_node(GN, namespace, json_f["assignTo"])
|
||||
|
||||
GN["tile_color"].setValue(int("0x3469ffff", 16))
|
||||
|
||||
self.log.info("Loaded lut setup: `{}`".format(GN["name"].value()))
|
||||
|
||||
return containerise(
|
||||
node=GN,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
# get corresponding node
|
||||
GN = nuke.toNode(container['objectName'])
|
||||
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
name = container['name']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
workfile_first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
namespace = container['namespace']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
GN,
|
||||
data_imprint
|
||||
)
|
||||
|
||||
# getting data from json file with unicode conversion
|
||||
with open(file, "r") as f:
|
||||
json_f = {self.byteify(key): self.byteify(value)
|
||||
for key, value in json.load(f).iteritems()}
|
||||
|
||||
# get correct order of nodes by positions on track and subtrack
|
||||
nodes_order = self.reorder_nodes(json_f["effects"])
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
# adding content to the group node
|
||||
with GN:
|
||||
# first remove all nodes
|
||||
[nuke.delete(n) for n in nuke.allNodes()]
|
||||
|
||||
# create input node
|
||||
pre_node = nuke.createNode("Input")
|
||||
pre_node["name"].setValue("rgb")
|
||||
|
||||
for ef_name, ef_val in nodes_order.items():
|
||||
node = nuke.createNode(ef_val["class"])
|
||||
for k, v in ef_val["node"].items():
|
||||
if k in self.ignore_attr:
|
||||
continue
|
||||
|
||||
try:
|
||||
node[k].value()
|
||||
except NameError as e:
|
||||
self.log.warning(e)
|
||||
continue
|
||||
|
||||
if isinstance(v, list) and len(v) > 3:
|
||||
node[k].setAnimated()
|
||||
for i, value in enumerate(v):
|
||||
if isinstance(value, list):
|
||||
for ci, cv in enumerate(value):
|
||||
node[k].setValueAt(
|
||||
cv,
|
||||
(workfile_first_frame + i),
|
||||
ci)
|
||||
else:
|
||||
node[k].setValueAt(
|
||||
value,
|
||||
(workfile_first_frame + i))
|
||||
else:
|
||||
node[k].setValue(v)
|
||||
node.setInput(0, pre_node)
|
||||
pre_node = node
|
||||
|
||||
# create output node
|
||||
output = nuke.createNode("Output")
|
||||
output.setInput(0, pre_node)
|
||||
|
||||
# try to find parent read node
|
||||
self.connect_read_node(GN, namespace, json_f["assignTo"])
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
GN["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
GN["tile_color"].setValue(int("0x3469ffff", 16))
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def connect_read_node(self, group_node, asset, subset):
|
||||
"""
|
||||
Finds read node and selects it
|
||||
|
||||
Arguments:
|
||||
asset (str): asset name
|
||||
|
||||
Returns:
|
||||
nuke node: node is selected
|
||||
None: if nothing found
|
||||
"""
|
||||
search_name = "{0}_{1}".format(asset, subset)
|
||||
node = [n for n in nuke.allNodes() if search_name in n["name"].value()]
|
||||
if len(node) > 0:
|
||||
rn = node[0]
|
||||
else:
|
||||
rn = None
|
||||
|
||||
# Parent read node has been found
|
||||
# solving connections
|
||||
if rn:
|
||||
dep_nodes = rn.dependent()
|
||||
|
||||
if len(dep_nodes) > 0:
|
||||
for dn in dep_nodes:
|
||||
dn.setInput(0, group_node)
|
||||
|
||||
group_node.setInput(0, rn)
|
||||
group_node.autoplace()
|
||||
|
||||
def reorder_nodes(self, data):
|
||||
new_order = OrderedDict()
|
||||
trackNums = [v["trackIndex"] for k, v in data.items()]
|
||||
subTrackNums = [v["subTrackIndex"] for k, v in data.items()]
|
||||
|
||||
for trackIndex in range(
|
||||
min(trackNums), max(trackNums) + 1):
|
||||
for subTrackIndex in range(
|
||||
min(subTrackNums), max(subTrackNums) + 1):
|
||||
item = self.get_item(data, trackIndex, subTrackIndex)
|
||||
if item is not {}:
|
||||
new_order.update(item)
|
||||
return new_order
|
||||
|
||||
def get_item(self, data, trackIndex, subTrackIndex):
|
||||
return {key: val for key, val in data.items()
|
||||
if subTrackIndex == val["subTrackIndex"]
|
||||
if trackIndex == val["trackIndex"]}
|
||||
|
||||
def byteify(self, input):
|
||||
"""
|
||||
Converts unicode strings to strings
|
||||
It goes trought all dictionary
|
||||
|
||||
Arguments:
|
||||
input (dict/str): input
|
||||
|
||||
Returns:
|
||||
dict: with fixed values and keys
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(input, dict):
|
||||
return {self.byteify(key): self.byteify(value)
|
||||
for key, value in input.iteritems()}
|
||||
elif isinstance(input, list):
|
||||
return [self.byteify(element) for element in input]
|
||||
elif isinstance(input, unicode):
|
||||
return input.encode('utf-8')
|
||||
else:
|
||||
return input
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
354
openpype/hosts/nuke/plugins/load/load_luts_ip.py
Normal file
354
openpype/hosts/nuke/plugins/load/load_luts_ip.py
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from openpype.hosts.nuke.api import lib
|
||||
|
||||
|
||||
class LoadLutsInputProcess(api.Loader):
|
||||
"""Loading colorspace soft effect exported from nukestudio"""
|
||||
|
||||
representations = ["lutJson"]
|
||||
families = ["lut"]
|
||||
|
||||
label = "Load Luts - Input Process"
|
||||
order = 0
|
||||
icon = "eye"
|
||||
color = style.colors.alert
|
||||
ignore_attr = ["useLifetime"]
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Loading function to get the soft effects to particular read node
|
||||
|
||||
Arguments:
|
||||
context (dict): context of version
|
||||
name (str): name of the version
|
||||
namespace (str): asset name
|
||||
data (dict): compulsory attribute > not used
|
||||
|
||||
Returns:
|
||||
nuke node: containerised nuke node object
|
||||
"""
|
||||
# import dependencies
|
||||
from avalon.nuke import containerise
|
||||
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
workfile_first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
namespace = namespace or context['asset']['name']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
# getting data from json file with unicode conversion
|
||||
with open(file, "r") as f:
|
||||
json_f = {self.byteify(key): self.byteify(value)
|
||||
for key, value in json.load(f).iteritems()}
|
||||
|
||||
# get correct order of nodes by positions on track and subtrack
|
||||
nodes_order = self.reorder_nodes(json_f["effects"])
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
GN = nuke.createNode("Group")
|
||||
|
||||
GN["name"].setValue(object_name)
|
||||
|
||||
# adding content to the group node
|
||||
with GN:
|
||||
pre_node = nuke.createNode("Input")
|
||||
pre_node["name"].setValue("rgb")
|
||||
|
||||
for ef_name, ef_val in nodes_order.items():
|
||||
node = nuke.createNode(ef_val["class"])
|
||||
for k, v in ef_val["node"].items():
|
||||
if k in self.ignore_attr:
|
||||
continue
|
||||
|
||||
try:
|
||||
node[k].value()
|
||||
except NameError as e:
|
||||
self.log.warning(e)
|
||||
continue
|
||||
|
||||
if isinstance(v, list) and len(v) > 4:
|
||||
node[k].setAnimated()
|
||||
for i, value in enumerate(v):
|
||||
if isinstance(value, list):
|
||||
for ci, cv in enumerate(value):
|
||||
node[k].setValueAt(
|
||||
cv,
|
||||
(workfile_first_frame + i),
|
||||
ci)
|
||||
else:
|
||||
node[k].setValueAt(
|
||||
value,
|
||||
(workfile_first_frame + i))
|
||||
else:
|
||||
node[k].setValue(v)
|
||||
|
||||
node.setInput(0, pre_node)
|
||||
pre_node = node
|
||||
|
||||
output = nuke.createNode("Output")
|
||||
output.setInput(0, pre_node)
|
||||
|
||||
# try to place it under Viewer1
|
||||
if not self.connect_active_viewer(GN):
|
||||
nuke.delete(GN)
|
||||
return
|
||||
|
||||
GN["tile_color"].setValue(int("0x3469ffff", 16))
|
||||
|
||||
self.log.info("Loaded lut setup: `{}`".format(GN["name"].value()))
|
||||
|
||||
return containerise(
|
||||
node=GN,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
# get main variables
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
# get corresponding node
|
||||
GN = nuke.toNode(container['objectName'])
|
||||
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
name = container['name']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
workfile_first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
namespace = container['namespace']
|
||||
colorspace = version_data.get("colorspace", None)
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"colorspaceInput": colorspace,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
GN,
|
||||
data_imprint
|
||||
)
|
||||
|
||||
# getting data from json file with unicode conversion
|
||||
with open(file, "r") as f:
|
||||
json_f = {self.byteify(key): self.byteify(value)
|
||||
for key, value in json.load(f).iteritems()}
|
||||
|
||||
# get correct order of nodes by positions on track and subtrack
|
||||
nodes_order = self.reorder_nodes(json_f["effects"])
|
||||
|
||||
# adding nodes to node graph
|
||||
# just in case we are in group lets jump out of it
|
||||
nuke.endGroup()
|
||||
|
||||
# adding content to the group node
|
||||
with GN:
|
||||
# first remove all nodes
|
||||
[nuke.delete(n) for n in nuke.allNodes()]
|
||||
|
||||
# create input node
|
||||
pre_node = nuke.createNode("Input")
|
||||
pre_node["name"].setValue("rgb")
|
||||
|
||||
for ef_name, ef_val in nodes_order.items():
|
||||
node = nuke.createNode(ef_val["class"])
|
||||
for k, v in ef_val["node"].items():
|
||||
if k in self.ignore_attr:
|
||||
continue
|
||||
|
||||
try:
|
||||
node[k].value()
|
||||
except NameError as e:
|
||||
self.log.warning(e)
|
||||
continue
|
||||
|
||||
if isinstance(v, list) and len(v) > 3:
|
||||
node[k].setAnimated()
|
||||
for i, value in enumerate(v):
|
||||
if isinstance(value, list):
|
||||
for ci, cv in enumerate(value):
|
||||
node[k].setValueAt(
|
||||
cv,
|
||||
(workfile_first_frame + i),
|
||||
ci)
|
||||
else:
|
||||
node[k].setValueAt(
|
||||
value,
|
||||
(workfile_first_frame + i))
|
||||
else:
|
||||
node[k].setValue(v)
|
||||
node.setInput(0, pre_node)
|
||||
pre_node = node
|
||||
|
||||
# create output node
|
||||
output = nuke.createNode("Output")
|
||||
output.setInput(0, pre_node)
|
||||
|
||||
# try to place it under Viewer1
|
||||
if not self.connect_active_viewer(GN):
|
||||
nuke.delete(GN)
|
||||
return
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
GN["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
GN["tile_color"].setValue(int("0x3469ffff", 16))
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def connect_active_viewer(self, group_node):
|
||||
"""
|
||||
Finds Active viewer and
|
||||
place the node under it, also adds
|
||||
name of group into Input Process of the viewer
|
||||
|
||||
Arguments:
|
||||
group_node (nuke node): nuke group node object
|
||||
|
||||
"""
|
||||
group_node_name = group_node["name"].value()
|
||||
|
||||
viewer = [n for n in nuke.allNodes() if "Viewer1" in n["name"].value()]
|
||||
if len(viewer) > 0:
|
||||
viewer = viewer[0]
|
||||
else:
|
||||
msg = str("Please create Viewer node before you "
|
||||
"run this action again")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return None
|
||||
|
||||
# get coordinates of Viewer1
|
||||
xpos = viewer["xpos"].value()
|
||||
ypos = viewer["ypos"].value()
|
||||
|
||||
ypos += 150
|
||||
|
||||
viewer["ypos"].setValue(ypos)
|
||||
|
||||
# set coordinates to group node
|
||||
group_node["xpos"].setValue(xpos)
|
||||
group_node["ypos"].setValue(ypos + 50)
|
||||
|
||||
# add group node name to Viewer Input Process
|
||||
viewer["input_process_node"].setValue(group_node_name)
|
||||
|
||||
# put backdrop under
|
||||
lib.create_backdrop(label="Input Process", layer=2, nodes=[viewer, group_node], color="0x7c7faaff")
|
||||
|
||||
return True
|
||||
|
||||
def reorder_nodes(self, data):
|
||||
new_order = OrderedDict()
|
||||
trackNums = [v["trackIndex"] for k, v in data.items()]
|
||||
subTrackNums = [v["subTrackIndex"] for k, v in data.items()]
|
||||
|
||||
for trackIndex in range(
|
||||
min(trackNums), max(trackNums) + 1):
|
||||
for subTrackIndex in range(
|
||||
min(subTrackNums), max(subTrackNums) + 1):
|
||||
item = self.get_item(data, trackIndex, subTrackIndex)
|
||||
if item is not {}:
|
||||
new_order.update(item)
|
||||
return new_order
|
||||
|
||||
def get_item(self, data, trackIndex, subTrackIndex):
|
||||
return {key: val for key, val in data.items()
|
||||
if subTrackIndex == val["subTrackIndex"]
|
||||
if trackIndex == val["trackIndex"]}
|
||||
|
||||
def byteify(self, input):
|
||||
"""
|
||||
Converts unicode strings to strings
|
||||
It goes trought all dictionary
|
||||
|
||||
Arguments:
|
||||
input (dict/str): input
|
||||
|
||||
Returns:
|
||||
dict: with fixed values and keys
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(input, dict):
|
||||
return {self.byteify(key): self.byteify(value)
|
||||
for key, value in input.iteritems()}
|
||||
elif isinstance(input, list):
|
||||
return [self.byteify(element) for element in input]
|
||||
elif isinstance(input, unicode):
|
||||
return input.encode('utf-8')
|
||||
else:
|
||||
return input
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
27
openpype/hosts/nuke/plugins/load/load_matchmove.py
Normal file
27
openpype/hosts/nuke/plugins/load/load_matchmove.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from avalon import api
|
||||
import nuke
|
||||
|
||||
|
||||
class MatchmoveLoader(api.Loader):
|
||||
"""
|
||||
This will run matchmove script to create track in script.
|
||||
"""
|
||||
|
||||
families = ["matchmove"]
|
||||
representations = ["py"]
|
||||
defaults = ["Camera", "Object"]
|
||||
|
||||
label = "Run matchmove script"
|
||||
icon = "empire"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
if self.fname.lower().endswith(".py"):
|
||||
exec(open(self.fname).read())
|
||||
|
||||
else:
|
||||
msg = "Unsupported script type"
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
|
||||
return True
|
||||
326
openpype/hosts/nuke/plugins/load/load_mov.py
Normal file
326
openpype/hosts/nuke/plugins/load/load_mov.py
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
import nuke
|
||||
import contextlib
|
||||
|
||||
from avalon import api, io
|
||||
from openpype.api import get_current_project_settings
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def preserve_trim(node):
|
||||
"""Preserve the relative trim of the Loader tool.
|
||||
|
||||
This tries to preserve the loader's trim (trim in and trim out) after
|
||||
the context by reapplying the "amount" it trims on the clip's length at
|
||||
start and end.
|
||||
|
||||
"""
|
||||
# working script frame range
|
||||
script_start = nuke.root()["first_frame"].value()
|
||||
|
||||
start_at_frame = None
|
||||
offset_frame = None
|
||||
if node['frame_mode'].value() == "start at":
|
||||
start_at_frame = node['frame'].value()
|
||||
if node['frame_mode'].value() == "offset":
|
||||
offset_frame = node['frame'].value()
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if start_at_frame:
|
||||
node['frame_mode'].setValue("start at")
|
||||
node['frame'].setValue(str(script_start))
|
||||
print("start frame of Read was set to"
|
||||
"{}".format(script_start))
|
||||
|
||||
if offset_frame:
|
||||
node['frame_mode'].setValue("offset")
|
||||
node['frame'].setValue(str((script_start + offset_frame)))
|
||||
print("start frame of Read was set to"
|
||||
"{}".format(script_start))
|
||||
|
||||
|
||||
def add_review_presets_config():
|
||||
returning = {
|
||||
"families": list(),
|
||||
"representations": list()
|
||||
}
|
||||
settings = get_current_project_settings()
|
||||
review_profiles = (
|
||||
settings["global"]
|
||||
["publish"]
|
||||
["ExtractReview"]
|
||||
["profiles"]
|
||||
)
|
||||
|
||||
outputs = {}
|
||||
for profile in review_profiles:
|
||||
outputs.update(profile.get("outputs", {}))
|
||||
|
||||
for output, properities in outputs.items():
|
||||
returning["representations"].append(output)
|
||||
returning["families"] += properities.get("families", [])
|
||||
|
||||
return returning
|
||||
|
||||
|
||||
class LoadMov(api.Loader):
|
||||
"""Load mov file into Nuke"""
|
||||
families = ["render", "source", "plate", "review"]
|
||||
representations = ["mov", "review", "mp4"]
|
||||
|
||||
label = "Load mov"
|
||||
order = -10
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
script_start = nuke.root()["first_frame"].value()
|
||||
|
||||
node_name_template = "{class_name}_{ext}"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
from avalon.nuke import (
|
||||
containerise,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
repr_id = context["representation"]["_id"]
|
||||
|
||||
orig_first = version_data.get("frameStart")
|
||||
orig_last = version_data.get("frameEnd")
|
||||
diff = orig_first - 1
|
||||
|
||||
first = orig_first - diff
|
||||
last = orig_last - diff
|
||||
|
||||
handle_start = version_data.get("handleStart", 0)
|
||||
handle_end = version_data.get("handleEnd", 0)
|
||||
|
||||
colorspace = version_data.get("colorspace")
|
||||
repr_cont = context["representation"]["context"]
|
||||
|
||||
self.log.debug(
|
||||
"Representation id `{}` ".format(repr_id))
|
||||
|
||||
context["representation"]["_id"]
|
||||
# create handles offset (only to last, because of mov)
|
||||
last += handle_start + handle_end
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
||||
file = self.fname
|
||||
|
||||
if not file:
|
||||
self.log.warning(
|
||||
"Representation id `{}` is failing to load".format(repr_id))
|
||||
return
|
||||
|
||||
file = file.replace("\\", "/")
|
||||
|
||||
name_data = {
|
||||
"asset": repr_cont["asset"],
|
||||
"subset": repr_cont["subset"],
|
||||
"representation": context["representation"]["name"],
|
||||
"ext": repr_cont["representation"],
|
||||
"id": context["representation"]["_id"],
|
||||
"class_name": self.__class__.__name__
|
||||
}
|
||||
|
||||
read_name = self.node_name_template.format(**name_data)
|
||||
|
||||
# Create the Loader with the filename path set
|
||||
with viewer_update_and_undo_stop():
|
||||
read_node = nuke.createNode(
|
||||
"Read",
|
||||
"name {}".format(read_name)
|
||||
)
|
||||
read_node["file"].setValue(file)
|
||||
|
||||
read_node["origfirst"].setValue(first)
|
||||
read_node["first"].setValue(first)
|
||||
read_node["origlast"].setValue(last)
|
||||
read_node["last"].setValue(last)
|
||||
|
||||
# start at script start
|
||||
read_node['frame_mode'].setValue("start at")
|
||||
read_node['frame'].setValue(str(self.script_start))
|
||||
|
||||
if colorspace:
|
||||
read_node["colorspace"].setValue(str(colorspace))
|
||||
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
if preset_clrsp is not None:
|
||||
read_node["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
# add additional metadata from the version to imprint Avalon knob
|
||||
add_keys = [
|
||||
"frameStart", "frameEnd", "handles", "source", "author",
|
||||
"fps", "version", "handleStart", "handleEnd"
|
||||
]
|
||||
|
||||
data_imprint = {}
|
||||
for key in add_keys:
|
||||
if key == 'version':
|
||||
data_imprint.update({
|
||||
key: context["version"]['name']
|
||||
})
|
||||
else:
|
||||
data_imprint.update({
|
||||
key: context["version"]['data'].get(key, str(None))
|
||||
})
|
||||
|
||||
data_imprint.update({"objectName": read_name})
|
||||
|
||||
read_node["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
return containerise(
|
||||
read_node,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint
|
||||
)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
read_node = nuke.toNode(container['objectName'])
|
||||
|
||||
assert read_node.Class() == "Read", "Must be Read"
|
||||
|
||||
file = self.fname
|
||||
|
||||
if not file:
|
||||
repr_id = representation["_id"]
|
||||
self.log.warning(
|
||||
"Representation id `{}` is failing to load".format(repr_id))
|
||||
return
|
||||
|
||||
file = file.replace("\\", "/")
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
version_data = version.get("data", {})
|
||||
|
||||
orig_first = version_data.get("frameStart")
|
||||
orig_last = version_data.get("frameEnd")
|
||||
diff = orig_first - 1
|
||||
|
||||
# set first to 1
|
||||
first = orig_first - diff
|
||||
last = orig_last - diff
|
||||
handles = version_data.get("handles", 0)
|
||||
handle_start = version_data.get("handleStart", 0)
|
||||
handle_end = version_data.get("handleEnd", 0)
|
||||
colorspace = version_data.get("colorspace")
|
||||
|
||||
if first is None:
|
||||
self.log.warning((
|
||||
"Missing start frame for updated version"
|
||||
"assuming starts at frame 0 for: "
|
||||
"{} ({})").format(
|
||||
read_node['name'].value(), representation))
|
||||
first = 0
|
||||
|
||||
# fix handle start and end if none are available
|
||||
if not handle_start and not handle_end:
|
||||
handle_start = handles
|
||||
handle_end = handles
|
||||
|
||||
# create handles offset (only to last, because of mov)
|
||||
last += handle_start + handle_end
|
||||
|
||||
# Update the loader's path whilst preserving some values
|
||||
with preserve_trim(read_node):
|
||||
read_node["file"].setValue(file)
|
||||
self.log.info("__ node['file']: {}".format(
|
||||
read_node["file"].value()))
|
||||
|
||||
# Set the global in to the start frame of the sequence
|
||||
read_node["origfirst"].setValue(first)
|
||||
read_node["first"].setValue(first)
|
||||
read_node["origlast"].setValue(last)
|
||||
read_node["last"].setValue(last)
|
||||
|
||||
# start at script start
|
||||
read_node['frame_mode'].setValue("start at")
|
||||
read_node['frame'].setValue(str(self.script_start))
|
||||
|
||||
if colorspace:
|
||||
read_node["colorspace"].setValue(str(colorspace))
|
||||
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
if preset_clrsp is not None:
|
||||
read_node["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
"representation": str(representation["_id"]),
|
||||
"frameStart": str(first),
|
||||
"frameEnd": str(last),
|
||||
"version": str(version.get("name")),
|
||||
"colorspace": version_data.get("colorspace"),
|
||||
"source": version_data.get("source"),
|
||||
"handleStart": str(handle_start),
|
||||
"handleEnd": str(handle_end),
|
||||
"fps": str(version_data.get("fps")),
|
||||
"author": version_data.get("author"),
|
||||
"outputDir": version_data.get("outputDir")
|
||||
})
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
read_node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
read_node["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
read_node, updated_dict
|
||||
)
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
assert node.Class() == "Read", "Must be Read"
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
162
openpype/hosts/nuke/plugins/load/load_script_precomp.py
Normal file
162
openpype/hosts/nuke/plugins/load/load_script_precomp.py
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
from avalon import api, style, io
|
||||
from avalon.nuke import get_avalon_knob_data
|
||||
import nuke
|
||||
|
||||
|
||||
class LinkAsGroup(api.Loader):
|
||||
"""Copy the published file to be pasted at the desired location"""
|
||||
|
||||
representations = ["nk"]
|
||||
families = ["workfile", "nukenodes"]
|
||||
|
||||
label = "Load Precomp"
|
||||
order = 0
|
||||
icon = "file"
|
||||
color = style.colors.alert
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from avalon.nuke import containerise
|
||||
# for k, v in context.items():
|
||||
# log.info("key: `{}`, value: {}\n".format(k, v))
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
||||
file = self.fname.replace("\\", "/")
|
||||
self.log.info("file: {}\n".format(self.fname))
|
||||
|
||||
precomp_name = context["representation"]["context"]["subset"]
|
||||
|
||||
self.log.info("versionData: {}\n".format(context["version"]["data"]))
|
||||
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
|
||||
"source", "author", "fps"]
|
||||
|
||||
data_imprint = {
|
||||
"startingFrame": first,
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname
|
||||
}
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: context["version"]['data'][k]})
|
||||
data_imprint.update({"objectName": precomp_name})
|
||||
|
||||
# group context is set to precomp, so back up one level.
|
||||
nuke.endGroup()
|
||||
|
||||
# P = nuke.nodes.LiveGroup("file {}".format(file))
|
||||
P = nuke.createNode(
|
||||
"Precomp",
|
||||
"file {}".format(file))
|
||||
|
||||
# Set colorspace defined in version data
|
||||
colorspace = context["version"]["data"].get("colorspace", None)
|
||||
self.log.info("colorspace: {}\n".format(colorspace))
|
||||
|
||||
P["name"].setValue("{}_{}".format(name, namespace))
|
||||
P["useOutput"].setValue(True)
|
||||
|
||||
with P:
|
||||
# iterate trough all nodes in group node and find pype writes
|
||||
writes = [n.name() for n in nuke.allNodes()
|
||||
if n.Class() == "Group"
|
||||
if get_avalon_knob_data(n)]
|
||||
|
||||
if writes:
|
||||
# create panel for selecting output
|
||||
panel_choices = " ".join(writes)
|
||||
panel_label = "Select write node for output"
|
||||
p = nuke.Panel("Select Write Node")
|
||||
p.addEnumerationPulldown(
|
||||
panel_label, panel_choices)
|
||||
p.show()
|
||||
P["output"].setValue(p.value(panel_label))
|
||||
|
||||
P["tile_color"].setValue(0xff0ff0ff)
|
||||
|
||||
return containerise(
|
||||
node=P,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
|
||||
root = api.get_representation_path(representation).replace("\\", "/")
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
"representation": str(representation["_id"]),
|
||||
"frameEnd": version["data"].get("frameEnd"),
|
||||
"version": version.get("name"),
|
||||
"colorspace": version["data"].get("colorspace"),
|
||||
"source": version["data"].get("source"),
|
||||
"handles": version["data"].get("handles"),
|
||||
"fps": version["data"].get("fps"),
|
||||
"author": version["data"].get("author"),
|
||||
"outputDir": version["data"].get("outputDir"),
|
||||
})
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
node,
|
||||
updated_dict
|
||||
)
|
||||
|
||||
node["file"].setValue(root)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0xff0ff0ff", 16))
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
346
openpype/hosts/nuke/plugins/load/load_sequence.py
Normal file
346
openpype/hosts/nuke/plugins/load/load_sequence.py
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
import nuke
|
||||
import contextlib
|
||||
|
||||
from avalon import api, io
|
||||
from openpype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def preserve_trim(node):
|
||||
"""Preserve the relative trim of the Loader tool.
|
||||
|
||||
This tries to preserve the loader's trim (trim in and trim out) after
|
||||
the context by reapplying the "amount" it trims on the clip's length at
|
||||
start and end.
|
||||
|
||||
"""
|
||||
# working script frame range
|
||||
script_start = nuke.root()["first_frame"].value()
|
||||
|
||||
start_at_frame = None
|
||||
offset_frame = None
|
||||
if node['frame_mode'].value() == "start at":
|
||||
start_at_frame = node['frame'].value()
|
||||
if node['frame_mode'].value() == "offset":
|
||||
offset_frame = node['frame'].value()
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if start_at_frame:
|
||||
node['frame_mode'].setValue("start at")
|
||||
node['frame'].setValue(str(script_start))
|
||||
print("start frame of Read was set to"
|
||||
"{}".format(script_start))
|
||||
|
||||
if offset_frame:
|
||||
node['frame_mode'].setValue("offset")
|
||||
node['frame'].setValue(str((script_start + offset_frame)))
|
||||
print("start frame of Read was set to"
|
||||
"{}".format(script_start))
|
||||
|
||||
|
||||
def loader_shift(node, frame, relative=False):
|
||||
"""Shift global in time by i preserving duration
|
||||
|
||||
This moves the loader by i frames preserving global duration. When relative
|
||||
is False it will shift the global in to the start frame.
|
||||
|
||||
Args:
|
||||
loader (tool): The fusion loader tool.
|
||||
frame (int): The amount of frames to move.
|
||||
relative (bool): When True the shift is relative, else the shift will
|
||||
change the global in to frame.
|
||||
|
||||
Returns:
|
||||
int: The resulting relative frame change (how much it moved)
|
||||
|
||||
"""
|
||||
# working script frame range
|
||||
script_start = nuke.root()["first_frame"].value()
|
||||
|
||||
if relative:
|
||||
node['frame_mode'].setValue("start at")
|
||||
node['frame'].setValue(str(script_start))
|
||||
else:
|
||||
node['frame_mode'].setValue("start at")
|
||||
node['frame'].setValue(str(frame))
|
||||
|
||||
|
||||
class LoadSequence(api.Loader):
|
||||
"""Load image sequence into Nuke"""
|
||||
|
||||
families = ["render", "source", "plate", "review"]
|
||||
representations = ["exr", "dpx"]
|
||||
|
||||
label = "Load Image Sequence"
|
||||
order = -20
|
||||
icon = "file-video-o"
|
||||
color = "white"
|
||||
|
||||
node_name_template = "{class_name}_{ext}"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
from avalon.nuke import (
|
||||
containerise,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
repr_id = context["representation"]["_id"]
|
||||
|
||||
self.log.info("version_data: {}\n".format(version_data))
|
||||
self.log.debug(
|
||||
"Representation id `{}` ".format(repr_id))
|
||||
|
||||
self.first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
self.handle_start = version_data.get("handleStart", 0)
|
||||
self.handle_end = version_data.get("handleEnd", 0)
|
||||
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
|
||||
# Fallback to asset name when namespace is None
|
||||
if namespace is None:
|
||||
namespace = context['asset']['name']
|
||||
|
||||
first -= self.handle_start
|
||||
last += self.handle_end
|
||||
|
||||
file = self.fname
|
||||
|
||||
if not file:
|
||||
repr_id = context["representation"]["_id"]
|
||||
self.log.warning(
|
||||
"Representation id `{}` is failing to load".format(repr_id))
|
||||
return
|
||||
|
||||
file = file.replace("\\", "/")
|
||||
|
||||
repr_cont = context["representation"]["context"]
|
||||
if "#" not in file:
|
||||
frame = repr_cont.get("frame")
|
||||
if frame:
|
||||
padding = len(frame)
|
||||
file = file.replace(frame, "#" * padding)
|
||||
|
||||
name_data = {
|
||||
"asset": repr_cont["asset"],
|
||||
"subset": repr_cont["subset"],
|
||||
"representation": context["representation"]["name"],
|
||||
"ext": repr_cont["representation"],
|
||||
"id": context["representation"]["_id"],
|
||||
"class_name": self.__class__.__name__
|
||||
}
|
||||
|
||||
read_name = self.node_name_template.format(**name_data)
|
||||
|
||||
# Create the Loader with the filename path set
|
||||
with viewer_update_and_undo_stop():
|
||||
# TODO: it might be universal read to img/geo/camera
|
||||
r = nuke.createNode(
|
||||
"Read",
|
||||
"name {}".format(read_name))
|
||||
r["file"].setValue(file)
|
||||
|
||||
# Set colorspace defined in version data
|
||||
colorspace = context["version"]["data"].get("colorspace")
|
||||
if colorspace:
|
||||
r["colorspace"].setValue(str(colorspace))
|
||||
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
if preset_clrsp is not None:
|
||||
r["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
loader_shift(r, first, relative=True)
|
||||
r["origfirst"].setValue(int(first))
|
||||
r["first"].setValue(int(first))
|
||||
r["origlast"].setValue(int(last))
|
||||
r["last"].setValue(int(last))
|
||||
|
||||
# add additional metadata from the version to imprint Avalon knob
|
||||
add_keys = ["frameStart", "frameEnd",
|
||||
"source", "colorspace", "author", "fps", "version",
|
||||
"handleStart", "handleEnd"]
|
||||
|
||||
data_imprint = {}
|
||||
for k in add_keys:
|
||||
if k == 'version':
|
||||
data_imprint.update({k: context["version"]['name']})
|
||||
else:
|
||||
data_imprint.update(
|
||||
{k: context["version"]['data'].get(k, str(None))})
|
||||
|
||||
data_imprint.update({"objectName": read_name})
|
||||
|
||||
r["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
if version_data.get("retime", None):
|
||||
speed = version_data.get("speed", 1)
|
||||
time_warp_nodes = version_data.get("timewarps", [])
|
||||
self.make_retimes(r, speed, time_warp_nodes)
|
||||
|
||||
return containerise(r,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def make_retimes(self, node, speed, time_warp_nodes):
|
||||
''' Create all retime and timewarping nodes with coppied animation '''
|
||||
if speed != 1:
|
||||
rtn = nuke.createNode(
|
||||
"Retime",
|
||||
"speed {}".format(speed))
|
||||
rtn["before"].setValue("continue")
|
||||
rtn["after"].setValue("continue")
|
||||
rtn["input.first_lock"].setValue(True)
|
||||
rtn["input.first"].setValue(
|
||||
self.handle_start + self.first_frame
|
||||
)
|
||||
|
||||
if time_warp_nodes != []:
|
||||
for timewarp in time_warp_nodes:
|
||||
twn = nuke.createNode(timewarp["Class"],
|
||||
"name {}".format(timewarp["name"]))
|
||||
if isinstance(timewarp["lookup"], list):
|
||||
# if array for animation
|
||||
twn["lookup"].setAnimated()
|
||||
for i, value in enumerate(timewarp["lookup"]):
|
||||
twn["lookup"].setValueAt(
|
||||
(self.first_frame + i) + value,
|
||||
(self.first_frame + i))
|
||||
else:
|
||||
# if static value `int`
|
||||
twn["lookup"].setValue(timewarp["lookup"])
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""Update the Loader's path
|
||||
|
||||
Nuke automatically tries to reset some variables when changing
|
||||
the loader's path to a new file. These automatic changes are to its
|
||||
inputs:
|
||||
|
||||
"""
|
||||
|
||||
from avalon.nuke import (
|
||||
update_container
|
||||
)
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
|
||||
assert node.Class() == "Read", "Must be Read"
|
||||
|
||||
repr_cont = representation["context"]
|
||||
|
||||
file = api.get_representation_path(representation)
|
||||
|
||||
if not file:
|
||||
repr_id = representation["_id"]
|
||||
self.log.warning(
|
||||
"Representation id `{}` is failing to load".format(repr_id))
|
||||
return
|
||||
|
||||
file = file.replace("\\", "/")
|
||||
|
||||
if "#" not in file:
|
||||
frame = repr_cont.get("frame")
|
||||
if frame:
|
||||
padding = len(frame)
|
||||
file = file.replace(frame, "#" * padding)
|
||||
|
||||
# Get start frame from version data
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
version_data = version.get("data", {})
|
||||
|
||||
self.first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
self.handle_start = version_data.get("handleStart", 0)
|
||||
self.handle_end = version_data.get("handleEnd", 0)
|
||||
|
||||
first = version_data.get("frameStart")
|
||||
last = version_data.get("frameEnd")
|
||||
|
||||
if first is None:
|
||||
self.log.warning(
|
||||
"Missing start frame for updated version"
|
||||
"assuming starts at frame 0 for: "
|
||||
"{} ({})".format(node['name'].value(), representation))
|
||||
first = 0
|
||||
|
||||
first -= self.handle_start
|
||||
last += self.handle_end
|
||||
|
||||
# Update the loader's path whilst preserving some values
|
||||
with preserve_trim(node):
|
||||
node["file"].setValue(file)
|
||||
self.log.info("__ node['file']: {}".format(node["file"].value()))
|
||||
|
||||
# Set the global in to the start frame of the sequence
|
||||
loader_shift(node, first, relative=True)
|
||||
node["origfirst"].setValue(int(first))
|
||||
node["first"].setValue(int(first))
|
||||
node["origlast"].setValue(int(last))
|
||||
node["last"].setValue(int(last))
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
"representation": str(representation["_id"]),
|
||||
"frameStart": str(first),
|
||||
"frameEnd": str(last),
|
||||
"version": str(version.get("name")),
|
||||
"colorspace": version_data.get("colorspace"),
|
||||
"source": version_data.get("source"),
|
||||
"handleStart": str(self.handle_start),
|
||||
"handleEnd": str(self.handle_end),
|
||||
"fps": str(version_data.get("fps")),
|
||||
"author": version_data.get("author"),
|
||||
"outputDir": version_data.get("outputDir"),
|
||||
})
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd84f20ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int("0x4ecd25ff", 16))
|
||||
|
||||
if version_data.get("retime", None):
|
||||
speed = version_data.get("speed", 1)
|
||||
time_warp_nodes = version_data.get("timewarps", [])
|
||||
self.make_retimes(node, speed, time_warp_nodes)
|
||||
|
||||
# Update the imprinted representation
|
||||
update_container(
|
||||
node,
|
||||
updated_dict
|
||||
)
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
def remove(self, container):
|
||||
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
|
||||
node = nuke.toNode(container['objectName'])
|
||||
assert node.Class() == "Read", "Must be Read"
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
87
openpype/hosts/nuke/plugins/publish/collect_backdrop.py
Normal file
87
openpype/hosts/nuke/plugins/publish/collect_backdrop.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import pyblish.api
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
import nuke
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class CollectBackdrops(pyblish.api.InstancePlugin):
|
||||
"""Collect Backdrop node instance and its content
|
||||
"""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.22
|
||||
label = "Collect Backdrop"
|
||||
hosts = ["nuke"]
|
||||
families = ["nukenodes"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
bckn = instance[0]
|
||||
|
||||
# define size of the backdrop
|
||||
left = bckn.xpos()
|
||||
top = bckn.ypos()
|
||||
right = left + bckn['bdwidth'].value()
|
||||
bottom = top + bckn['bdheight'].value()
|
||||
|
||||
# iterate all nodes
|
||||
for node in nuke.allNodes():
|
||||
|
||||
# exclude viewer
|
||||
if node.Class() == "Viewer":
|
||||
continue
|
||||
|
||||
# find all related nodes
|
||||
if (node.xpos() > left) \
|
||||
and (node.xpos() + node.screenWidth() < right) \
|
||||
and (node.ypos() > top) \
|
||||
and (node.ypos() + node.screenHeight() < bottom):
|
||||
|
||||
# add contained nodes to instance's node list
|
||||
instance.append(node)
|
||||
|
||||
# get all connections from outside of backdrop
|
||||
nodes = instance[1:]
|
||||
connections_in, connections_out = pnlib.get_dependent_nodes(nodes)
|
||||
instance.data["nodeConnectionsIn"] = connections_in
|
||||
instance.data["nodeConnectionsOut"] = connections_out
|
||||
|
||||
# make label nicer
|
||||
instance.data["label"] = "{0} ({1} nodes)".format(
|
||||
bckn.name(), len(instance) - 1)
|
||||
|
||||
instance.data["families"].append(instance.data["family"])
|
||||
|
||||
# Get frame range
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
|
||||
# get version
|
||||
version = instance.context.data.get('version')
|
||||
|
||||
if not version:
|
||||
raise RuntimeError("Script name has no version in the name.")
|
||||
|
||||
instance.data['version'] = version
|
||||
|
||||
# Add version data to instance
|
||||
version_data = {
|
||||
"handles": handle_start,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"version": int(version),
|
||||
"families": [instance.data["family"]] + instance.data["families"],
|
||||
"subset": instance.data["subset"],
|
||||
"fps": instance.context.data["fps"]
|
||||
}
|
||||
|
||||
instance.data.update({
|
||||
"versionData": version_data,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame
|
||||
})
|
||||
self.log.info("Backdrop content collected: `{}`".format(instance[:]))
|
||||
self.log.info("Backdrop instance collected: `{}`".format(instance))
|
||||
17
openpype/hosts/nuke/plugins/publish/collect_framerate.py
Normal file
17
openpype/hosts/nuke/plugins/publish/collect_framerate.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class CollectFramerate(pyblish.api.ContextPlugin):
|
||||
"""Collect framerate."""
|
||||
|
||||
order = pyblish.api.CollectorOrder
|
||||
label = "Collect Framerate"
|
||||
hosts = [
|
||||
"nuke",
|
||||
"nukeassist"
|
||||
]
|
||||
|
||||
def process(self, context):
|
||||
context.data["fps"] = nuke.root()["fps"].getValue()
|
||||
50
openpype/hosts/nuke/plugins/publish/collect_gizmo.py
Normal file
50
openpype/hosts/nuke/plugins/publish/collect_gizmo.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import pyblish.api
|
||||
import nuke
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class CollectGizmo(pyblish.api.InstancePlugin):
|
||||
"""Collect Gizmo (group) node instance and its content
|
||||
"""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.22
|
||||
label = "Collect Gizmo (Group)"
|
||||
hosts = ["nuke"]
|
||||
families = ["gizmo"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
grpn = instance[0]
|
||||
|
||||
# add family to familiess
|
||||
instance.data["families"].insert(0, instance.data["family"])
|
||||
# make label nicer
|
||||
instance.data["label"] = "{0} ({1} nodes)".format(
|
||||
grpn.name(), len(instance) - 1)
|
||||
|
||||
# Get frame range
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
|
||||
# Add version data to instance
|
||||
version_data = {
|
||||
"handles": handle_start,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"colorspace": nuke.root().knob('workingSpaceLUT').value(),
|
||||
"families": [instance.data["family"]] + instance.data["families"],
|
||||
"subset": instance.data["subset"],
|
||||
"fps": instance.context.data["fps"]
|
||||
}
|
||||
|
||||
instance.data.update({
|
||||
"versionData": version_data,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame
|
||||
})
|
||||
self.log.info("Gizmo content collected: `{}`".format(instance[:]))
|
||||
self.log.info("Gizmo instance collected: `{}`".format(instance))
|
||||
132
openpype/hosts/nuke/plugins/publish/collect_reads.py
Normal file
132
openpype/hosts/nuke/plugins/publish/collect_reads.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import os
|
||||
import re
|
||||
import nuke
|
||||
import pyblish.api
|
||||
from avalon import io, api
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class CollectNukeReads(pyblish.api.InstancePlugin):
|
||||
"""Collect all read nodes."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.04
|
||||
label = "Collect Source Reads"
|
||||
hosts = ["nuke", "nukeassist"]
|
||||
families = ["source"]
|
||||
|
||||
def process(self, instance):
|
||||
asset_data = io.find_one({"type": "asset",
|
||||
"name": api.Session["AVALON_ASSET"]})
|
||||
|
||||
self.log.debug("asset_data: {}".format(asset_data["data"]))
|
||||
|
||||
self.log.debug("checking instance: {}".format(instance))
|
||||
|
||||
node = instance[0]
|
||||
if node.Class() != "Read":
|
||||
return
|
||||
|
||||
file_path = node["file"].value()
|
||||
file_name = os.path.basename(file_path)
|
||||
items = file_name.split(".")
|
||||
|
||||
if len(items) < 2:
|
||||
raise ValueError
|
||||
|
||||
ext = items[-1]
|
||||
|
||||
# Get frame range
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = node['first'].value()
|
||||
last_frame = node['last'].value()
|
||||
|
||||
# colorspace
|
||||
colorspace = node["colorspace"].value()
|
||||
if "default" in colorspace:
|
||||
colorspace = colorspace.replace("default (", "").replace(")", "")
|
||||
|
||||
# # Easier way to sequence - Not tested
|
||||
# isSequence = True
|
||||
# if first_frame == last_frame:
|
||||
# isSequence = False
|
||||
|
||||
isSequence = False
|
||||
if len(items) > 1:
|
||||
sequence = items[-2]
|
||||
hash_regex = re.compile(r'([#*])')
|
||||
seq_regex = re.compile(r'[%0-9*d]')
|
||||
hash_match = re.match(hash_regex, sequence)
|
||||
seq_match = re.match(seq_regex, sequence)
|
||||
if hash_match or seq_match:
|
||||
isSequence = True
|
||||
|
||||
# get source path
|
||||
path = nuke.filename(node)
|
||||
source_dir = os.path.dirname(path)
|
||||
self.log.debug('source dir: {}'.format(source_dir))
|
||||
|
||||
if isSequence:
|
||||
source_files = [f for f in os.listdir(source_dir)
|
||||
if ext in f
|
||||
if items[0] in f]
|
||||
else:
|
||||
source_files = file_name
|
||||
|
||||
# Include start and end render frame in label
|
||||
name = node.name()
|
||||
label = "{0} ({1}-{2})".format(
|
||||
name,
|
||||
int(first_frame),
|
||||
int(last_frame)
|
||||
)
|
||||
|
||||
self.log.debug("collected_frames: {}".format(label))
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
'name': ext,
|
||||
'ext': ext,
|
||||
'files': source_files,
|
||||
"stagingDir": source_dir,
|
||||
"frameStart": "%0{}d".format(
|
||||
len(str(last_frame))) % first_frame
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
transfer = False
|
||||
if "publish" in node.knobs():
|
||||
transfer = node["publish"]
|
||||
|
||||
instance.data['transfer'] = transfer
|
||||
|
||||
# Add version data to instance
|
||||
version_data = {
|
||||
"handles": handle_start,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"colorspace": colorspace,
|
||||
"families": [instance.data["family"]],
|
||||
"subset": instance.data["subset"],
|
||||
"fps": instance.context.data["fps"]
|
||||
}
|
||||
|
||||
instance.data.update({
|
||||
"versionData": version_data,
|
||||
"path": path,
|
||||
"stagingDir": source_dir,
|
||||
"ext": ext,
|
||||
"label": label,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame,
|
||||
"colorspace": colorspace,
|
||||
"handles": int(asset_data["data"].get("handles", 0)),
|
||||
"step": 1,
|
||||
"fps": int(nuke.root()['fps'].value())
|
||||
})
|
||||
|
||||
self.log.debug("instance.data: {}".format(instance.data))
|
||||
40
openpype/hosts/nuke/plugins/publish/collect_slate_node.py
Normal file
40
openpype/hosts/nuke/plugins/publish/collect_slate_node.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import pyblish.api
|
||||
import nuke
|
||||
|
||||
|
||||
class CollectSlate(pyblish.api.InstancePlugin):
|
||||
"""Check if SLATE node is in scene and connected to rendering tree"""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.09
|
||||
label = "Collect Slate Node"
|
||||
hosts = ["nuke"]
|
||||
families = ["render", "render.local", "render.farm"]
|
||||
|
||||
def process(self, instance):
|
||||
node = instance[0]
|
||||
|
||||
slate = next((n for n in nuke.allNodes()
|
||||
if "slate" in n.name().lower()
|
||||
if not n["disable"].getValue()),
|
||||
None)
|
||||
|
||||
if slate:
|
||||
# check if slate node is connected to write node tree
|
||||
slate_check = 0
|
||||
slate_node = None
|
||||
while slate_check == 0:
|
||||
try:
|
||||
node = node.dependencies()[0]
|
||||
if slate.name() in node.name():
|
||||
slate_node = node
|
||||
slate_check = 1
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
if slate_node:
|
||||
instance.data["slateNode"] = slate_node
|
||||
instance.data["families"].append("slate")
|
||||
self.log.info(
|
||||
"Slate node is in node graph: `{}`".format(slate.name()))
|
||||
self.log.debug(
|
||||
"__ instance: `{}`".format(instance))
|
||||
103
openpype/hosts/nuke/plugins/publish/extract_backdrop.py
Normal file
103
openpype/hosts/nuke/plugins/publish/extract_backdrop.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
import nuke
|
||||
import os
|
||||
import openpype
|
||||
reload(pnlib)
|
||||
|
||||
class ExtractBackdropNode(openpype.api.Extractor):
|
||||
"""Extracting content of backdrop nodes
|
||||
|
||||
Will create nuke script only with containing nodes.
|
||||
Also it will solve Input and Output nodes.
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder
|
||||
label = "Extract Backdrop"
|
||||
hosts = ["nuke"]
|
||||
families = ["nukenodes"]
|
||||
|
||||
def process(self, instance):
|
||||
tmp_nodes = list()
|
||||
nodes = instance[1:]
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = "{0}.nk".format(instance.name)
|
||||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
# all connections outside of backdrop
|
||||
connections_in = instance.data["nodeConnectionsIn"]
|
||||
connections_out = instance.data["nodeConnectionsOut"]
|
||||
self.log.debug("_ connections_in: `{}`".format(connections_in))
|
||||
self.log.debug("_ connections_out: `{}`".format(connections_out))
|
||||
|
||||
# create input nodes and name them as passing node (*_INP)
|
||||
for n, inputs in connections_in.items():
|
||||
for i, input in inputs:
|
||||
inpn = nuke.createNode("Input")
|
||||
inpn["name"].setValue("{}_{}_INP".format(n.name(), i))
|
||||
n.setInput(i, inpn)
|
||||
inpn.setXYpos(input.xpos(), input.ypos())
|
||||
nodes.append(inpn)
|
||||
tmp_nodes.append(inpn)
|
||||
|
||||
anlib.reset_selection()
|
||||
|
||||
# connect output node
|
||||
for n, output in connections_out.items():
|
||||
opn = nuke.createNode("Output")
|
||||
self.log.info(n.name())
|
||||
self.log.info(output.name())
|
||||
output.setInput(
|
||||
next((i for i, d in enumerate(output.dependencies())
|
||||
if d.name() in n.name()), 0), opn)
|
||||
opn.setInput(0, n)
|
||||
opn.autoplace()
|
||||
nodes.append(opn)
|
||||
tmp_nodes.append(opn)
|
||||
anlib.reset_selection()
|
||||
|
||||
# select nodes to copy
|
||||
anlib.reset_selection()
|
||||
anlib.select_nodes(nodes)
|
||||
# create tmp nk file
|
||||
# save file to the path
|
||||
nuke.nodeCopy(path)
|
||||
|
||||
# Clean up
|
||||
for tn in tmp_nodes:
|
||||
nuke.delete(tn)
|
||||
|
||||
# restore original connections
|
||||
# reconnect input node
|
||||
for n, inputs in connections_in.items():
|
||||
for i, input in inputs:
|
||||
n.setInput(i, input)
|
||||
|
||||
# reconnect output node
|
||||
for n, output in connections_out.items():
|
||||
output.setInput(
|
||||
next((i for i, d in enumerate(output.dependencies())
|
||||
if d.name() in n.name()), 0), n)
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
# create representation
|
||||
representation = {
|
||||
'name': 'nk',
|
||||
'ext': 'nk',
|
||||
'files': filename,
|
||||
"stagingDir": stagingdir
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
self.log.info("Extracted instance '{}' to: {}".format(
|
||||
instance.name, path))
|
||||
|
||||
self.log.info("Data {}".format(
|
||||
instance.data))
|
||||
185
openpype/hosts/nuke/plugins/publish/extract_camera.py
Normal file
185
openpype/hosts/nuke/plugins/publish/extract_camera.py
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import nuke
|
||||
import os
|
||||
import math
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from pprint import pformat
|
||||
|
||||
|
||||
class ExtractCamera(openpype.api.Extractor):
|
||||
""" 3D camera exctractor
|
||||
"""
|
||||
label = 'Exctract Camera'
|
||||
order = pyblish.api.ExtractorOrder
|
||||
families = ["camera"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
# presets
|
||||
write_geo_knobs = [
|
||||
("file_type", "abc"),
|
||||
("storageFormat", "Ogawa"),
|
||||
("writeGeometries", False),
|
||||
("writePointClouds", False),
|
||||
("writeAxes", False)
|
||||
]
|
||||
|
||||
def process(self, instance):
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
step = 1
|
||||
output_range = str(nuke.FrameRange(first_frame, last_frame, step))
|
||||
|
||||
self.log.info("instance.data: `{}`".format(
|
||||
pformat(instance.data)))
|
||||
|
||||
rm_nodes = list()
|
||||
self.log.info("Crating additional nodes")
|
||||
subset = instance.data["subset"]
|
||||
staging_dir = self.staging_dir(instance)
|
||||
|
||||
# get extension form preset
|
||||
extension = next((k[1] for k in self.write_geo_knobs
|
||||
if k[0] == "file_type"), None)
|
||||
if not extension:
|
||||
raise RuntimeError(
|
||||
"Bad config for extension in presets. "
|
||||
"Talk to your supervisor or pipeline admin")
|
||||
|
||||
# create file name and path
|
||||
filename = subset + ".{}".format(extension)
|
||||
file_path = os.path.join(staging_dir, filename).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
# bake camera with axeses onto word coordinate XYZ
|
||||
rm_n = bakeCameraWithAxeses(
|
||||
nuke.toNode(instance.data["name"]), output_range)
|
||||
rm_nodes.append(rm_n)
|
||||
|
||||
# create scene node
|
||||
rm_n = nuke.createNode("Scene")
|
||||
rm_nodes.append(rm_n)
|
||||
|
||||
# create write geo node
|
||||
wg_n = nuke.createNode("WriteGeo")
|
||||
wg_n["file"].setValue(file_path)
|
||||
# add path to write to
|
||||
for k, v in self.write_geo_knobs:
|
||||
wg_n[k].setValue(v)
|
||||
rm_nodes.append(wg_n)
|
||||
|
||||
# write out camera
|
||||
nuke.execute(
|
||||
wg_n,
|
||||
int(first_frame),
|
||||
int(last_frame)
|
||||
)
|
||||
# erase additional nodes
|
||||
for n in rm_nodes:
|
||||
nuke.delete(n)
|
||||
|
||||
self.log.info(file_path)
|
||||
|
||||
# create representation data
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
'name': extension,
|
||||
'ext': extension,
|
||||
'files': filename,
|
||||
"stagingDir": staging_dir,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
instance.data.update({
|
||||
"path": file_path,
|
||||
"outputDir": staging_dir,
|
||||
"ext": extension,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"frameStartHandle": first_frame,
|
||||
"frameEndHandle": last_frame,
|
||||
})
|
||||
|
||||
self.log.info("Extracted instance '{0}' to: {1}".format(
|
||||
instance.name, file_path))
|
||||
|
||||
|
||||
def bakeCameraWithAxeses(camera_node, output_range):
|
||||
""" Baking all perent hiearchy of axeses into camera
|
||||
with transposition onto word XYZ coordinance
|
||||
"""
|
||||
bakeFocal = False
|
||||
bakeHaperture = False
|
||||
bakeVaperture = False
|
||||
|
||||
camera_matrix = camera_node['world_matrix']
|
||||
|
||||
new_cam_n = nuke.createNode("Camera2")
|
||||
new_cam_n.setInput(0, None)
|
||||
new_cam_n['rotate'].setAnimated()
|
||||
new_cam_n['translate'].setAnimated()
|
||||
|
||||
old_focal = camera_node['focal']
|
||||
if old_focal.isAnimated() and not (old_focal.animation(0).constant()):
|
||||
new_cam_n['focal'].setAnimated()
|
||||
bakeFocal = True
|
||||
else:
|
||||
new_cam_n['focal'].setValue(old_focal.value())
|
||||
|
||||
old_haperture = camera_node['haperture']
|
||||
if old_haperture.isAnimated() and not (
|
||||
old_haperture.animation(0).constant()):
|
||||
new_cam_n['haperture'].setAnimated()
|
||||
bakeHaperture = True
|
||||
else:
|
||||
new_cam_n['haperture'].setValue(old_haperture.value())
|
||||
|
||||
old_vaperture = camera_node['vaperture']
|
||||
if old_vaperture.isAnimated() and not (
|
||||
old_vaperture.animation(0).constant()):
|
||||
new_cam_n['vaperture'].setAnimated()
|
||||
bakeVaperture = True
|
||||
else:
|
||||
new_cam_n['vaperture'].setValue(old_vaperture.value())
|
||||
|
||||
new_cam_n['win_translate'].setValue(camera_node['win_translate'].value())
|
||||
new_cam_n['win_scale'].setValue(camera_node['win_scale'].value())
|
||||
|
||||
for x in nuke.FrameRange(output_range):
|
||||
math_matrix = nuke.math.Matrix4()
|
||||
for y in range(camera_matrix.height()):
|
||||
for z in range(camera_matrix.width()):
|
||||
matrix_pointer = z + (y * camera_matrix.width())
|
||||
math_matrix[matrix_pointer] = camera_matrix.getValueAt(
|
||||
x, (y + (z * camera_matrix.width())))
|
||||
|
||||
rot_matrix = nuke.math.Matrix4(math_matrix)
|
||||
rot_matrix.rotationOnly()
|
||||
rot = rot_matrix.rotationsZXY()
|
||||
|
||||
new_cam_n['rotate'].setValueAt(math.degrees(rot[0]), x, 0)
|
||||
new_cam_n['rotate'].setValueAt(math.degrees(rot[1]), x, 1)
|
||||
new_cam_n['rotate'].setValueAt(math.degrees(rot[2]), x, 2)
|
||||
new_cam_n['translate'].setValueAt(
|
||||
camera_matrix.getValueAt(x, 3), x, 0)
|
||||
new_cam_n['translate'].setValueAt(
|
||||
camera_matrix.getValueAt(x, 7), x, 1)
|
||||
new_cam_n['translate'].setValueAt(
|
||||
camera_matrix.getValueAt(x, 11), x, 2)
|
||||
|
||||
if bakeFocal:
|
||||
new_cam_n['focal'].setValueAt(old_focal.getValueAt(x), x)
|
||||
if bakeHaperture:
|
||||
new_cam_n['haperture'].setValueAt(old_haperture.getValueAt(x), x)
|
||||
if bakeVaperture:
|
||||
new_cam_n['vaperture'].setValueAt(old_vaperture.getValueAt(x), x)
|
||||
|
||||
return new_cam_n
|
||||
94
openpype/hosts/nuke/plugins/publish/extract_gizmo.py
Normal file
94
openpype/hosts/nuke/plugins/publish/extract_gizmo.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import utils as pnutils
|
||||
import nuke
|
||||
import os
|
||||
import openpype
|
||||
|
||||
|
||||
class ExtractGizmo(openpype.api.Extractor):
|
||||
"""Extracting Gizmo (Group) node
|
||||
|
||||
Will create nuke script only with the Gizmo node.
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder
|
||||
label = "Extract Gizmo (Group)"
|
||||
hosts = ["nuke"]
|
||||
families = ["gizmo"]
|
||||
|
||||
def process(self, instance):
|
||||
tmp_nodes = list()
|
||||
orig_grpn = instance[0]
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = "{0}.nk".format(instance.name)
|
||||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
orig_grpn_name = orig_grpn.name()
|
||||
tmp_grpn_name = orig_grpn_name + "_tmp"
|
||||
# select original group node
|
||||
anlib.select_nodes([orig_grpn])
|
||||
|
||||
# copy to clipboard
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
|
||||
# reset selection to none
|
||||
anlib.reset_selection()
|
||||
|
||||
# paste clipboard
|
||||
nuke.nodePaste("%clipboard%")
|
||||
|
||||
# assign pasted node
|
||||
copy_grpn = nuke.selectedNode()
|
||||
copy_grpn.setXYpos((orig_grpn.xpos() + 120), orig_grpn.ypos())
|
||||
|
||||
# convert gizmos to groups
|
||||
pnutils.bake_gizmos_recursively(copy_grpn)
|
||||
|
||||
# remove avalonknobs
|
||||
knobs = copy_grpn.knobs()
|
||||
avalon_knobs = [k for k in knobs.keys()
|
||||
for ak in ["avalon:", "ak:"]
|
||||
if ak in k]
|
||||
avalon_knobs.append("publish")
|
||||
for ak in avalon_knobs:
|
||||
copy_grpn.removeKnob(knobs[ak])
|
||||
|
||||
# add to temporary nodes
|
||||
tmp_nodes.append(copy_grpn)
|
||||
|
||||
# swap names
|
||||
orig_grpn.setName(tmp_grpn_name)
|
||||
copy_grpn.setName(orig_grpn_name)
|
||||
|
||||
# create tmp nk file
|
||||
# save file to the path
|
||||
nuke.nodeCopy(path)
|
||||
|
||||
# Clean up
|
||||
for tn in tmp_nodes:
|
||||
nuke.delete(tn)
|
||||
|
||||
# rename back to original
|
||||
orig_grpn.setName(orig_grpn_name)
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
# create representation
|
||||
representation = {
|
||||
'name': 'gizmo',
|
||||
'ext': 'nk',
|
||||
'files': filename,
|
||||
"stagingDir": stagingdir
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
self.log.info("Extracted instance '{}' to: {}".format(
|
||||
instance.name, path))
|
||||
|
||||
self.log.info("Data {}".format(
|
||||
instance.data))
|
||||
38
openpype/hosts/nuke/plugins/publish/extract_ouput_node.py
Normal file
38
openpype/hosts/nuke/plugins/publish/extract_ouput_node.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
from avalon.nuke import maintained_selection
|
||||
|
||||
class CreateOutputNode(pyblish.api.ContextPlugin):
|
||||
"""Adding output node for each ouput write node
|
||||
So when latly user will want to Load .nk as LifeGroup or Precomp
|
||||
Nuke will not complain about missing Output node
|
||||
"""
|
||||
label = 'Output Node Create'
|
||||
order = pyblish.api.ExtractorOrder + 0.4
|
||||
families = ["workfile"]
|
||||
hosts = ['nuke']
|
||||
|
||||
def process(self, context):
|
||||
# capture selection state
|
||||
with maintained_selection():
|
||||
active_node = [node for inst in context[:]
|
||||
for node in inst[:]
|
||||
if "ak:family" in node.knobs()]
|
||||
|
||||
if active_node:
|
||||
self.log.info(active_node)
|
||||
active_node = active_node[0]
|
||||
self.log.info(active_node)
|
||||
active_node['selected'].setValue(True)
|
||||
|
||||
# select only instance render node
|
||||
output_node = nuke.createNode("Output")
|
||||
|
||||
# deselect all and select the original selection
|
||||
output_node['selected'].setValue(False)
|
||||
|
||||
# save script
|
||||
nuke.scriptSave()
|
||||
|
||||
# add node to instance node list
|
||||
context.data["outputNode"] = output_node
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class ExtractOutputDirectory(pyblish.api.InstancePlugin):
|
||||
"""Extracts the output path for any collection or single output_path."""
|
||||
|
||||
order = pyblish.api.ExtractorOrder - 0.05
|
||||
label = "Output Directory"
|
||||
optional = True
|
||||
|
||||
# targets = ["process"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
path = None
|
||||
|
||||
if "output_path" in instance.data.keys():
|
||||
path = instance.data["path"]
|
||||
|
||||
if not path:
|
||||
return
|
||||
|
||||
if not os.path.exists(os.path.dirname(path)):
|
||||
os.makedirs(os.path.dirname(path))
|
||||
103
openpype/hosts/nuke/plugins/publish/extract_render_local.py
Normal file
103
openpype/hosts/nuke/plugins/publish/extract_render_local.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import pyblish.api
|
||||
import nuke
|
||||
import os
|
||||
import openpype
|
||||
import clique
|
||||
|
||||
|
||||
class NukeRenderLocal(openpype.api.Extractor):
|
||||
# TODO: rewrite docstring to nuke
|
||||
"""Render the current Fusion composition locally.
|
||||
|
||||
Extract the result of savers by starting a comp render
|
||||
This will run the local render of Fusion.
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder
|
||||
label = "Render Local"
|
||||
hosts = ["nuke"]
|
||||
families = ["render.local", "prerender.local"]
|
||||
|
||||
def process(self, instance):
|
||||
families = instance.data["families"]
|
||||
|
||||
node = None
|
||||
for x in instance:
|
||||
if x.Class() == "Write":
|
||||
node = x
|
||||
|
||||
self.log.debug("instance collected: {}".format(instance.data))
|
||||
|
||||
first_frame = instance.data.get("frameStartHandle", None)
|
||||
|
||||
# exception for slate workflow
|
||||
if "slate" in families:
|
||||
first_frame -= 1
|
||||
|
||||
last_frame = instance.data.get("frameEndHandle", None)
|
||||
node_subset_name = instance.data.get("name", None)
|
||||
|
||||
self.log.info("Starting render")
|
||||
self.log.info("Start frame: {}".format(first_frame))
|
||||
self.log.info("End frame: {}".format(last_frame))
|
||||
|
||||
# Ensure output directory exists.
|
||||
directory = os.path.dirname(node["file"].value())
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
# Render frames
|
||||
nuke.execute(
|
||||
node_subset_name,
|
||||
int(first_frame),
|
||||
int(last_frame)
|
||||
)
|
||||
|
||||
# exception for slate workflow
|
||||
if "slate" in families:
|
||||
first_frame += 1
|
||||
|
||||
path = node['file'].value()
|
||||
out_dir = os.path.dirname(path)
|
||||
ext = node["file_type"].value()
|
||||
|
||||
if "representations" not in instance.data:
|
||||
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
|
||||
}
|
||||
instance.data["representations"].append(repre)
|
||||
|
||||
self.log.info("Extracted instance '{0}' to: {1}".format(
|
||||
instance.name,
|
||||
out_dir
|
||||
))
|
||||
|
||||
# redefinition of families
|
||||
if "render.local" in families:
|
||||
instance.data['family'] = 'render'
|
||||
families.remove('render.local')
|
||||
families.insert(0, "render2d")
|
||||
elif "prerender.local" in families:
|
||||
instance.data['family'] = 'prerender'
|
||||
families.remove('prerender.local')
|
||||
families.insert(0, "prerender")
|
||||
instance.data["families"] = families
|
||||
|
||||
collections, remainder = clique.assemble(collected_frames)
|
||||
self.log.info('collections: {}'.format(str(collections)))
|
||||
|
||||
if collections:
|
||||
collection = collections[0]
|
||||
instance.data['collection'] = collection
|
||||
|
||||
self.log.info('Finished render')
|
||||
|
||||
self.log.debug("instance extracted: {}".format(instance.data))
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
import openpype
|
||||
reload(pnlib)
|
||||
|
||||
|
||||
class ExtractReviewDataLut(openpype.api.Extractor):
|
||||
"""Extracts movie and thumbnail with baked in luts
|
||||
|
||||
must be run after extract_render_local.py
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder + 0.005
|
||||
label = "Extract Review Data Lut"
|
||||
|
||||
families = ["review"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
def process(self, instance):
|
||||
families = instance.data["families"]
|
||||
self.log.info("Creating staging dir...")
|
||||
if "representations" in instance.data:
|
||||
staging_dir = instance.data[
|
||||
"representations"][0]["stagingDir"].replace("\\", "/")
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
instance.data["representations"][0]["tags"] = ["review"]
|
||||
else:
|
||||
instance.data["representations"] = []
|
||||
# get output path
|
||||
render_path = instance.data['path']
|
||||
staging_dir = os.path.normpath(os.path.dirname(render_path))
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
self.log.info(
|
||||
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
|
||||
|
||||
# generate data
|
||||
with anlib.maintained_selection():
|
||||
exporter = pnlib.ExporterReviewLut(
|
||||
self, instance
|
||||
)
|
||||
data = exporter.generate_lut()
|
||||
|
||||
# assign to representations
|
||||
instance.data["lutPath"] = os.path.join(
|
||||
exporter.stagingDir, exporter.file).replace("\\", "/")
|
||||
instance.data["representations"] += data["representations"]
|
||||
|
||||
if "render.farm" in families:
|
||||
instance.data["families"].remove("review")
|
||||
instance.data["families"].remove("ftrack")
|
||||
|
||||
self.log.debug(
|
||||
"_ lutPath: {}".format(instance.data["lutPath"]))
|
||||
self.log.debug(
|
||||
"_ representations: {}".format(instance.data["representations"]))
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import lib as pnlib
|
||||
import openpype
|
||||
|
||||
|
||||
class ExtractReviewDataMov(openpype.api.Extractor):
|
||||
"""Extracts movie and thumbnail with baked in luts
|
||||
|
||||
must be run after extract_render_local.py
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder + 0.01
|
||||
label = "Extract Review Data Mov"
|
||||
|
||||
families = ["review"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
# presets
|
||||
viewer_lut_raw = None
|
||||
bake_colorspace_fallback = None
|
||||
bake_colorspace_main = None
|
||||
|
||||
def process(self, instance):
|
||||
families = instance.data["families"]
|
||||
self.log.info("Creating staging dir...")
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = list()
|
||||
|
||||
staging_dir = os.path.normpath(
|
||||
os.path.dirname(instance.data['path']))
|
||||
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
self.log.info(
|
||||
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
|
||||
|
||||
# generate data
|
||||
with anlib.maintained_selection():
|
||||
exporter = pnlib.ExporterReviewMov(
|
||||
self, instance)
|
||||
|
||||
if "render.farm" in families:
|
||||
instance.data["families"].remove("review")
|
||||
instance.data["families"].remove("ftrack")
|
||||
data = exporter.generate_mov(farm=True)
|
||||
|
||||
self.log.debug(
|
||||
"_ data: {}".format(data))
|
||||
|
||||
instance.data.update({
|
||||
"bakeRenderPath": data.get("bakeRenderPath"),
|
||||
"bakeScriptPath": data.get("bakeScriptPath"),
|
||||
"bakeWriteNodeName": data.get("bakeWriteNodeName")
|
||||
})
|
||||
else:
|
||||
data = exporter.generate_mov()
|
||||
|
||||
# assign to representations
|
||||
instance.data["representations"] += data["representations"]
|
||||
|
||||
self.log.debug(
|
||||
"_ representations: {}".format(instance.data["representations"]))
|
||||
15
openpype/hosts/nuke/plugins/publish/extract_script_save.py
Normal file
15
openpype/hosts/nuke/plugins/publish/extract_script_save.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class ExtractScriptSave(pyblish.api.Extractor):
|
||||
"""
|
||||
"""
|
||||
label = 'Script Save'
|
||||
order = pyblish.api.Extractor.order - 0.1
|
||||
hosts = ['nuke']
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
self.log.info('saving script')
|
||||
nuke.scriptSave()
|
||||
169
openpype/hosts/nuke/plugins/publish/extract_slate_frame.py
Normal file
169
openpype/hosts/nuke/plugins/publish/extract_slate_frame.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import os
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
import pyblish.api
|
||||
import openpype
|
||||
|
||||
|
||||
class ExtractSlateFrame(openpype.api.Extractor):
|
||||
"""Extracts movie and thumbnail with baked in luts
|
||||
|
||||
must be run after extract_render_local.py
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder - 0.001
|
||||
label = "Extract Slate Frame"
|
||||
|
||||
families = ["slate"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
|
||||
def process(self, instance):
|
||||
if hasattr(self, "viewer_lut_raw"):
|
||||
self.viewer_lut_raw = self.viewer_lut_raw
|
||||
else:
|
||||
self.viewer_lut_raw = False
|
||||
|
||||
with anlib.maintained_selection():
|
||||
self.log.debug("instance: {}".format(instance))
|
||||
self.log.debug("instance.data[families]: {}".format(
|
||||
instance.data["families"]))
|
||||
|
||||
self.render_slate(instance)
|
||||
|
||||
def render_slate(self, instance):
|
||||
node_subset_name = instance.data.get("name", None)
|
||||
node = instance[0] # group node
|
||||
self.log.info("Creating staging dir...")
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = list()
|
||||
|
||||
staging_dir = os.path.normpath(
|
||||
os.path.dirname(instance.data['path']))
|
||||
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
self.log.info(
|
||||
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
|
||||
|
||||
frame_length = int(
|
||||
instance.data["frameEnd"] - instance.data["frameStart"] + 1
|
||||
)
|
||||
|
||||
temporary_nodes = []
|
||||
collection = instance.data.get("collection", None)
|
||||
|
||||
if collection:
|
||||
# get path
|
||||
fname = os.path.basename(collection.format(
|
||||
"{head}{padding}{tail}"))
|
||||
fhead = collection.format("{head}")
|
||||
|
||||
collected_frames_len = int(len(collection.indexes))
|
||||
|
||||
# get first and last frame
|
||||
first_frame = min(collection.indexes) - 1
|
||||
self.log.info('frame_length: {}'.format(frame_length))
|
||||
self.log.info(
|
||||
'len(collection.indexes): {}'.format(collected_frames_len)
|
||||
)
|
||||
if ("slate" in instance.data["families"]) \
|
||||
and (frame_length != collected_frames_len):
|
||||
first_frame += 1
|
||||
|
||||
last_frame = first_frame
|
||||
else:
|
||||
fname = os.path.basename(instance.data.get("path", None))
|
||||
fhead = os.path.splitext(fname)[0] + "."
|
||||
first_frame = instance.data.get("frameStartHandle", None) - 1
|
||||
last_frame = first_frame
|
||||
|
||||
if "#" in fhead:
|
||||
fhead = fhead.replace("#", "")[:-1]
|
||||
|
||||
previous_node = node
|
||||
|
||||
# get input process and connect it to baking
|
||||
ipn = self.get_view_process_node()
|
||||
if ipn is not None:
|
||||
ipn.setInput(0, previous_node)
|
||||
previous_node = ipn
|
||||
temporary_nodes.append(ipn)
|
||||
|
||||
if not self.viewer_lut_raw:
|
||||
dag_node = nuke.createNode("OCIODisplay")
|
||||
dag_node.setInput(0, previous_node)
|
||||
previous_node = dag_node
|
||||
temporary_nodes.append(dag_node)
|
||||
|
||||
# create write node
|
||||
write_node = nuke.createNode("Write")
|
||||
file = fhead + "slate.png"
|
||||
path = os.path.join(staging_dir, file).replace("\\", "/")
|
||||
instance.data["slateFrame"] = path
|
||||
write_node["file"].setValue(path)
|
||||
write_node["file_type"].setValue("png")
|
||||
write_node["raw"].setValue(1)
|
||||
write_node.setInput(0, previous_node)
|
||||
temporary_nodes.append(write_node)
|
||||
|
||||
# fill slate node with comments
|
||||
self.add_comment_slate_node(instance)
|
||||
|
||||
# Render frames
|
||||
nuke.execute(write_node.name(), int(first_frame), int(last_frame))
|
||||
# also render slate as sequence frame
|
||||
nuke.execute(node_subset_name, int(first_frame), int(last_frame))
|
||||
|
||||
self.log.debug(
|
||||
"slate frame path: {}".format(instance.data["slateFrame"]))
|
||||
|
||||
# Clean up
|
||||
for node in temporary_nodes:
|
||||
nuke.delete(node)
|
||||
|
||||
|
||||
def get_view_process_node(self):
|
||||
|
||||
# Select only the target node
|
||||
if nuke.selectedNodes():
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()]
|
||||
|
||||
ipn_orig = None
|
||||
for v in [n for n in nuke.allNodes()
|
||||
if "Viewer" in n.Class()]:
|
||||
ip = v['input_process'].getValue()
|
||||
ipn = v['input_process_node'].getValue()
|
||||
if "VIEWER_INPUT" not in ipn and ip:
|
||||
ipn_orig = nuke.toNode(ipn)
|
||||
ipn_orig.setSelected(True)
|
||||
|
||||
if ipn_orig:
|
||||
nuke.nodeCopy('%clipboard%')
|
||||
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()] # Deselect all
|
||||
|
||||
nuke.nodePaste('%clipboard%')
|
||||
|
||||
ipn = nuke.selectedNode()
|
||||
|
||||
return ipn
|
||||
|
||||
def add_comment_slate_node(self, instance):
|
||||
node = instance.data.get("slateNode")
|
||||
if not node:
|
||||
return
|
||||
|
||||
comment = instance.context.data.get("comment")
|
||||
intent_value = instance.context.data.get("intent")
|
||||
if intent_value and isinstance(intent_value, dict):
|
||||
intent_value = intent_value.get("value")
|
||||
|
||||
try:
|
||||
node["f_submission_note"].setValue(comment)
|
||||
node["f_submitting_for"].setValue(intent_value or "")
|
||||
except NameError:
|
||||
return
|
||||
instance.data.pop("slateNode")
|
||||
177
openpype/hosts/nuke/plugins/publish/extract_thumbnail.py
Normal file
177
openpype/hosts/nuke/plugins/publish/extract_thumbnail.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import os
|
||||
import nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
import pyblish.api
|
||||
import openpype
|
||||
|
||||
|
||||
class ExtractThumbnail(openpype.api.Extractor):
|
||||
"""Extracts movie and thumbnail with baked in luts
|
||||
|
||||
must be run after extract_render_local.py
|
||||
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder + 0.01
|
||||
label = "Extract Thumbnail"
|
||||
|
||||
families = ["review"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
# presets
|
||||
nodes = {}
|
||||
|
||||
def process(self, instance):
|
||||
if "render.farm" in instance.data["families"]:
|
||||
return
|
||||
|
||||
with anlib.maintained_selection():
|
||||
self.log.debug("instance: {}".format(instance))
|
||||
self.log.debug("instance.data[families]: {}".format(
|
||||
instance.data["families"]))
|
||||
|
||||
self.render_thumbnail(instance)
|
||||
|
||||
def render_thumbnail(self, instance):
|
||||
node = instance[0] # group node
|
||||
self.log.info("Creating staging dir...")
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = list()
|
||||
|
||||
staging_dir = os.path.normpath(
|
||||
os.path.dirname(instance.data['path']))
|
||||
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
self.log.info(
|
||||
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
|
||||
|
||||
temporary_nodes = []
|
||||
collection = instance.data.get("collection", None)
|
||||
|
||||
if collection:
|
||||
# get path
|
||||
fname = os.path.basename(collection.format(
|
||||
"{head}{padding}{tail}"))
|
||||
fhead = collection.format("{head}")
|
||||
|
||||
# get first and last frame
|
||||
first_frame = min(collection.indexes)
|
||||
last_frame = max(collection.indexes)
|
||||
else:
|
||||
fname = os.path.basename(instance.data.get("path", None))
|
||||
fhead = os.path.splitext(fname)[0] + "."
|
||||
first_frame = instance.data.get("frameStart", None)
|
||||
last_frame = instance.data.get("frameEnd", None)
|
||||
|
||||
if "#" in fhead:
|
||||
fhead = fhead.replace("#", "")[:-1]
|
||||
|
||||
path_render = os.path.join(staging_dir, fname).replace("\\", "/")
|
||||
# check if file exist otherwise connect to write node
|
||||
if os.path.isfile(path_render):
|
||||
rnode = nuke.createNode("Read")
|
||||
|
||||
rnode["file"].setValue(path_render)
|
||||
|
||||
rnode["first"].setValue(first_frame)
|
||||
rnode["origfirst"].setValue(first_frame)
|
||||
rnode["last"].setValue(last_frame)
|
||||
rnode["origlast"].setValue(last_frame)
|
||||
temporary_nodes.append(rnode)
|
||||
previous_node = rnode
|
||||
else:
|
||||
previous_node = node
|
||||
|
||||
# get input process and connect it to baking
|
||||
ipn = self.get_view_process_node()
|
||||
if ipn is not None:
|
||||
ipn.setInput(0, previous_node)
|
||||
previous_node = ipn
|
||||
temporary_nodes.append(ipn)
|
||||
|
||||
reformat_node = nuke.createNode("Reformat")
|
||||
|
||||
ref_node = self.nodes.get("Reformat", None)
|
||||
if ref_node:
|
||||
for k, v in ref_node:
|
||||
self.log.debug("k, v: {0}:{1}".format(k, v))
|
||||
if isinstance(v, unicode):
|
||||
v = str(v)
|
||||
reformat_node[k].setValue(v)
|
||||
|
||||
reformat_node.setInput(0, previous_node)
|
||||
previous_node = reformat_node
|
||||
temporary_nodes.append(reformat_node)
|
||||
|
||||
dag_node = nuke.createNode("OCIODisplay")
|
||||
dag_node.setInput(0, previous_node)
|
||||
previous_node = dag_node
|
||||
temporary_nodes.append(dag_node)
|
||||
|
||||
# create write node
|
||||
write_node = nuke.createNode("Write")
|
||||
file = fhead + "jpeg"
|
||||
name = "thumbnail"
|
||||
path = os.path.join(staging_dir, file).replace("\\", "/")
|
||||
instance.data["thumbnail"] = path
|
||||
write_node["file"].setValue(path)
|
||||
write_node["file_type"].setValue("jpeg")
|
||||
write_node["raw"].setValue(1)
|
||||
write_node.setInput(0, previous_node)
|
||||
temporary_nodes.append(write_node)
|
||||
tags = ["thumbnail", "publish_on_farm"]
|
||||
|
||||
# retime for
|
||||
first_frame = int(last_frame) / 2
|
||||
last_frame = int(last_frame) / 2
|
||||
|
||||
repre = {
|
||||
'name': name,
|
||||
'ext': "jpeg",
|
||||
"outputName": "thumb",
|
||||
'files': file,
|
||||
"stagingDir": staging_dir,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame,
|
||||
"tags": tags
|
||||
}
|
||||
instance.data["representations"].append(repre)
|
||||
|
||||
# Render frames
|
||||
nuke.execute(write_node.name(), int(first_frame), int(last_frame))
|
||||
|
||||
self.log.debug(
|
||||
"representations: {}".format(instance.data["representations"]))
|
||||
|
||||
# Clean up
|
||||
for node in temporary_nodes:
|
||||
nuke.delete(node)
|
||||
|
||||
def get_view_process_node(self):
|
||||
|
||||
# Select only the target node
|
||||
if nuke.selectedNodes():
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()]
|
||||
|
||||
ipn_orig = None
|
||||
for v in [n for n in nuke.allNodes()
|
||||
if "Viewer" == n.Class()]:
|
||||
ip = v['input_process'].getValue()
|
||||
ipn = v['input_process_node'].getValue()
|
||||
if "VIEWER_INPUT" not in ipn and ip:
|
||||
ipn_orig = nuke.toNode(ipn)
|
||||
ipn_orig.setSelected(True)
|
||||
|
||||
if ipn_orig:
|
||||
nuke.nodeCopy('%clipboard%')
|
||||
|
||||
# Deselect all
|
||||
[n.setSelected(False) for n in nuke.selectedNodes()]
|
||||
|
||||
nuke.nodePaste('%clipboard%')
|
||||
|
||||
ipn = nuke.selectedNode()
|
||||
|
||||
return ipn
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
import nuke
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class IncrementScriptVersion(pyblish.api.ContextPlugin):
|
||||
"""Increment current script version."""
|
||||
|
||||
order = pyblish.api.IntegratorOrder + 0.9
|
||||
label = "Increment Script Version"
|
||||
optional = True
|
||||
families = ["workfile", "render", "render.local", "render.farm"]
|
||||
hosts = ['nuke']
|
||||
|
||||
def process(self, context):
|
||||
|
||||
assert all(result["success"] for result in context.data["results"]), (
|
||||
"Publishing not succesfull so version is not increased.")
|
||||
|
||||
from openpype.lib import version_up
|
||||
path = context.data["currentFile"]
|
||||
nuke.scriptSaveAs(version_up(path))
|
||||
self.log.info('Incrementing script version')
|
||||
153
openpype/hosts/nuke/plugins/publish/precollect_instances.py
Normal file
153
openpype/hosts/nuke/plugins/publish/precollect_instances.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
from avalon import io, api
|
||||
from avalon.nuke import lib as anlib
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class PreCollectNukeInstances(pyblish.api.ContextPlugin):
|
||||
"""Collect all nodes with Avalon knob."""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.59
|
||||
label = "Pre-collect Instances"
|
||||
hosts = ["nuke", "nukeassist"]
|
||||
|
||||
# presets
|
||||
sync_workfile_version = False
|
||||
|
||||
def process(self, context):
|
||||
asset_data = io.find_one({
|
||||
"type": "asset",
|
||||
"name": api.Session["AVALON_ASSET"]
|
||||
})
|
||||
|
||||
self.log.debug("asset_data: {}".format(asset_data["data"]))
|
||||
instances = []
|
||||
|
||||
root = nuke.root()
|
||||
|
||||
self.log.debug("nuke.allNodes(): {}".format(nuke.allNodes()))
|
||||
for node in nuke.allNodes():
|
||||
|
||||
if node.Class() in ["Viewer", "Dot"]:
|
||||
continue
|
||||
|
||||
try:
|
||||
if node["disable"].value():
|
||||
continue
|
||||
except Exception as E:
|
||||
self.log.warning(E)
|
||||
|
||||
# get data from avalon knob
|
||||
avalon_knob_data = anlib.get_avalon_knob_data(
|
||||
node, ["avalon:", "ak:"])
|
||||
|
||||
self.log.debug("avalon_knob_data: {}".format(avalon_knob_data))
|
||||
|
||||
if not avalon_knob_data:
|
||||
continue
|
||||
|
||||
if avalon_knob_data["id"] != "pyblish.avalon.instance":
|
||||
continue
|
||||
|
||||
# establish families
|
||||
family = avalon_knob_data["family"]
|
||||
families_ak = avalon_knob_data.get("families", [])
|
||||
families = list()
|
||||
|
||||
if families_ak:
|
||||
families.append(families_ak.lower())
|
||||
|
||||
families.append(family)
|
||||
|
||||
# except disabled nodes but exclude backdrops in test
|
||||
if ("nukenodes" not in family) and (node["disable"].value()):
|
||||
continue
|
||||
|
||||
subset = avalon_knob_data.get(
|
||||
"subset", None) or node["name"].value()
|
||||
|
||||
# Create instance
|
||||
instance = context.create_instance(subset)
|
||||
instance.append(node)
|
||||
|
||||
# get review knob value
|
||||
review = False
|
||||
if "review" in node.knobs():
|
||||
review = node["review"].value()
|
||||
families.append("review")
|
||||
families.append("ftrack")
|
||||
|
||||
# Add all nodes in group instances.
|
||||
if node.Class() == "Group":
|
||||
# check if it is write node in family
|
||||
if "write" in families:
|
||||
target = node["render"].value()
|
||||
if target == "Use existing frames":
|
||||
# Local rendering
|
||||
self.log.info("flagged for no render")
|
||||
families.append("render")
|
||||
elif target == "Local":
|
||||
# Local rendering
|
||||
self.log.info("flagged for local render")
|
||||
families.append("{}.local".format("render"))
|
||||
elif target == "On farm":
|
||||
# Farm rendering
|
||||
self.log.info("flagged for farm render")
|
||||
instance.data["transfer"] = False
|
||||
families.append("{}.farm".format("render"))
|
||||
if "render" in families:
|
||||
families.remove("render")
|
||||
family = "write"
|
||||
|
||||
node.begin()
|
||||
for i in nuke.allNodes():
|
||||
instance.append(i)
|
||||
node.end()
|
||||
|
||||
self.log.debug("__ families: `{}`".format(families))
|
||||
|
||||
# Get format
|
||||
format = root['format'].value()
|
||||
resolution_width = format.width()
|
||||
resolution_height = format.height()
|
||||
pixel_aspect = format.pixelAspect()
|
||||
|
||||
# get publish knob value
|
||||
if "publish" not in node.knobs():
|
||||
anlib.add_publish_knob(node)
|
||||
|
||||
# sync workfile version
|
||||
if not next((f for f in families
|
||||
if "prerender" in f),
|
||||
None) and self.sync_workfile_version:
|
||||
# get version to instance for integration
|
||||
instance.data['version'] = instance.context.data['version']
|
||||
|
||||
instance.data.update({
|
||||
"subset": subset,
|
||||
"asset": avalon_knob_data["asset"],
|
||||
"label": node.name(),
|
||||
"name": node.name(),
|
||||
"subset": subset,
|
||||
"family": family,
|
||||
"families": families,
|
||||
"avalonKnob": avalon_knob_data,
|
||||
"step": 1,
|
||||
"publish": node.knob('publish').value(),
|
||||
"fps": nuke.root()['fps'].value(),
|
||||
"resolutionWidth": resolution_width,
|
||||
"resolutionHeight": resolution_height,
|
||||
"pixelAspect": pixel_aspect,
|
||||
"review": review
|
||||
|
||||
})
|
||||
self.log.info("collected instance: {}".format(instance.data))
|
||||
instances.append(instance)
|
||||
|
||||
# create instances in context data if not are created yet
|
||||
if not context.data.get("instances"):
|
||||
context.data["instances"] = list()
|
||||
|
||||
context.data["instances"].extend(instances)
|
||||
self.log.debug("context: {}".format(context))
|
||||
103
openpype/hosts/nuke/plugins/publish/precollect_workfile.py
Normal file
103
openpype/hosts/nuke/plugins/publish/precollect_workfile.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
import os
|
||||
import openpype.api as pype
|
||||
from avalon.nuke import lib as anlib
|
||||
reload(anlib)
|
||||
|
||||
|
||||
class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||
"""Collect current script for publish."""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.60
|
||||
label = "Pre-collect Workfile"
|
||||
hosts = ['nuke']
|
||||
|
||||
def process(self, context):
|
||||
root = nuke.root()
|
||||
|
||||
current_file = os.path.normpath(nuke.root().name())
|
||||
|
||||
knob_data = anlib.get_avalon_knob_data(root)
|
||||
|
||||
anlib.add_publish_knob(root)
|
||||
|
||||
family = "workfile"
|
||||
task = os.getenv("AVALON_TASK", None)
|
||||
# creating instances per write node
|
||||
staging_dir = os.path.dirname(current_file)
|
||||
base_name = os.path.basename(current_file)
|
||||
subset = family + task.capitalize()
|
||||
|
||||
# Get frame range
|
||||
first_frame = int(root["first_frame"].getValue())
|
||||
last_frame = int(root["last_frame"].getValue())
|
||||
|
||||
handle_start = int(knob_data.get("handleStart", 0))
|
||||
handle_end = int(knob_data.get("handleEnd", 0))
|
||||
|
||||
# Get format
|
||||
format = root['format'].value()
|
||||
resolution_width = format.width()
|
||||
resolution_height = format.height()
|
||||
pixel_aspect = format.pixelAspect()
|
||||
|
||||
# Create instance
|
||||
instance = context.create_instance(subset)
|
||||
instance.add(root)
|
||||
|
||||
script_data = {
|
||||
"asset": os.getenv("AVALON_ASSET", None),
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"resolutionWidth": resolution_width,
|
||||
"resolutionHeight": resolution_height,
|
||||
"pixelAspect": pixel_aspect,
|
||||
|
||||
# backward compatibility
|
||||
"handles": handle_start,
|
||||
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"step": 1,
|
||||
"fps": root['fps'].value(),
|
||||
|
||||
"currentFile": current_file,
|
||||
"version": int(pype.get_version_from_path(current_file)),
|
||||
|
||||
"host": pyblish.api.current_host(),
|
||||
"hostVersion": nuke.NUKE_VERSION_STRING
|
||||
}
|
||||
context.data.update(script_data)
|
||||
|
||||
# creating instance data
|
||||
instance.data.update({
|
||||
"subset": subset,
|
||||
"label": base_name,
|
||||
"name": base_name,
|
||||
"publish": root.knob('publish').value(),
|
||||
"family": family,
|
||||
"families": [family],
|
||||
"representations": list()
|
||||
})
|
||||
|
||||
# adding basic script data
|
||||
instance.data.update(script_data)
|
||||
|
||||
# creating representation
|
||||
representation = {
|
||||
'name': 'nk',
|
||||
'ext': 'nk',
|
||||
'files': base_name,
|
||||
"stagingDir": staging_dir,
|
||||
}
|
||||
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
self.log.info('Publishing script version')
|
||||
|
||||
# create instances in context data if not are created yet
|
||||
if not context.data.get("instances"):
|
||||
context.data["instances"] = list()
|
||||
|
||||
context.data["instances"].append(instance)
|
||||
169
openpype/hosts/nuke/plugins/publish/precollect_writes.py
Normal file
169
openpype/hosts/nuke/plugins/publish/precollect_writes.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import os
|
||||
import nuke
|
||||
import pyblish.api
|
||||
import openpype.api as pype
|
||||
from avalon import io, api
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class CollectNukeWrites(pyblish.api.InstancePlugin):
|
||||
"""Collect all write nodes."""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.58
|
||||
label = "Pre-collect Writes"
|
||||
hosts = ["nuke", "nukeassist"]
|
||||
families = ["write"]
|
||||
|
||||
# preset attributes
|
||||
sync_workfile_version = True
|
||||
|
||||
def process(self, instance):
|
||||
families = instance.data["families"]
|
||||
|
||||
node = None
|
||||
for x in instance:
|
||||
if x.Class() == "Write":
|
||||
node = x
|
||||
|
||||
if node is None:
|
||||
return
|
||||
|
||||
self.log.debug("checking instance: {}".format(instance))
|
||||
|
||||
# Determine defined file type
|
||||
ext = node["file_type"].value()
|
||||
|
||||
# Determine output type
|
||||
output_type = "img"
|
||||
if ext == "mov":
|
||||
output_type = "mov"
|
||||
|
||||
# Get frame range
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
frame_length = int(last_frame - first_frame + 1)
|
||||
|
||||
if node["use_limit"].getValue():
|
||||
first_frame = int(node["first"].getValue())
|
||||
last_frame = int(node["last"].getValue())
|
||||
|
||||
# get path
|
||||
path = nuke.filename(node)
|
||||
output_dir = os.path.dirname(path)
|
||||
self.log.debug('output dir: {}'.format(output_dir))
|
||||
|
||||
# create label
|
||||
name = node.name()
|
||||
# Include start and end render frame in label
|
||||
label = "{0} ({1}-{2})".format(
|
||||
name,
|
||||
int(first_frame),
|
||||
int(last_frame)
|
||||
)
|
||||
|
||||
if [fm for fm in families
|
||||
if fm in ["render", "prerender"]]:
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = list()
|
||||
|
||||
representation = {
|
||||
'name': ext,
|
||||
'ext': ext,
|
||||
"stagingDir": output_dir,
|
||||
"tags": list()
|
||||
}
|
||||
|
||||
try:
|
||||
collected_frames = [f for f in os.listdir(output_dir)
|
||||
if ext in f]
|
||||
if collected_frames:
|
||||
collected_frames_len = len(collected_frames)
|
||||
frame_start_str = "%0{}d".format(
|
||||
len(str(last_frame))) % first_frame
|
||||
representation['frameStart'] = frame_start_str
|
||||
|
||||
# in case slate is expected and not yet rendered
|
||||
self.log.debug("_ frame_length: {}".format(frame_length))
|
||||
self.log.debug(
|
||||
"_ collected_frames_len: {}".format(
|
||||
collected_frames_len))
|
||||
# this will only run if slate frame is not already
|
||||
# rendered from previews publishes
|
||||
if "slate" in instance.data["families"] \
|
||||
and (frame_length == collected_frames_len) \
|
||||
and ("prerender" not in instance.data["families"]):
|
||||
frame_slate_str = "%0{}d".format(
|
||||
len(str(last_frame))) % (first_frame - 1)
|
||||
slate_frame = collected_frames[0].replace(
|
||||
frame_start_str, frame_slate_str)
|
||||
collected_frames.insert(0, slate_frame)
|
||||
|
||||
representation['files'] = collected_frames
|
||||
instance.data["representations"].append(representation)
|
||||
except Exception:
|
||||
instance.data["representations"].append(representation)
|
||||
self.log.debug("couldn't collect frames: {}".format(label))
|
||||
|
||||
# Add version data to instance
|
||||
version_data = {
|
||||
"colorspace": node["colorspace"].value(),
|
||||
}
|
||||
|
||||
group_node = [x for x in instance if x.Class() == "Group"][0]
|
||||
deadlineChunkSize = 1
|
||||
if "deadlineChunkSize" in group_node.knobs():
|
||||
deadlineChunkSize = group_node["deadlineChunkSize"].value()
|
||||
|
||||
deadlinePriority = 50
|
||||
if "deadlinePriority" in group_node.knobs():
|
||||
deadlinePriority = group_node["deadlinePriority"].value()
|
||||
|
||||
instance.data.update({
|
||||
"versionData": version_data,
|
||||
"path": path,
|
||||
"outputDir": output_dir,
|
||||
"ext": ext,
|
||||
"label": label,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"frameStartHandle": first_frame,
|
||||
"frameEndHandle": last_frame,
|
||||
"outputType": output_type,
|
||||
"families": families,
|
||||
"colorspace": node["colorspace"].value(),
|
||||
"deadlineChunkSize": deadlineChunkSize,
|
||||
"deadlinePriority": deadlinePriority
|
||||
})
|
||||
|
||||
if "prerender" in families:
|
||||
instance.data.update({
|
||||
"family": "prerender",
|
||||
"families": []
|
||||
})
|
||||
|
||||
# * Add audio to instance if exists.
|
||||
# Find latest versions document
|
||||
version_doc = pype.get_latest_version(
|
||||
instance.data["asset"], "audioMain"
|
||||
)
|
||||
repre_doc = None
|
||||
if version_doc:
|
||||
# Try to find it's representation (Expected there is only one)
|
||||
repre_doc = io.find_one(
|
||||
{"type": "representation", "parent": version_doc["_id"]}
|
||||
)
|
||||
|
||||
# Add audio to instance if representation was found
|
||||
if repre_doc:
|
||||
instance.data["audio"] = [{
|
||||
"offset": 0,
|
||||
"filename": api.get_representation_path(repre_doc)
|
||||
}]
|
||||
|
||||
self.log.debug("families: {}".format(families))
|
||||
|
||||
self.log.debug("instance.data: {}".format(instance.data))
|
||||
22
openpype/hosts/nuke/plugins/publish/remove_ouput_node.py
Normal file
22
openpype/hosts/nuke/plugins/publish/remove_ouput_node.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import nuke
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class RemoveOutputNode(pyblish.api.ContextPlugin):
|
||||
"""Removing output node for each ouput write node
|
||||
|
||||
"""
|
||||
label = 'Output Node Remove'
|
||||
order = pyblish.api.IntegratorOrder + 0.4
|
||||
families = ["workfile"]
|
||||
hosts = ['nuke']
|
||||
|
||||
def process(self, context):
|
||||
try:
|
||||
output_node = context.data["outputNode"]
|
||||
name = output_node["name"].value()
|
||||
self.log.info("Removing output node: '{}'".format(name))
|
||||
|
||||
nuke.delete(output_node)
|
||||
except Exception:
|
||||
return
|
||||
70
openpype/hosts/nuke/plugins/publish/validate_backdrop.py
Normal file
70
openpype/hosts/nuke/plugins/publish/validate_backdrop.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import pyblish
|
||||
from avalon.nuke import lib as anlib
|
||||
import nuke
|
||||
|
||||
|
||||
class SelectCenterInNodeGraph(pyblish.api.Action):
|
||||
"""
|
||||
Centering failed instance node in node grap
|
||||
"""
|
||||
|
||||
label = "Center node in node graph"
|
||||
icon = "wrench"
|
||||
on = "failed"
|
||||
|
||||
def process(self, context, plugin):
|
||||
|
||||
# Get the errored instances
|
||||
failed = []
|
||||
for result in context.data["results"]:
|
||||
if (result["error"] is not None and result["instance"] is not None
|
||||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
all_xC = list()
|
||||
all_yC = list()
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
# collect all failed nodes xpos and ypos
|
||||
for instance in instances:
|
||||
bdn = instance[0]
|
||||
xC = bdn.xpos() + bdn.screenWidth() / 2
|
||||
yC = bdn.ypos() + bdn.screenHeight() / 2
|
||||
|
||||
all_xC.append(xC)
|
||||
all_yC.append(yC)
|
||||
|
||||
self.log.info("all_xC: `{}`".format(all_xC))
|
||||
self.log.info("all_yC: `{}`".format(all_yC))
|
||||
|
||||
# zoom to nodes in node graph
|
||||
nuke.zoom(2, [min(all_xC), min(all_yC)])
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class ValidateBackdrop(pyblish.api.InstancePlugin):
|
||||
"""Validate amount of nodes on backdrop node in case user
|
||||
forgoten to add nodes above the publishing backdrop node"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["nukenodes"]
|
||||
label = "Validate Backdrop"
|
||||
hosts = ["nuke"]
|
||||
actions = [SelectCenterInNodeGraph]
|
||||
|
||||
def process(self, instance):
|
||||
connections_out = instance.data["nodeConnectionsOut"]
|
||||
|
||||
msg_multiple_outputs = (
|
||||
"Only one outcoming connection from "
|
||||
"\"{}\" is allowed").format(instance.data["name"])
|
||||
assert len(connections_out.keys()) <= 1, msg_multiple_outputs
|
||||
|
||||
msg_no_content = "No content on backdrop node: \"{}\"".format(
|
||||
instance.data["name"])
|
||||
assert len(instance) > 1, msg_no_content
|
||||
58
openpype/hosts/nuke/plugins/publish/validate_gizmo.py
Normal file
58
openpype/hosts/nuke/plugins/publish/validate_gizmo.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import pyblish
|
||||
from avalon.nuke import lib as anlib
|
||||
import nuke
|
||||
|
||||
|
||||
class OpenFailedGroupNode(pyblish.api.Action):
|
||||
"""
|
||||
Centering failed instance node in node grap
|
||||
"""
|
||||
|
||||
label = "Open Gizmo in Node Graph"
|
||||
icon = "wrench"
|
||||
on = "failed"
|
||||
|
||||
def process(self, context, plugin):
|
||||
|
||||
# Get the errored instances
|
||||
failed = []
|
||||
for result in context.data["results"]:
|
||||
if (result["error"] is not None and result["instance"] is not None
|
||||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
# maintain selection
|
||||
with anlib.maintained_selection():
|
||||
# collect all failed nodes xpos and ypos
|
||||
for instance in instances:
|
||||
grpn = instance[0]
|
||||
nuke.showDag(grpn)
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class ValidateGizmo(pyblish.api.InstancePlugin):
|
||||
"""Validate amount of output nodes in gizmo (group) node"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["gizmo"]
|
||||
label = "Validate Gizmo (Group)"
|
||||
hosts = ["nuke"]
|
||||
actions = [OpenFailedGroupNode]
|
||||
|
||||
def process(self, instance):
|
||||
grpn = instance[0]
|
||||
|
||||
with grpn:
|
||||
connections_out = nuke.allNodes('Output')
|
||||
msg_multiple_outputs = "Only one outcoming connection from "
|
||||
"\"{}\" is allowed".format(instance.data["name"])
|
||||
assert len(connections_out) <= 1, msg_multiple_outputs
|
||||
|
||||
connections_in = nuke.allNodes('Input')
|
||||
msg_missing_inputs = "At least one Input node has to be used in: "
|
||||
"\"{}\"".format(instance.data["name"])
|
||||
assert len(connections_in) >= 1, msg_missing_inputs
|
||||
106
openpype/hosts/nuke/plugins/publish/validate_knobs.py
Normal file
106
openpype/hosts/nuke/plugins/publish/validate_knobs.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
|
||||
class ValidateKnobs(pyblish.api.ContextPlugin):
|
||||
"""Ensure knobs are consistent.
|
||||
|
||||
Knobs to validate and their values comes from the
|
||||
|
||||
Controlled by plugin settings that require json in following structure:
|
||||
"ValidateKnobs": {
|
||||
"enabled": true,
|
||||
"knobs": {
|
||||
"family": {
|
||||
"knob_name": knob_value
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = "Validate Knobs"
|
||||
hosts = ["nuke"]
|
||||
actions = [openpype.api.RepairContextAction]
|
||||
optional = True
|
||||
|
||||
def process(self, context):
|
||||
|
||||
invalid = self.get_invalid(context, compute=True)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
"Found knobs with invalid values:\n{}".format(invalid)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, context, compute=False):
|
||||
invalid = context.data.get("invalid_knobs", [])
|
||||
if compute:
|
||||
invalid = cls.get_invalid_knobs(context)
|
||||
|
||||
return invalid
|
||||
|
||||
@classmethod
|
||||
def get_invalid_knobs(cls, context):
|
||||
invalid_knobs = []
|
||||
|
||||
for instance in context:
|
||||
# Filter publisable instances.
|
||||
if not instance.data["publish"]:
|
||||
continue
|
||||
|
||||
# Filter families.
|
||||
families = [instance.data["family"]]
|
||||
families += instance.data.get("families", [])
|
||||
families = list(set(families) & set(cls.knobs.keys()))
|
||||
if not families:
|
||||
continue
|
||||
|
||||
# Get all knobs to validate.
|
||||
knobs = {}
|
||||
for family in families:
|
||||
for preset in cls.knobs[family]:
|
||||
knobs.update({preset: cls.knobs[family][preset]})
|
||||
|
||||
# Get invalid knobs.
|
||||
nodes = []
|
||||
|
||||
for node in nuke.allNodes():
|
||||
nodes.append(node)
|
||||
if node.Class() == "Group":
|
||||
node.begin()
|
||||
for i in nuke.allNodes():
|
||||
nodes.append(i)
|
||||
node.end()
|
||||
|
||||
for node in nodes:
|
||||
for knob in node.knobs():
|
||||
if knob not in knobs.keys():
|
||||
continue
|
||||
|
||||
expected = knobs[knob]
|
||||
if node[knob].value() != expected:
|
||||
invalid_knobs.append(
|
||||
{
|
||||
"knob": node[knob],
|
||||
"name": node[knob].name(),
|
||||
"label": node[knob].label(),
|
||||
"expected": expected,
|
||||
"current": node[knob].value()
|
||||
}
|
||||
)
|
||||
|
||||
context.data["invalid_knobs"] = invalid_knobs
|
||||
return invalid_knobs
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
invalid = cls.get_invalid(instance)
|
||||
for data in invalid:
|
||||
if isinstance(data["expected"], unicode):
|
||||
data["knob"].setValue(str(data["expected"]))
|
||||
continue
|
||||
|
||||
data["knob"].setValue(data["expected"])
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class RepairWriteResolutionDifference(pyblish.api.Action):
|
||||
|
||||
label = "Repair"
|
||||
icon = "wrench"
|
||||
on = "failed"
|
||||
|
||||
def process(self, context, plugin):
|
||||
|
||||
# Get the errored instances
|
||||
failed = []
|
||||
for result in context.data["results"]:
|
||||
if (result["error"] is not None and result["instance"] is not None
|
||||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
for instance in instances:
|
||||
reformat = instance[0].dependencies()[0]
|
||||
if reformat.Class() != "Reformat":
|
||||
reformat = nuke.nodes.Reformat(inputs=[instance[0].input(0)])
|
||||
|
||||
xpos = instance[0].xpos()
|
||||
ypos = instance[0].ypos() - 26
|
||||
|
||||
dependent_ypos = instance[0].dependencies()[0].ypos()
|
||||
if (instance[0].ypos() - dependent_ypos) <= 51:
|
||||
xpos += 110
|
||||
|
||||
reformat.setXYpos(xpos, ypos)
|
||||
|
||||
instance[0].setInput(0, reformat)
|
||||
|
||||
reformat["resize"].setValue("none")
|
||||
|
||||
|
||||
class ValidateOutputResolution(pyblish.api.InstancePlugin):
|
||||
"""Validates Output Resolution.
|
||||
|
||||
It is making sure the resolution of write's input is the same as
|
||||
Format definition of script in Root node.
|
||||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["render", "render.local", "render.farm"]
|
||||
label = "Write Resolution"
|
||||
hosts = ["nuke"]
|
||||
actions = [RepairWriteResolutionDifference]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
# Skip bounding box check if a crop node exists.
|
||||
if instance[0].dependencies()[0].Class() == "Crop":
|
||||
return
|
||||
|
||||
msg = "Bounding box is outside the format."
|
||||
assert self.check_resolution(instance), msg
|
||||
|
||||
def check_resolution(self, instance):
|
||||
node = instance[0]
|
||||
|
||||
root_width = instance.data["resolutionWidth"]
|
||||
root_height = instance.data["resolutionHeight"]
|
||||
|
||||
write_width = node.format().width()
|
||||
write_height = node.format().height()
|
||||
|
||||
if (root_width != write_width) or (root_height != write_height):
|
||||
return None
|
||||
else:
|
||||
return True
|
||||
83
openpype/hosts/nuke/plugins/publish/validate_read_legacy.py
Normal file
83
openpype/hosts/nuke/plugins/publish/validate_read_legacy.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import os
|
||||
import toml
|
||||
|
||||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
from avalon import api
|
||||
from bson.objectid import ObjectId
|
||||
|
||||
|
||||
class RepairReadLegacyAction(pyblish.api.Action):
|
||||
|
||||
label = "Repair"
|
||||
icon = "wrench"
|
||||
on = "failed"
|
||||
|
||||
def process(self, context, plugin):
|
||||
|
||||
# Get the errored instances
|
||||
failed = []
|
||||
for result in context.data["results"]:
|
||||
if (result["error"] is not None and result["instance"] is not None
|
||||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
for instance in instances:
|
||||
|
||||
data = toml.loads(instance[0]["avalon"].value())
|
||||
data["name"] = instance[0].name()
|
||||
data["xpos"] = instance[0].xpos()
|
||||
data["ypos"] = instance[0].ypos()
|
||||
data["extension"] = os.path.splitext(
|
||||
instance[0]["file"].value()
|
||||
)[1][1:]
|
||||
|
||||
data["connections"] = []
|
||||
for d in instance[0].dependent():
|
||||
for i in range(d.inputs()):
|
||||
if d.input(i) == instance[0]:
|
||||
data["connections"].append([i, d])
|
||||
|
||||
nuke.delete(instance[0])
|
||||
|
||||
loader_name = "LoadSequence"
|
||||
if data["extension"] == "mov":
|
||||
loader_name = "LoadMov"
|
||||
|
||||
loader_plugin = None
|
||||
for Loader in api.discover(api.Loader):
|
||||
if Loader.__name__ != loader_name:
|
||||
continue
|
||||
|
||||
loader_plugin = Loader
|
||||
|
||||
api.load(
|
||||
Loader=loader_plugin,
|
||||
representation=ObjectId(data["representation"])
|
||||
)
|
||||
|
||||
node = nuke.toNode(data["name"])
|
||||
for connection in data["connections"]:
|
||||
connection[1].setInput(connection[0], node)
|
||||
|
||||
node.setXYpos(data["xpos"], data["ypos"])
|
||||
|
||||
|
||||
class ValidateReadLegacy(pyblish.api.InstancePlugin):
|
||||
"""Validate legacy read instance[0]s."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["read.legacy"]
|
||||
label = "Read Legacy"
|
||||
hosts = ["nuke"]
|
||||
actions = [RepairReadLegacyAction]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
msg = "Clean up legacy read node \"{}\"".format(instance)
|
||||
assert False, msg
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
from openpype.api import ValidationException
|
||||
import clique
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class RepairCollectionAction(pyblish.api.Action):
|
||||
label = "Repair"
|
||||
on = "failed"
|
||||
icon = "wrench"
|
||||
|
||||
def process(self, context, plugin):
|
||||
self.log.info(context[0][0])
|
||||
files_remove = [os.path.join(context[0].data["outputDir"], f)
|
||||
for r in context[0].data.get("representations", [])
|
||||
for f in r.get("files", [])
|
||||
]
|
||||
self.log.info("Files to be removed: {}".format(files_remove))
|
||||
for f in files_remove:
|
||||
os.remove(f)
|
||||
self.log.debug("removing file: {}".format(f))
|
||||
context[0][0]["render"].setValue(True)
|
||||
self.log.info("Rendering toggled ON")
|
||||
|
||||
|
||||
class ValidateRenderedFrames(pyblish.api.InstancePlugin):
|
||||
""" Validates file output. """
|
||||
|
||||
order = pyblish.api.ValidatorOrder + 0.1
|
||||
families = ["render", "prerender"]
|
||||
|
||||
label = "Validate rendered frame"
|
||||
hosts = ["nuke", "nukestudio"]
|
||||
actions = [RepairCollectionAction]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
for repre in instance.data.get('representations'):
|
||||
|
||||
if not repre.get('files'):
|
||||
msg = ("no frames were collected, "
|
||||
"you need to render them")
|
||||
self.log.error(msg)
|
||||
raise ValidationException(msg)
|
||||
|
||||
collections, remainder = clique.assemble(repre["files"])
|
||||
self.log.info('collections: {}'.format(str(collections)))
|
||||
self.log.info('remainder: {}'.format(str(remainder)))
|
||||
|
||||
collection = collections[0]
|
||||
|
||||
frame_length = int(
|
||||
instance.data["frameEndHandle"] - instance.data["frameStartHandle"] + 1
|
||||
)
|
||||
|
||||
if frame_length != 1:
|
||||
if len(collections) != 1:
|
||||
msg = "There are multiple collections in the folder"
|
||||
self.log.error(msg)
|
||||
raise ValidationException(msg)
|
||||
|
||||
if not collection.is_contiguous():
|
||||
msg = "Some frames appear to be missing"
|
||||
self.log.error(msg)
|
||||
raise ValidationException(msg)
|
||||
|
||||
# if len(remainder) != 0:
|
||||
# msg = "There are some extra files in folder"
|
||||
# self.log.error(msg)
|
||||
# raise ValidationException(msg)
|
||||
|
||||
collected_frames_len = int(len(collection.indexes))
|
||||
self.log.info('frame_length: {}'.format(frame_length))
|
||||
self.log.info(
|
||||
'len(collection.indexes): {}'.format(collected_frames_len)
|
||||
)
|
||||
|
||||
if ("slate" in instance.data["families"]) \
|
||||
and (frame_length != collected_frames_len):
|
||||
collected_frames_len -= 1
|
||||
|
||||
assert (collected_frames_len == frame_length), (
|
||||
"{} missing frames. Use repair to render all frames"
|
||||
).format(__name__)
|
||||
|
||||
instance.data['collection'] = collection
|
||||
|
||||
return
|
||||
123
openpype/hosts/nuke/plugins/publish/validate_script.py
Normal file
123
openpype/hosts/nuke/plugins/publish/validate_script.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import pyblish.api
|
||||
from avalon import io
|
||||
from openpype import lib
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class ValidateScript(pyblish.api.InstancePlugin):
|
||||
""" Validates file output. """
|
||||
|
||||
order = pyblish.api.ValidatorOrder + 0.1
|
||||
families = ["workfile"]
|
||||
label = "Check script settings"
|
||||
hosts = ["nuke"]
|
||||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
ctx_data = instance.context.data
|
||||
asset_name = ctx_data["asset"]
|
||||
asset = lib.get_asset(asset_name)
|
||||
asset_data = asset["data"]
|
||||
|
||||
# These attributes will be checked
|
||||
attributes = [
|
||||
"fps",
|
||||
"frameStart",
|
||||
"frameEnd",
|
||||
"resolutionWidth",
|
||||
"resolutionHeight",
|
||||
"handleStart",
|
||||
"handleEnd"
|
||||
]
|
||||
|
||||
# Value of these attributes can be found on parents
|
||||
hierarchical_attributes = [
|
||||
"fps",
|
||||
"resolutionWidth",
|
||||
"resolutionHeight",
|
||||
"pixelAspect",
|
||||
"handleStart",
|
||||
"handleEnd"
|
||||
]
|
||||
|
||||
missing_attributes = []
|
||||
asset_attributes = {}
|
||||
for attr in attributes:
|
||||
if attr in asset_data:
|
||||
asset_attributes[attr] = asset_data[attr]
|
||||
|
||||
elif attr in hierarchical_attributes:
|
||||
# Try to find fps on parent
|
||||
parent = asset['parent']
|
||||
if asset_data['visualParent'] is not None:
|
||||
parent = asset_data['visualParent']
|
||||
|
||||
value = self.check_parent_hierarchical(parent, attr)
|
||||
if value is None:
|
||||
missing_attributes.append(attr)
|
||||
else:
|
||||
asset_attributes[attr] = value
|
||||
else:
|
||||
missing_attributes.append(attr)
|
||||
|
||||
# Raise error if attributes weren't found on asset in database
|
||||
if len(missing_attributes) > 0:
|
||||
atr = ", ".join(missing_attributes)
|
||||
msg = 'Missing attributes "{}" in asset "{}"'
|
||||
message = msg.format(atr, asset_name)
|
||||
raise ValueError(message)
|
||||
|
||||
# Get handles from database, Default is 0 (if not found)
|
||||
handle_start = 0
|
||||
handle_end = 0
|
||||
if "handleStart" in asset_attributes:
|
||||
handle_start = asset_attributes["handleStart"]
|
||||
if "handleEnd" in asset_attributes:
|
||||
handle_end = asset_attributes["handleEnd"]
|
||||
|
||||
asset_attributes["fps"] = float("{0:.4f}".format(
|
||||
asset_attributes["fps"]))
|
||||
|
||||
# Get values from nukescript
|
||||
script_attributes = {
|
||||
"handleStart": ctx_data["handleStart"],
|
||||
"handleEnd": ctx_data["handleEnd"],
|
||||
"fps": float("{0:.4f}".format(ctx_data["fps"])),
|
||||
"frameStart": ctx_data["frameStart"],
|
||||
"frameEnd": ctx_data["frameEnd"],
|
||||
"resolutionWidth": ctx_data["resolutionWidth"],
|
||||
"resolutionHeight": ctx_data["resolutionHeight"],
|
||||
"pixelAspect": ctx_data["pixelAspect"]
|
||||
}
|
||||
|
||||
# Compare asset's values Nukescript X Database
|
||||
not_matching = []
|
||||
for attr in attributes:
|
||||
self.log.debug("asset vs script attribute \"{}\": {}, {}".format(
|
||||
attr, asset_attributes[attr], script_attributes[attr])
|
||||
)
|
||||
if asset_attributes[attr] != script_attributes[attr]:
|
||||
not_matching.append(attr)
|
||||
|
||||
# Raise error if not matching
|
||||
if len(not_matching) > 0:
|
||||
msg = "Attributes '{}' are not set correctly"
|
||||
# Alert user that handles are set if Frame start/end not match
|
||||
if (
|
||||
(("frameStart" in not_matching) or ("frameEnd" in not_matching)) and
|
||||
((handle_start > 0) or (handle_end > 0))
|
||||
):
|
||||
msg += " (`handle_start` are set to {})".format(handle_start)
|
||||
msg += " (`handle_end` are set to {})".format(handle_end)
|
||||
message = msg.format(", ".join(not_matching))
|
||||
raise ValueError(message)
|
||||
|
||||
def check_parent_hierarchical(self, entityId, attr):
|
||||
if entityId is None:
|
||||
return None
|
||||
entity = io.find_one({"_id": entityId})
|
||||
if attr in entity['data']:
|
||||
self.log.info(attr)
|
||||
return entity['data'][attr]
|
||||
else:
|
||||
return self.check_parent_hierarchical(entity['parent'], attr)
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
import nuke
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class RepairNukeBoundingBoxAction(pyblish.api.Action):
|
||||
|
||||
label = "Repair"
|
||||
icon = "wrench"
|
||||
on = "failed"
|
||||
|
||||
def process(self, context, plugin):
|
||||
|
||||
# Get the errored instances
|
||||
failed = []
|
||||
for result in context.data["results"]:
|
||||
if (result["error"] is not None and result["instance"] is not None
|
||||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
for instance in instances:
|
||||
crop = instance[0].dependencies()[0]
|
||||
if crop.Class() != "Crop":
|
||||
crop = nuke.nodes.Crop(inputs=[instance[0].input(0)])
|
||||
|
||||
xpos = instance[0].xpos()
|
||||
ypos = instance[0].ypos() - 26
|
||||
|
||||
dependent_ypos = instance[0].dependencies()[0].ypos()
|
||||
if (instance[0].ypos() - dependent_ypos) <= 51:
|
||||
xpos += 110
|
||||
|
||||
crop.setXYpos(xpos, ypos)
|
||||
|
||||
instance[0].setInput(0, crop)
|
||||
|
||||
crop["box"].setValue(
|
||||
(
|
||||
0.0,
|
||||
0.0,
|
||||
instance[0].input(0).width(),
|
||||
instance[0].input(0).height()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ValidateNukeWriteBoundingBox(pyblish.api.InstancePlugin):
|
||||
"""Validates write bounding box.
|
||||
|
||||
Ffmpeg does not support bounding boxes outside of the image
|
||||
resolution a crop is needed. This needs to validate all frames, as each
|
||||
rendered exr can break the ffmpeg transcode.
|
||||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["render", "render.local", "render.farm"]
|
||||
label = "Write Bounding Box"
|
||||
hosts = ["nuke"]
|
||||
actions = [RepairNukeBoundingBoxAction]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
# Skip bounding box check if a crop node exists.
|
||||
if instance[0].dependencies()[0].Class() == "Crop":
|
||||
return
|
||||
|
||||
msg = "Bounding box is outside the format."
|
||||
assert self.check_bounding_box(instance), msg
|
||||
|
||||
def check_bounding_box(self, instance):
|
||||
node = instance[0]
|
||||
|
||||
first_frame = instance.data["frameStart"]
|
||||
last_frame = instance.data["frameEnd"]
|
||||
|
||||
format_width = node.format().width()
|
||||
format_height = node.format().height()
|
||||
|
||||
# The trick is that we need to execute() some node every time we go to
|
||||
# a next frame, to update the context.
|
||||
# So we create a CurveTool that we can execute() on every frame.
|
||||
temporary_node = nuke.nodes.CurveTool()
|
||||
bbox_check = True
|
||||
for frame in range(first_frame, last_frame + 1):
|
||||
# Workaround to update the tree
|
||||
nuke.execute(temporary_node, frame, frame)
|
||||
|
||||
x = node.bbox().x()
|
||||
y = node.bbox().y()
|
||||
w = node.bbox().w()
|
||||
h = node.bbox().h()
|
||||
|
||||
if x < 0 or (x + w) > format_width:
|
||||
bbox_check = False
|
||||
break
|
||||
|
||||
if y < 0 or (y + h) > format_height:
|
||||
bbox_check = False
|
||||
break
|
||||
|
||||
nuke.delete(temporary_node)
|
||||
return bbox_check
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import pyblish.api
|
||||
import openpype.hosts.nuke.lib
|
||||
|
||||
|
||||
class RepairNukeWriteDeadlineTab(pyblish.api.Action):
|
||||
|
||||
label = "Repair"
|
||||
icon = "wrench"
|
||||
on = "failed"
|
||||
|
||||
def process(self, context, plugin):
|
||||
|
||||
# Get the errored instances
|
||||
failed = []
|
||||
for result in context.data["results"]:
|
||||
if (result["error"] is not None and result["instance"] is not None
|
||||
and result["instance"] not in failed):
|
||||
failed.append(result["instance"])
|
||||
|
||||
# Apply pyblish.logic to get the instances for the plug-in
|
||||
instances = pyblish.api.instances_by_plugin(failed, plugin)
|
||||
|
||||
for instance in instances:
|
||||
group_node = [x for x in instance if x.Class() == "Group"][0]
|
||||
|
||||
# Remove exising knobs.
|
||||
knob_names = openpype.hosts.nuke.lib.get_deadline_knob_names()
|
||||
for name, knob in group_node.knobs().iteritems():
|
||||
if name in knob_names:
|
||||
group_node.removeKnob(knob)
|
||||
|
||||
openpype.hosts.nuke.lib.add_deadline_tab(group_node)
|
||||
|
||||
|
||||
class ValidateNukeWriteDeadlineTab(pyblish.api.InstancePlugin):
|
||||
"""Ensure Deadline tab is present and current."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = "Deadline Tab"
|
||||
hosts = ["nuke"]
|
||||
optional = True
|
||||
families = ["render"]
|
||||
actions = [RepairNukeWriteDeadlineTab]
|
||||
|
||||
def process(self, instance):
|
||||
group_node = [x for x in instance if x.Class() == "Group"][0]
|
||||
|
||||
knob_names = openpype.hosts.nuke.lib.get_deadline_knob_names()
|
||||
missing_knobs = []
|
||||
for name in knob_names:
|
||||
if name not in group_node.knobs().keys():
|
||||
missing_knobs.append(name)
|
||||
assert not missing_knobs, "Missing knobs: {}".format(missing_knobs)
|
||||
107
openpype/hosts/nuke/plugins/publish/validate_write_legacy.py
Normal file
107
openpype/hosts/nuke/plugins/publish/validate_write_legacy.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import toml
|
||||
import os
|
||||
|
||||
import nuke
|
||||
|
||||
from avalon import api
|
||||
import re
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon.nuke import get_avalon_knob_data
|
||||
|
||||
|
||||
class ValidateWriteLegacy(pyblish.api.InstancePlugin):
|
||||
"""Validate legacy write nodes."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["write"]
|
||||
label = "Validate Write Legacy"
|
||||
hosts = ["nuke"]
|
||||
actions = [openpype.api.RepairAction]
|
||||
|
||||
def process(self, instance):
|
||||
node = instance[0]
|
||||
msg = "Clean up legacy write node \"{}\"".format(instance)
|
||||
|
||||
if node.Class() not in ["Group", "Write"]:
|
||||
return
|
||||
|
||||
# test avalon knobs
|
||||
family_knobs = ["ak:family", "avalon:family"]
|
||||
family_test = [k for k in node.knobs().keys() if k in family_knobs]
|
||||
self.log.debug("_ family_test: {}".format(family_test))
|
||||
|
||||
# test if render in family test knob
|
||||
# and only one item should be available
|
||||
assert len(family_test) == 1, msg + " > More avalon attributes"
|
||||
assert "render" in node[family_test[0]].value(), msg + \
|
||||
" > Not correct family"
|
||||
|
||||
# test if `file` knob in node, this way old
|
||||
# non-group-node write could be detected
|
||||
assert "file" not in node.knobs(), msg + \
|
||||
" > file knob should not be present"
|
||||
|
||||
# check if write node is having old render targeting
|
||||
assert "render_farm" not in node.knobs(), msg + \
|
||||
" > old way of setting render target"
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
node = instance[0]
|
||||
|
||||
if "Write" in node.Class():
|
||||
data = toml.loads(node["avalon"].value())
|
||||
else:
|
||||
data = get_avalon_knob_data(node)
|
||||
|
||||
# collect reusable data
|
||||
data["XYpos"] = (node.xpos(), node.ypos())
|
||||
data["input"] = node.input(0)
|
||||
data["publish"] = node["publish"].value()
|
||||
data["render"] = node["render"].value()
|
||||
data["render_farm"] = node["render_farm"].value()
|
||||
data["review"] = node["review"].value()
|
||||
data["use_limit"] = node["use_limit"].value()
|
||||
data["first"] = node["first"].value()
|
||||
data["last"] = node["last"].value()
|
||||
|
||||
family = data["family"]
|
||||
cls.log.debug("_ orig node family: {}".format(family))
|
||||
|
||||
# define what family of write node should be recreated
|
||||
if family == "render":
|
||||
Create_name = "CreateWriteRender"
|
||||
elif family == "prerender":
|
||||
Create_name = "CreateWritePrerender"
|
||||
|
||||
# get appropriate plugin class
|
||||
creator_plugin = None
|
||||
for Creator in api.discover(api.Creator):
|
||||
if Creator.__name__ != Create_name:
|
||||
continue
|
||||
|
||||
creator_plugin = Creator
|
||||
|
||||
# delete the legaci write node
|
||||
nuke.delete(node)
|
||||
|
||||
# create write node with creator
|
||||
new_node_name = data["subset"]
|
||||
creator_plugin(new_node_name, data["asset"]).process()
|
||||
|
||||
node = nuke.toNode(new_node_name)
|
||||
node.setXYpos(*data["XYpos"])
|
||||
node.setInput(0, data["input"])
|
||||
node["publish"].setValue(data["publish"])
|
||||
node["review"].setValue(data["review"])
|
||||
node["use_limit"].setValue(data["use_limit"])
|
||||
node["first"].setValue(data["first"])
|
||||
node["last"].setValue(data["last"])
|
||||
|
||||
# recreate render targets
|
||||
if data["render"]:
|
||||
node["render"].setValue("Local")
|
||||
if data["render_farm"]:
|
||||
node["render"].setValue("On farm")
|
||||
69
openpype/hosts/nuke/plugins/publish/validate_write_nodes.py
Normal file
69
openpype/hosts/nuke/plugins/publish/validate_write_nodes.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
import openpype.utils
|
||||
import openpype.hosts.nuke.lib as nukelib
|
||||
import avalon.nuke
|
||||
|
||||
@pyblish.api.log
|
||||
class RepairNukeWriteNodeAction(pyblish.api.Action):
|
||||
label = "Repair"
|
||||
on = "failed"
|
||||
icon = "wrench"
|
||||
|
||||
def process(self, context, plugin):
|
||||
instances = openpype.utils.filter_instances(context, plugin)
|
||||
|
||||
for instance in instances:
|
||||
node = instance[1]
|
||||
correct_data = nukelib.get_write_node_template_attr(node)
|
||||
for k, v in correct_data.items():
|
||||
node[k].setValue(v)
|
||||
self.log.info("Node attributes were fixed")
|
||||
|
||||
|
||||
class ValidateNukeWriteNode(pyblish.api.InstancePlugin):
|
||||
""" Validates file output. """
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
optional = True
|
||||
families = ["render"]
|
||||
label = "Write Node"
|
||||
actions = [RepairNukeWriteNodeAction]
|
||||
hosts = ["nuke"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
node = instance[1]
|
||||
correct_data = nukelib.get_write_node_template_attr(node)
|
||||
|
||||
check = []
|
||||
for k, v in correct_data.items():
|
||||
if k is 'file':
|
||||
padding = len(v.split('#'))
|
||||
ref_path = avalon.nuke.lib.get_node_path(v, padding)
|
||||
n_path = avalon.nuke.lib.get_node_path(node[k].value(), padding)
|
||||
isnt = False
|
||||
for i, p in enumerate(ref_path):
|
||||
if str(n_path[i]) not in str(p):
|
||||
if not isnt:
|
||||
isnt = True
|
||||
else:
|
||||
continue
|
||||
if isnt:
|
||||
check.append([k, v, node[k].value()])
|
||||
else:
|
||||
if str(node[k].value()) not in str(v):
|
||||
check.append([k, v, node[k].value()])
|
||||
|
||||
self.log.info(check)
|
||||
|
||||
msg = "Node's attribute `{0}` is not correct!\n" \
|
||||
"\nCorrect: `{1}` \n\nWrong: `{2}` \n\n"
|
||||
|
||||
if check:
|
||||
print_msg = ""
|
||||
for item in check:
|
||||
print_msg += msg.format(item[0], item[1], item[2])
|
||||
print_msg += "`RMB` click to the validator and `A` to fix!"
|
||||
|
||||
assert not check, print_msg
|
||||
Loading…
Add table
Add a link
Reference in a new issue