With version 1.5, Octane will have Lua scripting capabilities. To make users familiar with it we will post a series of topics on the forum introducing various aspects of the API. Until we get to a stable 1.5 release, the API is still a moving target and might contain some bugs. Hold off your scripts for world domination until it's considered stable...
We assume the user has some Lua programming experience. If you don't no worries, Lua is very easy to learn even for novice programmers. There's an excellent book online describing Lua 5.0 (Octane uses Lua 5.1 but most of the content is relevant). Go check it out:
http://www.lua.org/pil/contents.html
Lua scripting in Octane is done via the script editor (accessed via the menu Script > Open Editor...). Here you can write your scripts and run them. If you prefer you can write scripts in an external editor. You can also configure a script directory in the application preferences. Scripts in this directory will appear in the Script menu (click Script > Reload Scripts to refresh the list of scripts in the menu). The idea is that you can run your scripts from the Script menu. These scripts will be your personal Octane "toolbox".
While writing scripts, the API browser is your friend. This dialog has 2 tabs. The first tab gives an overview of the Octane Lua API with documentation for each available function. The second tab (Items) lists all the nodes and nodegraphs available in Octane with their pins and attributes. This is important when you're going to manipulate the node system.
To get our feet wet, lets start with a first script. The scripts will load an Octane project from disk, renders an image and saves this image on disk. Not very exciting but if you throw a few for loops at it you have a recipe for batch rendering Let's break it down:
First we assign the myProject variable the path of the project on disk:
- Code: Select all
-- modify this path to point to your own project
myProject = "/home/thomas/Documents/octane-projects/cube-test.ocs"
Now we try to load the project from disk, this will return false if it failed (and pop up an error dialog). Note that all Octane's API functions start with octane. We'll use the load function from the project module:
- Code: Select all
-- load an octane project
success = octane.project.load(myProject)
If the project failed to load we'll popup an error dialog (octane.project.load already shows a popup but let's display another because who doesn't like popup dialogs). We'll use the function showDialog from the gui module (octane.gui.showDialog). This function takes properties. Properties are a fancy name we gave to a Lua table with key/value pairs. A lot of our API functions take properties. The reason is that properties give us an easy way to pass in a myriad number of arguments without having API functions where the user has to remember heaps of arguments for each function. Using properties gives us nice "self-documenting" code that's easier to understand. Here we're using properties of type PROPS_ERROR_DIALOG. Strictly speaking the function octane.gui.showDialog takes PROPS_DIALOG as arguments but it will hapilly accepts PROPS_ERROR_DIALOG. In fact most API functions will accept almost any table given that a few mandatory table fields are specified, it will ignore the extra fields in the table. So if the project wasn't loaded successfully, we show the dialog and call error("..."), the error will stop the script with an error.
- Code: Select all
-- if loading failed, display an error dialog and stop the script
if not success then
dialogProperties =
{
type = octane.gui.dialogType.ERROR_DIALOG,
title = "Project Load Failed",
text = string.format("failed to load %s", myProject)
}
octane.gui.showDialog(dialogProperties)
error("failure")
end
Now that we have loaded the project we should try to find the render target node to render. First we need to get the nodegraph that describes the scene (the root graph). We do this via the function octane.nodegraph.getRootGraph(), this function returns the root graph. Hopefully there's a render target in this graph. We call findNodes(octane.NT_RENDERTARGET) on the graph to get a list of nodes of the said type. Note the special syntax here: scene:findNodes(octane.NT_RENDERTARGET). Actually there's nothing special about this, it's syntactic sugar in Lua to use argument before the colon as the first argument of the function call. It's equivalent with octane.nodegraph.findNodes(scene, octane.NT_RENDERTARGET) but it makes the a bit less verbose.
- Code: Select all
-- find the render target node for the project
scene = octane.nodegraph.getRootGraph()
renderTargets = scene:findNodes(octane.NT_RENDERTARGET)
If the list of render targets is empty, we display an error dialog and stop the script. This is the same as before.
- Code: Select all
-- if we haven't found a render target, display an error dialog
if #renderTargets == 0 then
octane.gui.showDialog
{
type = octane.gui.dialogType.ERROR_DIALOG,
title = "No Render Target",
text = "No Render Target found in project"
}
error("no rt found")
end
We use the first render target in the list and use that to render 100 samples/px with a call to octane.render.start. This function takes properties as well (PROPS_RENDER_START). The most import is the renderTargetNode, this specifies what we want to render. The function blocks until the requested samples are rendered. (In a later tutorial we'll see how to hook up a callback function to do something useful while waiting on a render).
- Code: Select all
-- render 100 samples/px for this render target
octane.render.start{ renderTargetNode=rtNode, maxSamples=100 }
When rendering is finished, we'll save the image as a png:
- Code: Select all
-- save the resulting picture
octane.render.saveImage("/tmp/myrender.png", octane.render.imageType.PNG16)
And that's it, you should end up with something like this:
- Code: Select all
-- modify this path to point to your own project
myProject = "/home/thomas/Documents/octane-projects/cube-test.ocs"
-- load an octane project
success = octane.project.load(myProject)
-- if loading failed, display an error dialog and stop the script
if not success then
dialogProperties =
{
type = octane.gui.dialogType.ERROR_DIALOG,
title = "Project Load Failed",
text = string.format("failed to load %s", myProject)
}
octane.gui.showDialog(dialogProperties)
error("failure")
end
-- find the render target node for the project
scene = octane.nodegraph.getRootGraph()
renderTargets = scene:findNodes(octane.NT_RENDERTARGET)
-- if we haven't found a render target, display an error dialog
if #renderTargets == 0 then
octane.gui.showDialog
{
type = octane.gui.dialogType.ERROR_DIALOG,
title = "No Render Target",
text = "No Render Target found in project"
}
error("no rt found")
end
-- take the first render target node
rtNode = renderTargets[1]
-- render 100 samples/px for this render target
octane.render.start{ renderTargetNode=rtNode, maxSamples=100 }
-- save the resulting picture
octane.render.saveImage("/tmp/myrender.png", octane.render.imageType.PNG16)
Once you get the hang of it there's not much to it and it's actually lots of fun. In the beginning it can be frustrating but don't let that discourage you. When in doubt post a question on the forum. Or you can always send me a private message.
cheers,
Thomas
DISCLAIMER: With great power comes great responsibility. We don't take any liability for what goes wrong while scripting Octane.