tips,tricks & geekie stuff

BE KIND, RETIME

tl;dr:  Retime your Camera Node like a PRO!

Working with retimed plates and elements in Nuke oftentimes means that you also need a corresponding retimed camera, so that both plate and camera work back together, and you can still use your fancy projection setups and other camera-dependent stuff.

Now, how on earth do I retime my Camera Node in Nuke? Well, you´re about to find out!

Spoiler: it only takes one click if you use my fresh "b_retime_my_cam" Python Script ;).

1 - A few things to take into account

When we speak about retiming plates, I´m assuming you are doing it how (arguably ) most of the people would: you hook up a Kronos Node to your plate, and use the "frame" option as a "timing" method. This way you can easily create a lookup table mapping input frame numbers into output frame numbers.

Regarding your Camera Node, you are good to use either "regular" Euler cameras or a matrix based one.

2 - Distribute the code

Copy the script and save it as a Python file called "b_retime_my_cam.py" where Nuke can find it. In other words, place it within the Nuke Path file structure. For more info, read my previous posts (or Google it, there´s tons of info covering this topic.)

3 - Edit the menu.py file

Now we have the Python module where Nuke can find it, we´re gonna add the following lines to our fantastic menu.py so that we can effectively use it:

n_menu = nuke.menu('Nuke')
b_menu = n_menu.addMenu('MenuName')
b_menu.addCommand('b_retime_my_cam', 'b_retime_my_cam.main_function()', 'ctrl+a')

Save the menu.py and restart Nuke.

4 - Try it!

You'll notice that there is a brand new Nuke menu in your upper menu bar, listing the new 'b_retime_my_cam' command and its chosen hotkey. Now just select the Kronos Node used to retime your plate and then the Camera Node you want to retime accordingly (strictly in this order) and press 'alt+a '(or any other hotkey of your convenience).

A tiny Nuke Panel like this one should appear:

 

 

 

 

 

 

 

 

 

 

 

 

 

Set the "camera type" (Euler or matrix) and click ok.


 

 

 

 

 

 

 

 

 

 

 

 

 

And voila, you will end up with something like this, which means you have now a new, retimed camera that you can use along with your previously retimed plate.

code
#######################################################################################################################

__author__ = "Boris Martinez Castillo"
__version__ = "1.0.1"
__maintainer__ = "Boris Martinez Castillo"
__email__ = "boris.vfx@outlook.com"

#######################################################################################################################


import nuke
import nukescripts


# CLASS DEFINITIONS

class RetimeCameraPanel(nukescripts.PythonPanel):

    def __init__(self):
        super(RetimeCameraPanel, self).__init__()

        # CREATE KNOBS

        self.name = nuke.Enumeration_Knob("camera type", "camera_type", ["euler", "matrix"])
        self.name.clearFlag(nuke.STARTLINE)
        self.author = nuke.Text_Knob("written by Boris Martinez")

        # ADD KNOBS

        self.addKnob(self.name)


def retime_my_cam(transformation_type):
    """Handle both cases. euler and matrix with given arg.

    Args:
        transformation_type (str): User defined type for transformation.
            Euler or Matrix.

    """
    selection = nuke.selectedNodes()
    nukescripts.clear_selection_recursive()
    classes = ["Camera2", "Kronos"]
    sel_classes = [str(node.Class()) for node in selection]

    colors = {'euler': 536805631,
              'matrix': 4278239231}

    if not sel_classes == classes:
        nuke.message("Please, select first a Kronos and then a Camera Node,in this order")
        return

    for node in selection:
        if node.Class() == "Camera2":
            parent_cam = node
            node["world_matrix"].value()

        elif node.Class() == "Kronos":
            new_cam = nuke.nodes.Camera2(xpos=parent_cam.xpos() + 300,
                                         ypos=parent_cam.ypos())
            new_cam.setInput(0, None)
            kronos_node_name = node.name()
			
            if transformation_type == 'matrix':
                do_matrix_specifics(parent_cam, new_cam, kronos_node_name)

            elif transformation_type == 'euler':
                do_euler_specifics(parent_cam, new_cam, kronos_node_name)

   
            new_cam['tile_color'].setValue(colors[transformation_type])
            new_cam.setName('RETIMED_{}_{}'.format(transformation_type.upper(),
                                                   parent_cam.name()))
            new_cam.setSelected(True)
            custom_backdrop("retimed {} cam".format(transformation_type))


def do_euler_specifics(parent_cam, new_cam, kronos_node_name):
    """Do only euler things.

    Args:
        parent_cam (nuke.Node): Parent cam node.
        new_cam (nuke.Node): New created camera.
        kronos_node_name (str): Kronos node name.

    """
	
    knobs = ['translate', 'rotate', 'scaling', 'uniform_scale', 'skew',
             'pivot', 'focal', 'haperture', 'vaperture', 'near', 'far',
             'win_translate', 'focal_point', 'fstop']

    for knob in knobs:
        expression = '{node}.{knob}({kronos}.timingFrame2)'.format(node=parent_cam.name(),
                                                                   knob=knob,
                                                                   kronos=kronos_node_name)
        new_cam[knob].setExpression(expression)


def do_matrix_specifics(parent_cam, new_cam, kronos_node_name):
    """Do only matrix specific things.

    Args:
        parent_cam (nuke.Node): Parent cam node.
        new_cam (nuke.Node): New created camera.
        kronos_node_name (str): Kronos node name.

    """
    new_cam["useMatrix"].setValue(True)
	
    expression = '{}.world_matrix({}.timingFrame2)'.format(parent_cam.name(),
                                                           kronos_node_name)
    new_cam["matrix"].setExpression(expression)


def custom_backdrop(txt):
    """Create custom backdropNode.

    Args:
        txt (str): Text used as label.

    """
    node = nukescripts.autoBackdrop()
    node['label'].setValue('<center><u><b>' + txt)
    node['note_font_size'].setValue(50)


def main_function():
    """Start up function."""
    panel = RetimeCameraPanel()
    if not panel.showModalDialog():
        print "script aborted"
        return
    else:
        retime_my_cam(panel.name.value())