Looking Through: part 4 — Cameras

In the last post the many represented lights were looked at in terms of their actual operator type and relationship to transforms and the viewport. This post will look a cameras in a similar fashion. The camera type selection is much slimmer and consists of …

  • camera of type cam
  •  stereo camera of type stereocamrig
  • stereo camera template of type stereocam

**NOTE** The names as per the GUI compared with the node type can be a little confusing. Similar to Light being of type hlight and light templates being of type light, the stereo camera is of type stereocamrig and NOT stereocam which is the type for the template.

stereocamrig includes a stereocam inside that is the same transform as the rig. Likewise, the center camera inside is transformed via stereocam and also has the same transform. Unlike three_point_light, only the top level of the Stereo Camera appears in the viewport pull down menu. However, the cameras internal to the asset can be looked through via dragging them onto the viewport. This makes the results somewhat ambiguous as any of the center cam, stereocam, and stereocamrig will all return as matches. When calling the narrowed version of viewportObjectsOfType() the appropriate flags will need to be set depending on if the result being looked for is a sterocamrig or individual cam. As an alternate method, all can be left on an the result sifted through.

Next the narrowed function will be constructed to return cameras and lights with the argument default logically set a described in this and the Lights section.

Looking Through: part 3 — Lights

There are several lights that appear both on the shelf and via the TAB menu. However, when you look closer, you will immediately notice alot of these are just setting parms differently on the same node types.

  • Caustic Light,Indirect Light, and GI Light are all of type indirectlight
  • Ambient Light is of type ambient
  • Environment Light is of type envlight (so is half of Portal Light and Skylight – explanation below)
  • Light Template is of type light
  • Light, Point, Spot, Area, and Distant are of type hlight ( half of Sky Light is an light too)

Below the various light are examined closer and in terms of how they affect viewport matching…

H12 Shelf Lights

H12 Shelf Lights

light template (light)
Light templates are basic stripped down version of light and according to the docs intended for building up a new light type without having to strip down a light (hlight type) instance.

three_point_light is a great included HDA for a 3-point setup with all the widgets for control. The key, fill, rim and bounce light inside the three_point_light setup are all Light instances and type hlight. Each light in the setup will therefore match properly. However, looking through a three_point_light, is the same as looking through its key light. This is important to note as scripts that use this function should take into account that this difference cannot be discerned.

indirect and ambient lights
Since we are comparing the transform of the light to that of the viewport, we are going to want to check against lights that have controls to position it. indirectlight and ambient types lack these controls. This is logical, as they are indirect, having the ability to absolutely position them via viewport widgets or parms would benefit little. Likewise, it would not make much sense to look through them in the first place as doing so in practice is usually to aim the light and to view what the light ‘sees’. However, as you are permitted to look through indirectlight, we will build in the ability to match them for completeness, although this will be flagged off by default.

envlight
Environment lights only have rotation parms. In the case of a hemisphere this may be particularly useful as well as  ” to orient directional illumination.” But, quick use of these unrotated will result in all coming up positive matches, as their transforms will all be identical and in practice it is less than useful to aim the light by looking through them, although the ability is present. Therefore they are flagged off by default and not considered although they can be flagged on.

About Portal and Skylight
Portal Light and Sky Light are special cases of envlight that drop two top level nodes.

Skylight drops a envlight named “skylight” and also an hlight named “sunlight” — the reason for this setup is not %100 clear, but it is what it is, so it needs to be dealt with it the scripts. There’s a viewer widget attached to the hlight that allows the translation of the sun, but not rotation. The rotation comes from the envlights Sky Environment Tab –> Sun Tab –> Rotate Parms. Changing these results in visible changes to the sun widget on the accompanying node. However, selecting the envlight node shows the viewer widget for rotation, but as per normal rotates the envlight dome a previously mentioned. Yikes! Fortunately when left unrotated, false positives are avoided on the dome by leaving envlights flagged off.  The rotation of the map will still be taken into account via the channel linking to the “sunlight” hlight node, which to be useful would need to be translated away from the origin,and the combo of these two things should produce a unique transform. As, hlights are considered by default, and “sunlight” is still of type hlight there should be usable results by testing against this nodes transform matrix.

Portal Light as provided by the shelf and TAB menus is basically a shortcut to create an envlight with the portal geometry per-assigned. The geometry then restricts the envlight so that it only passes through the assigned object. We can flag on to consider envlights, but the transform that actually is restricting is that of the objects assigned which is arbitrary, so can’t be considered, and additionally can not be looked through Considering envlight will also consider those used for portal light and may not produce the result expected as the envlight it self, as mentioned is at the origin and not useful to look through.

Hopefully this section provided a good overview of the relationship between the lights as depicted via the shelf/TAB menu and their actual type, as well as the default arg flags as will be used in the narrowed function calling the more general viewportObjectsOfType() to be explained in an upcoming post.

Next, a closer look at cameras and their relationship to the viewport.

 

Looking Through: part 2 — Basics

It is best practice to keep things as general and reusable as possible, so the first task is to design a function that takes a tuple of object types and returns all nodes of that type whose transform matrix matches that of the viewport — without being concerned about exactly what it is being looked through.. To get the current viewport, toolutils is made use of.  It gets imported along with the hou. toolutils is included in Houdini and is made use of by the shelf buttons amongst other places. I highly recommend exploring it.

import hou
import toolutils

As an arg to this function a tuple “node_types” expects a operator types as strings. You can quickly see the node type by pressing MMB over a node in the network editor.

def viewportObjectsOfType(node_types):

Included in toolutils is a method to grab the active sceneviewer tab and from there we can get to the viewport and then pull the transform.

cur_viewport      = toolutils.sceneViewer().curViewport()
cur_view_xform = cur_viewport.viewTransform()

node_types is ensured to be a list and then iterated through. We use hou.nodeType with scope limited to object types to gather all instances of each of the types passed in node_types and place them in a list, as lights and cameras are object level nodes.

# get all the objects of specified types
all_possible_nodes = []
for node_type in list(node_types):

found_nodes = hou.nodeType(hou.objNodeTypeCategory(), node_type).instances()
all_possible_nodes.extend(list(found_nodes))

The list of matching instances is then iterated through, pulling the transform and testing if its equal to the transform pulled from the viewport. If it is a match, its appended to the list matching_nodes, which is then turned into a tuple and returned.

**NOTE** However, if you set the viewport to look through a light ie “mylight”, pull the transform of the viewport and the light you might notice sometimes  they don’t entirely match and therefore fail an equality test. ie …

>>> cur_viewport.viewTransform() == mylight.worldTransform()
False

This is because of precision differences where the amount of decimal places will mean they aren’t always totally equal even though for all practical purposes they are. Fortunately sidefx dealt with this by providing a method as part of matrix and vector objects– isAlmostEqual() –which as the name implies returns True when almost equal.

# look for a matching transform
matching_nodes = []
for node in all_possible_nodes:

if node.worldTransform().isAlmostEqual(cur_view_xform):

matching_nodes.append(node)

# return as typically expected tuple
matching_nodes = tuple(matching_nodes)

return matching_nodes

  And thats it! -A general function that will match any object node of a specified types to the viewport. Another function is going to be built calling viewportObjectsOfType() but specifically for light and cameras.

Next is looking closer at Houdini light nodes and considering them in this second function.

Looking Through: part 1 — Looking For.

For those unaware, the small yellow box in the upper right of the viewport can be used to set a camera or light as an object to “look through.” Many are familiar with this behavior from other 3D packages. To give it a try, pull down the menu and select a camera from the list of use the submenu to select a light. Or alternatively, drag a light or camera node from the network graph and onto the viewport.

h12 upper right viewport

h12 upper right viewport

Determining the camera/light object already being looked through in the viewport has come up on forums as well as the sidefx listserve several times. I had myself sent out a message about it some time ago as I was sure there “had to be” a built in HOM function to pull this information, without delving into C++, after all, the little yellow box displays the name of what being looked through so theres some awareness, but it turns out there is no built in HOM method to do so. There needs to be a custom one.

In previous posts, moving up a level and matching locations was used as a way to determine if a node overlapped with a netbox. Similarly, the 3D transform matrix of the viewport can be matched to that of a light or a camera to determine if we are viewing through it. Of course, there is a risk of multiple objects with the same transform, but in practicality it is unlikey other than cases of unrotated objects at the world origin. The final scripts will do its best to minimize this scenario. There are also special case scenarios when dealing with stereo cameras and three point light which will be looked at.

First, lets look at the base function and how it will work.

 

eyevexTools updated to 0.3.1

Apparently, in my sleepy haze I had forgotten to add the py lib folder/name.
This new zip has it present and named correctly.

There are no other changes so, if you downloaded 0.3.0, just make sure all the python files (that were in a folder called ‘python’ in the zip) are installed inside the appropriate python location in a folder named ‘eyevexTools (without the quotes).

i.e. $HOME/houdini11.1/scripts/python/eyevexTools/*.py

** Thanks reader Dan W  for spotting my lack of coffee at time of upload… oops… Sorry about that…
(|;-D>

Working Netboxes: part 4 — On the Shelf.

A little documentation on the provided shelf tools and use…..
eyevexTools ver0.3.0 netbox shelfPick Up : Picks up under-nodes and includes them in the network box. Three use scenerios:

    • Only network boxes selected: all net boxes pick up all respective under-nodes
    • Only nodes selected: selected nodes are picked up by the network boxes above them
    • Mixed network boxes/nodes: selected nodes are picked up IF the network box above them is also selected — selected netboxes pick up selected nodes exclusive.

Drop: Drops a node in a network box to beneath. Usage scenerios are similar to Pick Up

Grow To: Grows the network box to include specified nodes. Select a single network box and nodes then press.

Move To: Moves nodes to inside a network box. Nodes do not need to be at the same hierarchical level, only of the same context (i.e. Sops). Select a single network box, nodes in one or more neteditors, and press. Layout flagged on by default.

Merge: Merge the contents of multiple Netboxes. Similar to Move To, but only network boxes are operated on. A Dailog open to select which network box is the destination.

Custom Color: Set a network box to a custom non-swatch color. Select network boxes and press button, in dialog enter RGB float values and press the ‘set color’ button. (0.05, 0, 0.1 makes a nice deep purple.)

Pick Up All: all network boxes on all open networkeditor panes pickup all their respective under-nodes. Just press it and it goes.

Layout: layout, sizes and and organizes the contents of network boxes. Select netboxes and press. Takes into account under-nodes by default

**NOTE (again)  : A word of precaution. The layout shelf tool allow multiple selected netboxes to be operated on in a single click. Layout does its best to recenter the resized netbox. As a result it can inadvertantly overlap with another box. In such cases an under-node can get “passed” from one box to a subsequant netbox being operated on, that detects it beneath it as well. Care should be taken when running on multple netboxes in close proximately to each other.

….Thats about it, hope a few people got something out of this feedback is always welcome.
(|;-D>

 

Working Netboxes: part 3 — Get One.

What’s selected: A way to query selected network boxes is essential for the interactive shelf buttons, in oder for there to be an easy way to pass network boxes as well as nodes for operations. This is another one of those things we expect to do because we can with nodes, but cannot. We can however check if a netbox is selected…

# check if a network box is selected
my_netbox.isPicked()

… so we need to gather a list of network boxes and return only the selected one by testing via the isPicked() method. This was the motivation behind the creation of ui.py mod. It currently contains two more general functions

  • paneTabsOfType() — while there is a function that return the first panetab found this one returns all.
  • currentPaneTabsOfType() — builds on paneTabsOfType() but only return those active (pane tabs in the foreground)

… so selectedNetworkBoxes() is the new function to return a tuple of hou.NetworkBox objects similar to hou.selectedNodes() or hou.selectedNodeBundles() by gathering the network editor paneTabs, retrieving the network boxes shown and checking their selection status. Since the related provided methods exist at hou.* level, similarly in addition to existing in netbox.py its imported to the lib root as eyevexTools.selectedNetworkBoxes()

Since, unlike the provided methods for node selections, theres no reliable way to know what the last selected netbox is, a GUI dialog requesting which is the destination netbox was created to the merge button. This lead to the creation of the ui.py module where these specific dialogs are now stored.

 

eyevexTools selectNetboxFromListFormMerge dialog

eyevexTools selectNetboxFromListFormMerge dialog

A netbox has  more in common with a sticky note  than a node. While network nodes, like subnets, are a type of node that can contain other nodes — this is obvious when we use them. However subnets also contain network boxes. These are not considered children. and when listed ..

# list all child nodes of a network node
my_subnet.children()

… only hou.Node types are in the tuple returned, even when networks boxes are present. They however can be listed …

# list all network boxes in a network and store result
netboxes = my_subnet.networkBoxes()

…. we can then get the nodes within a box…

# store a tuple of nodes contained with in the 1st returned netbox
my_netbox = netboxes[0]
my_netbox_nodes = my_netbox.nodes()

if any of the nodes returned are also a network, its children could be listed in so on. If we want to go from one of the nodes in my_netbox (also a child of my_subnet – remember netboxes aren’t part of the hierarchy) back to my subnet we can grab a ref with a hou.Node method parent() which is the counterpart to children()

# get the parent network from a node found in a netbox
my_node = my_netbox_nodes[0]
my_nodes_parent = my_node.parent()

this makes logical sense, as if we can go inside, we expect to be able to get back outside. However there is no similar counterpart to networkBoxes() method of hou.Node, so while we can get membership from a netbox — we can’t find the network box from one of its members. We might expect that we could because of our previous dealing with nodes. Fortunately Houdini will allow us to make this happen.

Two new functions are introduced…

node.evt_networkBoxAround() — eyevexTools function (shown with the method prefix) returns the netbox which the node is a member Since it has no hierarchical  function this isnt really a ‘parent’ but can losely be thought of as a sort of Uncle.

node.evt_networkBoxAbove() — eyevexTools function (shown with the method prefix) returns the netbox above the node – the node is the boxes ‘under-node’ and ghosted out.

For networkBoxAround() we need to find a netbox from one of its members, we do this by moving up one level, gathering all the netboxes and the looking amongst each members untill a match is found. a similar technique is employed in methods mentioned in the last section to find under-nodes by checking if each at a level fall withing the bounds of a netbox.  These aren’t  very algorhymically efficent but in a common technique in practice when necesary to search for somehting. There is a practicality and the trade off in time invested and in noticable gain. And for the moment, it scales ok, and can be improved upon to scale better later.

networkBoxAround() and networkBoxAbove() along with layoutNodes(), nodesOverlapping() and nodesUnder() form the basis for which most of the other custom function buld on and are what allow us to get a networkBox

The last section will look at the shelf tool use….

 

Working Netboxes: part 2 — Inside and Under

Often when working with a network box, a node may inadvertently be removed and appear to have dropped beneath it. To get it back inside it needs to be dragged outside the box, let go, and returned to the inside.  This is a well known bug.  A shelf button to pick up a node without dragging a u-turn in the graph would seem useful. Well… there’s a half sarcastic old code adage — ” A well documented bug is a feature ..” What if we took advantage of this behavior.

H11 Netbox with node dropped beneath

Typical network box with a node dropped beneath.

By making dropping and picking up nodes accessible, they can be used in new and interesting ways.  When the node is ‘under’ the netbox it has a ghosting effect highlighting the others. Another workflow could involve having a group of alternate nodes where one is dropped down to be worked with temporarily so the box can be collapsed moving the other nodes out of the way. I’m sure there are other interesting ways these behaviors can be taken advantage of and would be interested in hearing of them.

# get all nodes inside network box
my_node.networkBoxes()

… the above wouldn’t return what here forward will be referred to as ‘under-nodes’ ( the nodes dropped beneath). Since many the introduced functions/tools rely on querying node and netbox size/positions in the graph  they  currently rely on the netboxes NOT being collapsed. Logically this makes sense since they would not have much visual effect otherwise.  Most functions from these posts can optionally consider under-nodes via flags, however on the shelf, the merge button doesn’t consider them by default although allows this feature to be flagged on. The tools presented should accommodate such alternate workflows. Along with the provided hou.NetworkBox.nodes() method, there will be three ways of retrieving membership depending on what is being looked for.

  • my_netbox.nodes() — hou provided method returns the nodes ‘inside’ the netbox
  • evt_nodesOverlapping(nodes=[]) — eyevexTools function (shown with the method prefix) returns all nodes inside the box and under-nodes (netbox must NOT be collapsed). Optional nodes args allows passing of a list to filter through returning only the overlapping members rather than all overlapping nodes
  • evt_nodesUnder(nodes=[]) — similar to above nodesOverlapping() but only returns the under-nodes

Elemental to keeping things clean is the creation of a layoutNodes() function that will layout the contents of a network box similar to layout() as it applies to networks and nodes. This layout function is mapped to a shelf button for easy access to operate on selected network boxes, as well as a flag on most other tools including mergeNetBoxes and moveNodesInside It is flagged on by default on all shelf buttons and functions. It too relies on the netbox being uncollapsed (why would you layout something you can’t see anyway?) and preserves under-nodes by default.

**NOTE : A word of precaution. The layout shelf tool allow multiple selected netboxes to be operated on in a single click. Layout does its best to recenter the resized netbox. As a result it can inadvertently overlap with another box. In such cases an under-node can get “passed” from one box to a subsequent netbox being operated on, that detects it beneath it as well. Care should be taken when running on multple netboxes in close proximately to each other.

The next section will take a closer look at these and other techniques for getting a reference to a Network box.

Working Netboxes: part 1 — Where to Start …

Network Boxes or simply  ‘netboxes’ are  great ways to organize nodes in the network graph.  They don’t alter the node hierarchy and therefore a safe way to organize and visually group and can be a sort of nondestructive replacement for a subnetwork. However they are intended as visual aids they lack some scripting and interface features we have come to expect from nodes. Although some of these limitations makes absolute sense in terms within in the structure of Houdini, visually one might expect otherwise.  Some of these features can be added and made more convenient via some creative python and HOM

Typical H11 Network Box

Typical Network Box in default gray.

While working on the netbox.py mod I realized, although I have been trying up to this point to keep blog post in a 1:1 ratio with modules, it wasn’t worth the trouble for the confusion it will in this case likely cause. For example, a function retrieving a netbox from on of its member nodes — actually belongs in a module dealing with nodes, as we are requesting something of the nodes, in this case, and not a network box – despite what is being returned. As a result this series of post introduces node.py in addition to netbox.py. Also introduced is ui.py, which has more general functions and is in in a similar vein to hou.ui. Lastly,  dialogs.py stores GUI dialogs specific to eyevexTools (largely for shelf buttons), since it is a good practice to separate such interfaces which are not needed for command line functionality.

node.py mod includes a function extendHouNodeClass() and netbox incudes similarly extendHouNetworkBoxClass().  These two functions add the methods in the mods to the hou.Node and hou.Networkbox classes respectively in session .  Techniques like this is partly what makes python so popular and also what makes many coders of other compiled languages cringe. However it is convenient and allows the methods to be called along side the provided ones. The added functions appear on the object as methods prefixed with ‘evt_’ hopefully make the distinct None of the code in eyevexTools nor the shelves actually use this technique.

These will also be the first series of posts/download featuring shelf buttons.  For artists coming from other applications such as Maya or Mudbox, the shelf is a familiar interface and the first place where common tasks are often looked for. eyevexTools lib download will also have a folder ‘Icons’ as well as ‘toolbar’. The installation instructions have been updated to include these. Shelf tools are great way to make things quick and accessible via a mouse pointer. Many times when asked to create a ‘a simple tool’ for Houdini, all that was required is querying a selection and running a method that already existed, as there are so many functions it would be absurdly cluttered to present them all in the interface. So, sometimes special use cases just require elevating one of these function to be accessible to non-scripters.

SVGs are a scalable vector graphic and a standard format that are accepted many place in Houdini (some of which will also accepts PNGs) Since they are vector they scale better in the shelves for different ui sizes and look much better. Plus, places where PNG might be required, like a help card, a PNG can also be exported from most any vector graphic  software. Inkscape is a free and open fully featured native SVG editor that runs on linux, windows, and OSX. I used it to create all the icons and highly recommend it.

These posts will not be going further in depth on shelves. Basic creation of a shelf and tool is easy, create a new shelf via “+” button on the upper right of the shelf set. The RMB on a blank spot on the new shelf, and choose “New Tool.” Give it a name, label, and icon if desired and then decide what script you want to run and accept. You can then edit the shelf file directly by looking in you $HOME/houdiniXX.x/toolbar folder in you favorite text editor, to see what was written out. If you do want to make changes by hand makes sure all Houdini sessions are closed or it can become illegible quickly)

OK, lets get get started…..

Take and takes: part 2 — Where and about.

end thoughts …
All appears to work reasonably well in h11.1.xxx –Although, I have yet to test it in any  harsh or production environment. So, anyone who does, feel free to email me some feedback, it’d be appreciated.

There’s some conveniences I ran across in hscript docs that I might like in python. As mentioned in part 1, I wanted to keep the module clean, but I may implement a companion module, if I find myself really wanting it. I also made a fair attempt to add docstrings tha’tll show up in the help popups in Houdini and to keep it generally looking the same as the rest, but some finer ui things like dragging a take into the py shell and expecting the object aren’t working

There are some methods of Take object in particular that are best guess where assumptions had to be made as they were necessary features but have some error potential going forward. There are some places where there was no sense in implementing a feature and still others that are necessary ‘hacks’. This is all noted below.

what is available for download
This scripts are available in takes.py in the script downloads section as well as incorporated into eyevexTools py modules also available for download

basic use
import eyevexTools.takes

# use for creating a new take….
mytake = eyevexTools.takes.Take(‘mytake’)

# it is recommended to use find or a listing function instead of the wrapper directly
mytake = eyevexTools.takes.findTake(‘mytake’)
curenttake = eyevexTools.takes.curTake()
roottake = eyevexTools.takes.rootTake()
alltakes = eyevexTools.takes.allTakes()

caveats

  • unable to implent the XML export as the format can be arbitrary
  • although it is impossible to know is the path would be returned in the presented format, It was needed for this implementation but may not be compatable
  • It is not clear if insertTakeAbove and moveUnderTake will be returning parent or child. I assume it to be child.
  • EYEVEX_TAKE_PREFIX is a hscript env used to store the take prefix (when not default), as hscript provided no facility to retrieve it after being set
  • allAllParmsOfNode method in the docs is assumed a typo/error and that it is method addAllParmsOfNode -which is how it appears in this implementation, but can be a risky assumption.
  • asCode method doesn’t include the code for wrapping the take

(|;-D>