Kinds
The kind metadata is a special metadata entry on prims that can be written to mark prims with data what "hierarchy level type" it is. This way we can quickly traverse and select parts of the hierarchy that are of interest to us, without traversing into every child prim. The most common types are component
and group
. We use these (or sub-kinds) of these to make our stage more easily browse-able, so that we can visually/programmatically easily detect where assets start.
Table of Contents
TL;DR - Kinds In-A-Nutshell
The kind metadata is mainly used to for two things:
- Traversal: You can quickly detect (and stop traversal to children) where the assets are in your hierarchy by marking them as a a
model
subtype likecomponent
. A typical use case would be "find me allfx
assets": In your pipeline you would define a 'fx' model kind subtype and then you can traverse for all prims that have the 'fx' kind set. - DCCs use this to drive user selection in UIs. This way we can quickly select non-leaf prims that are of interest. For example to transform an asset in the hierarchy, we only want to select the asset root prim and not its children, so we can tell our DCC to select only
model
kind prims. This will limit the selection to all sub-kinds ofmodel
.
If you have created custom kinds, you can place icons (png/svg) in your $HOUDINI_PATH/config/Icons/SCENEGRAPH_kind_<name>.<ext>
folder and they will be shown in your scene graph tree panel.
What should I use it for?
In production, you'll use kinds as a way to filter prims when looking for data. As kind data is written in prim metadata, it is very fast to access and suited for high performance queries/traversals. For more info on traversals, take a look at the traversal section.
You should always tag all prims in the hierarchy with kinds at least to the asset level. Some Usd methods as well as DCCs will otherwise not traverse into the child hierarchy of a prim if they come across a prim without a kind being set.
So this means you should have at least group
kind metadata set for all parent prims of model
sub-kind prims.
Resources
Overview
Usd ships with these kinds by default, to register your own kinds, see the below examples for more details:
model
: The base kind for all model kinds, don't use it directly.group
: A group of model prims.assembly
: A collection of model prims, typically used for sets/a assembled collection of models or environments.
component
: A sub-kind of 'model' that has no other child prims of type 'model'
subcomponent
: An important subhierarchy of an component.
You should always tag all prims with kinds at least to the asset level in the hierarchy. Some DCCs will otherwise not traverse into the hierarchy if they come across a prim without a hierarchy.
So this means you should have group
kinds set for all parent prims of model
prims.
Reading and Writing Kinds
Kinds can be easily set via the high and low level APIs:
### High Level ###
from pxr import Kind, Sdf, Usd
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
model_API = Usd.ModelAPI(prim)
model_API.SetKind(Kind.Tokens.component)
# The prim class' IsModel/IsGroup method checks if a prim (and all its parents) are (sub-) kinds of model/group.
model_API.SetKind(Kind.Tokens.model)
kind = model_API.GetKind()
print(kind, (Kind.Registry.GetBaseKind(kind) or kind) == Kind.Tokens.model, prim.IsModel())
model_API.SetKind(Kind.Tokens.group)
kind = model_API.GetKind()
print(kind, (Kind.Registry.GetBaseKind(kind) or kind) == Kind.Tokens.group, prim.IsGroup())
### Low Level ###
from pxr import Kind, Sdf
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/bicycle")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.SetInfo("kind", Kind.Tokens.component)
An example Usd file could look likes this
def Xform "set" (
kind = "set"
)
{
def Xform "garage" (
kind = "group"
)
{
def Xform "bicycle" (
kind = "prop"
)
{
}
}
def Xform "yard" (
kind = "group"
)
{
def Xform "explosion" (
kind = "fx"
)
{
}
}
}
Creating own kinds
We can register kinds via the plugin system.
{
"Plugins":[
{
"Type": "python",
"Name": "usdSurvivalGuideKinds",
"Info":{
"Kinds":{
"character":{
"baseKind":"component",
"description":"A (hero) character"
},
"prop":{
"baseKind":"component",
"description":"A generic prop asset"
},
"fx":{
"baseKind":"component",
"description":"A FX asset"
},
"environment":{
"baseKind":"assembly",
"description":"A large scale environment like a city."
},
"set":{
"baseKind":"assembly",
"description":"A individual section of an environment, typically a movie set placed in an environment."
}
}
}
}
]
}
To register the above kinds, copy the contents into a file called plugInfo.json
. Then set your PXR_PLUGINPATH_NAME
environment variable to the folder containing the plugInfo.json
file.
For Linux this can be done for the active shell as follows:
export PXR_PLUGINPATH_NAME=/my/cool/plugin/resources:${PXR_PLUGINPATH_NAME}
If you downloaded this repo, we provide an example kind plugin here. All you need to do is point the environment variable there and launch a USD capable application.
Kind Registry
We can also check if a plugin with kind data was registered via Python.
from pxr import Kind
registry = Kind.Registry()
for kind in registry.GetAllKinds():
base_kind = Kind.Registry.GetBaseKind(kind)
print(f"{kind:<15} - Base Kind - {base_kind}")
# Returns:
"""
set - Base Kind - assembly
assembly - Base Kind - group
fx - Base Kind - component
environment - Base Kind - assembly
character - Base Kind - component
group - Base Kind - model
component - Base Kind - model
model - Base Kind
subcomponent - Base Kind
"""
print(registry.HasKind("fx")) # Returns: True
print(registry.IsA("fx", "model")) # Returns: True
Kind IsA Checks & Traversal
We can then also use our custom kinds for traversal checks.
Usd offers the prim.IsModel
and prim.IsGroup
checks as convenience methods to check if a kind is a sub-kind of the base kinds.
Make sure that your whole hierarchy has kinds defined (to the prim you want to search for), otherwise your prim.IsModel
and prim.IsGroup
checks will fail. This also affects how DCCs implement traversal: For example when using Houdini's LOPs selection rules with the %kind:component
syntax, the selection rule will not traverse into the prim children and will stop at the parent prim without a kind. Manually checking via registry.IsA(prim.GetKind(), "component")
still works though, as this does not include the parents in the check. (See examples below)
#ToDo Add icon
from pxr import Kind, Sdf, Usd
stage = Usd.Stage.CreateInMemory()
prim = stage.DefinePrim(Sdf.Path("/set"), "Xform")
Usd.ModelAPI(prim).SetKind("set")
prim = stage.DefinePrim(Sdf.Path("/set/garage"), "Xform")
Usd.ModelAPI(prim).SetKind("group")
prim = stage.DefinePrim(Sdf.Path("/set/garage/bicycle"), "Xform")
Usd.ModelAPI(prim).SetKind("prop")
prim = stage.DefinePrim(Sdf.Path("/set/yard"), "Xform")
Usd.ModelAPI(prim).SetKind("group")
prim = stage.DefinePrim(Sdf.Path("/set/yard/explosion"), "Xform")
Usd.ModelAPI(prim).SetKind("fx")
# Result:
print(stage.ExportToString())
"""
def Xform "set" (
kind = "set"
)
{
def Xform "garage" (
kind = "group"
)
{
def Xform "bicycle" (
kind = "prop"
)
{
}
}
def Xform "yard" (
kind = "group"
)
{
def Xform "explosion" (
kind = "fx"
)
{
}
}
}
"""
for prim in stage.Traverse():
print("{:<20} - IsModel: {} - IsGroup: {}".format(prim.GetPath().pathString, prim.IsModel(), prim.IsGroup()))
# Returns:
"""
/set - IsModel: True - IsGroup: True
/set/garage - IsModel: True - IsGroup: True
/set/garage/bicycle - IsModel: True - IsGroup: False
/set/yard - IsModel: True - IsGroup: True
/set/yard/explosion - IsModel: True - IsGroup: False
"""
registry = Kind.Registry()
for prim in stage.Traverse():
kind = Usd.ModelAPI(prim).GetKind()
print("{:<25} - {:<5} - {}".format(prim.GetPath().pathString, kind, registry.IsA("fx", "component")))
# Failed traversal because of missing kinds
stage = Usd.Stage.CreateInMemory()
prim = stage.DefinePrim(Sdf.Path("/set"), "Xform")
Usd.ModelAPI(prim).SetKind("set")
prim = stage.DefinePrim(Sdf.Path("/set/garage"), "Xform")
prim = stage.DefinePrim(Sdf.Path("/set/garage/bicycle"), "Xform")
Usd.ModelAPI(prim).SetKind("prop")
prim = stage.DefinePrim(Sdf.Path("/set/yard"), "Xform")
prim = stage.DefinePrim(Sdf.Path("/set/yard/explosion"), "Xform")
Usd.ModelAPI(prim).SetKind("fx")
registry = Kind.Registry()
for prim in stage.Traverse():
kind = Usd.ModelAPI(prim).GetKind()
print("{:<20} - Kind: {:10} - IsA('component') {}".format(prim.GetPath().pathString, kind, registry.IsA(kind, "component")))
print("{:<20} - IsModel: {} - IsGroup: {}".format(prim.GetPath().pathString, prim.IsModel(), prim.IsGroup()))
"""
/set - Kind: set - IsA('component') False
/set - IsModel: True - IsGroup: True
/set/garage - Kind: - IsA('component') False
/set/garage - IsModel: False - IsGroup: False
/set/garage/bicycle - Kind: prop - IsA('component') True
/set/garage/bicycle - IsModel: False - IsGroup: False
/set/yard - Kind: - IsA('component') False
/set/yard - IsModel: False - IsGroup: False
/set/yard/explosion - Kind: fx - IsA('component') True
/set/yard/explosion - IsModel: False - IsGroup: False
"""