SubEngines ********** |sparkles| **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 :py:class:`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 :py:class:`UsdProcessingEngine.EngineRegistry` in the :kat:ui:`Python` tab. All Foundry-defined engines are registered with detailed descriptions of their input arguments and behaviors. How To Define a SubEngine ========================= The :py:class:`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 :py:class:`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. .. code-block:: python 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. .. |sparkles| unicode:: U+2728