Inherits and Specializes
OpenUSD offers two very similar ways to derive from a prim. Let's review how they work and why you might use them.
The two most mysterious OpenUSD composition arcs are inherits
and specializes
. They both offer a pretty similar feature. You can have a prim that inherits or specializes a class
or another prim. You'd use this when you have a category of items and you want to be able to set values on the whole category.
Suppose you were making a movie and it has a cast of ants. There are a lot of ants to model, rig, etc. and you need to get started, but the look of the movie and the story is still not totally final (anyone from the film industry will be nodding their heads here; the look and story is never final until it's in theaters).
So, to anticipate artistic and story changes, you want to make your ants flexible. You can make each ant model inherit a base Ant class. In the Ant class you can set things like eye color, exoskeleton color, other shading parameters to hit the movie's look. Then later, when someone decides that all the ants' eyes will be brown, you can change them all in one place. If they also decide the main characters ants' eyes should be blue, you can override that class value for just those models.
That's the motivating idea behind these two composition arcs. I was too young to work at Pixar during the mid nineties, so my ant production story is not first hand. I heard this story when I was first trying to understand inherits
. Like me, OpenUSD is too young to have been at Pixar for A Bug's Life, but it sounds like the idea of inherits
already existed and was in use, getting the bugs worked out ;)
What's a Class?
The class
specifier doesn't come up often, so let's quickly get up to speed on what it is. In text form the class
specifier appears where you otherwise might see a def
or an over
.
A class is a type of prim that doesn't normally show up when you list USD prims, so it's called an "abstract prim". It's usually used for inherits
. It offers a way to have a prim that's invisible in most contexts, it doesn't draw, and its name doesn't show up in most editors by default.
class "Ant"
{
color3f eyecolor = (0.53, 0.31, 0.16)
}
Both inherits
and specializes
can refer to abstract prims like classes, but they can also refer to normal prims ("concrete prims" as opposed to "abstract prims"). In common practice inherits
usually refer to classes and specializes
usually refer to concrete prims.
Inherits Example
Seeing inherits
in action can be a good way to understand it, so I'll make some dumb little examples setting colors. The full files in text form can be seen on github.
We'll make a simple class called ColorClass first. The class can be as simple as this. I'll put a version in github that demonstrates this, but won't dig in too much here.
A primvar is a kind of variable that affects geometry in USD. In this case we'll use one with a single constant magenta color value so we can read it in a UsdPreviewSurface material. There are easier ways to set a color, but I want to use a material so the example is a little more general, you could use this approach for all kinds of material settings.
We need to define a material that uses theClassColor
. We could do that in each model that derives from ColorClass
, or we could define a material in a common layer that's shared by each model. To keep these examples clean and simple, I'm going to add the material to the class
itself. This makes the class more complicated, but that way anything that inherits this class will also get this material already bound. The material details aren't that important to understand, but here it is anyway.
Now let's make two models that inherit from Color. A cube called Box
and a sphere called Ball
. The Box looks like this, the Ball is similar.
Notice that we inherit from ColorClass
in this file but we don't reference it or get it in any other way. We're declaring that Box
inherits from this class but the class itself is not defined here. If you open the box.usda
file you will get an unshaded cube. We need to look at Box
in a context where the ColorClass is defined for the class to do anything.
Now we'll make a demo_set.usda
with a few layers. We'll get the class as a sublayer so it is defined, make a grid of Box and Ball references in a sublayer, and have one stronger layer for demo purposes. The files look like this.
If you look at this in a USD editor like usdview
you'll see that each Item_M_N
has the magenta class color and its mesh and material inside it, combining the referenced prim and the class. Now in any layer we choose we can set opinions on the class, and those will affect all the instances of both models. If I set theClassColor
in stronger_layer.usda
it will turn everything green.
In addition to setting opinions on the class, we can set them on individual items and override the class opinion.
Specializes
Specializes are often used differently than inherits, but they are just like inherits with one important distinction. If we keep the examples above but change the inherits
to specializes
everything is the same. You get all the same output.
There is no difference unless the theClassColor
value is changed across a reference. So, if we change the model definition in box.usda
to make boxes have a cyan color instead of the class default magenta, we can see the difference between an inherits and a specializes arc.
If we use inherits = </ColorClass>
we'll get this image in the demo_set. Notice that it's the same image we had when the Box didn't set cyan as its default color. In this case setting theClassColor
to green overrode Box's cyan setting.
If we change the inherits
to specializes = </ColorClass>
in box.usda
and ball.usda
we'll get this image in the demo_set.
When the model is loaded over a reference, a specialized model keeps its opinions, sometimes called specializations. So the Box models stay cyan even when the class is set to green. As we saw in the previous example, if these were inherits
the model could still be overridden by class opinions.
Specializing a prim is basically saying, "I want the values from the class (or prim) for everything except those things I set explicitly in my model."
You can still set each specialized Box's color by setting it on the referenced Item_M_N
. There's no way to change all the specialized boxes at once though, unless we change or remove the theClassColor
override in the model.
Something a little less theoretical
I mentioned earlier that specializes are used differently from inherits. Specializes were added to USD to address a specific need related to materials.
A classic example is a base Aluminum
material and a BrushedAluminum
derived material. If the Aluminum
base material changes we only want that to show up in BrushedAluminum
if BrushedAluminum
hasn't changed those values. That way an artist working on the "look" on a stronger layer can make changes to all kinds of Aluminum
in one go without worrying about breaking derived materials.
With inherits
the stronger Aluminum
opinions would win if the material library was loaded from a reference. With specializes
the specialized BrushedAluminum
opinions win over Aluminum
, even if the Aluminum
opinions are stronger.
Pixar wrote up a nice example of this, and it shows how specializes
is often used in actual practice.
You could use specializes
for anything where it fits your needs, it isn't just for materials. I just wanted to mention the most common usage.
Practical Considerations
Chances are your editing tool doesn't offer inherits
or specializes
workflows. Can you use this at all?
Anything that reads USD files will get correct inherits
and specializes
support even if it doesn't know what those are. So, if you create your content with these composition arcs you can at least read them in your renderer, game engine, editing tool. Creating them in the first place, and authoring values on them, is the sticky part.
SideFX Houdini and NVIDIA Omniverse are the only public tools I'm aware of that do allow editing inherits and specializes, at least as of July 2024.
If you're able to write scripts to create/edit it can be done that way, possibly as part of an asset build script or pipeline. It's not too tough to take the USD files output from your editor and sublayer them in a Model.usd file that doesn't change them, but adds inherits, specializes, or other things you'd like.
Outside of that though, I think a non-technical user in most USD capable editors probably can't use these features. I'm sorry if that's you and you read this far 😅 I hope there's some consolation in at least understanding how it works and what it takes to use.
Notes and Details
- You might see classes named like
_class_myclass
. The leading underscores and class are not necessary, just a common convention. It can make it easier to tell what's a class just by looking at text names. - To avoid weird recursive definitions a prim can't have an
inherits
orspecializes
from a prim that's one of its own parents or children. For this reasonclass
prims are often put at the root scope, with no parents. - I have heard the argument that USD should get rid of
specializes
and makeinherits
work the wayspecializes
does now. Basically, specializes is how inheritance should always work, and the inherits version is not useful enough to justify keeping. I imagine making that change would break too much existing content, but I think it's interesting to think about.