-- Create a Baking Node Graph from selection. -- -- @description Create a Baking Node Graph from a selected Render Target node -- @author Beppe Gullotta aka "bepeg4d" local version = "3.02" -------- BAKING default settings -------- UVset = 1 -- values from 1 to 3 paddingSize = 4 -- values from 0 to 16 edgeNoise = 0.5 -- values from 0 to 1 disablePost = true -- values true or false ------------------------------------------ -- dialogs -- local function showError(text) octane.gui.showDialog{ type = octane.gui.dialogType.BUTTON_DIALOG, title = "Error", text = text } end local function showInfo(text) octane.gui.showDialog{ type = octane.gui.dialogType.BUTTON_DIALOG, title = "INFO", icon = 2, text = text } end local function showResults(text) octane.gui.showDialog{ type = octane.gui.dialogType.BUTTON_DIALOG, title = "RESULT", icon = 4, text = text } end ------------------------------------------ local mesh = nil local rt = nil -- figure out the selection of the Render Target local selection = octane.project.getSelection() if #selection ~= 1 then showError("a render target node need to be selected") end if selection[1]:getProperties().type == octane.NT_RENDERTARGET then rt = selection[1] else showError("a render target node need to be selected") end -- get the position of the selected node in the graph editor pos = {} pos = selection[1].position -- Create the baking graph that collect everything params = {} params.type = octane.GT_STANDARD params.name = selection[1].name.."_BAKING" params.position = {pos[1], pos[2]+35} params.graphOwner = rt.graphOwner newGraph = octane.nodegraph.create(params) copy = newGraph:copyItemTree(rt) RT = newGraph:findFirstNode(octane.NT_RENDERTARGET) RT.name = rt.name.."_BAKING" -- Check the OctaneRender version and if it's prior then v3.xx, display an error, if it's prior then v3.00 alpha 4, find if materials have displacement and disconnect it specialDir = octane.file.getSpecialDirectories() nodesMat = {} if string.find(specialDir.currentExecutableFile, "OctaneRender 2") ~= nil or string.find(specialDir.currentExecutableFile, "OctaneRender_2") ~= nil or string.find(specialDir.currentExecutableFile, "alpha") ~= nil or string.find(specialDir.currentExecutableFile, "beta_1") ~= nil or string.find(specialDir.currentExecutableFile, "beta 1") ~= nil then showError("Please, use this script with OctaneRender v 3.00 beta 2 or above") end for i, v in pairs (nodesMat) do i = nodesMat[i]:disconnect(octane.P_DISPLACEMENT) end -- search for all possible Alembic nodes abcNode = newGraph:findNodes(octane.GT_STANDARD , true) for i, v in pairs (abcNode) do if abcNode[i].graphOwner.type ~= octane.GT_GEOMETRYARCHIVE then abcNode[i] = false else abcNode[i] = abcNode[i].graphOwner end end ABC = {} abcLayer = {} for i, v in pairs(abcNode) do if abcNode[i] ~= false then ABC[i] = abcNode[i] ABC[i]:setAttribute(octane.A_ABCIMP_MERGE_MATERIALS, true, true) ABC[i]:setAttribute(octane.A_ABCIMP_LOAD_OBJECTS, true, true) ABC[i]:setAttribute(octane.A_ABCIMP_MERGE_OBJECTS, false, true) for n, abcInLinker in ipairs (ABC[i]:getInputNodes()) do if abcInLinker.type == octane.NT_IN_OBJECTLAYER then abcLayer[n] = octane.node.create { type = octane.NT_OBJECTLAYER, name = string.gsub(abcInLinker.name, "Shape", "-abc"), position = {ABC[i].position[1]+(25*n), ABC[i].position[2]-35}, graphOwner = newGraph } abcInLinker:connectTo(octane.P_INPUT, abcLayer[n]) end end end end -- search for all possible mesh nodes groups = newGraph:findNodes(octane.NT_GEO_MESH , true) for i = 1, #groups do if #groups[i]:getAttribute(octane.A_FILENAME) >= 1 and groups[i].graphOwner == newGraph then groups[i]:setAttribute(octane.A_OBJIMP_IMPORT_MATERIALS, true, true) elseif #groups[i]:getAttribute(octane.A_FILENAME) == 0 and groups[i].graphOwner ~= newGraph then groups[i] = nil elseif #groups[i]:getAttribute(octane.A_FILENAME) >= 1 and groups[i].graphOwner ~= newGraph then showError("Sorry, the mesh nodes cannot be in a different node graph.\nPlease ungroup the mesh nodes and run the script again") break end end -- create the Object Layer Map node and all the Object layer nodes for each obj mesh nodes Object_Layer_Map = {} for i, v in pairs(groups) do Object_Layer_Map[i] = octane.node.create { type = octane.NT_OBJECTLAYER_MAP, name = octane.file.getFileNameWithoutExtension(groups[i]:getAttribute(octane.A_FILENAME)).."_Object_Layer_Map", position = {groups[i].position[1]+(50), groups[i].position[2]+35}, graphOwner = newGraph } dest = unpack(groups[i]:getDestinationNodes()) if dest.node.type == octane.NT_GEO_SCATTER or dest.node.type == octane.NT_GEO_PLACEMENT then dest.node:connectTo(octane.P_GEOMETRY, Object_Layer_Map[i]) elseif dest.node.type == octane.NT_RENDERTARGET then dest.node:connectTo(octane.P_MESH, Object_Layer_Map[i]) elseif dest.node.type == octane.NT_GEO_GROUP then dest.node:connectToIx(string.gsub(dest.pin, "%D", ""), Object_Layer_Map[i]) end Object_Layer_Map[i]:connectTo(octane.P_GEOMETRY, groups[i]) end -- check if we have to refer to the mesh node or to the object layer map node to create the object layer nodes mName = nil Object_Layer_Map = newGraph:findNodes(octane.NT_OBJECTLAYER_MAP , false) for x, y in pairs (Object_Layer_Map) do for pinIy=2, Object_Layer_Map[x]:getPinCount() do local nameX = string.gsub (Object_Layer_Map[x]:getPinInfoIx(pinIy).name, "%W", " ") Mesh1 = Object_Layer_Map[x]:getConnectedNode(octane.P_GEOMETRY) for z=1, Mesh1:getPinCount() do local matName = string.gsub (Mesh1:getPinInfoIx(z).name, "%W", " ") nameP = string.find (nameX, matName) if nameP ~= nil then nameY = string.sub (nameX, nameP) mName = true break elseif nameP == nil then mName = false end end end -- Create object layer nodes for all the pins of the mesh node or of the object layer map node objectLayer = {} if mName == true then Mesh1 =Object_Layer_Map[x]:getConnectedNodeIx(1) for pinIx=1, Mesh1:getPinCount() do objectLayer[pinIx] = octane.node.create { type = octane.NT_OBJECTLAYER, name = Mesh1:getPinInfoIx(pinIx).name, position = {Mesh1.position[1]+(75*pinIx), Mesh1.position[2]+35}, graphOwner = newGraph } end -- connect the object layer nodes to the proper pins of the object layer map for pinIy=2, Object_Layer_Map[x]:getPinCount() do local nameX = string.gsub (Object_Layer_Map[x]:getPinInfoIx(pinIy).name, "%W", " ") for i, v in pairs (objectLayer) do local objlName1 = string.gsub (objectLayer[i].name, "%W", " ") nameP = string.find (nameX, objlName1) if nameP ~= nil then nameY = string.sub (nameX, nameP) z = objectLayer[i] Object_Layer_Map[x]:connectToIx(pinIy, objectLayer[i]) end end end else objectLayer = {} for pinIy=2, Object_Layer_Map[x]:getPinCount() do objectLayer[pinIy] = octane.node.create { type = octane.NT_OBJECTLAYER, name = Object_Layer_Map[x]:getPinInfoIx(pinIy).name, position = {Object_Layer_Map[x].position[1]+(25*pinIy), Object_Layer_Map[x].position[2]-35}, graphOwner = newGraph } Object_Layer_Map[x]:connectToIx(pinIy, objectLayer[pinIy]) end end end newGraph:unfold() RT:deleteUnconnectedItems() -- set the backing layer number for all the object layer nodes objectLayer = newGraph:findNodes(octane.NT_OBJECTLAYER , false) for i, v in pairs (objectLayer) do objectLayer[i]:setPinValue(octane.P_LAYER_ID, i+1, true) objectLayer[i]:setPinValue(octane.P_BAKING_GROUP_ID, i+1, true) end -- create all the neccssary stuff for the baking render targets Bcam = octane.node.create { type = octane.NT_CAM_BAKING, name = "Baking_Camera", position = {RT.position[1]-(250), RT.position[2]-(40)}, graphOwner = newGraph } Bcam:setPinValue(octane.P_BAKING_GROUP_ID, 2) Bcam:setPinValue(octane.P_UV_SET, UVset) Bcam:setPinValue(octane.P_PADDING, paddingSize) Bcam:setPinValue(octane.P_TOLERANCE, edgeNoise) RT:connectTo(octane.P_CAMERA, Bcam) Res = octane.node.create { type = octane.NT_FILM_SETTINGS, name = "Baking_Resolution", position = {RT.position[1]-(100), RT.position[2]-(40)}, graphOwner = newGraph } res1 = Res:getConnectedNode(octane.P_RESOLUTION) res1:setAttribute(octane.A_VALUE, {512, 512}) res1:setAttribute(octane.A_ASPECT_RATIO, 1) RT:connectTo(octane.P_FILM_SETTINGS, Res) Rpass = octane.node.create { type = octane.NT_RENDER_PASSES, name = "Baking_Passes", position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } Rpass:setPinValue(octane.P_RENDER_PASS_INFO_MAX_SAMPLES, 1024) Rpass:setPinValue(octane.P_BUMP, true) Rpass:setPinValue(octane.P_RENDER_PASS_TANGENT_NORMAL, true) Rpass:setPinValue(octane.P_RENDER_PASS_AMBIENT_OCCLUSION, true) Rpass:setPinValue(octane.P_RENDER_PASS_OPACITY, true) Rpass:setPinValue(octane.P_RENDER_PASS_ROUGHNESS, true) Rpass:setPinValue(octane.P_RENDER_PASS_IOR, true) Rpass:setPinValue(octane.P_RENDER_PASS_DIFFUSE_FILTER_INFO, true) Rpass:setPinValue(octane.P_RENDER_PASS_REFLECTION_FILTER_INFO, true) Rpass:setPinValue(octane.P_RENDER_PASS_REFRACTION_FILTER_INFO, true) Rpass:setPinValue(octane.P_RENDER_PASS_TRANSMISSION_FILTER_INFO, true) RT:connectTo(octane.P_RENDER_PASSES, Rpass) -- get the kernel local kern = RT:getConnectedNode(octane.P_KERNEL) Rkernel = octane.node.create { type = kern.type, name = kern.name, position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } Rkernel:updateProperties(kern:getProperties()) for i = 1, kern:getPinCount() do Rkernel:setPinValueIx(i, kern:getPinValueIx(i), true) end RT:connectTo(octane.P_KERNEL, Rkernel) -- get the camera imager node local imag = RT:getConnectedNode(octane.P_IMAGER) imgNode = octane.node.create { type = imag.type, name = imag.name, position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } imgNode:updateProperties(imag:getProperties()) for i = 1, imag:getPinCount() do imgNode:setPinValueIx(i, imag:getPinValueIx(i), true) end RT:connectTo(octane.P_IMAGER, imgNode) -- get the environment node local envy = RT:getConnectedNode(octane.P_ENVIRONMENT) envNode = octane.node.create { type = envy.type, name = envy.name, position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } envNode:updateProperties(envy:getProperties()) for i = 1, envy:getPinCount() do if envy:getPinInfoIx(i).name ~= "medium" and envy:getPinInfoIx(i).name ~= "texture" then envNode:setPinValueIx(i, envy:getPinValueIx(i), true) elseif envy:getPinInfoIx(i).name == "texture" and envy:getPinValueIx(i) == nil then envNode:copyFromIx(i, envy:getConnectedNodeIx(i), true) end end RT:connectTo(octane.P_ENVIRONMENT, envNode) -- get the render layer node local rend = RT:getConnectedNode(octane.P_RENDER_LAYER) if rend ~= nil then rendNode = octane.node.create { type = rend.type, name = rend.name, position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } rendNode:updateProperties(rend:getProperties()) for i = 1, rend:getPinCount() do rendNode:setPinValueIx(i, rend:getPinValueIx(i), true) end else rendNode = octane.node.create { type = octane.NT_RENDER_LAYER, position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } end RT:connectTo(octane.P_RENDER_LAYER, rendNode) -- get the post effect node local post = RT:getConnectedNode(octane.P_POST_PROCESSING) postNode = octane.node.create { type = post.type, name = post.name, position = {RT.position[1]+(200), RT.position[2]-(70)}, graphOwner = newGraph } postNode:updateProperties(post:getProperties()) for i = 1, post:getPinCount() do postNode:setPinValueIx(i, post:getPinValueIx(i), true) end RT:connectTo(octane.P_POST_PROCESSING, postNode) if disablePost == true then postNode:setPinValue(octane.P_ON_OFF, false, true) else postNode:setPinValue(octane.P_ON_OFF, true, true) end RT:deleteUnconnectedItems() -- create a copy of the render target for each object layer node rtBase = newGraph:findNodes(octane.NT_RENDERTARGET) rtCopy = {} BcamCopy = {} groupedO = {} objlm = newGraph:findNodes(octane.NT_OBJECTLAYER_MAP) rtNum = newGraph:findNodes(octane.NT_OBJECTLAYER) outRT = {} for pinIy=1, #rtNum do rtCopy[pinIy] = newGraph:copyFrom(rtBase) rtCopy[pinIy][1]:connectTo(octane.P_MESH, RT:getConnectedNode(octane.P_MESH)) rtCopy[pinIy][1]:connectTo(octane.P_RENDER_PASSES, Rpass) rtCopy[pinIy][1]:connectTo(octane.P_FILM_SETTINGS, Res) rtCopy[pinIy][1]:connectTo(octane.P_KERNEL, Rkernel) rtCopy[pinIy][1]:connectTo(octane.P_ENVIRONMENT, envNode) rtCopy[pinIy][1]:connectTo(octane.P_IMAGER, imgNode) rtCopy[pinIy][1]:connectTo(octane.P_RENDER_LAYER, rendNode) rtCopy[pinIy][1]:connectTo(octane.P_POST_PROCESSING, postNode) rtCopy[pinIy][1].name = "RT_BAKE_"..rtNum[pinIy].name BcamCopy[pinIy] = rtCopy[pinIy][1]:copyFrom(octane.P_CAMERA, Bcam) BcamCopy[pinIy]:setPinValue(octane.P_BAKING_GROUP_ID, rtNum[pinIy]:getPinValue(octane.P_LAYER_ID)) end -- collect all the baking render target in a group list = {} for i, v in pairs (rtCopy) do list[i] = rtCopy[i][1], outRT[i] end if #list == 0 then showError("Something has gone wrong, please check your scene and try again") end grouped = newGraph:group(list, true) grouped.name = #rtCopy.."_RT_BAKING_GROUP" ownedItems = grouped:getOwnedItems() for i, v in pairs (ownedItems) do if ownedItems[i].type == octane.NT_RENDERTARGET then outRT[i] = octane.node.create {type = octane.NT_OUT_RENDERTARGET, name = ownedItems[i].name.."_out", label = ownedItems[i].name.."_out", graphOwner = grouped} outRT[i]:connectTo(octane.P_INPUT, ownedItems[i]) end end octane.nodegraph.unfold(grouped) -- create an annotation node Note = octane.node.create { type = octane.NT_ANNOTATION, name = "Baking_Group_IDs", position = {RT.position[1]-(250), RT.position[2]}, graphOwner = newGraph } items = newGraph:findNodes(octane.NT_OBJECTLAYER) -- Let's print some infos in the annotation object and in the log print("\nTotal number of Baking Layers: ", #items) print("\nBaking Layers ID:\n") pText = {} for i, v in pairs(items) do print("ID "..items[i]:getPinValue(octane.P_BAKING_GROUP_ID).."\t > \t"..items[i].name.."\n") pText[i] = string.format("ID %4d \t > \t%s\n", items[i]:getPinValue(octane.P_BAKING_GROUP_ID), items[i].name) end function add (t) local sum = "" for i,v in ipairs(t) do sum = sum..t[i] end return sum end Note:setAttribute(octane.A_VALUE, "Baking Layers = "..#items .." \nIDs from 2 to "..(#items +1).."\n---\n"..add(pText)) -- let's make a bit of order in the graph octane.nodegraph.unfold(newGraph) -- new position for the generic RT and the annotation node rtBase[1].position = {grouped.position[1], grouped.position[2]+70,} Note.position = {rtBase[1].position[1], rtBase[1].position[2]+40,} if #items >= 1 and mName == true and #ABC == 0 then showResults(string.format("Baking Graph created successfully.\nTotal number of Baking Layers created:\n%d", #items)) elseif #items >= 1 and mName == false and #ABC == 0 then showInfo(string.format("Baking Graph created successfully.\nWas not possible to read the grouping material info for some mesh nodes.\nAn Object Layer node has been created for every objects of those mesh nodes.\nTotal number of Baking Layers created:\n%d", #items)) elseif #ABC ~= 0 then showInfo(string.format("Baking Graph created successfully.\nWas not possible to read the grouping material info for some Alembic mesh nodes.\nAn Object Layer node has been created for every objects of those Alembic mesh nodes.\nTotal number of Baking Layers created:\n%d", #items)) elseif #items == 0 and #ABC == 0 then showError("Something has gone wrong, please check your scene and try again") end