There's 2 ways of extending USD for your pipeline
- Fork USD and add the features in that you want (and then PR it back so others can use it!)
- Use Plugins
Option #2 is way easier to do and easier to maintain. But how are you supposed to know which USD features can be changed by using plugins? There's no master-list of how USD uses their own Plugin Framework . And there's no way to query that information in code, either. The best you can do is query what plugins are discoverable (which we'll get to in a bit).
Rather than write cookbook projects for every USD plugin, it makes more sense to make a glossary for reference and explain how to find out more information.
With that said, this guide will teach:
- What plugins USD uses
- The source-code location to find out more about those plugins
- How to find plugins, yourself
USD has two main methods of querying plugins. USD either iterates over all plugins and searches for a specific key or it searches for plugins that inherit from a known USD type. We'll call these "By-Name" Plugins and "By-Type" Plugins, respectively.
Skip to How To Find Where To Look to find out how to search these definitions by yourself.
Here's a quick list of both styles that USD uses to query plugins.
- UsdGeomMetrics - Set Your Default Scene Up Axis
- UsdColorConfigFallbacks - Set Up Color Management
- UsdUtilsPipeline - UsdUtilsPipeline
- MaterialsScopeName - Set Up A Registered Material Prim
- PrimaryCameraName - Set Up A Default Camera Name
- RegisteredVariantSets - Set Up Variant Selection Export Policies
- DefaultMaterialsScopeName - Set Up A Registered Material Prim
- DefaultPrimaryCameraName - Set Up A Default Camera Name
- UsdVariantFallbacks - Set Up Variant Selection Fallbacks
- Kinds - Subclass Your Own Kind
- SdfMetadata - Extend Metadata
- SdfFileFormat - Register A File Format
- ArResolver - Adding A Custom Resolver
- ArPackageResolver - Adding a Package Resolver
- UsdImagingPrimAdapter - Pair A usdImaging Adapter Class With A Usd Prim Type
- ShaderResources - Add Shader Resources To Hydra
- UsdSchemaBase - Create A Custom Schema
- NdrParserPlugin - Encode Shaders As USD Prims
- GlfImage - OpenGL USD Types
If you're only interested in finding out what plugins are loaded, USD already has a convenience method for that. That said, if you're using a default build of USD, the methods won't return very interesting results. But in a studio pipeline this list is a great opportunity for "discovering" projects that you may not have been aware of.
from pxr import Plug
for plugin in Plug.Registry().GetAllPlugins():
print('Plugin: "{plugin.name}" is loaded: "{plugin.isLoaded}".'.format(plugin=plugin))
print('Path: "{plugin.path}".'.format(plugin=plugin))
Example Output on a default build of USD:
Plugin: "usd" is loaded: "False".
Path: "/usr/local/USD-19.07/lib/libusd.so".
Plugin: "usdGeom" is loaded: "False".
Path: "/usr/local/USD-19.07/lib/libusdGeom.so".
Plugin: "usdLux" is loaded: "False".
Path: "/usr/local/USD-19.07/lib/libusdLux.so".
Plugin: "usdHydra" is loaded: "False".
Path: "/usr/local/USD-19.07/lib/libusdHydra.so".
...
USD uses its own plugin system to register many of its own modules. So you'll always see its libraries mixed in with any proprietary ones that a studio has added.
This section goes over some simple plugins, what they do, and links to
learn more. If a section contains sample plugin text, assume that the
file needs to be named plugInfo.json
and its directory should be found
in the PXR_PLUGINPATH_NAME environment variable.
If you don't know what this means, check out The Variant Selection Fallback Plugin or The Plugin Metadata Plugin or The Custom Resolver Plugin projects to find out more information. Also, there's the USD PluginRegistry documentation
Summary: Set a value for the up axis for USD scenes that don't have an authored up-axis value
Key: UsdGeomMetrics
Related Links:
Source Code Link: pxr/usd/lib/usdGeom/metrics.cpp
Plugin Sample Text:
plugInfo.json
{
"Plugins": [
{
"Type": "resource",
"Name": "scene_up_plugin",
"Info": {
"UsdGeomMetrics": {
"upAxis": "Z"
}
}
}
]
}
Relevant Commands:
SdfSchema::GetInstance().GetFallback(UsdGeomTokens->upAxis)
TfToken UsdGeomGetFallbackUpAxis();
from pxr import Usd, UsdGeom
UsdGeom.GetFallbackUpAxis()
stage = Usd.Stage.CreateInMemory()
print(UsdGeom.GetStageUpAxis(stage)) # Prints "Y" normally but will print "Z" if the plugin is installed
Summary: A way to store and interpret per-attribute color-space values
Key: UsdColorConfigFallbacks
Description: USD lets you set colorspace information in layers and attribute as metadata. If there's nothing authored, the color configuration fallback plugin is queried, instead. This is very useful because it lets different projects use different configurations without changing any USD files.
Source Code Link: pxr/usd/lib/stage.cpp
Related Links:
- Color Configuration API
- Warning: The example plugInfo.json in the documentation has a syntax error ("colorConfiguration" = "https://github.com/imageworks/OpenColorIO-Configs/blob/master/aces_1.0.1/config.ocio"). Refer to the sample text below, instead:
Plugin Sample Text:
plugInfo.json
{
"Plugins": [
{
"Type": "resource",
"Name": "color_management_definition",
"Info": {
"UsdColorConfigFallbacks": {
"colorConfiguration": "https://github.com/imageworks/OpenColorIO-Configs/blob/master/aces_1.0.1/config.ocio",
"colorManagementSystem": "OpenColorIO"
}
}
}
]
}
Defining Values in USDA, Explicitly:
Looks.usda (This code can be used to explicitly set color management settings, per-layer)
#usda 1.0
(
colorManagementSystem = "ocio"
customLayerData = {
string colorSpace = "acescg"
}
)
Looks.usda (This code can be used to explicitly set color management settings, per-attribute)
color3f inputs:otherColor6 = (0.1, 0.1, 0.1) (
colorSpace = "lin_rec709"
)
Relevant Commands:
SdfAssetPath UsdStage::GetColorConfiguration() const;
TfToken UsdStage::GetColorManagementSystem() const;
UsdStage::SetColorConfiguration(const SdfAssetPath &colorConfig) const;
void UsdStage::SetColorManagementSystem(const TfToken &cms) const;
Usd.Stage.GetColorConfiguration()
Usd.Stage.GetColorManagementSystem()
Usd.Stage.SetColorConfiguration()
Usd.Stage.SetColorManagementSystem()
The UsdUtilsPipeline
plugin key is a plugin that configures some default
USD stage settings.
Summary: Store the name of the "primary/preferred camera" of your USD stage.
Description: If you define a PrimaryCameraName in UsdUtilsPipeline, that camera name is used. Otherwise, USD falls back to the site-defined DefaultPrimaryCameraName.
Default: If no primary camera name is given using this plugin, USD falls back to "main_cam".
Key:
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
{
"Plugins": [
{
"Name": "Default Camera Name",
"Type": "resource",
"Info": {
"UsdUtilsPipeline": {
"PrimaryCameraName": "SomeCameraName"
},
"DefaultPrimaryCameraName": "SomeFallbackName"
}
}
]
}
Relevant Commands:
TfToken UsdUtilsGetPrimaryCameraName(const bool forceDefault);
UsdUtils.GetPrimaryCameraName()
Summary: Let USD know that Prims given a certain name have materials as its child Prims.
Description: From the USD documentation: "The name of the USD prim under which materials are expected to be authored"
Key:
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
{
"Plugins": [
{
"Name": "Default Material Prim Container",
"Type": "resource",
"Info": {
"UsdUtilsPipeline": {
"MaterialsScopeName": "SomePrimName"
},
"DefaultMaterialsScopeName": "SomeFallbackPrimName"
}
}
]
}
Relevant Commands:
TfToken UsdUtilsGetMaterialsScopeName(const bool forceDefault);
UsdUtils.GetMaterialsScopeName()
There's already an example project for this plugin, located at concepts/variant_fallbacks. But, for completion, let's also summarize the information here, too.
Summary: If a VariantSet has no selection, this plugin can control which variant gets selected, if needed, by-default.
Key: UsdVariantFallbacks
Related Links:
Source Code Link:
Plugin Sample Text:
{
"Plugins": [
{
"Name": "Variant Set Fallbacks",
"Type": "resource",
"Info": {
"UsdVariantFallbacks": {
"some_variant_set_name": ["possible_fallback_1", "possible_fallback_2", "possible_fallback_3", "foo", "bar"]
}
}
}
]
}
Relevant Commands:
static PcpVariantFallbackMap GetGlobalVariantFallbacks();
static void SetGlobalVariantFallbacks(const PcpVariantFallbackMap &fallbacks);
Usd.Stage.GetGlobalVariantFallbacks()
# Set variant fallbacks explicitly (this will override any plugInfo.json fallbacks)
Usd.Stage.SetGlobalVariantFallbacks({"some_variant_set_name": ["foo", "bar"]}) # Must be done before creating a stage
There's already an example project for this plugin, located at guides/registered_variant_selection_export_policies. But, for completion, let's also summarize the information here, too.
Summary: Decide which USD VariantSet is allowed to be written to disk and under what conditions
Description: This plugin doesn't appear to do anything at the API level. It seems to be more like a reference for tools to use on-export to decide how to deal with VariantSets. USD uses this plugin in a number of places, mainly in Katana and Maya's translator plugins.
Key: RegisteredVariantSets
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
{
"Plugins": [
{
"Name": "Variant Selection Export Policies",
"Type": "resource",
"Info": {
"UsdUtilsPipeline": {
"RegisteredVariantSets": {
"some_variant_set": {
"selectionExportPolicy": "never"
},
"standin": {
"selectionExportPolicy": "never"
}
}
}
}
}
]
}
Relevant Commands
const std::set<UsdUtilsRegisteredVariantSet>& UsdUtilsGetRegisteredVariantSets();
UsdUtils.GetRegisteredVariantSets()
Summary: Create a custom Kind by using one of USD's existing Kinds as a base class.
Key: Kinds
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
(This was copy/pasted from pxr/usd/lib/kind/testenv/testKindRegistry/lib/python/plugInfo.json)
{
"Plugins": [
{
"Type": "python",
"Name": "TestKindModule",
"Info": {
"Kinds": {
# Add "test_model_kind" as a kind of model.
"test_model_kind": {
"baseKind": "model"
},
# Add "test_root_kind" as a root kind.
"test_root_kind": {
}
}
}
}
]
}
Important: It's highly recommended to inherit from an existing Kind, rather than create a new root kind. USD traversals use predicate flags to quickly get models. Custom root kinds can still be traversed normally but it will be much slower. It's faster to inherit from a model Kind and use UsdPrimRange like normal. You can still make a root kind of course, it's just not recommended. Source conversation here.
Relevant Commands:
UsdPrimRange::Stage(const UsdStagePtr &stage, const Usd_PrimFlagsPredicate &predicate);
UsdPrimRange::UsdPrimRange(Usd_PrimDataConstPtr begin, Usd_PrimDataConstPtr end, const SdfPath& proxyPrimPath, const Usd_PrimFlagsPredicate &predicate = UsdPrimDefaultPredicate);
There's already an example project for this plugin, located at concepts/plugin_metadata. But, for completion, let's also summarize the information here, too.
Summary: Registry new metadata types so you can add your own custom metadata onto layers, attributes, prims, and more.
Key: SdfMetadata
Related Links:
- A very detailed description of how to extend metadata
- Object Model and How the Classes Work Together
Source Code Link:
Plugin Sample Text:
plugInfo.json
{
"Plugins": [
{
"Name": "Plugin double extension",
"Type": "resource",
"Info": {
"SdfMetadata": {
"another_metadata": {
"type": "double[]",
"appliesTo": "layers",
"default": [5.0, 13.0]
},
"my_custom_double": {
"type": "double",
"appliesTo": "prims",
"default": 12.0
}
}
}
}
]
}
Relevant Commands:
TfToken SdfSchemaBase::SpecDefinition::GetMetadataFieldDisplayGroup(const TfToken& name) const
TfTokenVector SdfSchemaBase::SpecDefinition::GetMetadataFields() const;
bool SdfSchemaBase::SpecDefinition::IsMetadataField(const TfToken& name) const;
const VtValue& SdfSchemaBase::GetFallback(const TfToken &fieldKey) const;
const VtValue& SdfSpec::GetFallbackForInfo( const TfToken & key ) const
std::vector<TfToken> SdfSpec::GetMetaDataInfoKeys() const;
TfToken GetMetaDataDisplayGroup(TfToken const &key) const;
Sdf.Spec.GetFallbackForInfo()
Sdf.Spec.GetMetadataInfoKeys()
Sdf.Spec.GetMetadataDisplayGroup()
Summary: Add a file format so that it can be natively converted to and from USD.
Key: SdfFileFormat
Description: This plugin takes a dictionary with a number of allowed keys:
- bases - The registered File Format name that this plugin inherits. It must inherit from a type that inherits from SdfFileFormat or it must inherit SdfFileFormat, directly.
- extensions - list of strings. It's the filetype extensions that can be processed by this formatter
- formatId - string - A unique ID that is used to find this file format plugin. Usually this value matches the extension of the file format. But it's not a requirement.
- primary - If this file format plugin should be the preferred plugin for its extensions
- target - To be honest, I'm not really sure what this is for. Maybe it's the file format that this registered file format is meant to be converted into? All of the examples online that I see convert to either "usd" or "sdf", which seems to support that idea.
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
(This was copied from USD's usd module)
{
"Plugins": [
{
"Info": {
"Types": {
# ...
"UsdUsdcFileFormat": {
"bases": [
"SdfFileFormat"
],
"displayName": "USD Crate File Format",
"extensions": [
"usdc"
],
"formatId": "usdc",
"primary": true,
"target": "usd"
}
# ...
}
}
}
]
}
Relevant Commands:
Everything from the FileFormatRegistry class
This plugin has been covered by the custom_resolver project in this repository. But, for completion, let's also summarize the information here, too.
Summary: A resolver plugin is used to convert any string to a path on-disk. For example, USD Asset paths may be a totally arbitrary string syntax, such as: "[foo][bar[bazz]??usda|v=001]". A custom resolver's job is to convert that string into a path on-disk like "/tmp/foo_folder/bar_bazz_v001.usda".
Key: ArResolver (any plugin that inherits this class)
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
(Copied file from the generated results of this custom resolver project)
{
"Plugins": [
{
"Info": {
"Types": {
"URIResolver" : {
"bases": ["ArResolver"]
}
}
},
"LibraryPath": "../libURIResolver.so",
"Name": "URIResolver",
"Type": "library"
}
]
}
Relevant Commands:
void ArSetPreferredResolver(const std::string& resolverTypeName);
ArResolver& ArGetResolver();
std::vector<TfType> ArGetAvailableResolvers();
Ar.SetPreferredResolver()
Ar.GetResolver()
Summary: Like "Adding A Custom Resolver" but for USD package resolvers.
Description: There's not that much documentation about package resolvers. That said, USD package resolvers are similar in concept to an asset resolver but with the following differences:
- You aren't supposed to instantiate package resolvers yourself. USD does this internally, for you. Always prefer ArGetResolver
- The ArResolver class converts a logical path (a string or URI) into a physical path.
- The ArPackageResolver class specifically handles "package" file formats like files with ".pack" extension. It is responsible for [resolving information about assets stored within that package](https://graphics.pixar.com/usd/docs/api/class_ar_package_resolv er.html)
- In other words, ArPackageResolver resolves a subset of a file on disk (an asset within a package file) and ArResolver resolves a whole file, instead.
So the question is, how do you define a package resolver? You do it the same way as an asset resolver, just with a different key.
Key: ArPackageResolver
Related Links:
- A required macro to registry a package resolver, AR_DEFINE_PACKAGE_RESOLVER
- ArPackageResolver - Read the Detailed Description Section
- ArResolver - Read the Detailed Description Section
Source Code Link:
Plugin Sample Text:
plugInfo.json
(Copied from The ArPackageResolver documentation)
{
"Plugins": [
{
"Info": {
"Types" : {
"CustomPackageResolver" : {
"bases": [ "ArPackageResolver" ],
"extensions": [ "pack" ]
}
}
},
...
},
...
]
}
Summary: Add a usdImaging Adapter Type for a given USD Prim type.
Description: This plugin has 3 main keys:
- bases - list of strings - The USD adapter class that this adapter is based off of.
- isInternal - bool - External adapters can be turned on and off
depending on if the
USDIMAGING_ENABLE_PLUGINS
environment variable is enabled. This environment variable is mainly used for debugging purposes. Internal adapters stay on. - primTypeName - string - The USD Prim type that the adapter is for
Key: UsdImagingPrimAdapter (must be derived from this class)
Related Links:
Source Code Link:
Plugin Sample Text
plugInfo.json
(Copied from pxr/usdImaging/lib/usdVolImaging/plugInfo.json)
"UsdImagingOpenVDBAssetAdapter": {
"bases": [
"UsdImagingFieldAdapter"
],
"isInternal": true,
"primTypeName": "OpenVDBAsset"
},
"UsdImagingField3DAssetAdapter": {
"bases": [
"UsdImagingFieldAdapter"
],
"isInternal": true,
"primTypeName": "Field3DAsset"
}
Relevant Commands:
Everything in the UsdImagingAdapterRegistry class
Summary: A way to add additional shader definitions to Hydra.
Source Code Link:
Plugin Sample Text:
plugInfo.json
(Copied from pxr/imaging/lib/hdx/plugInfo.json)
{
"Plugins": [
{
"Info": {
"ShaderResources": "shaders"
},
"LibraryPath": "@PLUG_INFO_LIBRARY_PATH@",
"Name": "hdSt",
"ResourcePath": "@PLUG_INFO_RESOURCE_PATH@",
"Root": "@PLUG_INFO_ROOT@",
"Type": "library"
}
]
}
Relevant Commands:
Anything in the ShaderResourceRegistry class
Summary: Define a USD type to use in your C++ / Python projects. This system is the same as the one that USD itself uses for most of its class types.
Key: UsdSchemaBase
Related Links:
- UsdSchemaBase
- UsdSchemaRegistry
- API Schema
- IsA Schema
- UsdGeomComputeExtentFunction
- UsdGeomRegisterComputeExtentFunction. A function that must be defined to compute extents of USD types
- Working With Schema Classes
Source Code Link:
Plugin Sample Text:
{
"Plugins": [
{
"Info": {
"Types": {
# ...
"UsdGeomCube": {
"alias": {
"UsdSchemaBase": "Cube"
},
"autoGenerated": true,
"bases": [
"UsdGeomGprim"
],
"implementsComputeExtent": true
}
# ...
}
}
}
]
}
Relevant Commands:
Everything in the UsdSchemaRegistry class
The USD documentation is fairly extensive about this plugin so, instead of re-iterating its points, links will be provided, below:
- UsdShade Based Shader Definition
- NdfParserPlugin
- UsdShadeShaderDefUtils::GetNodeDiscoveryResults
- UsdShadeShaderDefParserPlugin
Summary: To be honest, I'm not read up enough on OpenGL and how it is consumed by USD to discuss this particular plugin. Maybe this section can be filled in once someone with experience sees this.
Judging from the plugInfo.json
file, it looks like this plugin is
used to define classes to consume texture file types for viewport
rendering. It has some kind of a ranked ordering, using "precedence" as
the ordering key. But this is all just a guess.
Key: GlfImage
Related Links:
Source Code Link:
Plugin Sample Text:
plugInfo.json
(Copied from plugInfo.json)
{
"Plugins": [
{
"Info": {
"ShaderResources": "shaders",
"Types": {
"Glf_OIIOImage" : {
"bases": ["GlfImage"],
"imageTypes": ["bmp", "exr", "jpg", "png", "tif", "zfile", "tx"],
"precedence": 0
},
"Glf_StbImage" : {
"bases": ["GlfImage"],
"imageTypes": ["bmp", "jpg", "png", "tga", "hdr"],
"precedence": 2
},
"GlfPtexTexture" : {
"bases": ["GlfTexture"],
"textureTypes": ["ptx", "ptex"],
"precedence": 1
},
"GlfUVTexture" : {
"bases": ["GlfBaseTexture"],
"textureTypes": ["*"],
"precedence": 0
}
}
},
"LibraryPath": "@PLUG_INFO_LIBRARY_PATH@",
"Name": "glf",
"ResourcePath": "@PLUG_INFO_RESOURCE_PATH@",
"Root": "@PLUG_INFO_ROOT@",
"Type": "library"
}
]
}
Like mentioned in past sections, there's no one way of finding out how USD uses its own plugins. At best, you can query what plugins are currently being used and work backwards from there.
That said, it's not all hopeless. Here's a few methods for searching that were used to write this article
USD's plugin system relies on these files so it makes sense that we can find pretty much any plugin under the sun just by searching for those files
Linux:
git clone https://github.com/PixarAnimationStudios/USD && cd USD
find -type f -name "plugInfo.json"
Plugins are typically "found" in USD by either a special key under "Info", like "UsdUtilsPipeline"
{
"Plugins": [
{
"Name": "Default Camera Name",
"Type": "resource",
"Info": {
"UsdUtilsPipeline": {
# ...
}
}
}
]
}
or USD will search for a specific type, using the "bases" key of a plugin, like this
{
"Plugins": [
{
"Info": {
"Types": {
"URIResolver" : {
"bases": ["ArResolver"]
}
}
}
}
]
}
In both cases, once you see a plugInfo.json, you can read the keys
under "Info" or search for PluginRegistry for methods that require
ArResolver
and you're pretty likely to find exactly where that
plugInfo.json
is meant to be used.
Searching C++ code for phrases like GetAllPlugins
,
GetAllDerivedTypes
, and really any public
member function in the PlugRegistry class
will find many plugins, right away.
The only thing to keep in mind with this search method is that there are multiple registries in USD. There's:
And possibly others that just aren't listed here yet.
So keep that in mind while searching. If you search by-registry and can't find what you're looking for, consider the idea that you may need to be looking for a different registry.
There's a USD repository called
parallax/ar-export
that has a list of many plugInfo.json
file examples that is a decent
reference. There's also repositories from VFX companies, like Luma and
RodeoFX. Many of those repositories will have example plugin uses.