-
-
Save greenvfx/7c46f3bb9e84f6dbab8c883464f721b1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import nuke, nukescripts, nuke.rotopaint as rp, _curvelib as cl | |
''' | |
## LINK TOOLS | |
Utility functions for creating linked nodes. This script can create roto linked to trackers, | |
transform linked to trackers, or cameras linked to each other, or held on a projection frame. | |
Installation: | |
Put link_tools.py somewhere in your nuke path. | |
You can add the script as commands to your Nodes panel. This code creates a custom menu entry called "Scripts". | |
I have them set to the shortcuts Alt-O for roto and Alt-L for generalized link (works with tracker, transform, and camera nodes). | |
import link_tools | |
nuke.toolbar('Nodes').addMenu('Scripts').addCommand('Link Roto', 'link_tools.link("roto")', 'alt+o') | |
nuke.toolbar('Nodes').addMenu('Scripts').addCommand('Link Tool', 'link_tools.link()', 'alt+l') | |
''' | |
def link_transform(target_node): | |
""" | |
Utility function for link_transform: creates linked transform node | |
""" | |
target_name = target_node.name() | |
nclass = target_node.Class() | |
if "Tracker" not in nclass and "Transform" not in nclass: | |
print "Must select Tracker or Transform node!" | |
return | |
grid_x = int(nuke.toNode('preferences').knob('GridWidth').value()) | |
grid_y = int(nuke.toNode('preferences').knob('GridHeight').value()) | |
target_node.setSelected(False) | |
# Create linked Transform Node | |
trans = nuke.nodes.Transform() | |
trans.setName('TransformLink') | |
trans.setSelected(True) | |
trans.knob('help').setValue("<b>TransformLink</b>\n\nA Transform node with options for linking to a Tracker or a Transform node. \n\nAllows you to set a seperate identity transform frame from the linked Tracker. Select the link target and click 'Set Target', or set the link target by name. You can also bake the expression link into keyframes if you want it independant from the target node. \n\nThe transform Matchmoves or Stabilizes depending on what the parent tracker node is set to, but you can invert this by enabling the 'invert' checkbox.") | |
trans.knob('label').setValue('Matchmove: [value link_target]') | |
trans.setXYpos(target_node.xpos()-grid_x*0, target_node.ypos()+grid_y*2) | |
trans.addKnob(nuke.Tab_Knob('TrackLink')) | |
trans.addKnob(nuke.Int_Knob('identity_frame', 'Identity Frame')) | |
if 'Tracker' in nclass: | |
trans.knob('identity_frame').setValue( int(target_node.knob('reference_frame').value()) ) | |
else: | |
trans.knob('identity_frame').setValue(nuke.frame()) | |
trans.addKnob(nuke.PyScript_Knob('identity_to_curframe', 'Set to Current Frame')) | |
trans.knob('identity_to_curframe').setTooltip("Set identity frame to current frame.") | |
trans.knob('identity_to_curframe').setCommand("nuke.thisNode().knob('identity_frame').setValue(nuke.frame())") | |
trans.addKnob(nuke.PyScript_Knob('del_relative', 'Del Relative')) | |
trans.knob('del_relative').setTooltip('Delete relative transformation.\n\nUse original Transform values.') | |
trans.knob('del_relative').setCommand("# Delete Rel - remove identity frame transformations on transform link\ndef del_relative():\n n = nuke.thisNode()\n # Get target node name\n target_name = n['translate'].animation(0).expression().split(' - ')[0].split('parent.')[1].split('.translate')[0]\n n.knob('translate').clearAnimated()\n n.knob('translate').setExpression('parent.{0}.translate'.format(target_name))\n n.knob('rotate').clearAnimated()\n n.knob('rotate').setExpression('parent.{0}.rotate'.format(target_name))\n n.knob('scale').clearAnimated()\n n.knob('scale').setExpression('parent.{0}.scale'.format(target_name))\n n.knob('skewX').clearAnimated()\n n.knob('skewX').setExpression('parent.{0}.skewX'.format(target_name))\n n.knob('skewY').clearAnimated()\n n.knob('skewY').setExpression('parent.{0}.skewY'.format(target_name))\n n.knob('center').clearAnimated()\n n.knob('center').setExpression('parent.{0}.center'.format(target_name))\nif __name__ == '__main__':\n del_relative()") | |
trans.addKnob(nuke.String_Knob('link_target', 'Link Target', target_name)) | |
trans.knob('link_target').setTooltip('Type in a node name to link to.') | |
trans.addKnob(nuke.PyScript_Knob('set_target', 'Set Target')) | |
trans.knob('set_target').clearFlag(nuke.STARTLINE) | |
trans.knob('set_target').setTooltip('Sets the link target to the selected node.\n\nIf no node is selected, set target node to the value of Link Target.') | |
trans.knob('set_target').setCommand("# Set Target Button\ndef link_to_target():\n trans = nuke.thisNode()\n selected_nodes = nuke.selectedNodes()\n if len(selected_nodes) is 0:\n target_name = nuke.thisNode().knob('link_target').getValue()\n target_node = nuke.toNode(target_name)\n if target_node is None:\n nuke.message('Target node does not exist!')\n return\n if len(selected_nodes) is 1:\n target_node = selected_nodes[0]\n target_name = target_node.name()\n trans.knob('link_target').setValue(target_name)\n if len(selected_nodes) > 1:\n print 'Error: Exactly 1 target node must be specified.'\n return\n target_class = target_node.Class()\n if target_class in ['Tracker3', 'Tracker4', 'Transform', 'TransformMasked']:\n trans.knob('translate').clearAnimated()\n trans.knob('translate').setExpression('parent.{0}.translate - parent.{0}.translate(identity_frame)'.format(target_name))\n trans.knob('rotate').clearAnimated()\n trans.knob('rotate').setExpression('parent.{0}.rotate - parent.{0}.rotate(identity_frame)'.format(target_name))\n trans.knob('scale').clearAnimated()\n trans.knob('scale').setExpression('parent.{0}.scale / parent.{0}.scale(identity_frame)'.format(target_name))\n trans.knob('skewX').clearAnimated()\n trans.knob('skewX').setExpression('parent.{0}.skewX - parent.{0}.skewX(identity_frame)'.format(target_name))\n trans.knob('skewY').clearAnimated()\n trans.knob('skewY').setExpression('parent.{0}.skewY - parent.{0}.skewY(identity_frame)'.format(target_name))\n trans.knob('center').clearAnimated()\n trans.knob('center').setExpression('parent.{0}.center+parent.{0}.translate(identity_frame)'.format(target_name))\n if target_class in ['Tracker3', 'Tracker4']:\n trans.knob('identity_frame').setValue( int(nuke.toNode(target_name).knob('reference_frame').getValue()) )\n else:\n print 'Error: Must select a Tracker or Transform class node.'\nif __name__ == '__main__':\n link_to_target()") | |
trans.addKnob(nuke.PyScript_Knob('bake_link', 'Bake Expression Links')) | |
trans.knob('bake_link').setFlag(nuke.STARTLINE) | |
trans.knob('bake_link').setTooltip('Bake expression links to keyframes') | |
trans.knob('bake_link').setCommand("# Bake Expression Links Button\ndef bake_expression_links():\n trans = nuke.thisNode()\n link_target_name = trans.knob('link_target').getValue()\n target_node = nuke.toNode(link_target_name)\n if target_node.knob('translate').isAnimated():\n # Get animation framerange from translate knob of target tracker node\n target_anims = target_node.knob('translate').animations()\n first_key = target_anims[0].keys()[0].x\n last_key = target_anims[0].keys()[-1].x\n else:\n nuke.message('No Keys on Source!')\n return\n # Bake knob values\n def bake_knob( node, knob, first_key, last_key):\n first_key = str(first_key)\n last_key = str(last_key)\n def horizontal_ends(knob, i):\n # Set start and end keyframe to horizontal interpolation so it matches expression before and after animation\n anim = knob.animation(i)\n anim.changeInterpolation( [anim.keys()[0], anim.keys()[-1]], nuke.HORIZONTAL)\n return\n if knob.width() == 1:\n nuke.animation('{0}.{1}'.format(node.name(), knob.name()), 'generate', (first_key, last_key, '1', 'y', '{0}'.format(knob.name())))\n horizontal_ends(knob, 0)\n if knob.width() == 2: \n if knob.name() == 'scale':\n if knob.singleValue() == True:\n nuke.animation('{0}.scale'.format(node.name()), 'generate', (first_key, last_key, '1', 'y', 'scale'))\n horizontal_ends(knob, 0)\n else:\n for i, dim in enumerate(['w', 'h']):\n nuke.animation('{0}.{1}.{2}'.format(node.name(), knob.name(), dim), 'generate', (first_key, last_key, '1', 'y', '{0}.{1}'.format(knob.name(), dim)))\n horizontal_ends(knob, i)\n else:\n for i, dim in enumerate(['x', 'y']):\n nuke.animation('{0}.{1}.{2}'.format(node.name(), knob.name(), dim), 'generate', (first_key, last_key, '1', 'y', '{0}.{1}'.format(knob.name(), dim)))\n horizontal_ends(knob, i)\n for knob_name, knob in trans.knobs().iteritems():\n if knob_name in ['translate', 'rotate', 'scale', 'skewX', 'skewY', 'center']:\n print 'Baking {0}'.format(knob_name)\n bake_knob(trans, knob, first_key, last_key)\nif __name__ == '__main__':\n bake_expression_links()") | |
trans.addKnob(nuke.Text_Knob("")) | |
trans.addKnob(nuke.Double_Knob('motionblur_gui', 'gui mblur')) | |
trans.knob('motionblur_gui').setValue(0) | |
trans.knob('motionblur_gui').setTooltip('Set the $gui motion blur value') | |
# Link knobs | |
trans.knob('translate').setExpression('parent.{0}.translate - parent.{0}.translate(identity_frame)'.format(target_name)) | |
trans.knob('rotate').setExpression('parent.{0}.rotate - parent.{0}.rotate(identity_frame)'.format(target_name)) | |
trans.knob('scale').setExpression('parent.{0}.scale / parent.{0}.scale(identity_frame)'.format(target_name)) | |
trans.knob('skewX').setExpression('parent.{0}.skewX - parent.{0}.skewX(identity_frame)'.format(target_name)) | |
trans.knob('skewY').setExpression('parent.{0}.skewY - parent.{0}.skewY(identity_frame)'.format(target_name)) | |
trans.knob('center').setExpression('parent.{0}.center+parent.{0}.translate(identity_frame)'.format(target_name)) | |
trans.knob('motionblur').setExpression('$gui ? motionblur_gui : 4') | |
trans.knob('shutteroffset').setValue('centred') | |
def link_roto(tracker_node, roto_node=False): | |
''' | |
Utility function: Creates a layer in roto_node linked to tracker_node | |
if roto_node is False, creates a roto node next to tracker node to link to | |
''' | |
grid_x = int(nuke.toNode('preferences').knob('GridWidth').value()) | |
grid_y = int(nuke.toNode('preferences').knob('GridHeight').value()) | |
tracker_name = tracker_node.name() | |
tracker_node.setSelected(False) | |
# If Roto node not selected, create one. | |
if not roto_node: | |
roto_node = nuke.nodes.Roto() | |
roto_node.setXYpos(tracker_node.xpos()-grid_x*0, tracker_node.ypos()+grid_y*2) | |
roto_node.setSelected(True) | |
# Create linked layer in Roto Node | |
curves_knob = roto_node["curves"] | |
stab_layer = rp.Layer(curves_knob) | |
stab_layer.name = "stab_"+tracker_name | |
trans_curve_x = cl.AnimCurve() | |
trans_curve_y = cl.AnimCurve() | |
trans_curve_x.expressionString = "parent.{0}.translate.x".format(tracker_name) | |
trans_curve_y.expressionString = "parent.{0}.translate.y".format(tracker_name) | |
trans_curve_x.useExpression = True | |
trans_curve_y.useExpression = True | |
rot_curve = cl.AnimCurve() | |
rot_curve.expressionString = "parent.{0}.rotate".format(tracker_name) | |
rot_curve.useExpression = True | |
scale_curve = cl.AnimCurve() | |
scale_curve.expressionString = "parent.{0}.scale".format(tracker_name) | |
scale_curve.useExpression = True | |
center_curve_x = cl.AnimCurve() | |
center_curve_y = cl.AnimCurve() | |
center_curve_x.expressionString = "parent.{0}.center.x".format(tracker_name) | |
center_curve_y.expressionString = "parent.{0}.center.y".format(tracker_name) | |
center_curve_x.useExpression = True | |
center_curve_y.useExpression = True | |
# Define variable for accessing the getTransform() | |
transform_attr = stab_layer.getTransform() | |
# Set the Animation Curve for the Translation attribute to the value of the previously defined curve, for both x and y | |
transform_attr.setTranslationAnimCurve(0, trans_curve_x) | |
transform_attr.setTranslationAnimCurve(1, trans_curve_y) | |
# Index value of setRotationAnimCurve is 2 even though there is only 1 parameter... | |
# http://www.mail-archive.com/[email protected]/msg02295.html | |
transform_attr.setRotationAnimCurve(2, rot_curve) | |
transform_attr.setScaleAnimCurve(0, scale_curve) | |
transform_attr.setScaleAnimCurve(1, scale_curve) | |
transform_attr.setPivotPointAnimCurve(0, center_curve_x) | |
transform_attr.setPivotPointAnimCurve(1, center_curve_y) | |
curves_knob.rootLayer.append(stab_layer) | |
def link_camera(src_cam, proj_frame, expr_link, clone, index): | |
""" | |
Create a linked camera to src_cam -- this camera can be frozen on a projection frame | |
""" | |
# Default grid size is 110x24. This enables moving by grid increments. | |
# http://forums.thefoundry.co.uk/phpBB2/viewtopic.php?t=3739&sid=c40e65b1f575ba9166583faf807184ee | |
# Offset by 1 grid in x for each additional iteration | |
grid_x = int(nuke.toNode('preferences').knob('GridWidth').value()) | |
grid_y = int(nuke.toNode('preferences').knob('GridHeight').value())*index | |
src_cam.setSelected(False) | |
# Create Projection Camera | |
proj_cam = nuke.nodes.Camera2() | |
# Create Projection Frame knob | |
frame_tab = nuke.Tab_Knob('Frame') | |
proj_cam.addKnob(frame_tab) | |
proj_frame_knob = nuke.Double_Knob('proj_frame') | |
if clone: | |
proj_frame_knob.setExpression('t') | |
else: | |
proj_frame_knob.setValue(proj_frame) | |
proj_cam.addKnob(proj_frame_knob) | |
## Copy the knob values of the Source Camera | |
for knob_name, knob in src_cam.knobs().iteritems(): | |
# For Animated knobs, copy or link the values depending on if Expression Links are enabled. | |
if knob.isAnimated(): | |
#print "setting animated knob", knob_name | |
if expr_link == True: | |
#print "setting expression for animated knobs" | |
for index in range(src_cam.knob(knob_name).arraySize()): | |
if src_cam.knob(knob_name).isAnimated(index): | |
proj_cam[knob_name].copyAnimation(index, src_cam[knob_name].animation(index)) | |
proj_cam[knob_name].setExpression('parent.' + src_cam.name() + "." + knob_name + "(proj_frame)") | |
# http://www.nukepedia.com/python/knob-animation-and-python-a-primer/ | |
else: | |
for index in range(src_cam.knob(knob_name).arraySize()): | |
if src_cam.knob(knob_name).isAnimated(index): | |
proj_cam[knob_name].copyAnimation(index, src_cam[knob_name].animation(index)) | |
proj_cam[knob_name].setExpression('curve(proj_frame)') | |
# For all non-animated knobs that are not set to default values, match value from src_cam | |
elif hasattr(knob, "notDefault") and knob.notDefault(): | |
try: | |
#print "changing ", knob_name | |
proj_cam[knob_name].setValue(knob.value()) | |
except TypeError: | |
pass | |
# Set label, color, name, and position | |
proj_cam.setXYpos(src_cam.xpos()-grid_x, src_cam.ypos()-grid_y*4) | |
if clone: | |
proj_cam.setName("{0}_CLONE_".format(src_cam.name())) | |
proj_cam["gl_color"].setValue(0xff5f00ff) | |
proj_cam["tile_color"].setValue(0xff5f00ff) | |
else: | |
proj_cam.setName("{0}_PROJ_".format(src_cam.name())) | |
proj_cam["gl_color"].setValue(0xffff) | |
proj_cam["tile_color"].setValue(0xffff) | |
proj_cam["label"].setValue("FRAME [value proj_frame]") | |
def link(link_type=None): | |
nodes = nuke.selectedNodes() | |
track_nodes = [n for n in nodes if 'Tracker' in n.Class()] | |
cam_nodes = [n for n in nodes if n.Class() in ['Camera', 'Camera2']] | |
roto_nodes = [n for n in nodes if n.Class() in ['Roto', 'RotoPaint', 'SplineWarp3']] | |
xform_nodes = [n for n in nodes if n.Class() == 'Transform'] | |
if link_type == 'roto': | |
if len(track_nodes) == 0: | |
print "Error: At least one Tracker node must be selected." | |
return | |
if len(roto_nodes) > 1 and len(track_nodes) > 1: | |
print "Error: if multiple roto nodes are selected, exactly 1 Tracker node must be selected." | |
return | |
for track_node in track_nodes: | |
if len(roto_nodes) is 0: | |
link_roto(track_node) | |
if len(roto_nodes) >= 1: | |
for roto_node in roto_nodes: | |
link_roto(track_node, roto_node) | |
if link_type == None: | |
for track_node in track_nodes: | |
link_transform(track_node) | |
for xform_node in xform_nodes: | |
link_transform(xform_node) | |
for cam_node in cam_nodes: | |
## Set up camera linker panel | |
p = nuke.Panel('Camera Linker: {0}'.format(cam_node.name())) | |
p.setWidth(450) | |
p.addSingleLineInput('Frame', str(nuke.frame())) | |
p.addBooleanCheckBox("Clone", False) | |
p.addBooleanCheckBox('Link', True) | |
if p.show(): | |
clone = p.value('Clone') | |
framestring = p.value('Frame') | |
expr_link = p.value('Link') | |
else: | |
# Cancelled | |
return | |
## Parse frame string | |
if "," in framestring: | |
framelist = map(int, framestring.split(',')) | |
for i, frame in enumerate(framelist): | |
link_camera(cam_node, frame, expr_link, clone, i) | |
else: | |
try: | |
framestring = int(framestring) | |
except: | |
print "Error converting frame to integer!" | |
return | |
link_camera(cam_node, framestring, expr_link, clone, 0) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment