Creating Nodeless States#

"""
State:          1 Course - Nodeless State Example
State type:     course::nodeless_state_example::1.0
Description:    Course::nodeless state example::1.0
Author:         GuiSh
Date Created:   January 23, 2025 - 02:44:23
"""


import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    #state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_typename = "course::nodeless_state_example::1.0"
    state_label = "1 Course - Nodeless State Example"
    state_cat = hou.objNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    # template.bindIcon(kwargs["type"].icon())
    template.bindIcon("MISC_python")

    return template

Creating Asset State#

import hou
import viewerstate.utils as su

class State(object):
    MSG = "LMB to add points to the construction plane."

    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer

        self.pressed = False
        self.index = 0
        self.node = None

    def pointCount(self):
        """ This is how you get the number of instances
            in a multiparm.
        """
        try:
            multiparm = self.node.parm("points")
            return multiparm.evalAsInt()
        except:
            return 0

    def start(self):
        if not self.pressed:
            self.scene_viewer.beginStateUndo("Add point")
            self.index = self.pointCount()
            multiparm = self.node.parm("points")
            multiparm.insertMultiParmInstance(self.index)

        self.pressed = True

    def finish(self):
        if self.pressed:
            self.scene_viewer.endStateUndo()
        self.pressed = False


    def onEnter(self, kwargs):
        self.node = kwargs["node"]

        if not self.node:
            raise

        self.scene_viewer.setPromptMessage( State.MSG )

    def onInterrupt(self,kwargs):
        self.finish()

    def onResume(self, kwargs):
        self.scene_viewer.setPromptMessage( State.MSG )

    def onMouseEvent(self, kwargs):
        """ Find the position of the point to add by
            intersecting the construction plane.
        """
        ui_event = kwargs["ui_event"]
        device = ui_event.device()
        origin, direction = ui_event.ray()

        position = su.cplaneIntersection(self.scene_viewer, origin, direction)

        # Create/move point if LMB is down
        if device.isLeftButton():
            self.start()
            # set the point position
            self.node.parm("usept%d" % self.index).set(1)
            self.node.parmTuple("pt%d" % self.index).set(position)

        else:
            self.finish()

        return True


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "Course::asset viewer state example::1.0"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    return template

UI Event Handlers#

Mouse events#

"""
State:          2 Course - Event Handlers
State type:     course::event_handlers::1.0
Description:    Course::event handlers::1.0
Author:         GuiSh
Date Created:   January 23, 2025 - 03:15:00
"""


import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None
        self.mouseCoords = [0,0]

    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        # print kwargs in the viewer state console if "Debug log" is
        # enabled
        self.log("ENTERED STATE")


    def onInterrupt(self, kwargs):
        """ Called when the state is interrupted e.g when the mouse
        moves outside the viewport
        """
        self.log("INTERRUPTED STATE")

    def onResume(self, kwargs):
        """ Called when an interrupted state resumes
        """
        self.log("RESUME STATE")

    def onExit(self,kwargs):
        """ Called when the state terminates
        """
        state_parms = kwargs["state_parms"]
        self.log("EXIT STATE")

    def onMouseEvent(self, kwargs):
        """ Process mouse events
        """
        ui_event = kwargs["ui_event"]
        dev = ui_event.device()
        reason = ui_event.reason()

        self.mouseCoords = [dev.mouseX(), dev.mouseY()]

        modifier = 1000
        # moved
        # self.node.parmTuple("scale").set((self.mouseCoords[0]/modifier, self.mouseCoords[1]/modifier, 1))
        if dev.isLeftButton():
            modifier = 1000
        elif dev.isRightButton():
            modifier = 100
        elif dev.isMiddleButton():
            modifier = 10
        #self.log("LMB pressed=", dev.isLeftButton())
        #self.log("MMB pressed=", dev.isMiddleButton())
        #self.log("RMB pressed=", dev.isRightButton())

    # before Picked
        if reason == hou.uiEventReason.Active:
            self.log("LMB Click")
            self.node.parmTuple("scale").set((self.mouseCoords[0]/modifier, self.mouseCoords[1]/modifier, 1))
        #elif reason == hou.uiEventReason.Start:
        #    self.log("LMB was pressed down")
        #elif reason == hou.uiEventReason.Active:
        #    self.log("Mouse dragged with LMB down")
        #elif reason == hou.uiEventReason.Changed:
        #    self.log("LMB was released")
        # self.log(dev)
        #self.log("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton())

        # Must return True to consume the event
        return False

    def onMouseWheelEvent(self, kwargs):
        """ Process a mouse wheel event
        """

        ui_event = kwargs["ui_event"]
        state_parms = kwargs["state_parms"]

        dev = ui_event.device()
        scrollamount = dev.mouseWheel()
        #self.log("scrollamount:", scrollamount)
        current_divisions = self.node.parm("divisions").evalAsInt()
        current_divisions += int(scrollamount)
        self.node.parm("divisions").set(current_divisions)
        # Must return True to consume the event
        return False


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "2 Course - Event Handlers"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    return template

Keyboard events#

# in attrib wrangle / VEXpression
int iteration = detail(0, "iteration", 0) + 1;
i@ObjectID = chi("../object_id_" + itoa(iteration));
"""
State:          2 Course - UI Events 2
State type:     course::ui_event_2::1.0
Description:    Course::ui event 2::1.0
Author:         GuiSh
Date Created:   January 23, 2025 - 04:40:40
"""


import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None

    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        # print kwargs in the viewer state console if "Debug log" is
        # enabled
        self.log("onEnter=",kwargs)

    def onKeyEvent(self, kwargs):
        """ Called for processing a keyboard event
        """

        ui_event = kwargs["ui_event"]
        state_parms = kwargs["state_parms"]
        #self.log('key string', ui_event.device().keyString())
        #self.log('key value', ui_event.device().keyValue())
        #self.log('key isAutoRepeat', ui_event.device().isAutoRepeat())


        self.key_pressed = ui_event.device().keyString()

        multiparm = self.node.parm("entries")
        numentries = multiparm.evalAsInt()

        if self.key_pressed in ('1', '2', '3'):
            numentries += 1
            multiparm.set(numentries)
            self.node.parm("object_id_{}".format(numentries)).set(int(self.key_pressed)-1)
            return True

        # Must returns True to consume the event
        return False

    def onKeyTransitEvent(self, kwargs):
        """ Called for processing a transitory key event
        """
        ui_event = kwargs["ui_event"]
        state_parms = kwargs["state_parms"]

        self.log('key',      ui_event.device().keyString())
        self.log('key up',   ui_event.device().isKeyUp())
        self.log('key down', ui_event.device().isKeyDown())

        # Must returns True to consume the event
        return False


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "2 Course - UI Events 2"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    return template

Context Menus#

Introduction#

"""
State:          3 Course - Context Menu
State type:     course::context_menu_maze::1.0
Description:    Course::context menu maze::1.0
Author:         GuiSh
Date Created:   January 23, 2025 - 10:56:47
"""


import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None

    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        # print kwargs in the viewer state console if "Debug log" is
        # enabled
        self.log("onEnter=",kwargs)

    def onMouseWheelEvent(self, kwargs):
        """ Process a mouse wheel event
        """

        ui_event = kwargs["ui_event"]
        state_parms = kwargs["state_parms"]

        # Must return True to consume the event
        return False

    def onMenuPreOpen(self, kwargs):
        """ Implement this callback to update the menu content before
        it is drawn.
        """
        menu_states = kwargs["menu_states"]
        menu_item_states = kwargs["menu_item_states"]
        print(menu_item_states)
        print(kwargs['menu'])
        if kwargs['menu'] == 'menuexample':
            menu_item_states['first']['enable'] = False
            menu_item_states['first']['visible'] = False

    def onMenuAction(self, kwargs):
        """ Callback implementing the actions of a bound menu. Called
        when a menu item has been selected.
        """

        menu_item = kwargs["menu_item"]
        state_parms = kwargs["state_parms"]

        if menu_item == "first":
            print(kwargs["clicked first"])
        if menu_item == "second":
            print(kwargs["clicked second"])
        if menu_item == "third":
            print(kwargs["clicked third"])
        if menu_item == "myradiostrip":
            print(kwargs["myradiostrip"])
        if menu_item == "firsttoggle":
            print(kwargs["firsttoggle"])


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "3 Course - Context Menu"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    menu = hou.ViewerStateMenu("menuexample", "Menu Example")
    hk = su.hotkey(state_typename, 'firstactionitem', '1', 'First Action Item', 'description...', state_cat)

    menu.addActionItem("first", "Action One", hotkey=hk)
    menu.addActionItem("second", "Action Two")
    menu.addActionItem("third", "Action Three")

    menu.addSeparator()

    menu.addToggleItem("firsttoggle", "My Toggle", True)

    menu.addSeparator()

    menu.addRadioStrip("myradiostrip", "My Radio Strip", "radio3")
    menu.addRadioStripItem("myradiostrip", "radio1", "Option 1")
    menu.addRadioStripItem("myradiostrip", "radio2", "Option 2")
    menu.addRadioStripItem("myradiostrip", "radio3", "Option 3")

    template.bindMenu(menu)

    return template

Logic implementation#

"""
State:          3 Course - Context Menu
State type:     course::context_menu_maze::1.0
Description:    Course::context menu maze::1.0
Author:         GuiSh
Date Created:   January 23, 2025 - 10:56:47
"""


import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer

        self.node = None
        self.forwardmovement = True
        self.shapeid = 0

    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        self.node.parm("steps").set(1)

        # print kwargs in the viewer state console if "Debug log" is
        # enabled
        self.log("onEnter=",kwargs)

    def onMouseWheelEvent(self, kwargs):
        """ Process a mouse wheel event
        """

        ui_event = kwargs["ui_event"]
        state_parms = kwargs["state_parms"]

        device = kwargs["ui_event"].device()

        scroll = device.mouseWheel()

        number = self.GetMultiparmEntries(kwargs)
        multiplier = 0.1

        if self.forwardmovement:
            # Changing the forward parameter
            parameter = self.node.parm("forward_{}").format(number)
            originalvalue = parameter.evalAsFloat()
            parameter.set(originalvalue+(scroll*multiplier))
        else:
            # Changing the horizontal parameter
            parameter = self.node.parm("xposition_{}".format(number)
            originalvalue = parameter.evalAsFloat()
            parameter.set(originalvalue+(scroll*multiplier))
        print("Scroll:", scroll)

        # Must return True to consume the event
        return False

    def onMenuPreOpen(self, kwargs):
        """ Implement this callback to update the menu content before
        it is drawn.
        """
        menu_states = kwargs["menu_states"]
        menu_item_states = kwargs["menu_item_states"]


    def GetMultiparmEntries(self, kwargs):
        return self.node.parm("steps").evalAsInt()

    def SetMultiparmEntries(self, numentries, kwargs):
        self.node.parm("steps").set(numentries)

    def onMenuAction(self, kwargs):
        """ Callback implementing the actions of a bound menu. Called
        when a menu item has been selected.
        """

        menu_item = kwargs["menu_item"]
        state_parms = kwargs["state_parms"]
        number = self.GetMultiparmEntries(kwargs)

        if menu_item == "nextstep":
            print("Add multiparm entriy")
            self.SetMultiparmEntries(GetMultiparmEntries(kwargs)+1, kwargs)
            self.node.parm("shapedid_{}".format(number+1)).set(self.shapeid)

        if menu_item == "myradiostrip":
            selectedshape = kwargs["myradiostrip"]

            if selectedshape == "box":
                self.node.parm("shapeid_{}".format(number)).set(0)
                self.shapeid = 0
            elif selectedshape == "sphere":
                self.node.parm("shapeid_{}".format(number)).set(1)
                self.shapeid = 1
            elif selectedshape == "paul":
                self.node.parm("shapeid_{}".format(number)).set(2)
                self.shapeid = 2
            elif selectedshape == "cylinder":
                self.node.parm("shapeid_{}".format(number)).set(3)
                self.shapeid = 3

        if menu_item == "forward":
            self.forwardmovement = kwargs["forward"]


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "3 Course - Context Menu"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    menu = hou.ViewerStateMenu("menuexample", "Menu Example")
    hk = su.hotkey(state_typename, 'firstactionitem', '1', 'First Action Item', 'description...', state_cat)

    menu.addActionItem("nextstep", "Next Step", hotkey=hk)

    menu.addSeparator()

    menu.addToggleItem("forward", "Forward", True)

    menu.addSeparator()

    menu.addRadioStrip("myradiostrip", "Shape Selection", "box")

    hk = su.hotkey(state_typename, 'shape1', '1', 'Box')
    menu.addRadioStripItem("myradiostrip", "box", "Box", hotkey=hk)

    hk = su.hotkey(state_typename, 'shape2', '2', 'Sphere')
    menu.addRadioStripItem("myradiostrip", "sphere", "Sphere", hotkey=hk)

    hk = su.hotkey(state_typename, 'shape3', '3', 'Paul')
    menu.addRadioStripItem("myradiostrip", "paul", "Paul", hotkey=hk)

    hk = su.hotkey(state_typename, 'shape4', '4', 'Cylinder')
    menu.addRadioStripItem("myradiostrip", "cylinder", "Cylinder", hotkey=hk)

    template.bindMenu(menu)

    return template

Drawable#

Basics#

import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None

        # self.mysimpledrawable = hou.SimpleDrawable(
        #     self.scene_viewer,
        #     hou.drawablePrimitive.Tube, # .Sphere, .Circle
        #     "mysimpledrawable")

        # self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.CurrentViewportMode)
        # self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.WireframeMode)
        # self.mysimpledrawable.setWireframeColor(hou.Color(0,1,0))
        # self.mysimpledrawable.enable(True)
        # self.mysimpledrawable.show(True)




    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        pigheadnode = self.node.node("GUIDE") # live reference

        self.mysimpledrawable = hou.SimpleDrawable(
            self.scene_viewer,
            pigheadnode.geometry(),
            "mysimpledrawable")

        self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.CurrentViewportMode)
        # self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.WireframeMode)
        self.mysimpledrawable.setWireframeColor(hou.Color(0,1,0))
        self.mysimpledrawable.enable(True)
        self.mysimpledrawable.show(True)

    def onInterrupt(self, kwargs):
        """ Called when the state is interrupted e.g when the mouse
        moves outside the viewport
        """
        self.mysimpledrawable.show(False)

    def onResume(self, kwargs):
        """ Called when an interrupted state resumes
        """
        self.mysimpledrawable.show(True)

    def onMouseEvent(self, kwargs):
        """ Process mouse events
        """
        ui_event = kwargs["ui_event"]
        dev = ui_event.device()
        self.log("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton())

        # Must return True to consume the event
        return False

    def onMenuAction(self, kwargs):
        """ Callback implementing the actions of a bound menu. Called
        when a menu item has been selected.
        """

        menu_item = kwargs["menu_item"]
        state_parms = kwargs["state_parms"]



    def onMenuPreOpen(self, kwargs):
        """ Implement this callback to update the menu content before
        it is drawn.
        """
        menu_states = kwargs["menu_states"]
        menu_item_states = kwargs["menu_item_states"]



    def onDraw(self, kwargs):
        """ Called for rendering a state e.g. required for
        hou.AdvancedDrawable objects
        """
        draw_handle = kwargs["draw_handle"]


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "5 Course - Drawable"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    return template

State Geometry#

import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None
        self.stategeo = hou.Geometry()

        # self.mysimpledrawable = hou.SimpleDrawable(
        #     self.scene_viewer,
        #     hou.drawablePrimitive.Tube, # .Sphere, .Circle
        #     "mysimpledrawable")

        # self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.CurrentViewportMode)
        # self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.WireframeMode)
        # self.mysimpledrawable.setWireframeColor(hou.Color(0,1,0))
        # self.mysimpledrawable.enable(True)
        # self.mysimpledrawable.show(True)




    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        pigheadnode = self.node.node("GUIDE") # live reference
        pigheadgeo = pigheadnode.geometry()

        polyextrude = hou.sopNodeTypeCategory().nodeVerb("polyextrude::2.0")
        polyextrude.setParms(
            {
                "splittype":0,
                "dist":0.1,
            }
        )
        polyextrude.execute(self.stategeo, [pigheadgeo])


        self.mysimpledrawable = hou.SimpleDrawable(
            self.scene_viewer,
            self.stategeo,
            "mysimpledrawable")

        self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.CurrentViewportMode)
        # self.mysimpledrawable.setDisplayMode(hou.drawableDisplayMode.WireframeMode)
        self.mysimpledrawable.setWireframeColor(hou.Color(0,1,0))
        self.mysimpledrawable.enable(True)
        self.mysimpledrawable.show(True)

    def onInterrupt(self, kwargs):
        """ Called when the state is interrupted e.g when the mouse
        moves outside the viewport
        """
        self.mysimpledrawable.show(False)

    def onResume(self, kwargs):
        """ Called when an interrupted state resumes
        """
        self.mysimpledrawable.show(True)

    def onMouseEvent(self, kwargs):
        """ Process mouse events
        """
        ui_event = kwargs["ui_event"]
        dev = ui_event.device()
        self.log("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton())

        # Must return True to consume the event
        return False

    def onMenuAction(self, kwargs):
        """ Callback implementing the actions of a bound menu. Called
        when a menu item has been selected.
        """

        menu_item = kwargs["menu_item"]
        state_parms = kwargs["state_parms"]



    def onMenuPreOpen(self, kwargs):
        """ Implement this callback to update the menu content before
        it is drawn.
        """
        menu_states = kwargs["menu_states"]
        menu_item_states = kwargs["menu_item_states"]



    def onDraw(self, kwargs):
        """ Called for rendering a state e.g. required for
        hou.AdvancedDrawable objects
        """
        draw_handle = kwargs["draw_handle"]


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "5 Course - Drawable"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    return template

SOP Verbs#

node = hou.pwd()
geo = node.geometry()

box = hou.sopNodeTypeCategory().nodeVerb("box")
box.execute(geo, [])

polyextrude = hou.sopNodeTypeCategory().nodeVerb("polyextrude::2.0")

polyextrude.setParms(
    {
        "dist": 0.06,
        "splittype":0
    })

polyextrude.execute(geo, [geo])
node = hou.pwd()
geo = node.geometry()

boxgeo = hou.Geometry()
box = hou.sopNodeTypeCategory().nodeVerb("box")
box.execute(boxgeo, [])


spheregeo = hou.Geometry()
sphere = hou.sopNodeTypeCategory().nodeVerb("sphere")
sphere.setParms(
    {
        "type":2,
        "scale":0.6,
    }
)
sphere.execute(spheregeo, [])

boolean = hou.sopNodeTypeCategory().nodeVerb("boolean::2.0")

boolean.execute(geo, [boxgeo,spheregeo])
node = hou.pwd()
geo = node.geometry()

boxgeo = hou.Geometry()
box = hou.sopNodeTypeCategory().nodeVerb("box")
box.execute(boxgeo, [])


spheregeo = hou.Geometry()
sphere = hou.sopNodeTypeCategory().nodeVerb("sphere")
sphere.setParms(
    {
        "t":hou.Vector3(2,0,0),
    }
)
sphere.execute(spheregeo, [])

geo.merge(boxgeo)
geo.merge(spheregeo)

Text Drawable#

import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None
        self.text_drawable = hou.TextDrawable(self.scene_viewer, "mytextdrawable")
        self.text_drawable.show(True)
        self.mousepos = hou.Vector3(0,0,0)


    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        state_parms = kwargs["state_parms"]

        # print kwargs in the viewer state console if "Debug log" is
        # enabled
        self.log("onEnter=",kwargs)

    def onInterrupt(self, kwargs):
        """ Called when the state is interrupted e.g when the mouse
        moves outside the viewport
        """
        pass

    def onResume(self, kwargs):
        """ Called when an interrupted state resumes
        """
        pass

    def onMouseEvent(self, kwargs):
        """ Process mouse events
        """
        ui_event = kwargs["ui_event"]
        dev = ui_event.device()
        self.log("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton())
        self.mousepos = hou.Vector3(dev.mouseX(), dev.mouseY(), 0)

        # Must return True to consume the event
        return False

    def onDraw(self, kwargs):
        """ Called for rendering a state e.g. required for
        hou.AdvancedDrawable objects
        """
        handle = kwargs["draw_handle"]
        textparms = {
            "text": "hello world!",
            "scale": hou.Vector3(3,3,3),
            "color1":hou.Color(0,1,0),
            "translate":self.mousepos,
        }
        self.text_drawable.draw(handle, textparms)


def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "GuiSh subnet1"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

Geo Drawable#

import hou
import viewerstate.utils as su

class State(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.node = None
        self.inputnode = None
        self.inputgeo = hou.Geometry()

        self.geometryintersector = None
        self.text_drawable = hou.TextDrawable(self.scene_viewer, "mytextdrawable")
        self.text_drawable.show(True)

        self.geo_drawable = hou.GeometryDrawable(
            self.scene_viewer,
            hou.drawableGeometryType.Face,
            "mygeodrawable",
            label=None,
            geometry=None,
            params={
                "color1" : hou.Color(1,0,0)
                })
        self.geo_drawable.show(True)

        self.mousepos = hou.Vector3(0,0,0)


    def onEnter(self,kwargs):
        """ Called on node bound states when it starts
        """
        self.node = kwargs["node"]
        self.inputnode = self.node.node("INPUT_GEO")
        self.inputgeo = self.inputnode.geometry()

        self.geometryintersector = su.GeometryIntersector(
            self.inputgeo,
            scene_viewer=self.scene_viewer,
            test_dist=0.5,
            tolerance=0.03,
            snap_options={},
            pattern=None)

        self.geo_drawable.setGeometry(self.input_geo)

        state_parms = kwargs["state_parms"]

        # print kwargs in the viewer state console if "Debug log" is
        # enabled
        self.log("onEnter=",kwargs)

    def onInterrupt(self, kwargs):
        """ Called when the state is interrupted e.g when the mouse
        moves outside the viewport
        """
        pass

    def onResume(self, kwargs):
        """ Called when an interrupted state resumes
        """
        pass

    def onMouseEvent(self, kwargs):
        """ Process mouse events
        """
        ui_event = kwargs["ui_event"]
        dev = ui_event.device()



        self.mousepos = hou.Vector3(dev.mouseX(), dev.mouseY(), 0)


        ray_origin, ray_dir = ui_event.ray()
        self.geometryintersector.intersect(
            ray_origin,
            ray_dir,
            snapping=True,
            min_hit=0.0,
            max_hit=1e18)
        # values of intersection result
        print(self.geometryintersector.intersected)
        print(self.geometryintersector.position)
        print(self.geometryintersector.normal)
        print(self.geometryintersector.uvw)
        print(self.geometryintersector.prim_num)
        print(self.geometryintersector.geometry)
        print(self.geometryintersector.ray_origin)
        print(self.geometryintersector.ray_dir)

        if self.geometryintersector.prim_num != -1:
            blast = hou.sopNodeTypeCategory().nodeVerb("blast")
            blast.setParms(
                {
                    "group": "!" + str(self.geometryintersector.prim_num),
                    "grouptype": 2
                }
            )
            isolatedprim = hou.Geometry()

            blast.execute(isolatedprim, [self.inputgeo])

            self.geo_drawable.setGeometry(isolatedprim)
        else:
            self.geo_drawable.setGeometry(hou.Geometry())

        # Must return True to consume the event
        return False

    def onDraw(self, kwargs):
        """ Called for rendering a state e.g. required for
        hou.AdvancedDrawable objects
        """
        handle = kwargs["draw_handle"]
        textvalue = "no hit"
        if self.geometryintersector.intersected:
            textvalue = "hit!"

        textparms = {
            "text": textvalue,
            "scale": hou.Vector3(3,3,3),
            "color1":hou.Color(0,1,0),
            "translate":self.mousepos,
        }
        self.text_drawable.draw(handle, textparms)
        self.geo_drawable.draw(handle)


    def createViewerStateTemplate():
        """ Mandatory entry point to create and return the viewer state
            template to register. """

        state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
        state_label = "GuiSh subnet1"
        state_cat = hou.sopNodeTypeCategory()

        template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
        template.bindFactory(State)
        template.bindIcon(kwargs["type"].icon())

Selections#

Manual selections#

import stateutils
import soptoolutils

pane = stateutils.activePane(kwargs)
# Only run the selector script if we are in a viewer (not putting the node)
# down in a network editor
if isinstance(pane, hou.SceneViewer):
    # First we'll ask for the primitive(s) to copy
    source = stateutils.Selector(
        name="select_polys",
        geometry_types=[hou.geometryType.Primitives],
        prompt="Select primitive(s) to copy, then press Enter",
        primitive_types=[hou.primType.Polygon],
        # Which paramerer to fill with the prim nums
        group_parm_name="sourcegroup",
        # Which input on the new node to wire this selection to
        input_index=0,
        input_required=True,
    )
    # Then, we'll ask for the points to copy onto
    target = stateutils.Selector(
        name="select_points",
        geometry_types=[hou.geometryType.Points],
        prompt="Select points to copy onto, then press Enter",
        group_parm_name="targetgroup",
        # Remember to wire each selection into the correct input :)
        input_index=1,
        input_required=True,
    )

    # This function takes the list of Selector objects and prompts the user for
    # each selection
    container, selections = stateutils.runSelectors(
        pane, [source, target], allow_obj_selection=True
    )

    print(selections)
    sourceselection = selections[0][0]
    targetselection = selections[1][0]

    print(sourceselection, targetselection)

    # This function takes the container and selections from runSelectors() and
    # creates the new node, taking into account merges and create-in-context
    newnode = stateutils.createFilterSop(
        kwargs, "$HDA_NAME", container, selections
    )

    newnode.parm("sourcegroup").set(sourceselection.selectionStrings()[0])
    newnode.parm("targetgroup").set(targetselection.selectionStrings()[0])

    # Finally enter the node's state
    pane.enterCurrentNodeState()

else:
    # For interactions other than in a viewer, fall back to the low-level
    # function
    soptoolutils.genericTool(kwargs, "$HDA_NAME")

Geometry selectors#