SubEngines
✨ New in Katana 8.5
SubEngines provide procedural on-demand functionality to the UsdSuperLayer node. Unlike static edits to the ContentLayer, which are applied once when parameters change, SubEngines can dynamically react to changes in the upstream scene graph. While expressions provide some procedural capabilities, they cannot respond to future upstream modifications.
SubEngines can be encoded onto a Prim using the usg.FnSubEngineAPI multi-apply schema.
This allows one or more SubEngines to be attached per prim, each capable of reading and reacting
to the input scene in the same way as any existing Engine in the main Engine chain.
Key Characteristics:
All SubEngines share the same input Engine as the LayerEngine itself.
SubEngines cannot be chained together (they operate independently).
The execution order and layer combination behavior is currently undefined.
Finding Available Engines
To discover available Engines and their documentation, use the
UsdProcessingEngine.EngineRegistry in the Python tab.
All Foundry-defined engines are registered with detailed descriptions of their input arguments
and behaviors.
How To Define a SubEngine
The usg.FnSubEngineAPI provides utilities to assist in defining SubEngines on a Prim.
Similar to appending Engines to a node’s Engine chain, we use the same
UsdProcessingEngine.EngineArgsBuilder helper class which can be used to simplify
specifying the SubEngine’s arguments.
Example: Adding Transform Rotation with TransformSetEngine
The following example shows how to use the TransformSetEngine to automatically apply a 45-degree
X-axis rotation to incoming transforms on a specific prim in a UsdSuperLayer node.
from UsdProcessingEngine import EngineRegistry, EngineArgsBuilder
import usg
# Step 1: Explore available engines (optional)
engineRegistry = EngineRegistry()
# Read the arguments and the description of the Engine you want to use
print(engineRegistry.describeEngine("TransformSetEngine"))
# Step 2: Create a simple input scene using the UsdCubeCreate node
cubeCreateNode = NodegraphAPI.CreateNode("UsdCubeCreate", NodegraphAPI.GetRootNode())
cubeCreateNode.getParameter("primPath").setValue("/root/cube", 0)
# Step 3: Create and connect the UsdSuperLayer node
uslNode = NodegraphAPI.CreateNode("UsdSuperLayer", NodegraphAPI.GetRootNode())
uslNode.getInputPortByIndex(0).connect(cubeCreateNode.getOutputPortByIndex(0))
# Step 4: Get the StageSubtreeController and create an override prim
ssc = uslNode.getStageSubtreeController()
# Create an "over" prim (no type override) to store the edited transform
cubePrim = ssc.createPrim("/root/cube", "", usg.PrimSpecifier.Over)
# Step 5: Configure the SubEngine
fnSubEngineAPI = usg.FnSubEngineAPI(cubePrim, usg.Token("Transform"))
args = EngineArgsBuilder(0)
# Note: No need to set "primPaths" - LayerEngine automatically provides the current primPath
# See "Prim Path Injection" section below for details
args["editMode"] = usg.Value(1, usg.Value.Type.Bool)
args["writeAsComponents"] = usg.Value(1, usg.Value.Type.Bool)
args["order"] = usg.Value("Scale Rot Trans", usg.Value.Type.String)
args["rotateOrder"] = usg.Value("Rx Ry Rz", usg.Value.Type.String)
args["xformOpName"] = usg.Value("", usg.Value.Type.String)
args["rotate"] = usg.Value(usg.Vec3d(45, 0, 0), usg.Value.Type.Double3) # 45° X rotation
# Step 6: Apply the SubEngine configuration
# float("nan") represents the "Default" USD time sample
# This method handles both creation and updates, cleaning up old properties as needed
fnSubEngineAPI.createOrUpdateSubEngineAttrs(
"TransformSetEngine",
args.build(),
float("nan"),
)
# Step 7: Sync the changes to the UsdSuperLayer Node
uslNode.syncLayerAndParameters()
# Result: The cube will always have a 45° X rotation applied, even if you modify
# the original cube's translation or other transform properties
Automatic Prim Path Injection
When SubEngines are copied or when prims containing them are renamed or reparented, any manually
specified primPaths arguments would become invalid. To prevent this issue the best practice is
to omit writing the primPaths argument when configuring SubEngines. The LayerEngine
automatically injects the current prim’s path as the primPaths argument for all SubEngines,
ensuring they remain valid regardless of structural changes to the scene.