Useful Scripts/Snippets
From VFXPedia
This page is a place to share small bits of code (LUA and Python) that solve a specific task. A lot of example code can be found by digging through the scripts that are available, but often the variables they rely on are scattered throughout the code. The snippets on this page, however, should be self-contained so they can be pasted into your own scripts easily.
Getting and Setting Inputs

bc = BrightnessContrast() -- won't work, as this assigns the Gain slider's Input object to the variable (seen by typing ==x at the console) x = bc.Gain -- get value of a property that isn't animated x = bc.Gain[TIME_UNDEFINED] -- if a property is animated, you need to query it at the current time: x = bc.Gain[comp.CurrentTime] -- ...or any other time you want x = bc.Gain[20] -- set input's value. If tool is animated, a keyframe will be created at the current time. bc.Gain[comp.CurrentTime] = 0.5 -- shorthand to set input's value if input is not animated. If it is, nothing will happen. bc.Gain = 0.5
The shorthand doesn't work, however, if the input is no longer accessed via the dot-notation of its parent object. It's a LUA pitfall or rather a side effect of how this shorthand notation is implemented. Consider this:
-- this doesn't work. It will assign "2" to the inp variable instead of changing the input object. inp = bc.Gain inp = 2 -- using square brackets you can make it work: inp = bc.Gain inp[TIME_UNDEFINED] = 2 -- works if input isn't animated inp[comp.CurrentTime] = 2 -- works always
This, however, isn't enough if you want to animate an input. Usually, you just assign it a BezierSpline (1D), Path or XYPath (2D) object or whatever modifier you require:
-- animate gain slider: bc.Gain = BezierSpline() -- remove animation: bc.Gain = nil -- If you use a temporary variable, this won't work. It will assign an empty BezierSpline to the temporary variable -- instead of connecting it to the gain slider: inp = bc.Gain inp = BezierSpline() -- but even the array notation doesn't work in this case. inp = bc.Gain inp[TIME_UNDEFINED] = BezierSpline() -- the correct way to animate an input in this case is via the ConnectTo() method: inp = bc.Gain inp:ConnectTo(BezierSpline()) -- but you could also use this option if you still know the tool the input belongs to ("bc" in this case): bc[inp.ID] = BezierSpline() -- ...which uses the input's scripting ID as if you had written this: bc["Gain"] = BezierSpline()
Following Inputs and Outputs

A tool's image input can be accessed like any other input using the dot notation:
inp = tool.Background
However, the name is not always "Background", some tools have a main input called "Input". So you need to use the method FindMainInput(). An input is connected to another tool's output, and the output will tell you which tool it belongs to. Here's the complete helper function to traverse backwards through the flow:
function GetUpstream(tool) -- the 1 means 1st input in case there are more (merges, dissolves) local inp = tool:FindMainInput(1) -- get output connected to the input local outp = inp and inp:GetConnectedOutput() or nil -- finally get the upstream tool or nil if no tool is connected return ( outp and outp:GetTool() or nil ) end
Similarly, this function returns the downstream tool(s) as an array:
function GetDownstream(tool) local outp = tool:FindMainOutput(1) local inpTable = outp:GetConnectedInputs() local tools = {} for i,t in ipairs(inpTable) do tools[i] = t:GetTool() end return tools end
Creating a Loader or Saver

composition:Lock() local new_loader = Loader({Clip = "C:\\mymovie.avi"}) composition:Unlock()
or if you want to use the AddTool method, which allows you to define the coordinates the new tool will be placed at:
composition:Lock() local new_loader = composition:AddTool("Loader", 100, 100) new_loader.Clip[TIME_UNDEFINED] = "C:\\mymovie.avi" composition:Unlock()
Remember that the script might abort unintentionally due to an error or intentionally by using return or exit() somewhere along the way. So unlock the comp as soon as possible and catch any errors that might occur.
There's a downside to using Lock() and Unlock(). These commands will clear caches and force a re-render of the current comp. To disable the file selector while creating Loaders it might be wiser to temporarily turn off the corresponding option in the preferences. This can be done in a script as well. The following snippet (hat tip to User:Stuart) will first save and later restore the original value of the AutoClipBrowse preference:
autobrowse = fusion:GetPrefs("Global.UserInterface.AutoClipBrowse") fusion:SetPrefs("Global.UserInterface.AutoClipBrowse", false) -- Add loader fusion:SetPrefs("Global.UserInterface.AutoClipBrowse", autobrowse)
Creating a Saver With Custom Options

-- define some output formats OutputFormats = { {Label = "EXR float", Ext = ".exr", Settings = {["OpenEXRFormat.Depth"] = 1}}, {Label = "DPX log", Ext = ".dpx", Settings = {["DPXFormat.BypassConversion"] = 0}}, {Label = "DPX linear 10bit", Ext = ".dpx", Settings = {["DPXFormat.BypassConversion"] = 1}}, {Label = "JPG 8bit 85% Quality", Ext = ".jpg", Settings = {["JpegFormat.Quality"] = 85}}, } -- create list of format names for dialog box formatItems = {} for i,f in pairs(OutputFormats) do table.insert(formatItems, f.Label) end ask = composition:AskUser("Create Saver", { {"f", Name = "Format: ", "Dropdown", Options = formatItems}, }) if ask == nil then return end -- create saver (default filename based on composition name) composition:Lock() clipname = "Comp:" .. composition:GetAttrs().COMPS_Name .. ".0000" .. OutputFormats[ask.f + 1].Ext sv = Saver({Clip = clipname}) -- loop through table of pre-defined formats and transfer settings to saver for key,val in pairs(OutputFormats[ask.f + 1].Settings) do sv[key] = val end composition:Unlock()
Updating a Loader

-- "ld" is a Loader object ld.Clip[TIME_UNDEFINED] = "" ld.Clip[TIME_UNDEFINED] = "Comp:new_clip.0000.exr" -- optionally reset trimming and "hold first frame": ld.HoldFirstFrame = 0 ld.ClipTimeStart = 0 -- now toggle pass-through to refresh the loader ld:SetAttrs({TOOLB_PassThrough = true}) ld:SetAttrs({TOOLB_PassThrough = false})
If you want to trigger "auto detect clip length" for a loader, you can also use this command (mentioned by Eric Doiron on the mailing list):
-- "ldr" is a Loader object ldr.Clip[comp.CurrentTime] = ldr.Clip[comp.CurrentTime]
Changing a Node's Color

-- get all bitmap masks in comp masks = comp:GetToolList(false, "BitmapMask") -- Predefined colors in an array whose keys match the channel selection dropdown of bitmap masks. colors = { Red = {R = 1, G = 0, B = 0}, Green = {R = 0, G = .8, B = 0}, Blue = {R = 0, G = 0, B = 1}} for i,m in pairs(masks) do -- for values without a corresponding key in the colors array, "nil" will be assigned -- to TileColor, which resets the mask's color to its default hue. m.TileColor = colors[ m.Channel[TIME_UNDEFINED] ] end
This could even be put into an event suite to be run each time the mask is selected or the comp is opened.
Switching to the Script Console

if filename == "" then print("Filename is missing!") composition:GetFrameList()[1]:SwitchMainView('ConsoleView') exit() end
How can I find out all abbreviations for Fusion's tools?

for i,v in ipairs(fu:GetRegSummary(CT_Tool)) do if v.REGS_OpIconString then print(v.REGS_OpIconString .." = ".. v.REGS_Name) end end
(suggested by Chad Capeland on the pigsfly forum)
How can I print a list of all inputs and outputs that a tool has?

for _, inp in ipairs(comp.ActiveTool:GetInputList()) do print(inp:GetAttrs().INPS_ID .. ": " .. inp:GetAttrs().INPS_Name) end
Use this to print all outputs (most tools only have one, but the Tracker for example has a lot more).
for _, out in ipairs(comp.ActiveTool:GetOutputList()) do print(out:GetAttrs().OUTS_ID .. ": " .. out:GetAttrs().OUTS_Name) end
How can I find out valid values for combo controls (dropdown lists)?

-- color space dropdown in the Gamut tool gam1 = GamutConvert() dump(gam1.SourceSpace:GetAttrs()['INPIDT_ComboControl_ID']) -- format dropdown in the FBX Exporter geo_out = ExporterFBX() dump(geo_out.Format:GetAttrs()['INPIDT_ComboControl_ID'])
(credits to Ilia Zaslavsky on the Fusion mailing list)
For loaders, an attribute table like this also contains the channel names of EXR files. The first FuID really is that funky (it's the "None" entry).
-- make sure loader is really loading an EXR file... local tattrs = tool:GetAttrs() if tattrs.TOOLS_RegID == "Loader" and tattrs.TOOLST_Clip_FormatName[1] == "OpenEXRFormat" then dump(tool.Clip1.OpenEXRFormat.RedName:GetAttrs().INPIDT_ComboControl_ID) end -- 1 = SomethingThatWontMatchHopefully -- 2 = R -- 3 = G -- 4 = B -- 5 = A -- 6 = Z -- 7 = GI.B -- 8 = GI.G -- 9 = GI.R -- ...
(credits to Sven Neve)
Using FuIDs in InTool scripts

Renderer3D1.Camera = "Camera3D1"
InTool scripts (like the Frame Render Script), however, don't support this shortcut. In this case, you need to turn your string into a FuID object. This snippet demonstrates a frame render script for a Renderer3D tool, that loops through (pre-defined) cameras automatically, depending on the current frame number:
-- these 4 cameras have to exist in your scene cams = {"Camera_FRONT", "Camera_LEFT", "Camera_RIGHT", "Camera_BACK"} Camera = FuID(cams[time % 3 + 1])
How can I scroll the flow view to a specific tool?

-- scroll to first Loader tlist = composition:GetToolList(false, "Loader") if #tlist > 0 then composition:SetActiveTool(tlist[1]) end
Three ways of printing the current frame in a TextTool

Text1 is the name of the texttool in this example.
TimeCode Modifier:
Text1.StyledText = TimeCode({Hrs = 0, Mins = 0, Secs = 0, Frms = 1})
FrameRenderScript:
Text1.FrameRenderScript = "StyledText = time"
Text1.StyledText:SetExpression("Text(time)")
Add a macro from within a script

s = "C:\\macro.setting" f = eyeon.readfile(s) comp:Paste(f)
(credits to John Barclay)
Parsing Paths

path = "x:\\projects\\cool_show\\shots\\ABC_000_010\\comps\\" project, shot = path:lower():match("\\projects\\([^\\]+)\\shots\\([^\\]+)\\") -- result: "cool_show", "abc_000_010"
The string is lowercased so it also works if the string contains capital letters as in Comps or Projects. This will also return a lower-cased shot name. If you want to preserve case, you'll find that LUA misses a regular expression flag that does case-insensitive matching. You can use this workaround (only practical for short strings):
project, shot = path:match("\\[Pp][Rr][Oo][Jj][Ee][Cc][Tt][Ss]\\([^\\]+)\\[Ss][Hh][Oo][Tt][Ss]\\([^\\]+)\\") -- result: "cool_show", "ABC_000_010"
This is an example that increases a version number inside a string:
path = "x:\\COOL_SHOW\\render\\shot01_V009" -- look for version number (in real situations it might be necessary to improve the regexp depending -- on your naming conventions, since this will also catch "v6motorcommercial") oldversion = path:match("[Vv](%d+)") if oldversion ~= nil then -- increase version number and produce a string with the same padding as before (e.g. 010) newversion = string.format("%0" .. #oldversion .. "d", oldversion + 1) -- this works but would lowercase the V character all the time new_path = path:gsub("[Vv]" .. oldversion, "v" .. newversion) -- better: captures the V-character (parentheses) and uses it again as a back-reference (%1) so its case is preserved new_path = path:gsub("([Vv])" .. oldversion, "%1" .. newversion) end
For more string tips, refer to the scripting documentation.
Saving AskUser Dialog Values

Fusion allows you to save arbitrary data to LUA's "globals" table. These values will be kept in memory as long as Fusion is running. Here's a bare-bones script that demonstrates how to use globals as a data storage:
-- retrieve options table from globals (if it exists) -- use a name that doesn't clash with other scripts, "MySavedOptions" is just an example options = globals.MySavedOptions or {} -- build user dialog. Slider will be set to saved value or 0 if no saved value was found dialog = {} dialog[1] = {"value", "Slider", Name = "Select Value:", Default = options.value or 0} ret = composition:AskUser("Save Settings Demo:", dialog) if ret == nil then exit() end -- remember options for next invocation of script globals.MySavedOptions = ret