--------------------------------------------------------------------------------------------------- -- Note: This code was generated, any changes to this file will be lost when the tool is run again. --------------------------------------------------------------------------------------------------- -------------------------------------------- -- @author OTOY -- @description Selector. -- @version 1.2.1772 -- @node-registry-category |Other|Switch -------------------------------------------- local function __selector_loader() local staticPinsInfo = {} local staticPins = {} local dynamicPinsInfo = {} local outputPins = {} local minCount = 2 local maxCount = 32 local enumType = {} local enumTypeFriendly = {} local enumSelector = {"1"} -- Initial values. These might be updated before we call the first setInputLinkers if we detect we are reloading. local typeIndex = 4 -- Camera. local count = 2 local selection = 1 local function endsWidth(str, ending) return ending == "" or str:sub(-#ending) == ending end local function buildSupportedTypes() -- The list of supported types should match whatever is under menu > Input. -- ID for these special node types are built from 20000+(pin type). We use this fact to list them. local types = {} local nodeTypes = octane.apiinfo.getNodeTypes() for _, nodeType in ipairs(nodeTypes) do if nodeType >= 20000 and nodeType < 30000 then local nodeInfo = octane.apiinfo.getNodeInfo(nodeType) local type = nodeInfo.outputType local name = nodeInfo.defaultName local ending = " in" if (endsWidth(name, ending)) then name = name:sub(1, -(#ending+1)) end table.insert(types, {type=type, name=name}) end end table.sort(types, function(a, b) return a.name < b.name end) enumType = {} enumTypeFriendly = {} for _, type in ipairs(types) do table.insert(enumType, type.type) table.insert(enumTypeFriendly, type.name) end end local function findTypeIndex(type) for i, v in ipairs(enumType) do if v == type then return i end end return nil end -- Set the initial value of static input linker nodes. local function initializeStaticInputNodes() -- If the node already exist we are re-loading a scene, don't change it. local nodeType = staticPins.type:getConnectedNode(octane.pinId.P_INPUT) if nodeType == nil then nodeType = octane.node.create { type = staticPins.count.defaultNodeType, pinOwnerNode = staticPins.type, pinOwnerName = "input" } nodeType:setAttribute(octane.attributeId.A_VALUE, typeIndex) else -- Enforce the correct type. A mismatch may happen if the type list change between versions. local oldIndex = nodeType:getAttribute(octane.attributeId.A_VALUE) if oldIndex ~= typeIndex then nodeType:setAttribute(octane.attributeId.A_VALUE, typeIndex) end end if staticPins.count:getConnectedNode(octane.pinId.P_INPUT) == nil then local nodeCount = octane.node.create { type = staticPins.count.defaultNodeType, pinOwnerNode = staticPins.count, pinOwnerName = "input" } nodeCount:setAttribute(octane.attributeId.A_VALUE, count) end if staticPins.value:getConnectedNode(octane.pinId.P_INPUT) == nil then local nodeValue = octane.node.create { type = staticPins.count.defaultNodeType, pinOwnerNode = staticPins.value, pinOwnerName = "input" } nodeValue:setAttribute(octane.attributeId.A_VALUE, 1) end end local function getOutputPinInfos() return { { key = "output", label = "Switch", type = enumType[typeIndex] } } end local function getStaticPinsInfo() local pinInfoType = { key = "type", label = "Type", type = octane.pinType.PT_ENUM, defaultNodeType = octane.nodeType.NT_ENUM, defaultValue = typeIndex, enum = enumTypeFriendly, } local pinInfoCount = { key = "count", label = "Number of options", type = octane.pinType.PT_INT, defaultNodeType = octane.nodeType.NT_INT, defaultValue = count, bounds = {minCount, maxCount}, } local pinInfoValue = { key = "value", label = "Selected option", type = octane.pinType.PT_ENUM, defaultNodeType = octane.nodeType.NT_ENUM, defaultValue = 1, enum = enumSelector, } return { pinInfoType, pinInfoCount, pinInfoValue } end ---------------------- -- Rebuild the input pins (input linker nodes). local function setInputLinkers(graph) -- We always provide the complete set of pins, since we have a dynamic number of pins in the middle, -- we can't assume the existing pins are valid. local pinsInfo = {} -- Pins order: the order of the static pins is determined by the order returned in getStaticPinsInfo, -- and the dynamic pins are inserted in bulk at a specific index. local mergeIndex = #staticPinsInfo + 1 for _, info in ipairs(staticPinsInfo) do table.insert(pinsInfo, info) end for i, info in ipairs(dynamicPinsInfo) do table.insert(pinsInfo, i + (mergeIndex - 1), info) end local inputs = graph:setInputLinkers(pinsInfo) -- Assign results to static and dynamic pins. -- Static pins, before merge point. for i = 1, mergeIndex - 1 do staticPinsInfo[i].pin = inputs[i] staticPins[staticPinsInfo[i].key] = staticPinsInfo[i].pin end -- Dynamic pins. for i = 1, #dynamicPinsInfo do dynamicPinsInfo[i].pin = inputs[i + mergeIndex - 1] end -- Static pins, after merge point. for i = 1, (#staticPinsInfo - (mergeIndex - 1)) do local staticIndex = i + mergeIndex - 1 local inputIndex = i + mergeIndex - 1 + #dynamicPinsInfo staticPinsInfo[staticIndex].pin = inputs[inputIndex] staticPins[staticPinsInfo[staticIndex].key] = staticPinsInfo[staticIndex].pin end end ---------------------- -- Rebuild the output pins (output linker nodes). local function setOutputLinkers(graph, outputPinInfos) local outputs = graph:setOutputLinkers(outputPinInfos) for i, info in ipairs(outputPinInfos) do outputPins[info.key] = outputs[i] end end ---------------------- local function changeSelection(newSelection) -- As long as we have a corresponding pin we connect it, even if it's currently empty. selection = newSelection local connected = false if count > 0 and selection <= count then local pin = dynamicPinsInfo[selection].pin if pin ~= nil then outputPins.output:connectTo(octane.pinId.P_INPUT, pin) connected = true end end if not connected then outputPins.output:disconnect(octane.pinId.P_INPUT) end end local function storeValues(script) if script._storage == nil then script._storage = {} end script._storage.type = enumType[typeIndex] script._storage.count = count end local function beforeResetDynamicPins(script, graph, startIndex, endIndex) -- Disconnect collapsed nodes that are about to be lost. if (endIndex < startIndex) then return end for i=startIndex, endIndex do if dynamicPinsInfo[i] ~= nil and dynamicPinsInfo[i].pin ~= nil then local ownedItem = dynamicPinsInfo[i].pin:getOwnedItem(octane.pinId.P_INPUT) if ownedItem ~= nil then -- If the item is a graph, this will cause an error in the log when Octane tries to reconnect it. -- But it will be expanded correctly. ownedItem:expandOutOfPin() end end end end local function resetDynamicPins(script, graph, newCount) -- Add slots for the options and update the selector drop-down. count = newCount storeValues(script) enumSelector = {} local newDynamicPinsInfo = {} for i=1, count do local pinInfoItem = { key = tostring(i), label = tostring(i), group = "Options", type = enumType[typeIndex], } table.insert(newDynamicPinsInfo, pinInfoItem) table.insert(enumSelector, tostring(i)) end staticPinsInfo[3].enum = enumSelector dynamicPinsInfo = newDynamicPinsInfo setInputLinkers(graph) end local function changeType(script, graph, newTypeIndex) -- This invalidates everything. typeIndex = newTypeIndex beforeResetDynamicPins(script, graph, 1, count) resetDynamicPins(script, graph, count) local outputPinInfo = getOutputPinInfos() setOutputLinkers(graph, outputPinInfo) changeSelection(1) end ---------------------- -- Callback which is called after initializing the scripted graph. -- This runs when creating the node, changing the script source, -- clicking the reload button or reopening the scene in Octane. local function onInit(script, graph) buildSupportedTypes() staticPinsInfo = getStaticPinsInfo() local restoring = script._storage ~= nil and script._storage.type ~= nil and script._storage.count ~= nil if restoring then typeIndex = findTypeIndex(script._storage.type) or 4 count = script._storage.count end resetDynamicPins(script, graph, count) local outputPinInfo = getOutputPinInfos() setOutputLinkers(graph, outputPinInfo) initializeStaticInputNodes() changeSelection(1) octane.nodegraph.unfold(graph, true) end local function onEvaluate(script, graph) if script:inputWasChanged(staticPins.type) then local newTypeIndex = script:getInputValue(staticPins.type) changeType(script, graph, newTypeIndex) octane.nodegraph.unfold(graph, true) return true end local newCount = script:getInputValue(staticPins.count) if newCount ~= count then beforeResetDynamicPins(script, graph, newCount + 1, count) resetDynamicPins(script, graph, newCount) octane.nodegraph.unfold(graph, true) end local newSelection = script:getInputValue(staticPins.value) if newSelection ~= selection then changeSelection(newSelection) end end return { onInit = onInit, onEvaluate = onEvaluate } end local selector = __selector_loader() ------------------------------------------------------------------------ local function __decoration_loader() local icon = octane.image.fromBase64("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGkSURBVDiNjZPBalNBFIa/mTmlba7QNhgMkb6AUB+g0tI3EDcB6cJdcZEHqPv4BkUQ3Cku4la3SnFTK30Al6K0wSqhQpve2zlzXFTTm6q5/TdzGP7zn3/+mXFA1mg2O1mWtaeCCBNhAJypxuOfx73Dw/6WNBrNzsbGw+7K6pr4EMaIdlGO7SVV3m9v33729AlSy7L28p0V6XYfs//1ywX/f3DQarXY3HwkL188b0sIXsxB/2Cf2VrG9MzMP5vKyPOCBPgQRAxQTXjvqGXXmF+oV3lg8OM7mhJYQrCEquK9JwSPSPh75CWEENAYMQwxM1Tjb4GAVF0EEIInqoKBGBA1sX5vift3b1U2A7x++5mUFMPwmKGqODfZdhneeWJUkoEkIEbl1ZtPvNsdcL1xo1LgW/+A9QcJs1EGWsrgaiFGjQAICeKYwNVCVFUsGWJcdjBZwP1xEPX8HajGWBQ5NxcXR+erwny9Tl4UqGqU4fCk9/HDztLy6tqUd35EKn2n0nJeqCp7uztnw9NhzwHZ3NxCZ7o22/beVwcApKQxPzntHR0Ntn4BWjTGJoTjXUoAAAAASUVORK5CYII=", 3) local color = { 0.5, 0.5, 0.5 } return { icon = icon, color = color } end local decoration = __decoration_loader() ------------------------------------------------------------------------ local node = {} node._name = "Switch" node._icon = decoration.icon node._evaluateAllChanges = true function node.onInit(self, graph) graph:setAttribute(octane.attributeId.A_COLOR, decoration.color, true) selector.onInit(self, graph) end function node.onEvaluate(self, graph) return selector.onEvaluate(self, graph) end return node