Introducing the Octane Lua API

Forums: Introducing the Octane Lua API
Forum for OctaneRender Lua scripting examples, discussion and support.

Introducing the Octane Lua API

Postby stratified » Thu Dec 05, 2013 10:36 pm

stratified Thu Dec 05, 2013 10:36 pm
Hi all,

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".

script_editor.png
Lua Script Editor

script_menu.png
The script menu, your own personal toolbox
script_menu.png (43.62 KiB) Viewed 4951 times


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.

api_browser.png
The API browser, your new best friend


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.
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Re: Introducing the Octane Lua API

Postby r-username » Mon Dec 09, 2013 5:04 pm

r-username Mon Dec 09, 2013 5:04 pm
If a project is already loaded can we use the script to just render several cameras in the scene.

much like the command line function in 1.2:
load camera 1, render, save
loac camera 2, render, save...

another script I would like to see is camera navigation much like the brigade demos on youtube, if you could show how to move camera forward one meter in a script that would be great.
i7 960 - W7x64 - 12 GB - 2x GTX 780ti
http://www.startsimple.com/ - http://www.gigavr.com/
r-username
Licensed Customer
Licensed Customer
 
Posts: 217
Joined: Thu Nov 24, 2011 3:39 pm

Re: Introducing the Octane Lua API

Postby stratified » Mon Dec 09, 2013 6:55 pm

stratified Mon Dec 09, 2013 6:55 pm
r-username wrote:If a project is already loaded can we use the script to just render several cameras in the scene.

much like the command line function in 1.2:
load camera 1, render, save
loac camera 2, render, save...

another script I would like to see is camera navigation much like the brigade demos on youtube, if you could show how to move camera forward one meter in a script that would be great.


sure that's no problem. Today or tomorrow I'll post an example script to render a turntable animation from a project.

Moving in the camera direction over 1 unit would look something like this:

Code: Select all
-- create a camera node
camNode   = octane.node.create{ type=octane.NT_CAM_THINLENS, name="MyCam" }
-- get the target and position of the camera
camTarget = camNode:getPinValue(octane.P_TARGET)
camPosition = camNode:getPinValue(octane.P_POSITION)
-- calculate the normalized direction
camDir = octane.vec.sub(camTarget, camPosition)
camDir = octane.vec.normalized(camDir)
-- move position & target 1 unit along the direction
camNode:setPinValue(octane.P_TARGET, octane.vec.add(camTarget, camDir))
camNode:setPinValue(octane.P_POSITION, octane.vec.add(camPosition, camDir))


cheers,
Thomas
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Re: Introducing the Octane Lua API

Postby pixelrush » Mon Dec 09, 2013 7:28 pm

pixelrush Mon Dec 09, 2013 7:28 pm
Can Lua scripts be used to do malicious things? I mean should there be a security feature warning of possibility and getting permission to run? :roll:
i7-3820 @4.3Ghz | 24gb | Win7pro-64
GTS 250 display + 2 x GTX 780 cuda| driver 331.65
Octane v1.55
User avatar
pixelrush
Licensed Customer
Licensed Customer
 
Posts: 1618
Joined: Mon Jan 11, 2010 7:11 pm
Location: Nelson, New Zealand

Re: Introducing the Octane Lua API

Postby abstrax » Mon Dec 09, 2013 7:44 pm

abstrax Mon Dec 09, 2013 7:44 pm
pixelrush wrote:Can Lua scripts be used to do malicious things? I mean should there be a security feature warning of possibility and getting permission to run? :roll:

Well you can delete files for example like with every other scripting language. As long as you run only your own scripts there is no issue. When people start sharing scripts you have to use them on your own risk. We plan to provide the possibility to upload scripts to the LiveDB and we will probably moderate them, before they come visible to the user.
In theory there is no difference between theory and practice. In practice there is. - Yogi Berra
User avatar
abstrax
OctaneRender Team
OctaneRender Team
 
Posts: 5069
Joined: Tue May 18, 2010 11:01 am
Location: Auckland, New Zealand

Re: Introducing the Octane Lua API

Postby Goldorak » Mon Dec 09, 2013 11:50 pm

Goldorak Mon Dec 09, 2013 11:50 pm
One reason we chose Lua is that it is pretty easy to run a lua script in a safe sandbox when it is from an untrusted source. What we may do in the future is have 3rd party scripts from LiveDB request permissions for non-sandbox modules (i.e. file, network, device) and require the user to accept the list before running the script with full permissions. Lua itself can handle sandboxing this on it's own, so an enterprising user might be able to create such a Live DB script loader/ filter right now using the built in UI script commands in Octane.
User avatar
Goldorak
OctaneRender Team
OctaneRender Team
 
Posts: 1923
Joined: Sun Apr 22, 2012 8:09 pm

Re: Introducing the Octane Lua API

Postby r-username » Tue Dec 10, 2013 2:04 pm

r-username Tue Dec 10, 2013 2:04 pm
stratified wrote:
Code: Select all
-- create a camera node
camNode   = octane.node.create{ type=octane.NT_CAM_THINLENS, name="MyCam" }


I'm starting to get the hang of it. If I wanted to select a camera node (or render taarget) that was already in the scene vs. create a new one what would be the statement? I looked for something like octane.node.select or is it camNode = octane.node {name = "myCam" } but did not see something similar.

Not sure what the larger group needs in terms of tutorials, but i'm looking for small simple scripts to start with. (pan, zoom camera, swap out render targets or mesh files and render overnight)
i7 960 - W7x64 - 12 GB - 2x GTX 780ti
http://www.startsimple.com/ - http://www.gigavr.com/
r-username
Licensed Customer
Licensed Customer
 
Posts: 217
Joined: Thu Nov 24, 2011 3:39 pm

Re: Introducing the Octane Lua API

Postby phdubrov » Tue Dec 10, 2013 5:54 pm

phdubrov Tue Dec 10, 2013 5:54 pm
octane.nodegraph.findItemsByName

items = octane.nodegraph.findItemsByName(graph, name)

items = graph:findItemsByName(name)

or

octane.nodegraph.findNodes

nodes = octane.nodegraph.findNodes(graph, nodeType)

nodes = graph:findNodes(nodeType)

Returns the nodes in the graph of the provided type.


i.e. something like
Code: Select all
scene = octane.nodegraph.getRootGraph()
thinLens = scene:findNodes(octane.NT_CAM_THINLENS)
myCam = scene:findItemsByNames(MyCoolCamera)


phdubrov
Licensed Customer
Licensed Customer
 
Posts: 15
Joined: Sun Mar 03, 2013 1:47 pm

Return to Lua Scripting


Who is online

Users browsing this forum: No registered users and 3 guests

Tue Sep 17, 2019 2:14 am [ UTC ]