On this page | |
Since | 17.0 |
Overview ¶
For technical or integration tasks that would be difficult to express using LOP nodes, you can use this node to access the full power of the USD API to read and modify the scene graph tree, the power of HOM to access and control the rest of Houdini, as well as the full power of Python and its libraries to access external data and integrate with your pipeline and other systems.
This node is often useful to script custom pipeline-specific tasks.
See Pixar’s Python API tutorials for help with the APIs.
Scripting the stage ¶
-
Call
hou.pwd()
to get a reference to this node as a hou.Node. You can get either the current stage through this reference (see below). You can also use it to access spare parameters you might add to this node. -
Call
hou.pwd().editableStage()
to get an editablepxr.Usd.Stage
object. This represents the output of this node, including any changes you make using the API.(The USD Edit Target is automatically set to the active layer for you. Do not change the edit target. Doing so will have unpredictable negative consequences.)
-
Call
hou.pwd().inputs()[0].stage()
to read from an input stage, then stash any evaluated prim patterns or other values, **before** callinghou.pwd().editablestage()
. When this happens in the wrong order, Houdini will still usually run the code, but will not be as efficient.
Examples ¶
Add new prims to the stage
stage = hou.pwd().editableStage() xform = stage.DefinePrim('/shapes', 'Xform') cube = stage.DefinePrim('/shapes/cube1', 'Cube')
Loop over every prim in the scene graph tree
stage = hou.pwd().editableStage() for prim in stage.Traverse(): # Access or edit the prim pass
Use USD’s high-level APIs to manipulate complex attributes
from pxr import UsdGeom stage = hou.pwd().editableStage() cube = stage.GetPrimAtPath("/shapes/cube1") # Change attributes directly cube.GetAttribute("size").Set(1.5) # APIs can have useful shortcuts cube_api = UsdGeom.Cube(cube) color = cube_api.GetDisplayColorAttr() color.Set([(0, 0, 1.0)]) # Use the XformCommonAPI to manipulate transforms instead of messing # with matrix attributes directly UsdGeom.XformCommonAPI(cube).SetTranslate((4, 5, 6))
Loop over all lights, copying their exposure into their intensity and clearing the exposure
from pxr import UsdLux stage = hou.pwd().editableStage() # UsdLux has a special API for finding all lights in the scene efficiently api = UsdLux.ListAPI(stage.GetPseudoRoot()) for lightprim in light api.ComputeLightList(): exposure_attr = lightprim.GetAttribute("exposure") intensity_attr = lightprim.GetAttribute("intensity") intensity = intensity_attr.Get() exposure = exposure_attr.Get() if exposure: intensity_attr.Set(intensity * (exposure ** 2)) exposure_attr.Set(0)
Reduce the intensity of all lights to half their current values, using a hou.LopSelectionRule.
node = hou.pwd() # First, find all the light prims on our input. # We must do this before getting our editable stage to # avoid an expensive recook of the input node. ls = hou.LopSelectionRule() ls.setPathPattern('{ usd_istype(0, @primpath, "UsdLuxLight") }') paths = ls.expandedPaths(node.inputs()[0]) # Now that we have our set of prim path, we can get the # editable stage and start making changes. stage = node.editableStage() for path in paths: prim = stage.GetPrimAtPath(path) intensity = prim.GetAttribute('intensity') intensity.Set(intensity.Get() * 0.5)
Scripting the active layer instead ¶
-
Instead of manipulating the stage using
Usd
APIs, you can edit the active layer using lower-levelSdf
APIs. You cannot do both in the same script. -
You probably only want to do this if you have a reason to prefer the lower-level APIs.
pxr.Usd
methods are higher level methods that can be much easier to use. They perform more error checking and data validation. However thepxr.Sdf
APIs can be dramatically faster, especially when creating new USD primitives. -
To get an editable
pxr.Sdf.Layer
reference to the active layer, callhou.pwd().editableLayer()
. If you do this, do not also callhou.pwd().editableStage()
in the same script (doing so will just returnNone
). -
The returned editable layer is not on any USD stage while the python LOP is cooking, so there is no need to use
Sdf.ChangeBlock
objects to batch change notifications when making edits. -
After calling
editableLayer
, you may callhou.pwd().uneditableStage()
to get access to a read-onlyUsd.Stage
. This stage will be the output of the node connected to this node’s input. But unlike callinghou.pwd().stage()
orhou.pwd().input(0).stage()
, this approach will not trigger recursion errors or warnings about getting a read lock for an already locked stage. The returned stage can be used to access information about the current state of the stage to inform the edits being made to the editable layer. Because the editable layer is not composed onto this uneditable stage, changes made to the layer will not affect the content of this stage.
Tips ¶
-
When you are editing code, the node will cook if you click outside the editor, or press Alt + Enter.
-
You can add spare parameters onto the node to give the node a graphical interface. For example, you could add a text box named
primpath
that takes a primitive path. Then you can access the parameter value in the script:node = hou.pwd() primpath = node.evalParm("primpath")
-
If you make a useful Python Script node you want to share, you can turn a node instance into a LOP digital asset. Right-click the node in the network editor and choose Create digital asset. The script is stored as part of the digital asset, and the node functions the same way, but as an asset that can be shared and versioned.
-
The pop-up menu button to the right of the editor pulls snippets from any
PythonScripts.txt
files in the Houdini path. You can create a$HOUDINI_USER_PREFS_DIR/PythonScripts.txt
file and add snippets to have them appear in this menu. Read$HFS/houdini/PythonScripts.txt
to see the required file format. -
If your script needs to “find all prims that…”, instead of using the USD traversal methods, consider using hou.LopSelectionRule. This object implements Houdini’s USD prim pattern matching. It generates a list of
Sdf.Path
objects. You can turn anSdf.Path
into aUsd.Prim
object usingstage.GetPrimAtPath(pathstring)
. When using this approach you can either evaluate the selection rule on the input node, or directly on theUsd.Stage
returned bypwd().editableStage()
. If using the input node, evaluate the selection rule before getting this node’s editable stage to avoid an expensive duplication of the input stage. -
It is almost always a bad idea to call hou.setContextOption() from within the script of a Python LOP. This causes a global context option to be created. Global context options are accessible anywhere in the hip file, which means cooking this LOP and running its script can potentially cause nodes anywhere else in the hip file to become dirty, if they use this context option. Further, the global context option will only be set when the node cooks, but changing the global context option value in the context option editor or using Python code (in a python shell or in another Python LOP) will not dirty this node, and so it will not recook and reset the option value as might be expected. Instead, setting context options locally within a LOP network should be done using the Edit Context Options LOP.
-
You must not edit the root layer of the stage. Although this root layer is accessible through the Python API, the LOP cooking architecture requires that it be free to manage that root layer free from interference from any single LOP node. The most common use case that leads to this mistake is wanting to add a sublayer to the root layer, which can instead be done using the hou.LopNode.addSubLayer method.
Parameters ¶
Python Code
The Python code to run when cooking this node. See the main help above for tips and examples.
Maintain State
When enabled, the underlying Python interpreter will not be cleared between cooks. This may give some performance gains, but can also lead to unexpected behaviour. It is generally recommended to leave this disabled.