Batch render environment change

Forums: Batch render environment change
Generic forum to discuss Octane Render, post ideas and suggest improvements.
Forum rules
Please add your OS and Hardware Configuration in your signature, it makes it easier for us to help you analyze problems. Example: Win 7 64 | Geforce GTX680 | i7 3770 | 16GB

Batch render environment change

Postby gah5118 » Wed Feb 10, 2016 9:51 pm

gah5118 Wed Feb 10, 2016 9:51 pm
Is there a way to set the environment per frame when using an alembic file and batch rendering?

I do technical illustrations and each image needs different lighting. batch rendering and alembic gives me almost everything needed except for the ability to change the environment used per frame.


On this note, I have used other rendering packages in the past which allows for a batch. Press add to queue and it saves all information ready for batch rendering.

Currently my way of handling this process, is saving multiple packages, one per frame. I have written a program that uses the command line to open each package rendering one target at a time, then moves on to the next package.

If this isn't possible. is it possible via the command line, to tell octane to save all render passes? I could just add this to the string to my program.

thanks in advance!
Windows 8.1 | i7 950 | GTX 780ti | 24gb ddr3
gah5118
Licensed Customer
Licensed Customer
 
Posts: 358
Joined: Thu Jun 02, 2011 4:25 pm

Re: Batch render environment change

Postby bepeg4d » Thu Feb 11, 2016 9:46 am

bepeg4d Thu Feb 11, 2016 9:46 am
Hi,
sorry but I have not correctly understood your need :roll:
With "set the environment per frame" do you mean changing the environment HDRI at every frame or changing the physical sky parameters at every frame or both?
You can add a Lua script to the command line to perform any kind of modification, even saving all the passes.
If you are not in a hurry, I have almost completed a Lua script workflow for batch command line rendering especially thinked for rendering multiple animated alembic scenes in one go.
The scripts are ready to use under Mac, but I need more time to make it work also with Windows and Linux and make some final tests with net rendering ;)
ciao beppe
User avatar
bepeg4d
Octane Guru
Octane Guru
 
Posts: 9940
Joined: Wed Jun 02, 2010 6:02 am
Location: Italy

Re: Batch render environment change

Postby gah5118 » Tue Feb 16, 2016 3:06 pm

gah5118 Tue Feb 16, 2016 3:06 pm
Thanks for the reply!

What I mean is to change the environment hdr image every frame.

I attached the render tool I programmed in visual basic.

currently, the way I am setting it up is,

(in octane project)

3 render targets for (main render, shading normal, material id) named t1, t2, t3

for each frame exported out of maya, I set the camera and environment image and export as a package into its own folder numbered 1 - x

the program then will do a for next loop from the specified number to the final specified number. currently, it is reopening the same file 3 times for each render target since I don't know a command line to execute the render passes. this would solve half the problem.

however, as you are exploring, which is what i would like to do, but don't know lua script nor have the time, is to be able to use alembic with the environment image changing per frame. this would eliminate the need to load a new file per frame all together shaving the load times off.
Attachments
rendertool13.rar
(15.37 KiB) Downloaded 312 times
Windows 8.1 | i7 950 | GTX 780ti | 24gb ddr3
gah5118
Licensed Customer
Licensed Customer
 
Posts: 358
Joined: Thu Jun 02, 2011 4:25 pm

Re: Batch render environment change

Postby bepeg4d » Wed Feb 17, 2016 11:01 am

bepeg4d Wed Feb 17, 2016 11:01 am
Hi gah5118,
sorry but I cannot help you with visual basic.
Here is a simple Lua script example that change the HDR image for every frames of a scene with an animated alembic node:
Code: Select all
-- Change HDR every frame
-- @description  Script that change the HDRI on an environment node every frame of an alembic animated package
-- @author       beppeg4d

-- get the Render Target nodes of the current project
local sceneGraph = octane.project.getSceneGraph()
RTnodes = sceneGraph:findNodes(octane.NT_RENDERTARGET, true)
print(unpack(RTnodes))

-- get the animation interval
local interval = sceneGraph:getAnimationTimeSpan()
-- get the framerate configured for this project
local fps = octane.project.getProjectSettings():getAttribute(octane.A_FRAMES_PER_SECOND)
-- calculate the time delta between frames
local deltaT = 1 / fps
-- calculate the #frames in the animation
local totalFrames = fps * (interval[2] - interval[1]) or 1
print(string.format("animation timespan: [%fs, %fs] framerate: %d #frames %d", interval[1],
                  interval[2], fps, totalFrames))

hdrPath ="/Users/Username/Documents/" -- set the path of your HDR files

-- get the environment node of all the Render Target nodes and change the HDRI at any frame
envNode = {}
hdri    = {}
for frame=001,totalFrames do
    -- update the time in our scene
    local time = (frame - 1) * deltaT
    sceneGraph:updateTime(time)
    for i, v in pairs(RTnodes) do
        if RTnodes[i].name ~= "Old Mesh Preview RenderTarget" then
            envNode[i] = RTnodes[i]:getConnectedNode(octane.P_ENVIRONMENT)
            if envNode[i].type == octane.NT_ENV_TEXTURE then
                hdri[i] = envNode[i]:getConnectedNode(octane.P_TEXTURE)
                if frame <= 9 then
                    hdri[i]:setAttribute(octane.A_FILENAME, string.format("%s/HDR000%d.hdr", hdrPath, frame), true)
                else
                    hdri[i]:setAttribute(octane.A_FILENAME, string.format("%s/HDR00%d.hdr", hdrPath, frame), true)
                end
                print(hdri[i]:getAttribute(octane.A_FILENAME), frame)
            end
        end
    end
end
   

The script presumes that you have an animated .abc node in your scene and that the HDR file name structure is HDR####.hdr.
You have to write your personal path to the HDRI images folder.
Hope it could help ;)
ciao beppe
User avatar
bepeg4d
Octane Guru
Octane Guru
 
Posts: 9940
Joined: Wed Jun 02, 2010 6:02 am
Location: Italy

Re: Batch render environment change

Postby gah5118 » Wed Feb 17, 2016 1:41 pm

gah5118 Wed Feb 17, 2016 1:41 pm
sorry for the confusion, no help was needed with vb coding. Was providing it for clarity and maybe to spark ideas.

is there a way to integrate your script into the batch script?

thanks!!
Windows 8.1 | i7 950 | GTX 780ti | 24gb ddr3
gah5118
Licensed Customer
Licensed Customer
 
Posts: 358
Joined: Thu Jun 02, 2011 4:25 pm

Re: Batch render environment change

Postby bepeg4d » Sat Feb 20, 2016 7:52 am

bepeg4d Sat Feb 20, 2016 7:52 am
Hi gah5118,
in order to better understand your workflow, could you share the command line script that you are using?
ciao beppe
User avatar
bepeg4d
Octane Guru
Octane Guru
 
Posts: 9940
Joined: Wed Jun 02, 2010 6:02 am
Location: Italy

Re: Batch render environment change

Postby gah5118 » Tue Feb 23, 2016 8:46 pm

gah5118 Tue Feb 23, 2016 8:46 pm
; an example of what it would execute: -g 0 -g 2 "\1\" -t "t1" -s "4000" --film-height "2250" --film-width "3000" -o "1-1.png" -e -q

however, when it's finished with t1, it reopens the project with t2, then t3. after that it moves on to the next folder. however, there is a batch operation that has all of the features my program does aside from environment change. my program doesn't change environments however, it simply opens saved scenes where 1 octane scene is saved per folder.

this is the snip of code that controls it

Dim rtarget As String
Dim rsamples As String
Dim rscene As String
Dim outputpng As String
Dim runrender2 As String
Dim rout As String
Dim routfile As String
Dim gpu1text As String
Dim gpu2text As String
Dim gpu3text As String

gpu1text = ""
gpu2text = ""
gpu3text = ""


'Dim i As Integer
rout = " -o "
routfile = "png"

If gpucheck1.Checked = True Then
gpu1text = " -g 0"
End If

If gpucheck2.Checked = True Then
gpu2text = " -g 1"
End If

If gpucheck3.Checked = True Then
gpu3text = " -g 2"
End If

If png.Checked = True Then
rout = " -o "
routfile = "png"
ElseIf exr.Checked = True Then
rout = " --output-exr "
routfile = "exr"
ElseIf exrt.Checked = True Then
rout = " --output-exr-tm "
routfile = "exr"
ElseIf png16.Checked = True Then
rout = " --output-png16"
routfile = "png"
End If



For i As Integer = startfolder.Text To pfolderin.Text
pfolder.Text = i.ToString
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
GoTo here
Else
'direct lighting
outputpng = rroot.Text & "\" & pfolder.Text & "-1." & routfile
rsamples = rsamplesin.Text
rtarget = t1name.Text
rscene = rroot.Text & "\" & pfolder.Text & "\" & rsfile.Text
runrender2 = octanepath.Text & " " & gpu1text & gpu2text & gpu3text & " " & Chr(34) & rscene & Chr(34) & " -t " & Chr(34) & rtarget & Chr(34) & " -s " & Chr(34) & rsamples & Chr(34) & " --film-height " & Chr(34) & rheight.Text & Chr(34) & " --film-width " & Chr(34) & rwidth.Text & Chr(34) & rout & Chr(34) & outputpng & Chr(34) & " -e" & " -q"
'rsfile.Text = runrender2
'MessageBox.Show(runrender2)
Shell(runrender2, AppWinStyle.NormalFocus, True)
End If
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
GoTo here
Else
'shading normals
outputpng = rroot.Text & "\" & pfolder.Text & "-2." & routfile
rsamples = TextBox1.Text
rtarget = t2name.Text
rscene = rroot.Text & "\" & pfolder.Text & "\" & rsfile.Text
runrender2 = octanepath.Text & " " & gpu1text & gpu2text & gpu3text & " " & Chr(34) & rscene & Chr(34) & " -t " & Chr(34) & rtarget & Chr(34) & " -s " & Chr(34) & rsamples & Chr(34) & " --film-height " & Chr(34) & rheight.Text & Chr(34) & " --film-width " & Chr(34) & rwidth.Text & Chr(34) & rout & Chr(34) & outputpng & Chr(34) & " -e" & " -q"
Shell(runrender2, AppWinStyle.NormalFocus, True)
'MessageBox.Show(runrender2)

End If
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
GoTo here
Else
'material id
outputpng = rroot.Text & "\" & pfolder.Text & "." & routfile
rsamples = TextBox2.Text
rtarget = t3name.Text
rscene = rroot.Text & "\" & pfolder.Text & "\" & rsfile.Text
runrender2 = octanepath.Text & " " & gpu1text & gpu2text & gpu3text & " " & Chr(34) & rscene & Chr(34) & " -t " & Chr(34) & rtarget & Chr(34) & " -s " & Chr(34) & rsamples & Chr(34) & " --film-height " & Chr(34) & rheight.Text & Chr(34) & " --film-width " & Chr(34) & rwidth.Text & Chr(34) & rout & Chr(34) & outputpng & Chr(34) & " -e" & " -q"
Shell(runrender2, AppWinStyle.NormalFocus, True)
'MessageBox.Show(runrender2)

End If

If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
GoTo here
Else
End If
If tcheck4.Checked = False Then
GoTo nextloop
Else

'material id
outputpng = rroot.Text & "\" & pfolder.Text & "." & routfile
rsamples = TextBox2.Text
rtarget = t3name.Text
rscene = rroot.Text & "\" & pfolder.Text & "\" & rsfile.Text
runrender2 = octanepath.Text & " " & gpu1text & gpu2text & gpu3text & " " & Chr(34) & rscene & Chr(34) & " -t " & Chr(34) & rtarget & Chr(34) & " -s " & Chr(34) & rsamples & Chr(34) & " --film-height " & Chr(34) & rheight.Text & Chr(34) & " --film-width " & Chr(34) & rwidth.Text & Chr(34) & rout & Chr(34) & outputpng & Chr(34) & " -e" & " -q"
Shell(runrender2, AppWinStyle.NormalFocus, True)
'MessageBox.Show(runrender2)

End If
nextloop:

Next
here:

End Sub
Windows 8.1 | i7 950 | GTX 780ti | 24gb ddr3
gah5118
Licensed Customer
Licensed Customer
 
Posts: 358
Joined: Thu Jun 02, 2011 4:25 pm

Re: Batch render environment change

Postby gah5118 » Tue Feb 23, 2016 8:47 pm

gah5118 Tue Feb 23, 2016 8:47 pm
here is the lua script for the batch rendering from octane.

----------------------------------------------------------------------------------------------------
-- Renders all the available render targets of the loaded project.
-- Asks the user for an output directory and saves all the images into that directory
-- (e.g. <rendertargetname>_01.png, <rendertargetname>_02.png, ...)
--
-- @author Mark Basset, Octane dev team and others
-- @description Batch rendering for Octane.
-- @version 0.3
-- @script-id OctaneRender batch rendering

-- Global table with our settings. All global variables should be here to keep an overview.
-- TODO: a property table here would make everything more compact
local gSettings =
{
-- absolute path of the current project
projectPath = octane.project.getCurrentProject(),
-- the copied scene graph
sceneGraph = nil,
-- list of render target nodes in the current project together with their enable state
-- and export file format
renderTargets = {},
-- absolute path to the output directory of the rendered images
outputDirectory = nil,
-- true to override the max samples/px
overrideMaxSamples = false,
-- max samples/px
maxSamples = 1000,
-- filename template for the output files
template = "%n_%f_%p.%e",
-- true if the rendering was cancelled
cancelled = false,
-- handle for the progress update function (takes value and text)
progress = nil,
-- handle for the batch render function
batchRender = nil,
-- handle for the window
window = nil,
-- set to true to show the debug outlines of the groups
showGrpOutlines = false,
-- framerate
fps = nil,
-- delta value between 2 animation frames (1 / fps)
dT = nil,
-- frame number where we start rendering
startFrame = nil,
-- frame number where we finish rendering (inclusive)
endFrame = nil,
-- start time of the animation (s)
startTime = nil,
-- end time of the animation (s)
endTime = nil,
-- shutter time
shutterTime = nil,
-- frame number from which we start number files (0 means no offset)
fileNumber = 0,
-- skip existing files
skipExisting = false,
-- save all enabled render passes
saveAllPasses = true,
-- save the render passes as a layered exr
saveMultiLayerExr = false,
}


local function loadFromDisk()
for k, v in pairs(octane.storage.project) do
if k then
gSettings[k] = v
end
end
end


local function storeOnDisk()
local storage = octane.storage.project
storage.fps = gSettings.fps
storage.startTime = gSettings.startTime
storage.endTime = gSettings.endTime
storage.shutterTime = gSettings.shutterTime
storage.overrideMaxSamples = gSettings.overrideMaxSamples
storage.maxSamples = gSettings.maxSamples
storage.fileNumber = gSettings.fileNumber
storage.fileTemplate = gSettings.fileTemplate
storage.outputDirectory = gSettings.outputDirectory
storage.template = gSettings.template
storage.skipExisting = gSettings.skipExisting
storage.saveAllPasses = gSettings.saveAllPasses
storage.saveMultiLayerExr = gSettings.saveMultiLayerExr
end


-- Recursively enables or disables all components on a window.
local function setEnabled(component, enable)
if component.type == octane.gui.componentType.WINDOW or
component.type == octane.gui.componentType.GROUP then
for _, childComponent in ipairs(component.children) do
setEnabled(childComponent, enable)
end
else
component.enable = enable
end
end


-- sorts a table alpha numerically
-- (snippet from http://notebook.kulchenko.com/algorithm ... ans-in-lua)
local function alphanumsort(nodes)
local function padnum(d)
local dec, n = string.match(d, "(%.?)0*(.+)")
return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n)
end
local function compare(n0, n1)
local a, b = n0.name, n1.name
return a:gsub("%.?%d+",padnum)..("%3d"):format(#b)
< b:gsub("%.?%d+",padnum)..("%3d"):format(#a)
end
table.sort(nodes, compare)
end


local function cancelRendering()
gSettings.cancelled = true
octane.render.callbackStop()
end


local function isAnimatedScene()
local interval = gSettings.sceneGraph:getAnimationTimeSpan()
return interval[1] < interval[2]
end


local function displayTimeAsSeconds()
return octane.project.getPreferences():getAttribute(octane.A_TIME_DISPLAY) == octane.timeDisplay.SECONDS
end


local function displayShutterAsSeconds()
return octane.project.getPreferences():getAttribute(octane.A_SHUTTER_TIME_DISPLAY) == octane.shutterTimeDisplay.SECONDS
end


local function calcSceneFrameCount()
local interval = gSettings.sceneGraph:getAnimationTimeSpan()
return math.max(0, 1 + math.floor((interval[2] - interval[1] + .0001 * gSettings.dT) * gSettings.fps))
end


local function timeToFrame(time)
local interval = gSettings.sceneGraph:getAnimationTimeSpan()
return math.floor((time - interval[1] + .0001 * (gSettings.dT)) * gSettings.fps)
end


local function frameToTime(frame)
local interval = gSettings.sceneGraph:getAnimationTimeSpan()
return interval[1] + frame * gSettings.dT
end


local function calcProgressUnit()
local activeTargets = 0
for _, renderTarget in ipairs(gSettings.renderTargets) do
if renderTarget.render then
activeTargets = activeTargets + 1
end
end
return 1 / (activeTargets * (gSettings.endFrame - gSettings.startFrame + 1))
end


local function createFilename(ix, frame, name, imageType, pass)
-- common extension for our image output types
local fileExtensions =
{
[octane.render.imageType.PNG8] = "png",
[octane.render.imageType.PNG16] = "png",
[octane.render.imageType.EXR] = "exr",
[octane.render.imageType.EXRTONEMAPPED] = "exr",
}
local s = gSettings.template
-- %i -> index of the render target
s = string.gsub(s, "%%i", string.format("%d", ix))
-- %s -> frame number
s = string.gsub(s, "%%f", string.format("%d", gSettings.fileNumber + frame))
-- %n -> name of the node
s = string.gsub(s, "%%n", name)
-- %e -> extension
s = string.gsub(s, "%%e", fileExtensions[imageType])
-- %t -> timestamp (h_m_s)
s = string.gsub(s, "%%t", os.date("%H_%M_%S"))
-- %p -> render pass name
s = string.gsub(s, "%%p", pass)
return s
end


local function createRenderPassExportObjs(renderTargetNode)
-- get the render passes node
local rpNode = renderTargetNode:getInputNode(octane.P_RENDER_PASSES)
if not rpNode then
return nil
end

-- HACK: mix the info passes with the beauty passes
rpNode:setPinValue(octane.P_RENDER_PASS_INFO_AFTER_BEAUTY, false)

-- create the export objects
local objs = {}
for _, id in ipairs(octane.render.getAllRenderPassIds()) do
local info = octane.render.getRenderPassInfo(id)
if info.pinId ~= octane.P_UNKNOWN then
if rpNode:getPinValue(info.pinId) then
local exportObj =
{
["exportName"] = nil,
["origName"] = info.name,
["renderPassId"] = info.renderPassId,
}
table.insert(objs, exportObj)
end
else
local exportObj =
{
["exportName"] = nil,
["origName"] = info.name,
["renderPassId"] = info.renderPassId,
}
table.insert(objs, exportObj)
end
end
return objs
end


----------------------------------------------------------------------------------------------------
-- Main window creation

-- layout constants
local BUTTON_WIDTH = 100
local BUTTON_HEIGHT = 20
local WIDE_LBL_WIDTH = 200
local NARROW_LBL_WIDTH = 150
local LBL_HEIGHT = 20
local GRP_PAD = 2

-- Creates a tabular overview of all the render targets in the current project.
local function initRenderTargetOverview()
local tableChildren = {}
-- collect all the render targets from the current project
for ix, renderTarget in ipairs(gSettings.renderTargets) do
local description = string.format("%5d: %s", ix, renderTarget.node.name)
local lbl = octane.gui.createLabel
{
text = description,
width = WIDE_LBL_WIDTH,
height = LBL_HEIGHT
}
local renderBox = octane.gui.createCheckBox
{
text = "render",
enable = true,
width = 80,
checked = renderTarget.render,
callback = function(box)
-- toggle the for the render target enable state in the settings
renderTarget.render = box.checked
end
}
local fileTypeCombo = octane.gui.createComboBox
{
type = octane.gui.componentType.COMBO_BOX,
items =
{
"PNG (8-bit)",
"PNG (16-bit)",
"EXR",
},
selectedIx = 1,
width = 120,
callback = function(combo)
-- update the file type for this render target in the settings
local fileTypes =
{
octane.render.imageType.PNG8,
octane.render.imageType.PNG16,
octane.render.imageType.EXR,
}
renderTarget.fileType = fileTypes[combo.selectedIx]
end
}

-- add components in the list
table.insert(tableChildren, lbl)
table.insert(tableChildren, renderBox)
table.insert(tableChildren, fileTypeCombo)
end

local renderAllButton = octane.gui.createButton
{
width = BUTTON_WIDTH,
height = BUTTON_HEIGHT,
text = "Render all",
callback = function()
for _,renderTarget in ipairs(gSettings.renderTargets) do
renderTarget.render = true
end
for _,child in ipairs(tableChildren) do
if child.type == octane.gui.componentType.CHECK_BOX then
child.checked = true
end
end
end
}
local renderNoneButton = octane.gui.createButton
{
width = BUTTON_WIDTH,
height = BUTTON_HEIGHT,
text = "Render none",
callback = function()
for _,renderTarget in ipairs(gSettings.renderTargets) do
renderTarget.render = false
end

-- update the check boxes
for _,child in ipairs(tableChildren) do
if child.type == octane.gui.componentType.CHECK_BOX then
child.checked = false
end
end
end
}

-- add the disable button between 2 dummy labels
local dummyLbl = octane.gui.createLabel
{
text = "",
width = 1,
height = 1,
}
table.insert(tableChildren, dummyLbl)
table.insert(tableChildren, renderAllButton)
table.insert(tableChildren, renderNoneButton)

-- create a group to pack it all up
local renderTargetsGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
children = tableChildren,
border = true,
cols = 3,
rows = #tableChildren / 3,
border = false,
padding = { GRP_PAD },
debug = gSettings.showGrpOutlines,
}

-- wrap the group in a panel stack to make it scrollable
return octane.gui.create
{
type = octane.gui.componentType.PANEL_STACK,
height = 200,
children = { renderTargetsGrp },
open = { true },
captions = { "Render targets" },
}
end


-- Initializes a GUI group with all the config settings
local function initSettingGroup(width)
-- frames per second
local fpsLbl = octane.gui.createLabel
{
text = "Framerate",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
enable = isAnimatedScene(),
}
local sliderWidth = width - NARROW_LBL_WIDTH - 10
local fpsSlider = octane.gui.createSlider
{
value = gSettings.fps,
minValue = 1,
maxValue = 120,
step = 0.1,
width = sliderWidth,
enable = isAnimatedScene(),
}
-- start and end time/frame sliders
local startTimeLbl = octane.gui.createLabel
{
text = string.format("Start %s", displayTimeAsSeconds() and "time" or "frame"),
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
enable = isAnimatedScene(),
}
local startSlider = octane.gui.createSlider
{
width = sliderWidth,
enable = isAnimatedScene(),
}
local endTimeLbl = octane.gui.createLabel
{
text = string.format("End %s", displayTimeAsSeconds() and "time" or "frame"),
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
enable = isAnimatedScene(),
}
local endSlider = octane.gui.createSlider
{
width = sliderWidth,
enable = isAnimatedScene(),
}
if displayTimeAsSeconds() then
local interval = gSettings.sceneGraph:getAnimationTimeSpan()
startSlider.value = gSettings.startTime
startSlider.minValue = interval[1]
startSlider.maxValue = interval[2]
startSlider.step = gSettings.dT
endSlider.value = gSettings.endTime
endSlider.minValue = interval[1]
endSlider.maxValue = interval[2]
endSlider.step = gSettings.dT
else
startSlider.value = gSettings.startFrame
startSlider.minValue = 0
startSlider.maxValue = calcSceneFrameCount() - 1
startSlider.step = 1
endSlider.value = gSettings.endFrame
endSlider.minValue = 0
endSlider.maxValue = calcSceneFrameCount() - 1
endSlider.step = 1
end
-- shutter time
local shutterTimeLbl = octane.gui.createLabel
{
text = string.format("Shutter time (%s)", displayShutterAsSeconds() and "s" or "%"),
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
enable = isAnimatedScene(),
}
local shutterTimeSlider = octane.gui.createSlider
{
type = octane.gui.componentType.SLIDER,
logarithmic = true,
width = sliderWidth,
enable = isAnimatedScene(),
}
if displayShutterAsSeconds() then
shutterTimeSlider.value = gSettings.shutterTime
shutterTimeSlider.minValue = 0
shutterTimeSlider.maxValue = 1
shutterTimeSlider.step = 0.0001
else
shutterTimeSlider.value = gSettings.shutterTime * 100 * gSettings.fps
shutterTimeSlider.minValue = 0
shutterTimeSlider.maxValue = 1
shutterTimeSlider.step = 0.1
end
-- file numbering
local fileNbrLbl = octane.gui.createLabel
{
text = "File numbering",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
enable = isAnimatedScene(),
}
local fileNbrBox = octane.gui.createNumericBox
{
x = 10,
minValue = 0,
maxValue = 10000,
value = gSettings.fileNumber,
step = 1,
enable = isAnimatedScene() and gSettings.useFileNumber,
}
local fileNbrCheck = octane.gui.createCheckBox
{
text = "Start file numbering at:",
checked = gSettings.useFileNumber,
enable = isAnimatedScene() and fileNbrBox.checked,
callback = function(me)
if (me.checked) then
fileNbrBox.enable = true
gSettings.fileNumber = fileNbrBox.value
else
fileNbrBox.enable = false
gSettings.fileNbrBox = 0
end
end
}
local fileNbrGrp = octane.gui.createGroup
{
border = false,
children = { { fileNbrCheck, fileNbrBox } },
padding = { 0 },
inset = { 0 },
enable = isAnimatedScene(),
}

-- time controls are tightly coupled by this callback
local function timeSliderCallback(slider)
gSettings.fps = fpsSlider.value
gSettings.dT = 1 / fpsSlider.value

-- fps changed -> update time interval sliders range
if slider == fpsSlider then
if displayTimeAsSeconds() then
startSlider.step = gSettings.dT
endSlider.step = gSettings.dT
else
local frameCount = calcSceneFrameCount()
startSlider.maxValue = frameCount - 1
startSlider.value = math.min(startSlider.value, startSlider.maxValue)
endSlider.maxValue = frameCount - 1
endSlider.value = math.min(endSlider.value, endSlider.maxValue)
end
end

if displayTimeAsSeconds() then
gSettings.startTime = startSlider.value
gSettings.endTime = endSlider.value
gSettings.startFrame = timeToFrame(gSettings.startTime)
gSettings.endFrame = timeToFrame(gSettings.endTime)
else
gSettings.startFrame = startSlider.value
gSettings.endFrame = endSlider.value
gSettings.startTime = frameToTime(gSettings.startFrame, gSettings.fps)
gSettings.endTime = frameToTime(gSettings.endFrame , gSettings.fps)
end

-- make sure start and end don't cross over
if (slider == startSlider) then
gSettings.startFrame = math.min(gSettings.startFrame, gSettings.endFrame)
startSlider.value = math.min(startSlider.value, endSlider.value)
else
gSettings.endFrame = math.max(gSettings.startFrame, gSettings.endFrame)
endSlider.value = math.max(startSlider.value, endSlider.value)
end
end

startSlider.callback = timeSliderCallback
endSlider.callback = timeSliderCallback
fpsSlider.callback = timeSliderCallback

-- override label
local overrideLbl = octane.gui.createLabel
{
text = "Override s/px",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
}
local maxSamplexBox = octane.gui.createNumericBox
{
x = 10,
value = gSettings.maxSamples,
minValue = 1,
maxValue = 256000,
step = 1,
logarithmic = true,
enable = gSettings.overrideMaxSamples,
callback = function(me)
gSettings.maxSamples = me.value
end
}
local overrideChk = octane.gui.createCheckBox
{
text = "Samples/px:",
checked = false,
tooltip = "Override max samples in the kernel node",
checked = gSettings.overrideMaxSamples,
callback = function(box)
if box.checked then
maxSamplexBox.enable = box.checked
gSettings.overrideMaxSamples = true
gSettings.maxSamples = maxSamplexBox.value
else
maxSamplexBox.enable = false
gSettings.overrideMaxSamples = false
end
end
}
local overrideGrp = octane.gui.createGroup
{
border = false,
children = { { overrideChk, maxSamplexBox } },
padding = { 0 },
inset = { 0 },
}
-- template label
local templateLbl = octane.gui.createLabel
{
text = "Filename template",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
}
-- template text editor
local templateEditor = octane.gui.createTextEditor
{
text = gSettings.template,
width = width - templateLbl.width - 10,
height = 20,
enable = true,
tooltip = "Template parameters: %i render target index %n node name %e extension %t timestamp %f frame %p render pass name",
callback = function(editor)
gSettings.template = editor.text
end
}
-- create a button to browse for an output folder
local outputButton = octane.gui.createButton
{
text = "Output folder...",
width = BUTTON_WIDTH,
height = BUTTON_HEIGHT,
tooltip = "Specify the folder to receive the rendered output files."
}
-- create an editor that will show the chosen file path
local txtEditorDirectory = octane.gui.createTextEditor
{
text = gSettings.outputDirectory or "",
width = templateEditor.width,
height = 20,
enable = true,
callback = function(me)
gSettings.outputDirectory = me.text
end
}
-- skip existing files
local skipExistingLbl = octane.gui.createLabel
{
text = "Skip existing files",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
}
local skipExistingChk = octane.gui.createCheckBox
{
text = "",
checked = gSettings.skipExisting,
callback = function(me) gSettings.skipExisting = me.checked end
}
-- render passes
local renderPassLbl = octane.gui.createLabel
{
text = "Render passes",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
}
local renderPassesChk = octane.gui.createCheckBox
{
text = "Save all enabled passes",
checked = gSettings.saveAllPasses,
callback = function(me)
gSettings.saveAllPasses = me.checked
end
}
local dummyLbl = octane.gui.createLabel
{
text = "",
width = NARROW_LBL_WIDTH,
height = LBL_HEIGHT,
}
local multiLayerExrChk = octane.gui.createCheckBox
{
text = "Save layered EXR (only when saved as EXR)",
checked = gSettings.saveMultiLayerExr,
width = txtEditorDirectory.width,
callback = function(me)
gSettings.saveMultiLayerExr = me.checked
end
}

-- callback function for the output button
outputButton.callback = function()
-- ask the user for an output directory for the results
local ret = octane.gui.showDialog
{
type = octane.gui.dialogType.FILE_DIALOG,
title = "Select output directory for the render results",
path = octane.file.getParentDirectory(gSettings.projectPath),
browseDirectory = true,
save = false,
}
if not ret.result or ret.result == "" then
gSettings.outputDirectory = nil
else
gSettings.outputDirectory = ret.result
txtEditorDirectory.text = gSettings.outputDirectory
end
end

-- group holding it all together
return octane.gui.createGroup
{
children =
{
{ fpsLbl , fpsSlider },
{ startTimeLbl , startSlider },
{ endTimeLbl , endSlider },
{ shutterTimeLbl , shutterTimeSlider },
{ fileNbrLbl , fileNbrGrp },
{ overrideLbl , overrideGrp },
{ templateLbl , templateEditor },
{ outputButton , txtEditorDirectory },
{ skipExistingLbl, skipExistingChk },
{ renderPassLbl , renderPassesChk },
{ dummyLbl , multiLayerExrChk },
},
text = "Settings",
border = true,
inset = { GRP_PAD },
padding = { GRP_PAD },
debug = gSettings.showGrpOutlines,
}
end


-- Inits the progress bar.
local function initProgressBar(width)
local progressBar = octane.gui.createProgressBar { text = "", width = width-10, height = LBL_HEIGHT }
-- the global progress function needs to update the progress bar
gSettings.progress = function(progress, text)
progressBar.progress = progress
progressBar.text = text
end

return progressBar
end


-- Inits a GUI group for the control buttons.
local function initControlsGroup()
local cancelButton = octane.gui.createButton
{
text = "Cancel",
width = BUTTON_WIDTH,
height = BUTTON_HEIGHT,
tooltip = "Cancel batch processing.",
enable = false,
callback = function() cancelRendering() end
}
local startButton = octane.gui.createButton
{
text = "Start",
width = BUTTON_WIDTH,
height = BUTTON_HEIGHT,
tooltip = "Start batch processing.",
callback = function()
-- we will hang in this callback until rendering is finished, the only way to stay
-- responsive is via the render callback

-- clear cancel flag
gSettings.cancelled = false
-- disable all but the cancel button
setEnabled(gSettings.window, false)
cancelButton.enable = true
-- do the work (blocks)
gSettings.batchRender()
-- enable all but the cancel button
setEnabled(gSettings.window, true)
cancelButton.enable = false
if not gSettings.cancelled then
gSettings.progress(101, "Finished")
end
end
}
return octane.gui.create
{
type = octane.gui.componentType.GROUP,
children = { startButton, cancelButton },
rows = 1,
cols = 2,
border = false,
padding = { 2 },
debug = gSettings.showGrpOutlines,
}
end


-- Initializes the main window.
local function initMainWindow()
-- initialize the main pieces of the user interface
local renderTargetOverview = initRenderTargetOverview()
local userSettings = initSettingGroup(renderTargetOverview.width)
local progressBar = initProgressBar(userSettings.width)
local controlsGroup = initControlsGroup()

-- these guys go on the window top-to-bottom
local children = { renderTargetOverview, userSettings, progressBar, controlsGroup }

-- this group holds everything together
local layoutGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
children = children,
cols = 1,
rows = #children,
border = false,
padding = { 2 },
centre = true,
debug = gSettings.showGrpOutlines,
}
-- window that holds all components
return octane.gui.create
{
type = octane.gui.componentType.WINDOW,
text = "Batch rendering",
children = { layoutGrp },
width = layoutGrp.width,
height = layoutGrp.height,
callback = function()
-- cancel rendering on close
cancelRendering()
end
}
end



----------------------------------------------------------------------------------------------------
-- Batch rendering


-- The batch rendering function. This will render each frame for each selected render target.
gSettings.batchRender = function()
-- keeps track of the render progress (in range [0,1])
local progress = 0
local progressStep = calcProgressUnit()

-- render each animation frame
for frame = gSettings.startFrame,gSettings.endFrame do
-- update the time in the scene
gSettings.sceneGraph:updateTime(frameToTime(frame))

-- render all the render targets that are enabled
for ix, renderTarget in ipairs(gSettings.renderTargets) do
if renderTarget.render then
-- override max samples if configured
if gSettings.overrideMaxSamples then
renderTarget.node:getInputNode(octane.P_KERNEL):setPinValue(octane.P_MAX_SAMPLES, gSettings.maxSamples)
end

-- create the render pass export objects (returns nil when there aren't any passes)
local renderPassExportObjs = createRenderPassExportObjs(renderTarget.node)

-- 1) save out all the render passes
if renderPassExportObjs and gSettings.saveAllPasses then
-- a) multi-layer EXR
if renderTarget.fileType == octane.render.imageType.EXR and gSettings.saveMultiLayerExr then
for _, exportObj in ipairs(renderPassExportObjs) do
-- don't prefix the name of the beauty pass, the beauty pass should be
-- in the RGBA channels
if exportObj.renderPassId == octane.renderPassId.BEAUTY then
exportObj.exportName = ""
else
exportObj.exportName = exportObj.origName
end
end
-- create an output path for the image
local filename = createFilename(ix, frame, renderTarget.node.name, renderTarget.fileType, "all")
local path
if gSettings.outputDirectory then
path = octane.file.join(gSettings.outputDirectory, filename)
else
path = string.format("%s (dry-run)", filename)
end

-- update the progress
gSettings.progress(progress, string.format("frame %d/%d - %s -> %s (layerd EXR)", frame, calcSceneFrameCount(), renderTarget.node.name, path))
progress = progress + progressStep

local skipFrame = gSettings.skipExisting and gSettings.outputDirectory and octane.file.exists(path)
-- do the rendering of the image
if not skipFrame then
octane.render.start
{
renderTargetNode = renderTarget.node,
callback = function()
if gSettings.cancelled then
octane.render.callbackStop()
end
end
}
end

-- cancelled -> bail out and update progress bar
if gSettings.cancelled then
gSettings.progress(0, "Cancelled")
return
end

-- save out the multi layer EXR
if gSettings.outputDirectory and path and not skipFrame then
octane.render.saveRenderPassesMultiExr(path, renderPassExportObjs)
end

-- b) each render pass as a discrete file
else
-- create file names for each file
for _, exportObj in ipairs(renderPassExportObjs) do
exportObj.exportName = createFilename(ix, frame, renderTarget.node.name, renderTarget.fileType, exportObj.origName)
end
local path
if gSettings.outputDirectory then
path = gSettings.outputDirectory
else
path = "(dry-run)"
end

-- update the progress
gSettings.progress(progress, string.format("frame %d/%d - %s -> %s (discrete files)", frame, calcSceneFrameCount(), renderTarget.node.name, path))
progress = progress + progressStep

-- check if at least 1 file doesn't exits
local skipFrame = gSettings.skipFrame
if skipFrame then
for _, exportObj in ipairs(renderPassExportObjs) do
local fullPath = octane.file.join(path, exportObj.exportName)
if not octane.file.exists(fullPath) then
skipFrame = false
break
end
end
end

-- do the rendering of the image
if not skipFrame then
octane.render.start
{
renderTargetNode = renderTarget.node,
callback = function()
if gSettings.cancelled then
octane.render.callbackStop()
end
end
}
end

-- cancelled -> bail out and update progress bar
if gSettings.cancelled then
gSettings.progress(0, "Cancelled")
return
end

-- save out the passes as discrete files
if gSettings.outputDirectory and path and not skipFrame then
octane.render.saveRenderPasses(path, renderPassExportObjs, renderTarget.fileType)
end
end

-- 2) only save out the beauty pass
else
-- create an output path for the image
local filename = createFilename(ix, frame, renderTarget.node.name, renderTarget.fileType, "")
local path
if gSettings.outputDirectory then
path = octane.file.join(gSettings.outputDirectory, filename)
else
path = string.format("%s (dry-run)", filename)
end

-- update the progress
gSettings.progress(progress, string.format("frame %d/%d - %s -> %s", frame, calcSceneFrameCount(), renderTarget.node.name, path))
progress = progress + progressStep

local skipFrame = gSettings.skipExisting and gSettings.outputDirectory and octane.file.exists(path)
-- do the rendering of the image
if not skipFrame then
octane.render.start
{
renderTargetNode = renderTarget.node,
callback = function()
if gSettings.cancelled then
octane.render.callbackStop()
end
end
}
end

-- cancelled -> baild out and update progress bar
if gSettings.cancelled then
gSettings.progress(0, "Cancelled")
return
end

-- save out the image
if gSettings.outputDirectory and path and not skipFrame then
octane.render.saveImage(path, renderTarget.fileType)
end
end
end
end
end
end



---------------------------------------------------------------------------------------------------
-- Main script

-- create a copy of the original project
gSettings.sceneGraph = octane.nodegraph.createRootGraph("Project Copy")
gSettings.sceneGraph:copyFromGraph(octane.project.getSceneGraph())


loadFromDisk()
-- find out the time settings for this scene (if not loaded from disk)
local interval = gSettings.sceneGraph:getAnimationTimeSpan()
gSettings.shutterTime = gSettings.shutterTime or octane.render.getShutterTime()
gSettings.fps = gSettings.fps or octane.project.getProjectSettings():getAttribute(octane.A_FRAMES_PER_SECOND)
gSettings.dT = 1 / gSettings.fps
gSettings.startTime = gSettings.startTime or interval[1]
gSettings.endTime = gSettings.endTime or interval[2]
gSettings.startFrame = timeToFrame(gSettings.startTime)
gSettings.endFrame = timeToFrame(gSettings.endTime)
gSettings.fileNumber = 1

-- fetch all the render target nodes
local renderTargetNodes = gSettings.sceneGraph:findNodes(octane.NT_RENDERTARGET, true)
-- if no render targets are found -> error out
if #renderTargetNodes == 0 then
error("No render targets in this project.")
end

-- sort all the render target nodes alphanumerically
alphanumsort(renderTargetNodes)

-- initialize the state for the render targets
for _, node in ipairs(renderTargetNodes) do
local state =
{
["node"] = node,
["render"] = true,
["fileType"] = octane.render.imageType.PNG8,
}
table.insert(gSettings.renderTargets, state)
end

-- the script blocks here until the window is closes
gSettings.window = initMainWindow()
gSettings.window:showWindow()

storeOnDisk()
Windows 8.1 | i7 950 | GTX 780ti | 24gb ddr3
gah5118
Licensed Customer
Licensed Customer
 
Posts: 358
Joined: Thu Jun 02, 2011 4:25 pm

Return to General Discussion


Who is online

Users browsing this forum: No registered users and 25 guests

Thu Mar 28, 2024 12:26 pm [ UTC ]