How to introduce a new usd schema that built in C++ dll into omniverse

I am new to the Omniverse, I have a new usd schema and generated dll/lib that directly using pxr schema generation pipeline. It is used it in my project. I didn’t export it as pyd file since not needed yet. But I don’t know how to introduce this new schema into Omniverse (so I can see the usd that containing new schema type displayed correctly with the new type instead of defaultPrim).
Do I have to export it as pyd to be picked up by Omniverse? Does regular C export to pyd will work if I do have to? or I have to use Omniverse Native Interface python binding(omni.bind) to export it?
This document mention the greet example, but I can’t find full source code even I download Kit, Create and Code. also I can’t find omni.bind that mentioned in the doc. Omniverse Native Interfaces in Extensions — Omniverse Kit 102.1.2+release.46667.2027b54e.teamcity documentation
Is there a full example of creating such new schema and being used (in C++) in Omniverse?
Thanks.

It should be possible to load custom USD schema into Omniverse. You need to create an extension, that will make sure that the USD schema is registered in USD. Also this extension needs to be loaded from start (you can check that box in Create->Extensions once you have it).
Here is a documentation for the USD schema usage in Omniverse. You can try to find the schema extensions within the create install directory, they all are starting with omni.usd.schema…

USD Schema extensions


USD libraries are part of omni.usd.libs extension and are loaded as one of the first extensions to ensure that USD dlls are available for other extensions.

USD schemas itself are each in individual extension that can be part of any repository. USD schema extension is loaded after omni.usd.libs and ideally before omni.usd.

Example of schema extension config.toml file:

… code:: python

[core]
reloadable = false
# Load at the start, load all schemas with order -100 (with order -1000 the USD libs are loaded)
order = -100

[package]
category = "Simulation"
keywords = ["physics", "usd"]

# pxr modules to load
[[python.module]]
name = "pxr.UsdPhysics"
        
# python loader module
[[python.module]]
name = "usd.physics.schema"

# pxr libraries to be preloaded
[[native.library]]
path = "bin/${lib_prefix}usdPhysics${lib_ext}"

Schema extension contains pxr::Schema, its plugin registry and config.toml definition file. Additionally it contains a loading module omni/schema/_schema_name that does have python init.py file containing the plugin registry code.

Example:

… code:: python

import os


from pxr import Plug

pluginsRoot = os.path.join(os.path.dirname(__file__), '../../../plugins')
physicsSchemaPath = pluginsRoot + '/UsdPhysics/resources'

Plug.Registry().RegisterPlugins(physicsSchemaPath)

So I have followed the omni.usd.schema.physics to set up the folder structure and .toml setup. I actually copy the entire folder and renamed the individual files and path) It is all loaded fine until I replace the actual dll to use my own built dll, then it gives me this error “Could not load dynamic library”. Strangely enough, I can register the exact dll plugin in maya 22 usd and plain commandline prompt using pxr plug registerPlugins and Load() ), That lead me to a conclusion that it might be because how I build dll (maybe linked to a library path missing?), So I copied the linker lib into bin folder with the dll, still no luck. I think I need an example how to build dll in omniverse. Do I need to set up some macro or linked to omniverse pxr version?

Yes, you will need to run the schema code gen against Omniverse pxr version. You can get it through CONNECT SAMPLE OMNIVERSE CONNECTOR. Though I am afraid I dont know the details how exactly run the schema gen with that version. Will try to ask our USD experts.

Once you have the nv_usd package from the Connect sample, you can set up environment variables in your terminal to add its paths per the instructions here – USD Tutorials — Universal Scene Description 21.11 documentation

Then you can run usdGenSchema per Pixar’s instructions at Generating New Schema Classes — Universal Scene Description 21.11 documentation and Universal Scene Description: Creating New Schema Classes with usdGenSchema (i.e., likely the same way you originally generated the dll/lib binaries for your schema.

The following in on Windows10, used python 3.6.8, omniverse download from Pixar Universal Scene Description (USD) | NVIDIA Developer, build on vs2019.
I generate usdMy.dll with very simple schema:
#usda 1.0
(
“This is the USD schema for the usdMy dll. It provides all of our custom types and APIs for USD.”
subLayers = [
@usd/schema.usda@
]
)

over “GLOBAL” (
customData = {
string libraryName = “usdSMS”
string libraryPath = “.”
}
)
{ }

class “myGuidAPI” (
inherits =
doc = “GUID API.”
customData = {
string className = “GuidAPI”
token apiSchemaType = “singleApply”
}
)
{
string guid (
customData = {
string apiName = “guid”
}
doc = “whatever”
)
}

the project builds usdMy.dll succesfully, then I run usdGenSchema.cmd(from omniverse package) to generate bunch of .h, .cpp, generatedSchema.usda and plugInfo.json,
To test the extension, I just create a folder in exts directly
I copy over the dll into \AppData\Local\ov\pkg\code-2022.1.0\kit\exts\omni.usd.schema.my\bin\usdMy.dll
I copy over all generated .h, .cpp, generatedSchema.usda and pluginfo.json into \AppData\Local\ov\pkg\code-2022.1.0\kit\exts\omni.usd.schema.sms\plugins\UsdSMS\resources, the original schema.usda, i move to the \usdMy\ inside resources to be clear
My pluginfo.json, I tweak the last bits to be the following (physics extension sample, don’t understand why it is not /bin/usdMy.dll instead, but from the result, it seems able to locate the right one)
“LibraryPath”: “…/…/…/…/lib/usdMy.dll”,
“Name”: “usdMy”,
“ResourcePath”: “resources”,
“Root”: “…”,
“Type”: “library”

My extension.toml, I don’t have pyd for my module, so just native
[core]
reloadable = true
order = -100
[package]
category = “Internal”
title = “USD My Test schema”
description=“USD My Test schema”
version = “1.0.0”
[dependencies]
“omni.usd” = {} # tried without this, still no luck
“omni.usd.libs” = {}

[[native.library]]
path = “bin/${lib_prefix}usdMy${lib_ext}”

my python __init__py that sit at \AppData\Local\ov\pkg\code-2022.1.0\kit\exts\omni.usd.schema.my\usd\schema\usdMy
import os
from pxr import Plug
pluginsRoot = os.path.join(os.path.dirname(file), ‘…/…/…/plugins’)
smsSchemaPath = pluginsRoot + ‘/UsdMy/resources’

without above all, I use the launcher → library, Code, Extensions tab, find my schema, load using the toggle button
I got the error message in console: 2022-01-25 18:03:32 [Error] [omni.ext.plugin] Could not load the dynamic library from /appdata/local/ov/pkg/code-2022.1.0/kit/exts/omni.usd.schema.my/bin/usdMy.dll. Error: The specified module could not be found.

some interesting detail: the following exact script:
import sys
sys.path.append(’/AppData/Local/ov/pkg/code-2022.1.0/kit/exts/omni.usd.schema.my/plugins/UsdMy/resources’)
from pxr import Plug
Plug.Registry().RegisterPlugins(’/AppData/Local/ov/pkg/code-2022.1.0/kit/exts/omni.usd.schema.my/plugins/UsdMy/resources’)
for plugin in Plug.Registry().GetAllPlugins():
print("{} {}".format(plugin.name, plugin.isLoaded))
print(Plug.Registry().GetPluginWithName(“usdMy”).Load())

in python36 commandline prompt, (outside of omniverse code):
It will print out usdMy.dll in all the registered plugin
It will print out True

But in Omniverse Script Editor:
It won’t print out usdMy.dll as registered first of all,
Then of course It is None
To avoid confusion, I used the absolute path in plugininfo.json for library and also absolute path in python Plug Register function

The USD binaries used in OV are not the binaries on usd.nvidia.com (the latter are built from the unmodified Pixar GitHub source). We are working to align OV’s USD packages with those of a standard Pixar distro, but don’t have a firm date on when that effort will be complete.

To get the nv_usd package used in OV, you can get the Connect Samples from Launcher. Building those samples will download the nv_usd artifacts, which you can use to compile your schema.

Hope that helps; sorry for the confusion.

Thank you very much for the reply. I will take a look of the Connect Sample, see what nv_usd artifacts will be downloaded and if I can use it

After I use everything (include usdGenSchema, include and libs) from nv_usd folder. the dll finally loaded! Thanks. I can see difference from a simple schema class I made between pxr vs ov. Thanks a lot,
There is a glitch of I put this schema in extension:
the schema extension will toggle on from Code->Extensions tab, will not be loaded (seem that what I put in the exts\omni.usd.schema.my\ folder the init.py won’t get called at all). The print GetAllPlugins() won’t have my dll either. but if in the python editor, I registerPlugins( with absolute path to the plugin.json). The module will be loaded. Is there some environment variable I need to set for omniverse?
Also, there is a console message, I am not sure if it is related " [Warning] [omni.ext.plugin] [ext: omni.usd-1.4.7] ‘/appdata/local/ov/pkg/code-2022.1.0/kit/plugins’ in ‘[[native.plugin]]’ was not found."

Yes, you need to toggle the autoload, if you just enable the extension the USD wont register the schema correctly as it was already initialized. If you set autoload and open Create again, it should load the extension and register your schema correctly.

Thanks for the reply. I am trying to understand how do I test if a custom schema really got picked up by omniverse. I used the schema that from pixar tutorial Generating New Schema Classes — Universal Scene Description 21.11 documentation, which contains simplePrim, complexPrim and paramsApi. my usd looks like the following:
#usda 1.0
(
defaultPrim = “MyFirstComplex”
metersPerUnit = 0.01
upAxis = “Y”
)

def ComplexPrim “MyFirstComplex”
{
string complexString = “a really complex string”
int intAttr = 10
add rel target =
}

def Xform “MyTestObject” (
prepend apiSchemas = [“ParamsAPI”]
)
{
custom double params:mass = 1.0;
custom double params:velocity = 10.0;
custom double params:volume = 4.0;
}

def Xform “Blahblah” (
prepend apiSchemas = [“UndefinedAPI”]
)
{
custom double params:what = 6.0;
}

When I open the usd, omniverse seems have no problem display the undefinedAPI type, (which I didn’t introduce in schema.usda. It does show ComplexPrim as Type for the MyFirstComplex. Does it mean custom schema successfully got picked up?