Prims and Properties
Prims
A prim
(short for primitive) is a dictionary of key-value pairs. Prims are the main components of a USD scene graph which gets composed from a stage after assembling all of the layers, resolving their opinions and deciding which final value a prim gets for e.g. its scaling or translation or rotation values.
Prims are hierarchical in the scene so you can have a wheel
prim and a car_chassis
prim and each one of those meshes containing: in their key field points
the XYZ coordinates of each vertex for the mesh, in the faceVertexCounts
the number of points composing each face, in faceVertexIndices
the indices for each vertex so the face can have a face-up face-down orientation, the normals
for each vertex, etc..
def Xform "World"
{
def Xform "Car"
{
double3 xformOp:rotateXYZ = (0, 0, 0)
double3 xformOp:scale = (1, 1, 1)
double3 xformOp:translate = (0, 0, 0)
uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
def Mesh "Wheel"
{
float3[] extent = [(-50, -50, -50), (50, 50, 50)]
int[] faceVertexCounts = [3, 3, ...a lot of values...]
int[] faceVertexIndices = [0, 1, 2, 0,....]
normal3f[] normals = [(0, -50, 0),...](
interpolation = "faceVarying"
)
point3f[] points = [(0, -50, 0), ...]
texCoord2f[] primvars:st = [(1, 0), (1, 0.0625), ...]
(
interpolation = "faceVarying"
)
uniform token subdivisionScheme = "none"
double3 xformOp:rotateXYZ = (0, 0, 0)
double3 xformOp:scale = (1, 1, 1)
double3 xformOp:translate = (46.61611128680149, -2.08943043408906, 63.14526081950993)
uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
}
def Mesh "Car_Chassis"
{
float3[] extent = [(-50, -50, -50), (50, 50, 50)]
int[] faceVertexCounts = [4, 4, ...]
int[] faceVertexIndices = [0, 1, 3, ...] (
interpolation = "faceVarying"
)
point3f[] points = [(-50, -50, 50), ...]
texCoord2f[] primvars:st = [(0, 0), ...] (
interpolation = "faceVarying"
)
uniform token subdivisionScheme = "none"
double3 xformOp:rotateXYZ = (0, 0, 0)
double3 xformOp:scale = (1, 1, 3.1101341016510786)
double3 xformOp:translate = (0, 50, 0)
uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
}
}
}
Prims can have types and much more too:
def Mesh "Car_Chassis" (apiSchemas = ["GiveThisPrimPropertiesToBehaveAsAMetalPiece"])
#1 #2 #3 #4
specifier
(whether this is a prim definition, an OVERride to override properties of another prim, etc.)type
, this is the type of the prim (e.g. Mesh, Xform, DomeLight, etc..)name
- this is the name (part of the SdfPath so it cannot contain spaces) of the prim- prim
metadata
, API schemas, references, variantsets, etc. are specified here, together with user-defined metadata in a predefined dictionary here (i.e.customData
)
A mesh is a type of prim meant to store render-able data (points, normals, maybe UV coords, etc.), an xform prim stores a transform matrix that applies to its child prims (that can be the identity prim - in that case the xform prim name can be useful to group other prims and have a name that is meaningful to human readers/artists, much like the Car
xform prim that we used in the image above to group together the wheel and the car chassis), etc.
Properties
Properties are the key+value pairs that are contained in prims (e.g. normals
is a property in the prims above, or maybe a radius
can be a property of a sphere prim).
There are two types of properties:
-
Attributes:
normals
andradius
fall in this category: key-value pairs with well-defined values (e.g. afloat3
or adouble
). Attributes can be time-sampled, i.e. they can vary over time (so USD supports the concept of animations as well).def Sphere "Sphere" { float3[] extent = [(-30, -30, -30), (30, 30, 30)] # an attribute (not time-sampled) double radius = 50 # another attribute double radius.timeSamples = { # a time-sampled attribute 1: 1, # set the sphere with a radius of 1 at frame time 1 50: 50, # and with a radius of 50 at frame time 50 } # the above values will be linearly interpolated by default }
-
Relationships: the classical example here is a material binding where a prim has a relationship property (currently not time sampled)
def Xform "World" { def Mesh "Cube" ( prepend apiSchemas = ["MaterialBindingAPI"] ) { float3[] extent = [(-50, -50, -50), (50, 50, 50)] int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] int[] faceVertexIndices = [0, 1, 3, 2, 4, 6, 7, 5, 6, 2, 3, 7, 4, 5, 1, 0, 4, 0, 2, 6, 5, 7, 3, 1] rel material:binding = </World/Materials/SimpleRedSurface> ( # This is a relationship property! bindMaterialAs = "weakerThanDescendants" ) } def Scope "Materials" { def Material "SimpleRedSurface" { token outputs:displacement.connect = </World/Materials/SimpleRedSurface/Shader.outputs:displacement> token outputs:surface.connect = </World/Materials/SimpleRedSurface/Shader.outputs:surface> def Shader "Shader" { uniform token info:id = "UsdPreviewSurface" color3f inputs:diffuseColor = (1, 0, 0) ( # Red color customData = { float3 default = (0.18, 0.18, 0.18) } hidden = false renderType = "color" ) } } } }
The
material:binding
relationship is defined in theUsdShadeMaterialBindingAPI
so it’s part of a schema as well for all those prims which are meant to have a material applied to them.
Sometimes “properties” and “attributes” are used somewhat interchangeably in USD code and documentations, but it’s important to know the difference (properties include relationships as well).
Prims can also have custom properties too (something that maybe holds meaning for a 3D modeling program) and those properties can (or can not) be part of a Schema
(e.g. a prim with a CollisionAPI
schema will have physical properties like contactOffset
or similar physics-specific ones). In this example we have a prim with a MyCustomSchemaAPI
applied (a schema that might be owned by our own Omniverse extension), a custom attribute and some custom metadata applied to the generated prim as well
def Mesh "Wheel" (
prepend apiSchemas = ["PhysicsRigidBodyAPI", ... , "MyCustomSchemaAPI"]
customData = {
string thisIsMyCustomOmniverseExtensionMetadata = "This great prim was created by MyExtension v1.0"
}
)
{
float3[] extent = [(-50, -50, -50), (50, 50, 50)]
int myCustomProperty = 22
...
}
Both prims and properties are identified by unique SdfPaths: examples for the above are
/World/Car/Wheel
/World/Car/Wheel.myCustomProperty
/World/Car/Wheel.material:binding
OV Composer has a handy property visualization pane which summarizes all properties of a prim and allows you to also copy the full SdfPaths by simply right-clicking on a visualized property