Fuses are Lua scripts that allow you to create your own tools in DaVinci Resolve Fusion. You can create everything from regular nodes, to modifier nodes, or even console commands. This guide will just go over the most basic use cases to get you started. You can check out my FusionSDK Reference page after to dive deeper.
Fuses have their own Fuses folder. It is located in the same directroy as your Macros folder. If you don't know where your Macros folder is, you might need to familiarize yourself a bit more with Fusion before continuing. Macros offer a great way to create your own tools with much less technical knowladge and effort.
Fusion/
- Fuses/
- Macros/
- Templates/
- Modules/
Every Fuse must have 3 things:
FuRegisterClass().Create() function.Process() function.
FuRegisterClass() is called when DaVinci Resolve or Fusion first loads our .fuse file on startup.
This means that any changes to FuRegisterClass require restarting the app. This also includes creating new .fuse files.
FuRegisterClass(id, class_type, tags);| Argument | Type | Usage |
|---|---|---|
| id | String | A unique String for this tool. This is what will show up in .settings files. |
| class_type | Int | A constant indicating what type of tool we are creating. Common class types includeCT_Tool, CT_SourceTool, CT_SinkTool, CT_ConsoleUtility, and CT_Modifier. |
| tags | Table | Most functions in the FusionSDK end with a tags argument, a tags table contains key value pairs. The tags table is where most of the magic happens. |
FuRegisterClass("PizzaBlu.SampleOpacity", CT_Tool, {
REGS_Name = "SampleOpacity",
REG_NoPreCalcProcess = true, -- combines the PreCalcProcess() and Process() functions, this is fine for most tools
});
This is where we create any Inputs and Outputs that our tool needs. Create() is called both
when a tool is first added, and when the comp is loaded again. If Create() is being called when loading an existing node
Fusion will automatically update the inputs to their saved values after we create them.
There are a few methods that you will frequently see used in the Create() function:
| Method | Type | Usage |
|---|---|---|
self:AddInput(name, id, tags) | Input | Create Inputs & setup controls. |
self:AddOutput(name, id, tags) | Output | Create Outputs. |
self:BeginControlNest(name, id, expand) | Input | Inputs between a call to BeginControlNest and EndControlNest will automatically be grouped into a collapsable nest. |
self:EndControlNest() | nil | See above. |
You can find a more complete reference for the tags for these functions in my FusionSDK Reference.
function Create()
-- create an "Opacity" slider
InOpacity = self:AddInput("Opacity", "Opacity", {
LINKID_DataType = "Number",
INPID_InputControl = "SliderControl",
INP_Default = 1.0,
});
-- our main Image Input
-- main Inputs/Outputs show up in the Fusion node graph, instead of the Inspector
InImage = self:AddInput("Input", "Input", {
LINKID_DataType = "Image",
LINK_Main = 1, -- this is the first main input
});
-- our main Image Output
OutImage = self:AddOutput("Output", "Output", {
LINKID_DataType = "Image",
LINK_Main = 1, -- this is the first main output
});
end
The Process(req) function is the most important part of our fuse. It gets called whenever Fusion needs us to to process new data, usually once per frame.
It will also get called whenever our controls change, or if a node we are connected to changes.
The Process Function accepts one argument: the Request object which contains important information
like the frame being requested and the values of our inputs at that time. It is our job here to update the Request
with our own Output data.
Input:GetValue(Request)The GetValue() shortcut is how we extract our Inputs data from the Request. Not every DataType will require the .Value at the end, but our Number does.
local opacity = InOpacity:GetValue(req).Value;
local img = InImage:GetValue(req);
Output:Set(Request, Parameter)
The Set() shortcut is how we update the Request with our output data.
Parameter is the generic name for any data passing through an Input or Output.
Make sure to call the correct constructor for your output parameter.
The example below passes our input image through unchanged, so it doesn't need to construct a new Image yet.
OutImage:Set(req, img);
Image(tags)
The Image() constructor is how we create a new Image.
Like most functions in the FusionSDK, you can configure it with a tags table.
Note that if IMG_Like is used, you can still override specific tags.
| Tag | Type | Usage |
|---|---|---|
| IMG_Like | Image | Pass in an existing Image object and copy its attributes (but not image data). This is often all you need for simple one-input, one-output nodes. |
| IMG_Document | Composition | If you do not pass in an existing image with IMG_Like you will need to specify the composition using this tag. |
| IMG_Width | Number | The horizontal resolution of the image. |
| IMG_Height | Number | The vertical resolution of the image. |
Additional advanced tags: IMG_GPUImage IMG_GPUProcess IMG_IsMask IMG_DoAutoTransform and IMG_DoAutoDoD are also available for Image inputs.
-- if we handle copying the image data ourselves
local out = Image({IMG_Like = img});
We will use the Image:CopyOf() method to create a new copy of our input image with the image data already copied over.
-- let fusion copy the image data for us
local out = img:CopyOf();
To apply our Opacity adjustment we will use the built in Image:Gain() method. It is best to use the built in
image processing methods when possible to take advantage of the faster non-lua code powering them. The Gain()
It is important we call Gain() on our own copy of the image, rather than the input or we would be messing with another
tools memory, which is very dangerous.
The Gain() method accepts 4 arguments. One for each of the Red, Green, Blue, and Alpha channels.
out:Gain(opacity, opacity, opacity, opacity);
Now, lets put all of that together to create our Process() function.
function Process(req)
local opacity = InOpacity:GetValue(req).Value;
local img = InImage:GetValue(req);
local out = img:CopyOf();
out:Gain(opacity, opacity, opacity, opacity);
OutImage:Set(req, out);
end
Here's what that looks like all together. If you haven't figured it by now, we just created our own Opacity node!
FuRegisterClass("PizzaBlu.SampleOpacity", CT_Tool, {
REGS_Name = "SampleOpacity",
REG_NoPreCalcProcess = true,
});
function Create()
InOpacity = self:AddInput("Opacity", "Opacity", {
LINKID_DataType = "Number",
INPID_InputControl = "SliderControl",
INP_Default = 1.0,
});
InImage = self:AddInput("Input", "Input", {
LINKID_DataType = "Image",
LINK_Main = 1,
});
OutImage = self:AddOutput("Output", "Output", {
LINKID_DataType = "Image",
LINK_Main = 1,
});
end
function Process(req)
local opacity = InOpacity:GetValue(req).Value;
local img = InImage:GetValue(req);
local out = img:CopyOf();
out:Gain(opacity, opacity, opacity, opacity);
OutImage:Set(req, out);
end