Turntable animations in Lua (part 1)

Forums: Turntable animations in Lua (part 1)
Forum for OctaneRender Lua scripting examples, discussion and support.

Turntable animations in Lua (part 1)

Postby stratified » Tue Dec 10, 2013 5:06 am

stratified Tue Dec 10, 2013 5:06 am
hi all,

Some of you already noticed that the turntable and daylight animations disappeared. Let's fix that by rebuilding them in Lua. In this first tutorial we'll show how to build the user interface for the turntable animation. In the next tutorial we'll show how to do the actual rendering of these animations.

Gui components in the Lua API are managed via properties, just a fancy name for a table of key-value pairs. In the API browser you can find what properties look like. All properties in the API have the PROP_ prefix. When creating a gui component we just create properties and pass them into the octane.gui.create function. When creating a component, all properties are optional except for the type property. Octane needs to now what kind of component to create. When certain properties are omitted, Octane will choose some sane defaults.

The things we like to control in a turntable animation are:

  • The rotation angle, the number of degrees we turn around the target position on a circle arc.
  • The start angle, we might want to start somewhere halfway the circle.
  • The target offset, the distance from the camera position to the target position.
  • The duration in seconds of our animation.
  • The framerate for our animation
  • The number of frames we'd like to render, this is coupled to the duration and the frame rate.
  • The quality, how many samples/px we like per frame of our animation.
  • It's always handy to specify where the rendered frames are saved.
  • A render button to start rendering the animation.
  • A cancel button for when we change our mind.
  • Just for eye candy let's add a progress bar so we know if we still have time for coffee.

Our component will have heaps of text labels so lets create a utility function to create a text label. All the function does is create a label from the passed in parameters and return it:

Code: Select all
-- 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


Numeric values in Octane are usually manipulated with sliders so lets create a function to create a slider:

Code: Select all
-- 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


Now let's make sure our functions pay off and create some sliders and labels. Because slider don't have text, we put a text label in front of each slider to tell what the slider does:

Code: Select all
-- lets create a bunch of labels and sliders
local degLbl       = createLabel("Degrees")
local offsetLbl    = createLabel("Start Angle")
local targetLbl    = createLabel("Target Offset")
local durationLbl  = createLabel("Duration")
local frameRateLbl = createLabel("Framerate")
local frameLbl     = createLabel("Frames")
local samplesLbl = createLabel("Samples/px")

local degSlider       = createSlider(360, -360 , 360, 1)
local offsetSlider    = createSlider(0  , -180 , 180, 1)
local targetSlider    = createSlider(10 , 0.001, 100, 0.001)
local durationSlider  = createSlider(10 , 1 , 3600  , 1)
local frameRateSlider = createSlider(25 , 10, 120   , 1)
local frameSlider     = createSlider(250, 10, 432000, 1)
local samplesSlider   = createSlider(400, 1 , 16000, 1)


With Octane you can choose to manually layout all the components by specifying the x and y offsets of the components. This will get you pixel perfect interfaces but it's tedious. We prefer to use the group component. A group component is a grid of rows and columns. Each cell of the grid takes exactly one component. The group component will make sure that the components are nicely aligned. Optionally the group component can have a border around it with a title. So let's create a group component to layout our labels and sliders:

Code: Select all
-- 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 =
    {
        degLbl           , degSlider       ,
        offsetLbl        , offsetSlider    ,
        targetLbl        , targetSlider    ,
        durationLbl      , durationSlider  ,
        frameRateLbl     , frameRateSlider ,
        frameLbl         , frameSlider     ,
        samplesLbl       , samplesSlider   ,
    },
    padding  = { 2 },   -- internal padding in each cell
    inset    = { 5 },   -- inset of the group component itself
}


We don't want to manually specify the path to the output file so let's create a button that when clicked shows the user a dialog where he can choose a file. Next to the button we create a text editor that shows the path the user has chosen. We don't enable the text editor to prevent the user
from manually typing in some bogus. We pack these components in a group component:

Code: Select all
-- 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
local fileEditor = octane.gui.create
{
    type    = octane.gui.componentType.TEXT_EDITOR,
    text    = "",
    x       = 20,
    width   = 400,
    height  = 20,
    enable  = false,   
}

-- 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 },
}


For eye candy let's add a progress bar. We'd like to have it 80% the width of the file group above
so we just get the properties of the file group and get the width property and multiply it by 0.8. Afterwards we put the progress bar in a group. We could add the progress bar directly to the window (we create later) but it's easier to layout when it's embedded in a group:

Code: Select all
-- eye candy, a progress bar
local 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,
}


We need 2 buttons to start rendering and to cancel if necessary. The same routine, we create the buttons and add them to a group for convenience. On this group, we don't show the border:

Code: Select all
-- 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,
}

local buttonGrp = octane.gui.create
{
    type     = octane.gui.componentType.GROUP,
    text     = "",
    rows     = 1,
    cols     = 2,
    children = { renderButton, cancelButton },
    padding  = { 5 },
    border   = false,
}


To make sure all groups are nicely stacked, we add all the groups in another "layout" group. For this group don't show a border. Quick tip: notice the debug property in the group properties. When this property is set to true it will draw the grid of the group component. This may come in handy when trying to get a layout right.

Code: Select all
-- 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
}


Now we have all our components we're ready to create our window. This window has one child, the layout group. We create it with the same size of the layout group to make sure we don't clip our components:

Code: Select all
-- window that holds all components
local turntableWindow = octane.gui.create
{
    type     = octane.gui.componentType.WINDOW,
    text     = "Turntable Animation",
    children = { layoutGrp },
    width    = layoutGrp:getProperties().width, -- same dimensions as the layout group
    height   = layoutGrp:getProperties().height,
}


So how do we actually react to a button click, a slider change or ... . This is done by hooking up a callback function in the component's properties. This function is called when the user does something with the component (e.g. clicks a button). Each component can have it's own callback function but here let's just have a single callback function. The callback function takes 2 parameters. The first is the component on which the event happened, the second is the event. For now let's say hello from each component:

Code: Select all
-- hookup the callback with all the GUI elements
degSlider:updateProperties        { callback = guiCallback }
offsetSlider:updateProperties     { callback = guiCallback }
targetSlider:updateProperties     { callback = guiCallback }
durationSlider:updateProperties   { callback = guiCallback }
frameRateSlider:updateProperties  { callback = guiCallback }
frameSlider:updateProperties      { callback = guiCallback }
samplesSlider:updateProperties    { callback = guiCallback }
fileChooseButton:updateProperties { callback = guiCallback }
renderButton:updateProperties     { callback = guiCallback }
cancelButton:updateProperties     { callback = guiCallback }
turntableWindow:updateProperties  { callback = guiCallback }


Phew, all we have to do now is show the window by calling octane.gui.showWindow. The script will block on this function. All the logic must be done via the callback functions from now on:

Code: Select all
-- the script will block here until the window closes
turntableWindow:showWindow()


If all went fine you should have something like this:

Code: Select all
--
-- Turntable animation script
--

-- 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 degLbl       = createLabel("Degrees")
local offsetLbl    = createLabel("Start Angle")
local targetLbl    = createLabel("Target Offset")
local durationLbl  = createLabel("Duration")
local frameRateLbl = createLabel("Framerate")
local frameLbl     = createLabel("Frames")
local samplesLbl = createLabel("Samples/px")

local degSlider       = createSlider(360, -360 , 360, 1)
local offsetSlider    = createSlider(0  , -180 , 180, 1)
local targetSlider    = createSlider(10 , 0.001, 100, 0.001)
local durationSlider  = createSlider(10 , 1 , 3600  , 1)
local frameRateSlider = createSlider(25 , 10, 120   , 1)
local frameSlider     = createSlider(250, 10, 432000, 1)
local samplesSlider   = createSlider(400, 1 , 16000, 1)

-- 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 =
    {
        degLbl           , degSlider       ,
        offsetLbl        , offsetSlider    ,
        targetLbl        , targetSlider    ,
        durationLbl      , durationSlider  ,
        frameRateLbl     , frameRateSlider ,
        frameLbl         , frameSlider     ,
        samplesLbl       , samplesSlider   ,
    },
    padding  = { 2 },   -- internal padding in each cell
    inset    = { 5 },   -- inset of the group component itself
}

-- file output

-- 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
local fileEditor = octane.gui.create
{
    type    = octane.gui.componentType.TEXT_EDITOR,
    text    = "",
    x       = 20,
    width   = 400,
    height  = 20,
    enable  = false,   
}

-- 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 },
}

-- progress bar

-- eye candy, a progress bar
local 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,
}

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 turntableWindow = octane.gui.create
{
    type     = octane.gui.componentType.WINDOW,
    text     = "Turntable Animation",
    children = { layoutGrp },
    width    = layoutGrp:getProperties().width,
    height   = layoutGrp:getProperties().height,
}


-- callback handling the GUI elements
local function guiCallback(component, event)
    if component == degSlider        then print("hello from degSlider")        end
    if component == offsetSlider     then print("hello from offsetSlider")     end
    if component == targetSlider     then print("hello from targetSlider")     end
    if component == durationSlider   then print("hello from durationSlider")   end
    if component == frameRateSlider  then print("hello from frameRateSlider")  end
    if component == frameSlider      then print("hello from frameSlider")      end
    if component == samplesSlider    then print("hello from samplesSlider")    end
    if component == fileChooseButton then print("hello from fileChooseButton") end
    if component == renderButton     then print("hello from renderButton")     end
    if component == cancelButton     then print("hello from cancelButton")     end
    if component == turntableWindow  then print("hello from turntableWindow")  end
end

-- hookup the callback with all the GUI elements
degSlider:updateProperties        { callback = guiCallback }
offsetSlider:updateProperties     { callback = guiCallback }
targetSlider:updateProperties     { callback = guiCallback }
durationSlider:updateProperties   { callback = guiCallback }
frameRateSlider:updateProperties  { callback = guiCallback }
frameSlider:updateProperties      { callback = guiCallback }
samplesSlider:updateProperties    { callback = guiCallback }
fileChooseButton:updateProperties { callback = guiCallback }
renderButton:updateProperties     { callback = guiCallback }
cancelButton:updateProperties     { callback = guiCallback }
turntableWindow:updateProperties  { callback = guiCallback }

-- the script will block here until the window closes
turntableWindow:showWindow()


And when you run it, it should look like this:

turntable.png
turntable animation component

turntable_debug.png
debugging enabled on the settings group


Next time we'll show you how to do the actual turntable animation rendering. We'll use this script when it's done in the standalone edition. So if somebody comes up with a nicer component, we'll include his instead. This is your chance for eternal glory ;)

cheers,
Thomas
Last edited by stratified on Tue Dec 10, 2013 8:44 am, edited 2 times in total.
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Re: Turntable animations in Lua (part 1)

Postby pixelrush » Tue Dec 10, 2013 6:48 am

pixelrush Tue Dec 10, 2013 6:48 am
Very good, following along...
What about clockwise/anti clockwise option? Spiral in/out? :o :P
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: Turntable animations in Lua (part 1)

Postby abstrax » Tue Dec 10, 2013 7:00 am

abstrax Tue Dec 10, 2013 7:00 am
pixelrush wrote:Very good, following along...
What about clockwise/anti clockwise option? Spiral in/out? :o :P

That will be your homework ;)
In theory there is no difference between theory and practice. In practice there is. - Yogi Berra
User avatar
abstrax
OctaneRender Team
OctaneRender Team
 
Posts: 5486
Joined: Tue May 18, 2010 11:01 am
Location: Auckland, New Zealand

Re: Turntable animations in Lua (part 1)

Postby pixelrush » Tue Dec 10, 2013 7:07 am

pixelrush Tue Dec 10, 2013 7:07 am
Come on guys! Where's your Xmas spirit? Take pity on an old man in the festive season. Gasp, cough...bloody wooden leg... :x
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: Turntable animations in Lua (part 1)

Postby glimpse » Tue Dec 10, 2013 7:54 am

glimpse Tue Dec 10, 2013 7:54 am
wow! thanks =)

Small question =) I assume it could be possible, but just for clarification.

Could we render turntable with animated piece of geometry?
or is it better to make cam animation for this? =)

amazing upgrade so far & thanks for tutorials!

cheers
User avatar
glimpse
Licensed Customer
Licensed Customer
 
Posts: 3715
Joined: Wed Jan 26, 2011 2:17 pm

Re: Turntable animations in Lua (part 1)

Postby stratified » Tue Dec 10, 2013 8:41 am

stratified Tue Dec 10, 2013 8:41 am
sure, we could modify them later to scrub through animated geometry. First I'll explain how to animate the camera.

We just have to get those damn turntable and daylight animation working again in the standalone ;)

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

Re: Turntable animations in Lua (part 1)

Postby pixelrush » Tue Dec 10, 2013 8:42 am

pixelrush Tue Dec 10, 2013 8:42 am
how do I show line numbers in the editor?
is there undo available? I see ctrl C/V works...
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: Turntable animations in Lua (part 1)

Postby stratified » Tue Dec 10, 2013 8:47 am

stratified Tue Dec 10, 2013 8:47 am
pixelrush wrote:Very good, following along...
What about clockwise/anti clockwise option? Spiral in/out? :o :P


clockwise/anti clockwise will be possible by setting a positive/negative angle.

Spiraling is not that hard, I'll show you after you've done your homework ;)

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

Re: Turntable animations in Lua (part 1)

Postby pixelrush » Tue Dec 10, 2013 9:05 am

pixelrush Tue Dec 10, 2013 9:05 am
OK so I cant find how to show line numbers in the editor. Need this please particularly where you get an error referencing a line no..
Second, looking up the API although the keywords are larger type, it would help if they were a distinct colour as well like red or gold


:arrow: Attachment to forum LUA not allowed

So here is some messing around with UI elements to pass as homework 8-)
Questions for boss coders -
-can you nest a group? possibly not...
-will we be able to run favourite scripts in the Octane UI from either from a pull down list or via an icon?

----
-- Homework - Turntable animation script with direction and spiral do-dads
--

-- 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 degLbl = createLabel("Degrees")
local offsetLbl = createLabel("Start Angle")
local targetIniLbl = createLabel("Initial")
local targetMidLbl = createLabel("Midpoint")
local targetFinLbl = createLabel("Finish")
local durationLbl = createLabel("Duration")
local frameRateLbl = createLabel("Framerate")
local frameLbl = createLabel("Frames")
local samplesLbl = createLabel("Samples/px")

local degSlider = createSlider(360, -360 , 360, 1)
local offsetSlider = createSlider(0 , -180 , 180, 1)
local targetIniSlider = createSlider(10 , 0.001, 100, 0.001)
local targetMidSlider = createSlider(10 , 0.001, 100, 0.001)
local targetFinSlider = createSlider(10 , 0.001, 100, 0.001)
local durationSlider = createSlider(10 , 1 , 3600 , 1)
local frameRateSlider = createSlider(25 , 10, 120 , 1)
local frameSlider = createSlider(250, 10, 432000, 1)
local samplesSlider = createSlider(400, 1 , 16000, 1)

-- manual layouting is tedious so let's add all our stuff in a group.
local settingsGrp1 = octane.gui.create
{
type = octane.gui.componentType.GROUP, -- type of component
text = "Settings", -- title for the group
rows = 6, -- 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 =
{
degLbl , degSlider ,
offsetLbl , offsetSlider ,
durationLbl , durationSlider ,
frameRateLbl , frameRateSlider ,
frameLbl , frameSlider ,
samplesLbl , samplesSlider ,
},
padding = { 2 }, -- internal padding in each cell
inset = { 5 }, -- inset of the group component itself
}

local settingsGrp2 = octane.gui.create
{
type = octane.gui.componentType.GROUP, -- type of component
text = "Target Offset", -- title for the group
rows = 3, -- 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 =
{
targetIniLbl , targetIniSlider ,
targetMidLbl , targetMidSlider ,
targetFinLbl , targetFinSlider ,
},
padding = { 2 }, -- internal padding in each cell
inset = { 10 }, -- inset of the group component itself
}

-- file output

-- 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
local fileEditor = octane.gui.create
{
type = octane.gui.componentType.TEXT_EDITOR,
text = "",
x = 20,
width = 400,
height = 20,
enable = false,
}

-- 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 },
}

-- progress bar

-- eye candy, a progress bar
local 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,
}

-- direction, render & cancel buttons

local reverseButton = octane.gui.create
{
type = octane.gui.componentType.CHECK_BOX,
checked = false,
text = "Reverse",
width = 80,
height = 20,
}

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,
}

local buttonGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
text = "",
rows = 1,
cols = 3,
children = { reverseButton, renderButton, cancelButton },
padding = { 5 },
border = false,
}

-- group that layouts the other groups
local layoutGrp = octane.gui.create
{
type = octane.gui.componentType.GROUP,
text = "",
rows = 5,
cols = 1,
children =
{
settingsGrp1,
settingsGrp2,
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 turntableWindow = octane.gui.create
{
type = octane.gui.componentType.WINDOW,
text = "Turntable Animation",
children = { layoutGrp },
width = layoutGrp:getProperties().width,
height = layoutGrp:getProperties().height,
}

-- callback handling the GUI elements
local function guiCallback(component, event)
if component == degSlider then print("hello from degSlider") end
if component == offsetSlider then print("hello from offsetSlider") end
if component == targetIniSlider then print("hello from targetIniSlider") end
if component == targetMidSlider then print("hello from targetMidSlider") end
if component == targetFinSlider then print("hello from targetEndSlider") end
if component == durationSlider then print("hello from durationSlider") end
if component == frameRateSlider then print("hello from frameRateSlider") end
if component == frameSlider then print("hello from frameSlider") end
if component == samplesSlider then print("hello from samplesSlider") end
if component == fileChooseButton then print("hello from fileChooseButton") end
if component == reverseButton then print("hello from reverseButton") end
if component == renderButton then print("hello from renderButton") end
if component == cancelButton then print("hello from cancelButton") end
if component == turntableWindow then print("hello from turntableWindow") end
end

-- hookup the callback with all the GUI elements
degSlider:updateProperties { callback = guiCallback }
offsetSlider:updateProperties { callback = guiCallback }
targetIniSlider:updateProperties { callback = guiCallback }
targetMidSlider:updateProperties { callback = guiCallback }
targetFinSlider:updateProperties { callback = guiCallback }
durationSlider:updateProperties { callback = guiCallback }
frameRateSlider:updateProperties { callback = guiCallback }
frameSlider:updateProperties { callback = guiCallback }
samplesSlider:updateProperties { callback = guiCallback }
fileChooseButton:updateProperties { callback = guiCallback }
reverseButton:updateProperties { callback = guiCallback }
renderButton:updateProperties { callback = guiCallback }
cancelButton:updateProperties { callback = guiCallback }
turntableWindow:updateProperties { callback = guiCallback }

-- the script will block here until the window closes
turntableWindow:showWindow()--
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: Turntable animations in Lua (part 1)

Postby stratified » Tue Dec 10, 2013 7:18 pm

stratified Tue Dec 10, 2013 7:18 pm
pixelrush wrote:how do I show line numbers in the editor?
is there undo available? I see ctrl C/V works...


Unfortunately we don't have line numbers, I know it would be very handy but our UI toolkit doesn't support it so this is something for the future.

Undo should work.

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

Return to Lua Scripting


Who is online

Users browsing this forum: No registered users and 1 guest

Fri May 10, 2024 5:03 am [ UTC ]