Siggraph Presentation

This guide will be officially introduced at Siggraph 2023 - Houdini Hive on Wednesday, 9. of August 2023 at 11:00 AM PST.

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

  1. Kinds In-A-Nutshell
  2. What should I use it for?
  3. Resources
  4. Overview
    1. Reading and Writing Kinds
    2. Kind Registry
    3. Kind IsA Checks/Travesal

TL;DR - Kinds In-A-Nutshell

Tip

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 like component. A typical use case would be "find me all fx 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 of model.

Pro Tip | Houdini Kind Icons

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?

Tip

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.

Important

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.

Important

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.

Important

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
"""