Ok, so it’s not only about the references which you asked for, it’s basically about the USD C++ API as a whole.
Please keep working through the USD API reference here: https://graphics.pixar.com/usd/docs/api/index.html
That example code is an excerpt of a small Omniverse connector I wrote while still learning USD and experimenting with references.
(1) pxr::SdfPath pathRef = pathWorld.AppendChild(pxr::TfToken("RefSphere"));
As I understand it, an SdfPath is basically a filesystem path. Here it looks like it starts with an absolute path to the folder containing the usd geometry file (is that what pathWorld is? It’s not defined in the shown scope), but I don’t understand what the purpose of the AppendChild function is, and by extension I don’t know what this “refSphere” token is for
Yes, all node locations in your USD stage are specified by a unique SdfPath
which is similar to a file system hierarchy.
This section in Pixar’s documentation describes SdfPath: Universal Scene Description: SdfPath Class Reference
The pathWorld
is the SdfPath to the root prim of the stage. That’s usually a UsdGeomXform.
The code which happens before that looks like this:
// With stage created....
pxr::UsdGeomSetStageUpAxis(stage, pxr::UsdGeomTokens->y);
pxr::SdfPath pathStage = pxr::SdfPath::AbsoluteRootPath();
pxr::SdfPath pathWorld = pathStage.AppendChild(pxr::TfToken("World"));
pxr::UsdGeomXform xformWorld = pxr::UsdGeomXform::Define(stage, pathWorld);
if (xformWorld)
{
stage->SetDefaultPrim(xformWorld.GetPrim());
// Reference the Models/Sphere.usd
{
...
}
// Reference the same or other models here.
...
}
The AppendChild()
function is simply creating a new SdfPath by concatenating the parent path and the child token.
So if your parent path was “World” then path.AppendChild(TfToken("Child"))
creates a path “World/Child”. Simple as that.
In this example it’s using the hardcoded name “RefSphere” for the child because that node is a reference of an external USD scene named “Sphere.usd”. So the pathRef
in that example becomes “World/RefSphere”.
That’s just some path. That does nothing to your stage, yet, which is the next step.
(2) pxr::UsdPrim primRef = stage->OverridePrim(pathRef); // Create an "over".
As I understand it we’re creating a USD prim here that will soon have geometry reference data ‘pushed’ to it (the geometry data won’t actually be pushed but it will be referenced). I just don’t know what the right side of this statement is doing or what the comment means by “over”
That command is making sure there is an UsdPrim at that path inside the stage which can be used to access and change parameters inside the later referenced *.usd. It doesn’t define what it is, yet. That’s resolved by USD internally later.
If you look into an USD ASCII file (*.usda) which you can create from any binary *.usd file by using the usdcat
helper tool inside the USD repository, then you’ll see that referenced layers are represented by an “over” keyword.
That comes from the C++ API function UsdStage::OverridePrim()
.
You need that to be able to override individual parameters like the transform or material assignments inside the referenced UsdPrim hierarchy.
Please read the whole USD Introduction and esp. this chapter on Referencing Layers: https://graphics.pixar.com/usd/docs/Referencing-Layers.html
and this chapter inside the USD API reference: https://graphics.pixar.com/usd/docs/api/_usd__page__scenegraph_instancing.html
which go into much more detail about references and instancing.
That’s all I used to get that example code working. Well, I needed to translate the introductory Python example code back to C++ by doing a lot of searches in the USD API reference.
(3) primRef.GetReferences().AddReference("Models/Sphere.usd");
This looks like it’s just setting the reference of our new prim to wherever our geometry is located. It doesn’t include the full filepath though so I assume it’s appended onto the “pathWorld” variable above in parallel to that child token (hence why I’m confused on what that token is doing).
Yes, the primRef
which has been added to the stage gets a reference attached at the back of the prepend list.
Explained here: https://graphics.pixar.com/usd/docs/api/class_usd_references.html
In this case that is a relative path to the location of the current stage.
So let’s say the current stage we are working on has been created in omniverse://localhost/Users/Shot.usd
the referenced file is in omniverse://localhost/Users/Models/Sphere.usd
Lastly, in that fifth line, the UsdGeomXformable is our instanced object right? i.e. creating another with a different name would yield a new transformable instance.
No, the primRef is the handle of the referenced object. It’s a layer of the UsdGeomXform of the referenced stage Models/Sphere.usd.
To be able to change the transformations (translation, rotation, scale, or full matrix), you need to access the UsdGeomXformable
of that prim. That xform
variable is just the handle to that transformable.
Explained here: https://graphics.pixar.com/usd/docs/api/class_usd_geom_xformable.html#details
Note that UsdGeomMeshes themselves can be transformed in USD as well, but since USD doesn’t like referencing UsdGeomeMeshes itself, but always wants to reference a node above that to define a “scope” since referenced objects can only access things in their scope, matters get rather complicated in USD when you really only want to instance mesh geometry.
There is this sentence inside the “scenegraph_instancing” link above which explains the reasoning:
“Since instancing shares the scenegraph hierarchy beneath instance prims, instancing a single prim that has no descendants provides no benefits. In the case that instancing a single prim is desired (e.g., instancing a mesh), that prim should be made a descendant of another prim that is referenced into the scene”)
You’ll get all sorts of warnings along the lines of “cannot access objects outside the referenced scope” when not adhering to that instancing methodology.
If you reference one and the same *.usd multiple times, add the set the instanceable flag with SetInstanceable(true)
on the referenced prim to let USD generate a single instance which is used multiple times.
In Omniverse you can see the effect of that when looking into the RTX renderer statistics and compare the number of instances and the number of meshes in geometry acceleration structures.
To reuse these meshes is the whole point of this referencing and instancing.
When reading the USD documentation about this, related topics are “internal references”, “payload”, variant set", and the whole layering concept in general.