Yunus Balcioglu

animatrix_

About Me

Senior FX Technical Director @ Industrial Light & Magic | Feature film credits include The Lord of the Rings: The Rings of Power, Marvel's Eternals, Star Wars: The Rise of Skywalker, X-Men: Dark Phoenix, X-Men: Apocalypse, Aquaman, Alien: Covenant, Pirates of the Caribbean, Justice League and many m...  more
EXPERTISE
Technical Director
INDUSTRY
Film/TV

Connect

LOCATION
Singapore, Singapore

Houdini Skills

ADVANCED
Procedural Modeling  | Digital Assets  | Mantra  | Pyro FX  | Fluids  | Destruction FX  | VEX  | Python
INTERMEDIATE
Realtime FX

Availability

Not Specified

My Tutorials

obj-image Advanced
Pragmatic VEX: Volume 1
obj-image Advanced
Pragmatic VEX: Volume 1

My Talks

obj-image HUG
Retiming Ocean Spectra & The Pragmatic Approach to Solving Technical Problems in VFX
obj-image HIVE
Face Peeling Using KineFX

Recent Forum Posts

Is there a shortcut to "insert node between two nodes"? Feb. 18, 2025, 3:19 a.m.

raincole
animatrix_
createNewNode

That's dope, but I'm not sure where I should call createNewNode. In a shelf tool? Or there is a callback that Houdini will call when a new node is created (but if so how to register this createNewNode as callback?)

It's part of a larger set of tools I use daily in my workflow which basically calls this function in nodegraphhooks.py when I press a specific key to create a specific node type based on the current key modifiers and the current network editor context. They work outside of the default Houdini hotkey system.

Because this is really involved, you can use the same function in a shelf to create a specific node by calling the function directly. You might need to modify it to make sure you pass the correct parameters like the current network editor, node type, etc.

Once you have a shelf tool, you can assign hotkeys, etc. I didn't want to do that because then it will result in 1000s of shelf tools and it's harder to change these each time but rather use a hot swappable CSV file that has all the hotkeys per context.

Another method is to use the OnCreated.py script to implement your logic to use wires, etc. I use it to connect new nodes to the currently selected nodes so even if a node is created using the TAB menu, it will be subject to my own custom rules.

How to "store node network state" so I can restore it later? Feb. 18, 2025, 12:29 a.m.

Hi,

AFAIK there is no default feature to do this. You can write some custom Python script to do this, likely storing which node is selected/templated/displayed, etc by name or type and decide how to figure out conflicts in case of ambiguity.

Is there a shortcut to "insert node between two nodes"? Feb. 18, 2025, 12:20 a.m.

You can also implement your own logic to insert nodes between wires if the new node is close enough to any wire(s) or if there are any wires selected, then use all of them regardless of distance.



def dropNodeOnWire(newNode, nodeConnections, pos, editor, nearbyOnly=True):
    newNodeType = newNode.type()
    ninputs = newNodeType.maxNumInputs()
    noutputs = newNodeType.maxNumOutputs()

    if ((len(nodeConnections) == ninputs and len(nodeConnections) == noutputs) or not nearbyOnly):
        sortedwires = sorted(nodeConnections, key=lambda c: c.outputItem().position().x())
        sortedwires = sorted(sortedwires, key=lambda c: c.outputIndex())
    else:
        sortedwires = sorted(nodeConnections, key=lambda c: pointLineProjection(pos, c.inputItem().position(), c.outputItem().position()).distanceTo(pos))

    uniquewires = []
    uniquenames = []

    if nearbyOnly:
        nearestwires = []
        for c in sortedwires:
            dist = pointLineProjection(pos, c.inputItem().position(), c.outputItem().position()).distanceTo(pos)
            # hard coded distance to wire connections
            if dist <= 0.42:
                nearestwires.append(c)

        sortedwires = nearestwires

    for w in sortedwires:
        uniquename = w.inputItem().name() + ":" + str(w.inputIndex())
        if uniquename not in uniquenames:
            uniquenames.append(uniquename)
            uniquewires.append(w)

    nwires = len(uniquewires)
    
    for i in range(min(ninputs, nwires)):
        c = uniquewires[i]
        newNode.setInput(i, c.inputItem(), c.outputIndex())


    for i in range(min(noutputs, nwires)):
        outputIndex = i
        if i >= noutputs - 1:
            outputIndex = noutputs - 1

        c = sortedwires[i]
        c.outputItem().setInput(c.inputIndex(), newNode, outputIndex)



def createNewNode(uievent, nodetypename, parms=None, name=None, pressbuttons=None, mousepos=None):
    import nodegraphutils as utils
    
    pwd = uievent.editor.pwd()
    path = pwd.path()
    context = pwd.childTypeCategory().name()
    if pwd and path and context:
        #nodetype = hou.nodeType(pwd.childTypeCategory(), nodetypename)
        #if nodetype:
        if True:
            selectedNodes = list(hou.selectedNodes())
            selectedConnections = hou.selectedConnections()
            selectedSubnetInputs = [item for item in hou.selectedItems() if type(item) == hou.OpSubnetIndirectInput]
            selectedNetworkDots= [item for item in hou.selectedItems() if type(item) == hou.OpNetworkDot]
            selectedNodes.extend(selectedSubnetInputs)
            selectedNodes.extend(selectedNetworkDots)

            selectedNodes = sorted(selectedNodes, key=lambda n: n.position().x())
                
            #with hou.undos.group("Create New Node: " + nodetype.description()):
            with hou.undos.group("Create New Node: " + nodetypename):
                pos = uievent.editor.cursorPosition()
                if mousepos:
                    pos = mousepos
                    
                newNode = pwd.createNode(nodetypename)

                radius = utils.getConnectorSnapRadius(uievent.editor)
                targets = utils.getPossibleDropTargets(uievent, radius)
                nodewires = [target.item for target in targets if target.name == "wire"]

                size = utils.getNewNodeHalfSize()
                #print ("size", size)
                pos[0] -= size.x()
                pos[1] -= size.y()

                setdisplayflag = False
                connectInputs = False

                if selectedConnections and len(hou.selectedNodes()) == 0:
                    dropNodeOnWire(newNode, selectedConnections, pos, uievent.editor, nearbyOnly=False)
                elif nodewires and len(selectedNodes) <= 1:
                    dropNodeOnWire(newNode, nodewires, pos, uievent.editor)
                elif selectedNodes and context != "Apex":
                    connectInputs = True
                    setdisplayflag = True

                if pressbuttons:
                    for button in pressbuttons:
                        newNode.parm(button).pressButton()
                        
                if name:
                    newNode.setName(name, unique_name=True)
                newNode.setPosition(pos)
                newNode.setSelected(True, clear_all_selected=True)

                nodecount = len(selectedNodes)
                if setdisplayflag and nodecount != 0:
                    toggleNodeDisplayFlag(newNode, context)
                    toggleNodeRenderFlag(newNode, context)


                if parms:
                    if isinstance(parms, dict):
                        for i, (key, value) in enumerate(parms.items()):
                            newNode.parm(key).set(value)

                    elif isinstance(parms, str):

                        if parms.startswith("de:"):
                            # Split parms into opcode and opfunc
                            opcode, opfunc = parms.split(":", 1)
                            hou.data.applyDecorationRecipe(opfunc, newNode)
                        else:
                            # Search inside legacy presets that has the same name
                            presets = hou.hscript("oppresetls " + newNode.path())
                            presets = [item.strip() for item in presets[0].splitlines() if item.strip()]

                            isrecipe = parms in presets
                            if isrecipe:
                                hou.hscript("oppresetload " + newNode.path() + " '{0}'".format(parms))
                            else:
                                hou.data.applyNodePresetRecipe(parms.strip("'"), newNode)


                if connectInputs:
                    connectNodesToNewNode(newNode, selectedNodes)


                return newNode