What's New in Silhouette V6
- there can be multiple nodes of each type in a Session, including Roto, Paint, Output, and various OFX nodes
- the complete rendering loop is implemented in scripts/tools/renderer.py
- rendering is triggered with the new Action in scripts/actions/render.py
- bundled actions that target specific nodes are updated to handle connected Output nodes
- shape path data can now contain per-point feathering information if per-point feathering is used
New Scripting Features
- nodes can be created, added, and removed from a Session
- nodes can be connected and disconnected
- new hooks: attach_pipe, remove_pipe, triggered when nodes are wired up or disconnected in the UI
- new hook: object_created, triggered when a new Node, Shape, Layer, or Tracker is created in the UI
- user-defined properties can be added and removed from Objects
- multi-part output format API
- new hook: output_node, triggered for each output node
- drop hooks: hooks can be called when a registered mime type is dropped in the new Trees view
- new Sessions can be created and activated
- new Sources can be created and added to the Project
- adding a Source to a Session creates a SourceNode and connects it
Nodes must be created using their interal node name. Registered node names can be queried using fx.nodes. Note that internal nodes have named like RotoNode and PaintNode. Most of the rest of the stock Silhouette nodes are implemented using OFX, and have node IDs such as com.digitalfilmtools.ofx.silhouette.sfx_composite and com.digitalfilmtools.ofx.silhouette.blur.
Nodes can be connected to other nodes using pipes and ports. Ports have specific types (usuallying image or data) and can only be connected to other ports with the same type. Output ports can have multiple pipes connected to them but input ports may only have one.
roto = Node("RotoNode") activeSession().addNode(roto) slapComp = Node("com.digitalfilmtools.ofx.silhouette.slapComp") activeSession().addNode(slapComp) roto.port("output").connect(slapComp.port("input"))
Hooks are only triggered from the UI, so if you want hooks triggered when nodes are created or connected, you must do so manually.
import hook roto = Node("RotoNode") activeSession().addNode(roto) hook.run("object_created", roto) pipe = roto.output(0).connect(slapComp.input(0)) hook.run("attach_pipe", pipe)
|called after a new object is created in the UI. The object is passed as the argument.||6.0|
|called after a new pipe connects two nodes||6.0|
|called just before a pipe is removed||6.0|
|called after an Output node finishes writing all of its parts. A meta-data dictionary of useful information is passed.||6.0|
The complete rendering loop is now encapsulated in the new Renderer class, implemented in the scripts/tools/renderer module. It is launched from the GUI using the new Render action in scripts/actions/render. Special-purpose renderers, such as Render Shapes to Separate Files or Render Layers to Separate Files make use of the new renderer by sublassing it and overriding the renderFrame method to control what objects are rendered and when, overring the output file name as necessary.
The important rendering change from V5 to V6 is in V5 there was only one output node and one output path. In V6 there can be multiple output nodes each rendering a specific part of the tree, and each output node can have a different file format, path, channel set, etc. The new multi-part output node can also generate an output file (in OpenEXR format) with multiple parts, each with their own resolution and other attributes. The new renderer embodies all of the logic to handle all of this output complexity, and its behavior can be overriden or outright replaced as needed.
The I/O module API has been enhanced to provide access to multi-part OpenEXR images. Use the new parts() method to query the list of available parts in a multi-part image (a List of part names). To write an image file, first create the file, write one more images using putImage(), then close the file.
module = MediaFormat("OpenEXR") module.create(path) parts = renderParts() for part in parts: module.putImage(part.raster, part=part.name, frame=frame, pixelAspect=pixelAspect, roi=roi, displayWindow=session_rect, channels=part.channelMask, premultiply=False, metadata=part.metadata) module.close() # actually writes the file