Today we'll give a brief overview of the node and graph module. Every Octane user is familiar with nodes and graphs but let's explain some details to get you up to speed. We won't build a full example here but we'll include some code snippets to experiment with. Note: not all the code here will work with version 1.21, some stuff was added in 1.22.
Before we take a deep dive, let's have a look at the octane module. This module contains common enumerations used in the node and graph modules:
- octane.attributeId: A table with all the attribute identifiers available in Octane. (prefix A_ e.g. A_VALUE).
- octane.attributeType: A table with all the attribute types (prefix AT_ e.g. AT_BYTE).
- octane.graphType: A table with all the graph types (prefix GT_ e.g. GT_STANDARD).
- octane.nodeType: A table with all the node types (prefix NT_ e.g. NT_CAM_THINLENS).
- octane.pinId: A table with all the pin identifiers (prefix P_ e.g. P_KERNEL).
- octane.pinType: A table with all the pin types (prefix PT_ e.g. PT_TEX).
The following code will list all nodes available. Note that all not all nodes can be created, some are internal to Octane (we probably hide them in the future):
Code: Select all
-- list all the node types
for type, value in pairs(octane.nodeType) do
print(type, value)
end
Nodes can be created from a script and they are added to the current project. We can create nodes either in a graph or internal in a pin (graph owned or pin owned). Internal nodes must have the same output type as the type of the pin they're created in and they are implicitely connected. For example:
Code: Select all
-- create a node in the root graph (a.k.a the scene graph)
rt = octane.node.create{ type=octane.NT_RENDERTARGET, name="Impl RT" } -- implicit in the root graph
bool = octane.node.create{ type=octane.NT_BOOL, name="Expl Bool", graphOwner=octane.nodegraph.getRootGraph() }
print(rt:getProperties().graphOwned, rt:getProperties().graphOwner)
-- create a panoramic camera in the camera pin of the render target
-- we need to specify both the node that has the pin and the pin itself
pcam = octane.node.create{ type=octane.NT_CAM_PANORAMIC, pinOwnerNode=rt, pinOwnerId=octane.P_CAMERA }
print(pcam:getProperties().pinOwned, pcam:getProperties().pinOwnerNode)
-- creating a bool node in the kernel pin of the render target won't work
-- there should be an error in the log
octane.node.create{ type=octane.NT_BOOL, name="Won't work", pinOwnerNode=rt, pinOwnerId=octane.P_KERNEL }
Code: Select all
-- create a bool node and get it's attribute value
boolNode = octane.node.create{ type=octane.NT_BOOL, name="My Bool" }
print(boolNode:getAttribute(octane.A_VALUE))
-- set the value of the attribute to true
boolNode:setAttribute(octane.A_VALUE, true)
print(boolNode:getAttribute(octane.A_VALUE))
-- create an image texture
texNode = octane.node.create{ type=octane.NT_TEX_IMAGE, name="Tex" }
-- none of the attributes on the node are set
print("before eval")
print(texNode:getAttribute(octane.A_TYPE))
print(texNode:getAttribute(octane.A_SOURCE_INFO))
-- set up the filename in the texture (MODIFY THIS TO SOME of your own textures)
-- on evaluation, Octane will try to load the texture and fill in the empty attributes
texNode:setAttribute(octane.A_FILENAME, "/home/thomas/Documents/textures/uv_test.jpg")
print("after eval")
print(texNode:getAttribute(octane.A_TYPE))
print(texNode:getAttribute(octane.A_SOURCE_INFO))
print(texNode:getAttribute(octane.A_SIZE)[1], texNode:getAttribute(octane.A_SIZE)[2])
Code: Select all
-- create a diffuse material
mat = octane.node.create{ type=octane.NT_MAT_DIFFUSE, name="Diffuse", position={ 500, 500 } }
-- let's create a grayscale colour
tex = octane.node.create{ type=octane.NT_TEX_FLOAT, name="GrayScale", position={ 400, 400 } }
-- connect the grayscale colour to the bump pin of the material
mat:connectTo(octane.P_BUMP, tex)
-- see if the connection was really made
print(mat:getConnectedNode(octane.P_BUMP) == tex)
-- get the value of the grayscale texture through the pin.
print(mat:getPinValue(octane.P_BUMP)[1])
-- set the value again through the pin, if the value is out of the range of this pin
-- [0, 1] in this case, it will be clamped if set via the pin
mat:setPinValue(octane.P_BUMP, { -1, 0, 0 })
-- get the value directly from the texture, it should be clamped to 0
print(tex:getAttribute(octane.A_VALUE))
-- now disconnect the node again by passing in nil
mat:disconnect(octane.P_BUMP)
print(mat:getConnectedNode(octane.P_BUMP))
Code: Select all
-- helper to generate a random position so we don't create
-- all the items on top of each other.
function rndPos()
return { math.random(400, 600), math.random(400, 600) }
end
-- create a graph in the scene's root graph
g = octane.nodegraph.create{ type=octane.GT_STANDARD, name="Black Box", position=rndPos() }
-- an output linker
outLink = octane.node.create{ type=octane.NT_OUT_FLOAT, name="Output", graphOwner=g, position=rndPos() }
-- create an input linker
inLink = octane.node.create{ type=octane.NT_IN_FLOAT, name="Input", graphOwner=g, position=rndPos() }
-- let's just connect through the graph (not that usefull ;)
outLink:connectTo(octane.P_INPUT, inLink)
-- now let's add some random nodes in the graph
for i=1,3 do
octane.node.create{ type=octane.NT_BOOL, name="Random Noise", graphOwner=g, position=rndPos() }
end
-- let's find out what is in our graph
print("-- items in the graph")
for i, v in ipairs(g:getOwnedItems()) do
print(i, v)
end
-- lets find all the bool nodes
print("-- bool nodes")
for i,v in ipairs(g:findNodes(octane.NT_BOOL)) do
print(i, v)
end
-- lets copy all the bool nodes directly in the scene graph
octane.nodegraph.getRootGraph():copyFrom(g:findNodes(octane.NT_BOOL))
-- let's create a graph and copy all the cruft in there
copy = octane.nodegraph.create{ type=octane.GT_STANDARD, name="Copy", position=rndPos() }
copy:copyFromGraph(g)
Code: Select all
-- load a project
octane.project.load("/home/thomas/Documents/octane-projects/cube-test.ocs")
-- create a root graph (without an owner)
rootGraph = octane.nodegraph.createRootGraph("Copier")
-- copy the whole scene in the root graph
rootGraph:copyFromGraph(octane.nodegraph.getRootGraph())
-- create a new project
octane.project.reset()
-- dump the content from our graph in the new project
octane.nodegraph.getRootGraph():copyFromGraph(rootGraph)
cheers,
Thomas