Relationships#
What Is a Relationship?#
Relationships establish connections between prims, acting as pointers or links between objects in the scene hierarchy. A relationship allows a prim to target or reference other prims, attributes, or even other relationships. This establishes dependencies between scenegraph objects.
How Does It Work?#
Relationships store path values that point to other scene elements. When you query a relationship’s targets, OpenUSD performs path translations to map the authored target paths to the corresponding objects in the composed scene prims.
Relationships are robust against path translations, a key advantage over hard-coded paths. If a target prim’s path changes due to editing or referencing, the relationship automatically remaps to the new location.
Relationships can have multiple targets, making them useful for grouping or collecting objects together. For example, a material relationship might target all geometry prims that should use that material.
Note that a relationship is an alternative type of property to an attribute. Unlike an attribute, it has no data type. It is a way of declaring, at creation time, that the only use for a property is to record linkage information. Conceptually, it is like an attribute whose data type is a “link”. This means you can not use a relationship to connect two already-existing attributes–for that, you can use attribute connections.
Working With Python#
Here are a few Python commands to familiarize yourself as you work with relationships. These can be useful as you establish connections between different scene elements, like materials and geometry.
1# Get the target paths of a relationship
2UsdRelationship.GetTargets()
3
4# Set the target paths for a relationship
5UsdRelationship.SetTargets()
6
7# Add a new target path to a relationship
8UsdRelationship.AddTarget()
9
10# Remove a target path from a relationship
11UsdRelationship.RemoveTarget()
Examples#
Example 1: Prim Collections with Relationships#
Relationships are properties that store one or more target paths. You can author a relationship with CreateRelationship, then later retrieve it with GetRelationship and inspect the targets with GetTargets.
1from pxr import Usd, UsdGeom, Gf
2
3file_path = "_assets/relationships_ex1.usda"
4stage = Usd.Stage.CreateNew(file_path)
5
6world_xform: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
7
8# Define a sphere under the World Xform:
9sphere: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world_xform.GetPath().AppendPath("Sphere"))
10
11# Define a cube under the World Xform and set it to be 5 units away from the sphere:
12cube: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world_xform.GetPath().AppendPath("Cube"))
13UsdGeom.XformCommonAPI(cube).SetTranslate(Gf.Vec3d(5, 0, 0))
14
15# Create typeless container for the group
16group = stage.DefinePrim("/World/Group")
17
18# Define the relationship
19group.CreateRelationship("members", custom=True).SetTargets(
20 [sphere.GetPath(), cube.GetPath()]
21)
22
23# List relationship targets
24members_rel = group.GetRelationship("members")
25print("Group members:", [str(p) for p in members_rel.GetTargets()])
26
27stage.Save()
Group members: ['/World/Sphere', '/World/Cube']
Example 2: Using a Built‑in Relationship (proxyPrim)#
Many built‑in schemas expose Relationships. UsdGeom.Imageable has proxyPrim, which points a high cost prim to a lightweight proxy.
1from pxr import Usd, UsdGeom
2
3file_path = "_assets/relationships_ex2.usda"
4stage = Usd.Stage.CreateNew(file_path)
5
6world_xform: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
7
8# Define a "high cost" Sphere Prim under the World Xform:
9high: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world_xform.GetPath().AppendPath("HiRes"))
10
11# Define a "low cost" Cube Prim under World Xfrom
12low: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world_xform.GetPath().AppendPath("Proxy"))
13
14UsdGeom.Imageable(high).GetPurposeAttr().Set("render")
15UsdGeom.Imageable(low).GetPurposeAttr().Set("proxy")
16
17# Author the proxy link on the render Prim
18UsdGeom.Imageable(high).GetProxyPrimRel().SetTargets([low.GetPath()])
19
20# Tools that honor proxyPrim should draw the proxy in preview
21draw_prim = UsdGeom.Imageable(high).ComputeProxyPrim() # returns Usd.Prim
22print("Preview should draw:", str(draw_prim[0].GetPath() if draw_prim else high.GetPath()))
23
24stage.Save()
Preview should draw: /World/Proxy
Example 3: Material Binding Relationships#
Material binding is encoded as a relationship named material:binding that targets a UsdShade.Material. The UsdShade.MaterialBindingAPI authors and reads this relationship. Here GreenMat is bound to two cubes and RedMat is bound to one cube.
1from pxr import Usd, UsdGeom, UsdShade, Gf, Sdf
2
3file_path = "_assets/relationships_ex3.usda"
4stage = Usd.Stage.CreateNew(file_path)
5
6world_xform: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
7
8
9cube_1: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world_xform.GetPath().AppendPath("Cube_1"))
10cube_2: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world_xform.GetPath().AppendPath("Cube_2"))
11UsdGeom.XformCommonAPI(cube_2).SetTranslate(Gf.Vec3d(5, 0, 0))
12cube_3: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world_xform.GetPath().AppendPath("Cube_3"))
13UsdGeom.XformCommonAPI(cube_3).SetTranslate(Gf.Vec3d(10, 0, 0))
14
15# Create typeless container for the materials
16looks = stage.DefinePrim("/World/Looks")
17
18# Create simple green material for preview
19green: UsdShade.Material = UsdShade.Material.Define(stage, looks.GetPath().AppendPath("GreenMat"))
20green_ps = UsdShade.Shader.Define(stage, green.GetPath().AppendPath("PreviewSurface"))
21green_ps.CreateIdAttr("UsdPreviewSurface")
22green_ps.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set(Gf.Vec3f(0.0, 1.0, 0.0))
23green.CreateSurfaceOutput().ConnectToSource(green_ps.ConnectableAPI(), "surface")
24
25# Create simple red material for preview
26red: UsdShade.Material = UsdShade.Material.Define(stage, looks.GetPath().AppendPath("RedMat"))
27red_ps = UsdShade.Shader.Define(stage, red.GetPath().AppendPath("PreviewSurface"))
28red_ps.CreateIdAttr("UsdPreviewSurface")
29red_ps.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set(Gf.Vec3f(1.0, 0.0, 0.0))
30red.CreateSurfaceOutput().ConnectToSource(red_ps.ConnectableAPI(), "surface")
31
32# Bind materials to Prims
33UsdShade.MaterialBindingAPI.Apply(cube_1.GetPrim()).Bind(green)
34UsdShade.MaterialBindingAPI.Apply(cube_2.GetPrim()).Bind(green)
35UsdShade.MaterialBindingAPI.Apply(cube_3.GetPrim()).Bind(red)
36
37# Verify by reading the direct binding
38for prim in [cube_1, cube_2, cube_3]:
39 mat = UsdShade.MaterialBindingAPI(prim).GetDirectBinding().GetMaterial()
40 print(f"{prim.GetPath()} -> {mat.GetPath() if mat else 'None'}")
41
42stage.Save()
/World/Cube_1 -> /World/Looks/GreenMat
/World/Cube_2 -> /World/Looks/GreenMat
/World/Cube_3 -> /World/Looks/RedMat
Key Takeaways#
Relationships enable robust encoding of dependencies and associations between scene elements, such as:
Binding geometry to materials
Grouping prims into collections
Establishing connections in shading networks
Associating scene elements with non-hierarchical links (e.g. material binding)
Using relationships instead of hard paths enhances:
Non-destructive editing workflows
Referencing and asset reuse across tools
Collaborative workflows across teams
Relationships are a way to link scene elements while enabling non-destructive editing and cross-tool collaboration. They enhance the flexibility and scalability of OpenUSD-based pipelines.