Code: Select all
-- gather some info about the stuff to render
local root = octane.nodegraph.getRootGraph()
local alltargets = root:findNodes(octane.NT_RENDERTARGET)
if #alltargets == 0 then
error("Please set up your render target in the main graph.", 0)
end
local rtnode = alltargets[1]
local interval = root:getAnimationTimeSpan()
-- cancel flag and other globals
isCanceled = false
startT = 0
endT = 0
dT = 0
currentT = 0
-- the actual rendering is quite easy.
-- Render callback
function renderCallback(result)
if isCanceled then
octane.render.stop()
end
local t = currentT + dT * result.samples / result.maxSamples
t = (t - startT) / (endT - startT)
progressBar:updateProperties{progress = t}
end
-- Render main loop
function render()
startT = startSlider:getProperties().value
endT = endSlider:getProperties().value
dT = 1 / fpsSlider:getProperties().value
if endT < startT then return end
local file = fileEditor:getProperties().text
-- check if we have a file name given
local prefix, sequenceMatch, suffix
-- If we have a file name, check where to substitute the sequence number:
-- pattern tutorial here: http://lua-users.org/wiki/PatternsTutorial
if file ~= "" then
-- strip png extension
file = file:gsub("%.png$", "")
-- split file into prefix, sequence number and suffix
-- make sure the sequence number is in the final file name part
prefix, sequenceMatch, suffix = file:match("(.-)(%d+)([^\\/]*)$")
-- if the file name doesn't contain a sequence number, the match fails
-- so just assume it is all prefix.
if sequenceMatch == nil then
prefix = file
sequenceMatch = "0000"
suffix = ""
end
-- pattern for string.format.
seqPattern = "%0"..sequenceMatch:len().."d"
-- add png extension
suffix = suffix..".png"
-- display resulting file name
fileEditor:updateProperties{text = prefix..sequenceMatch..suffix}
end
currentT = startT
local seq = 0
-- careful with rounding errors
while currentT < endT + .001 * dT do
-- time and sequence number
seq = seq + 1
root:updateTime(currentT)
-- render
local result = octane.render.start
{
renderTargetNode = rtnode,
callback = renderCallback,
maxSamples = samplesSlider:getProperties().value
}
if isCanceled then
-- canceled
progressBar:updateProperties{progress = 0}
return
elseif file ~= "" then
-- see if a file was given, and replace the sequence number
local thisFile = prefix..seqPattern:format(seq)..suffix
fileEditor:updateProperties{text = thisFile}
octane.render.saveImage(thisFile, octane.render.imageType.PNG8)
end
-- go to next time stamp
currentT = currentT + dT
end
-- set the progress bar to 100%. 1.0 activates indefinite mode, so...
progressBar:updateProperties{progress = .99999}
end
-- creates a text label and returns it
local function createLabel(text)
return octane.gui.create
{
type = octane.gui.componentType.LABEL, -- type of component
text = text, -- text that appears on the label
width = 100, -- width of the label in pixels
height = 24, -- height of the label in pixels
}
end
-- creates a slider and returns it
local function createSlider(value, min, max, step)
return octane.gui.create
{
type = octane.gui.componentType.SLIDER, -- type of the component
width = 400, -- width of the slider in pixels
height = 20, -- height of the slider in pixels
value = value, -- value of the slider
minValue = min, -- minimum value of the slider
maxValue = max, -- maximum value of the slider
step = step, -- interval between 2 discrete slider values
}
end
-- lets create a bunch of labels and sliders
local startLbl = createLabel("Start time")
local endLbl = createLabel("End time")
local fpsLbl = createLabel("Framerate")
local shutterLbl = createLabel("Shutter time")
local samplesLbl = createLabel("Samples/px")
startSlider = createSlider(interval[1], interval[1], interval[2], .001)
endSlider = createSlider(interval[2], interval[1], interval[2], .001)
fpsSlider = createSlider(25 , 1, 120 , 1)
shutterSlider = createSlider(0, 0, 1, 1/25)
samplesSlider = createSlider(400, 1 , 16000, 1)
samplesSlider:updateProperties{logarithmic = true}
-- manual layouting is tedious so let's add all our stuff in a group.
local settingsGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP, -- type of component
text = "Settings", -- title for the group
rows = 7, -- number of rows in the grid
cols = 2, -- number of colums in the grid
-- the children is a list of child component that go in each cell. The cells
-- are filled left to right, top to bottom. I just formatted the list to show
-- where each component goes in the grid.
children =
{
startLbl , startSlider ,
endLbl , endSlider ,
fpsLbl , fpsSlider ,
shutterLbl , shutterSlider ,
samplesLbl , samplesSlider ,
},
padding = { 2 }, -- internal padding in each cell
inset = { 5 }, -- inset of the group component itself
}
-- create a button to show a file chooser
local fileChooseButton = octane.gui.create
{
type = octane.gui.componentType.BUTTON,
text = "Output...",
width = 80,
height = 20,
}
-- create an editor that will show the chosen file path
fileEditor = octane.gui.create
{
type = octane.gui.componentType.TEXT_EDITOR,
text = "",
x = 20,
width = 400,
height = 20,
}
-- for layouting the button and the editor we use a group
local fileGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
text = "Output",
rows = 1,
cols = 2,
children =
{
fileChooseButton, fileEditor,
},
padding = { 2 },
inset = { 5 },
}
-- eye candy, a progress bar
progressBar = octane.gui.create
{
type = octane.gui.componentType.PROGRESS_BAR,
text = "render progress",
width = fileGrp:getProperties().width * 0.8, -- as wide as the group above
height = 20,
}
-- for layouting the progress bar
local progressGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
text = "",
rows = 1,
cols = 1,
children = { progressBar },
padding = { 10 },
centre = true, -- centre the progress bar in it's cell
border = false,
}
-- render & cancel buttons
local renderButton = octane.gui.create
{
type = octane.gui.componentType.BUTTON,
text = "Render",
width = 80,
height = 20,
}
local cancelButton = octane.gui.create
{
type = octane.gui.componentType.BUTTON,
text = "Cancel",
width = 80,
height = 20,
enable = false,
}
local buttonGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
text = "",
rows = 1,
cols = 2,
children = { renderButton, cancelButton },
padding = { 5 },
border = false,
}
-- group that layouts the other groups
local layoutGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
text = "",
rows = 4,
cols = 1,
children =
{
settingsGrp,
fileGrp,
progressGrp,
buttonGrp
},
centre = true,
padding = { 2 },
border = false,
debug = false, -- true to show the outlines of the group, handy
}
-- window that holds all components
local animationWindow = octane.gui.create
{
type = octane.gui.componentType.WINDOW,
text = "Render Animation",
children = { layoutGrp },
width = layoutGrp:getProperties().width, -- same dimensions as the layout group
height = layoutGrp:getProperties().height,
}
-- gui callback
-- Main Flow
-- global variable that holds the output path
OUT_PATH = nil
function guiCallback(component, event)
if event == octane.gui.eventType.BUTTON_CLICKED then
if component == fileChooseButton then
-- choose an output file
local ret = octane.gui.showDialog
{
type = octane.gui.dialogType.FILE_DIALOG,
title = "Choose the output file",
wildcards = "*.png",
save = true,
}
-- if a file is chosen
if ret.result ~= "" then
renderButton:updateProperties{ enable = true }
fileEditor:updateProperties{ text = ret.result }
OUT_PATH = ret.result
else
renderButton:updateProperties{ enable = false }
fileEditor:updateProperties{ text = "" }
OUT_PATH = nil
end
elseif component == renderButton then
isCanceled = false
renderButton:updateProperties{enable = false}
cancelButton:updateProperties{enable = true}
render()
renderButton:updateProperties{enable = true}
cancelButton:updateProperties{enable = false}
elseif component == cancelButton then
isCanceled = true
end
elseif event == octane.gui.eventType.WINDOW_CLOSE then
isCanceled = true
end
end
-- hookup the callback with all the GUI elements
fileChooseButton:updateProperties { callback = guiCallback }
renderButton:updateProperties { callback = guiCallback }
cancelButton:updateProperties { callback = guiCallback }
animationWindow:updateProperties { callback = guiCallback }
animationWindow:showWindow()

ciao beppe