-- Create a Glossy material starting from a Diffuse image texture. -- -- @description Create a Glossy material with textures -- @author Beppe Gullotta aka "bepeg4d" -- @version 0.3 -- GUI code -------------------------------------------- -- creates a note text 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 = 624, -- width of the label in pixels height = 24, -- height of the label in pixels } end -- lets create a note label local noteLbl1 = createLabel("Fill the path with a diffuse texture image to automatically create a glossy material.\nIf the suffix is not found, the diffuse texture is used as bump or Specular/Rouughness.") -- for layouting the notes we use a group local noteGrp = octane.gui.create { type = octane.gui.componentType.GROUP, text = "", rows = 1, cols = 1, children = { noteLbl1, }, padding = { 2 }, inset = { 5 }, } -- helper to pop-up an error dialog and optionally halts the script local function showError(text, halt) octane.gui.showDialog { type = octane.gui.dialogType.BUTTON_DIALOG, title = "Create Glossy Material Error", text = text, icon = octane.gui.dialogIcon.WARNING, } if halt then error("ERROR: "..text) end end -- DIFFUSE -- -- create a button to show a file chooser for Diffuse texture local fileChooseButtonD = octane.gui.create { type = octane.gui.componentType.BUTTON, text = "Choose path", width = 80, height = 20, } -- create an editor that will show the chosen file path for Diffuse texture local fileEditorD = octane.gui.create { type = octane.gui.componentType.TEXT_EDITOR, text = "", x = 20, width = 410, height = 20, enable = true, } -- create a label local noteLblD1 = octane.gui.create { type = octane.gui.componentType.LABEL, text = "suffix", width = 40, } -- create a text field for the suffix to add at the end of the name of the material local fileTextD = octane.gui.create { type = octane.gui.componentType.TEXT_EDITOR, text = "", width = 60, height = 20, enable = true, } -- for layouting all the elements for the Diffuse texture we use a group local difGrp = octane.gui.create { type = octane.gui.componentType.GROUP, text = "Diffuse Texture", rows = 1, cols = 4, children = { fileChooseButtonD, fileEditorD, noteLblD1, fileTextD, }, padding = { 2 }, inset = { 5 }, } -- BUMP/NORMAL -- -- create a label local noteLblB1 = octane.gui.create { type = octane.gui.componentType.LABEL, text = "Search for Bump suffix", width = 200, } -- create an editor that will show the chosen file path for Specular/Roughness texture local fileTextB = octane.gui.create { type = octane.gui.componentType.TEXT_EDITOR, text = "_b", x = 20, width = 60, height = 20, enable = true, } -- for layouting all the elements for the Specular/Roughness texture we use a group local bumpGrp = octane.gui.create { type = octane.gui.componentType.GROUP, text = "Bump Texture", rows = 1, cols = 2, children = { noteLblB1, fileTextB, }, padding = { 2 }, inset = { 5 }, } -- SPECULAR/ROUGHNESS -- -- create a label local noteLblR1 = octane.gui.create { type = octane.gui.componentType.LABEL, text = "Search for Specular/Roughness suffix", width = 200, } -- create an editor that will show the chosen file path for Specular/Roughness texture local fileTextR = octane.gui.create { type = octane.gui.componentType.TEXT_EDITOR, text = "_r", x = 20, width = 60, height = 20, enable = true, } -- for layouting all the elements for the Specular/Roughness texture we use a group local rougGrp = octane.gui.create { type = octane.gui.componentType.GROUP, text = "Specular/Roughness Texture", rows = 1, cols = 2, children = { noteLblR1, fileTextR, }, padding = { 2 }, inset = { 5 }, } -- for layouting all the elements for the Bump and Specular/Roughness texture we use a group local sufGrp = octane.gui.create { type = octane.gui.componentType.GROUP, text = "Suffix patterns", rows = 1, cols = 2, children = { bumpGrp, rougGrp, }, padding = { 2 }, inset = { 5 }, } -- BUTTONS -- local createButton = octane.gui.create { type = octane.gui.componentType.BUTTON, text = "Create", width = 160, height = 20, } local exitButton = octane.gui.create { type = octane.gui.componentType.BUTTON, text = "Exit", width = 160, height = 20, } local buttonGrp = octane.gui.create { type = octane.gui.componentType.GROUP, text = "", rows = 1, cols = 2, children = { createButton, exitButton, }, 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 = { noteGrp, difGrp, sufGrp, buttonGrp, }, centre = true, padding = { 2 }, border = false, debug = false, -- true to show the outlines of the group, handy } -- window that holds all components local MixMatWindow = octane.gui.create { type = octane.gui.componentType.WINDOW, text = "Create a Glossy Material v0.3", children = { layoutGrp }, width = layoutGrp:getProperties().width, height = layoutGrp:getProperties().height, } -- END GUI CODE ----------------------------------------- -- enable all the ui fileChooseButtonD:updateProperties{ enable = true} fileTextD :updateProperties{ enable = true} fileTextB :updateProperties{ enable = true} fileTextR :updateProperties{ enable = true} createButton :updateProperties{ enable = false } exitButton :updateProperties{ enable = true } --button functions local function createGraph() MixMatWindow:closeWindow(true) end local function exitAndDontCreateGraph() MixMatWindow:closeWindow(false) end -- global variable that holds the input path IND_PATH = nil INB_PATH = nil INR_PATH = nil -- callback handling the GUI elements local function guiCallback(component, event) -- FILE CHOOSING -- if component == fileChooseButtonD then -- choose an input RGB file local resDif = octane.gui.showDialog { type = octane.gui.dialogType.FILE_DIALOG, title = "Choose the texture image for the diffuse channel", wildcards = "*.jpg; *.png; *.psd; *.tif; *.tga", save = false } -- if a file is chosen for the Diffuse channel if resDif.result ~= "" then fileEditorD:updateProperties{ text = resDif.result } IND_PATH = resDif.result createButton:updateProperties{ enable = true } else createButton:updateProperties{ enable = false } fileEditorD:updateProperties{ text = "" } IND_PATH = nil end print("Loading D texture: ", IND_PATH) -- if a file is chosen, search for the Specular/Roughness texture if resDif.result ~= "" then pathR = IND_PATH.gsub(IND_PATH, "%_d", fileTextR.text) INR_PATH = pathR end print("Loading S texture: ", INR_PATH) -- if a file is chosen, search for the Bump texture if resDif.result ~= "" then pathB = IND_PATH.gsub(IND_PATH, "%_d", fileTextB.text) INB_PATH = pathB end print("Loading S texture: ", INB_PATH) -- close -- elseif component == createButton then createGraph() elseif component == exitButton then exitAndDontCreateGraph() elseif component == MixMatWindow then -- when the window closes, print something if event == octane.gui.eventType.WINDOW_CLOSE then print ("Let's go!") end end end -- hookup the callback with all the GUI elements fileChooseButtonD:updateProperties { callback = guiCallback } fileTextD :updateProperties { callback = guiCallback } fileTextR :updateProperties { callback = guiCallback } fileTextR :updateProperties { callback = guiCallback } createButton:updateProperties { callback = guiCallback } exitButton:updateProperties { callback = guiCallback } MixMatWindow:updateProperties { callback = guiCallback } -- the script will block here until the window closes local create = MixMatWindow:showWindow() -- stop the script if the user clicked the exit button if not create then return end -- This script create a node-group with a glossy material -- populate the various imput nodes with arroways textures. params = {} params.type = octane.GT_STANDARD params.name = "Glossy-mat" params.position = {500, 200} -- creates the "macro" node graph, ie. root for all other nodes root = octane.nodegraph.create(params) -- creates the glossy material node glossy = octane.node.create{type=octane.NT_MAT_GLOSSY, name="Glossy", graphOwner=root, position={450, 250}} -- creates an image texture node for diffuse channel Color1 = octane.node.create{type= octane.NT_TEX_IMAGE, name="Diffuse", graphOwner=root, position={200, 150}} Color1:setAttribute(octane.A_FILENAME, IND_PATH) Color1:setPinValue(octane.P_POWER, {0.9, 0.9, 0.9}) -- creates an image texture node for Bump channel Color2 = octane.node.create{type= octane.NT_TEX_FLOATIMAGE, name="Bump", graphOwner=root, position={500, 100}} Color2:setAttribute(octane.A_FILENAME, INB_PATH) -- creates an image texture node for Specular channel Color3 = octane.node.create{type= octane.NT_TEX_FLOATIMAGE, name="Specular", graphOwner=root, position={300, 100}} Color3:setAttribute(octane.A_FILENAME, INR_PATH) -- creates an image texture node for Roughness channel Color4 = octane.node.create{type= octane.NT_TEX_FLOATIMAGE, name="Roughness", graphOwner=root, position={400, 150}} Color4:setAttribute(octane.A_FILENAME, INR_PATH) --set the name of the material as the name of the texture and add the suffix fileName = octane.file.getFileNameWithoutExtension(IND_PATH) print (fileName) fileName= fileName.gsub(fileName, '_.+', "") fileName=(fileName .. fileTextD.text) print (fileName) root : updateProperties{name=fileName} -- creates some input nodes inFloat1 = octane.node.create{type=octane.NT_IN_FLOAT, name="Index", graphOwner=root, position={800, 150}} inFloat2 = octane.node.create{type=octane.NT_IN_FLOAT, name="Roughness-Gamma", graphOwner=root, position={400, 50}} inFloat3 = octane.node.create{type=octane.NT_IN_FLOAT, name="Specular-Gamma", graphOwner=root, position={200, 50}} inFloat4 = octane.node.create{type=octane.NT_IN_TEXTURE, name="Bump-power", graphOwner=root, position={520, 50}} inPrj = octane.node.create{type=octane.NT_IN_PROJECTION, name="Projection", graphOwner=root, position={750, 0}} inTra = octane.node.create{type=octane.NT_IN_TRANSFORM, name="UV-Transform", graphOwner=root, position={650, 0}} inBool1 = octane.node.create{type=octane.NT_IN_BOOL, name="Invert-Specular", graphOwner=root, position={280, 0}} inBool2 = octane.node.create{type=octane.NT_IN_BOOL, name="Invert-Roughness", graphOwner=root, position={450, 0}} inFloat1Node = octane.node.create{type=octane.NT_FLOAT, pinOwnerNode=inFloat1, pinOwnerId=octane.P_INPUT} inFloat2Node = octane.node.create{type=octane.NT_FLOAT, pinOwnerNode=inFloat2, pinOwnerId=octane.P_INPUT} inFloat3Node = octane.node.create{type=octane.NT_FLOAT, pinOwnerNode=inFloat3, pinOwnerId=octane.P_INPUT} inFloat4Node = octane.node.create{type=octane.NT_TEX_FLOAT, pinOwnerNode=inFloat4, pinOwnerId=octane.P_INPUT} inPrjNode = octane.node.create{type=octane.NT_PROJ_BOX, pinOwnerNode=inPrj, pinOwnerId=octane.P_INPUT} inTraNode = octane.node.create{type=octane.NT_TRANSFORM_VALUE, pinOwnerNode=inTra, pinOwnerId=octane.P_INPUT} inBool1Node = octane.node.create{type=octane.NT_BOOL, pinOwnerNode=inBool1, pinOwnerId=octane.P_INPUT} inBool2Node = octane.node.create{type=octane.NT_BOOL, pinOwnerNode=inBool2, pinOwnerId=octane.P_INPUT} -- change the power attribute for the Diffuse RGB texture nodes to not 100% bright and set the index value --Color1:setPinValue(octane.P_POWER, {0.9, 0.9, 0.9}) --Color2:setPinValue(octane.P_POWER, {0.3, 0.3, 0.3}) inFloat1Node:setAttribute(octane.A_VALUE, 1.3) inFloat2Node:setAttribute(octane.A_VALUE, 2.2) inFloat3Node:setAttribute(octane.A_VALUE, 2.2) inFloat4Node:setAttribute(octane.A_VALUE, 0.05) inBool2Node:setAttribute(octane.A_VALUE, true) -- creates the output pin, material type outMat = octane.node.create{type=octane.NT_OUT_MATERIAL, name="Material", graphOwner=root, position={450, 350}} -- connects everything glossy:connectTo(octane.P_DIFFUSE, Color1) glossy:connectTo(octane.P_BUMP, Color2) glossy:connectTo(octane.P_ROUGHNESS, Color4) glossy:connectTo(octane.P_SPECULAR, Color3) glossy:connectTo(octane.P_INDEX, inFloat1) outMat:connectTo(octane.P_INPUT, glossy) Color1:connectTo(octane.P_PROJECTION, inPrj) Color1:connectTo(octane.P_TRANSFORM, inTra) Color2:connectTo(octane.P_PROJECTION, inPrj) Color2:connectTo(octane.P_TRANSFORM, inTra) Color2:connectTo(octane.P_POWER, inFloat4) Color3:connectTo(octane.P_PROJECTION, inPrj) Color3:connectTo(octane.P_TRANSFORM, inTra) Color3:connectTo(octane.P_GAMMA, inFloat3) Color3:connectTo(octane.P_INVERT, inBool1) Color4:connectTo(octane.P_PROJECTION, inPrj) Color4:connectTo(octane.P_TRANSFORM, inTra) Color4:connectTo(octane.P_GAMMA, inFloat2) Color4:connectTo(octane.P_INVERT, inBool2) --reorganize all the nodes in the graph octane.nodegraph.unfold(root, false) print ("Goodbye")