Script to render imported animations

Forum for OctaneRender Lua scripting examples, discussion and support.
User avatar
bepeg4d
Octane Guru
Posts: 10326
Joined: Wed Jun 02, 2010 6:02 am
Location: Italy
Contact:

ok, thanks to the second part of the turntable tutorial, i was able to identify the missing part for choosing the output path and to integrated it in the animation script:

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()
thanks thomas and roeland, now rendering and saving alembic animation is super easy :)
ciao beppe
User avatar
stratified
OctaneRender Team
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Great work bepe! I think heaps of users will find this usefull.

cheers,
Thomas
User avatar
pixelrush
Licensed Customer
Posts: 1618
Joined: Mon Jan 11, 2010 7:11 pm
Location: Nelson, New Zealand

Could we allow Lua attachments in the forum please.
i7-3820 @4.3Ghz | 24gb | Win7pro-64
GTS 250 display + 2 x GTX 780 cuda| driver 331.65
Octane v1.55
User avatar
stratified
OctaneRender Team
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

We'll try to make that happen.

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

actually it's already possible
turntable.lua
turntable animation script
(16.49 KiB) Downloaded 347 times
p3taoctane
Licensed Customer
Posts: 1418
Joined: Mon Jan 25, 2010 12:53 am

Awesome
Thanks for sharing
Beppe any chance you can share the script that renders out an abc file also

Thanks

Peter
Windows 7 Pro_SP 1_64 bit_48 GB Ram_Intel Xeon X5660 2.80 GHZ x2_6 580GTX_1 Quadra 4800
User avatar
bepeg4d
Octane Guru
Posts: 10326
Joined: Wed Jun 02, 2010 6:02 am
Location: Italy
Contact:

p3taoctane wrote:Awesome
Thanks for sharing
Beppe any chance you can share the script that renders out an abc file also

Thanks

Peter
sure ;)
ciao beppe
Attachments
render-aniamtion-02.lua
(10.48 KiB) Downloaded 349 times
p3taoctane
Licensed Customer
Posts: 1418
Joined: Mon Jan 25, 2010 12:53 am

Awesome thanks mate

Peter
Windows 7 Pro_SP 1_64 bit_48 GB Ram_Intel Xeon X5660 2.80 GHZ x2_6 580GTX_1 Quadra 4800
Tugpsx
Licensed Customer
Posts: 1150
Joined: Thu Feb 04, 2010 8:04 pm
Location: Chicago, IL
Contact:

Scripting coming along nicely. thanks all for sharing.
Win 11 64GB | NVIDIA RTX3060 12GB
User avatar
bepeg4d
Octane Guru
Posts: 10326
Joined: Wed Jun 02, 2010 6:02 am
Location: Italy
Contact:

hi, i'm having a big fun with this script :D
i have tested it also under os x with 1.22 and it works smoothly, except for the fact that the script window is not on top and you need to move the octane window to show it.
need to test with 1.23 when it will work under os x again ;)
just a little request: it's possible to add a radio button for reversing the sequence in order to start the render from the last frame instead of the first?
in this way we can render the same sequence with two machines in the same time without worrying about the subdivision of the frames ;)
ciao beppe
Post Reply

Return to “Lua Scripting”