Physics Schemas#
Physics behavior in a USD scene is described by schemas: typed sets of
attributes and relationships applied to prims. ovstage populates scenes authored
with these schemas into an ovstage.Stage; ovphysx attaches that stage and
simulates it. This page explains the schema layers ovphysx understands and how
to make the PhysX-specific ones available, both to ovstage when it populates a
scene and to a stock usd-core when you author or validate offline.
Schema Layers#
USD Physics Schema (
UsdPhysics) — the standard OpenUSD schema for annotating assets with physics:PhysicsScene,PhysicsRigidBodyAPI,PhysicsCollisionAPI,PhysicsMassAPI,PhysicsMaterialAPI, the joint types, and articulation APIs. It ships with stockusd-coreand has typed Python bindings, for exampleUsdPhysics.RigidBodyAPI.Apply(prim). Refer to the OpenUSD UsdPhysics reference.PhysX Schema (
Physx*, codeless) — extendsUsdPhysicswith PhysX-specific functionality and tuning:PhysxSceneAPI(solver, GPU settings),PhysxRigidBodyAPI(CCD, sleep, stabilization),PhysxCollisionAPI(contact and rest offsets),PhysxForceAPI, joint extensions such asPhysxJointAxisAPI(refer to Joints), deformable extensions (refer to Deformables), and more. ovphysx ships this schema as codeless artifacts (plugInfo.jsonplusgeneratedSchema.usda, no compiled library).Omni Physics Deformable Schema (codeless) — extends
UsdPhysicswith deformable bodies (OmniPhysicsDeformableBodyAPIand friends). Refer to Deformables.Newton Schema (
newton:*, not shipped) — the Newton USD schema, a subset of whose attributes ovphysx reads as fallbacks for the PhysX spellings. Install and register it yourself; refer to Registering the Newton USD schema.
The complete attribute set for each schema — types, defaults, and allowed values — is authoritative in the schema definitions themselves and is rendered in the Omni Physics documentation and the OpenUSD UsdPhysics reference. This guide describes intent, not full attribute definitions, so the two do not drift.
How ovphysx Ships the PhysX Schemas#
ovphysx exposes the PhysX USD schemas (PhysxSchema and
OmniUsdPhysicsDeformableSchema) as codeless artifacts: a root
plugInfo.json (with Includes: ["*/resources/"]) plus, per module, a
<Module>/resources/ directory holding a plugInfo.json (Type: resource) and
a generatedSchema.usda, with no compiled library. They live under
schemas/physx/ in the SDK and under ovphysx/schemas/physx/ in the wheel.
Codeless schemas carry no typed helper class — there is no
PhysxSchema.PhysxRigidBodyAPI binding. You apply them by identifier and author
their attributes generically.
ovphysx never modifies the environment (PXR_PLUGINPATH_NAME or any other
plugin-path variable) and leaves registration to the application; it verifies at
attach_ovstage that the registration happened before the first population and
refuses the attach otherwise. It tells the application where the schemas are:
C:
ovphysx_get_codeless_schema_root(ovphysx_string_t* out_root)returns the schema root directory. The string is NUL-terminated, owned by ovphysx, and valid until the calling thread calls the function again. It returnsOVPHYSX_API_INVALID_ARGUMENTfor a NULL argument andOVPHYSX_API_ERROR(details inovphysx_get_last_error()) when the schema tree is missing. It does not initialize ovphysx, load USD, acquire Carbonite, or modify the environment, and is safe to call beforeovphysx_create_instance().Python:
ovphysx.codeless_schema_root()returns the same root as apathlib.Path, andovphysx.codeless_schema_paths()returns the per-moduleresourcesdirectories. Both are pure Python and never trigger native loading.
Because they are codeless, the core UsdPhysics typed APIs (rigid body,
collider, mass, scene) work out of the box with stock usd-core, while
Physx* attributes require registering the codeless schemas first.
Making Schemas Available#
The application owns the USD runtime(s) in its process and registers the codeless schemas with each of them. There are two registration paths, depending on which USD runtime is in play.
Registering with ovstage#
ovstage ingests USD scenes through its own internal namespaced OpenUSD runtime,
and that runtime knows nothing about the PhysX schemas until the application
registers them. Register them once per process, before the first population call
(open_usd, apply_usd_changes, or an export), by passing the schema root to
ovstage.population.register_usd_schemas():
import ovphysx
import ovstage
ovstage.population.register_usd_schemas([str(ovphysx.codeless_schema_root())])
stage = ovstage.Stage("scene")
In C, obtain the root with ovphysx_get_codeless_schema_root() and pass it to
ovstage_population_register_usd_schemas():
#include <ovphysx/ovphysx.h>
#include <ovstage/ovstage.h>
#include <ovstage/ovstage_population.h>
static int register_physx_schemas(void)
{
ovphysx_string_t root;
if (ovphysx_get_codeless_schema_root(&root).status != OVPHYSX_API_SUCCESS)
{
return 0;
}
ovx_string_t path;
path.ptr = root.ptr;
path.length = root.length;
return ovstage_population_register_usd_schemas(&path, 1) == OVSTAGE_OK;
}
The bundled samples do exactly this: the Python samples call
register_usd_schemas() in their attach_scene helper right before creating
the ovstage.Stage, and the C samples call
ovphysx_sample_register_physx_schemas() from
tests/c_samples/common/ovstage_sample.h.
The ordering is not advisory. USD assembles its schema registry once, on first
read, and ignores plugins registered afterwards; a registration that arrives
after ovstage’s first schema read registers cleanly but contributes nothing,
and ovstage reports it as an error (OVSTAGE_ERROR_OP_FAILED, an
ovstage.OvstageError in Python). A registration that arrives after some other
USD consumer in the process read the schemas cannot be detected and fails
silently. No re-registration can repair the registry for the rest of the
process. Without the registration, population resolves only the properties
authored in the file rather than each prim’s full schema-declared property set.
Registration is process-scoped and irreversible; registering the same root
twice is a no-op.
Registering the Newton USD schema#
ovphysx also reads a subset of the
Newton USD schema
(NewtonJointAPI, NewtonArticulationRootAPI, NewtonSceneAPI, …) as
fallbacks for the PhysX spellings. newton:velocityLimit, for example, maps onto
physxJoint:maxJointVelocity: an authored PhysX limit below FLT_MAX wins, then
an authored Newton value, then the PhysX default. An explicitly unlimited PhysX
value (inf or FLT_MAX) counts as no PhysX opinion and yields to an authored
Newton limit; the per-attribute contract is in
PhysxJointAPI. ovphysx does not ship this schema. It is a
separate package that the application installs and registers, in the same call
and under the same ordering rule as the PhysX schemas:
pip install newton-usd-schemas
import ovphysx
import ovstage
ovstage.population.register_usd_schemas(
[str(ovphysx.codeless_schema_root()), str(ovphysx.newton_schema_root())]
)
The bundled Python samples register it this way; their project file declares
newton-usd-schemas, so uv run resolves it, and a plain pip install ovphysx
environment needs pip install newton-usd-schemas before running them.
ovphysx.newton_schema_root() returns the directory of the installed
newton-usd-schemas package without importing it (importing it requires pxr
and registers the schema with that stock USD runtime instead), and raises
FileNotFoundError with the install hint when the package is missing. Without
the pip package, download the schema from its
GitHub repository and pass
the directory holding its plugInfo.json to register_usd_schemas() (in C,
ovstage_population_register_usd_schemas()) yourself.
ovstage populates an attribute only when a registered schema defines it, so a
scene that authors newton:* attributes without this registration loses them
silently: a joint authoring only newton:velocityLimit simulates unlimited.
PhysX.attach_ovstage() therefore checks, once per process and before the
native attach, that the installed newton-usd-schemas package was registered
before the first population and emits a RuntimeWarning otherwise. When the
package is not installed at all it logs a warning on the ovphysx Python logger
instead: nothing is wrong for a scene without newton:* attributes, and a suite
that promotes warnings to errors keeps running. ovstage keys registration on the plugin
family, so a complete copy of the Newton schema registered from any directory
(a GitHub checkout) is recognized. The check probes only once USD has built its
schema definitions; a stage authored procedurally through ovphysx.population
and attached before any USD population is left alone, so the check never
registers a schema on the application’s behalf. A
registration ovstage cannot observe (another USD consumer in the process read
the schema definitions first) is not detected; silence the check in that case
with PhysXConfig(carbonite_overrides={"/ovphysx/schemas/warnMissingNewtonSchema": False})
(the string "false" is accepted too). The C API cannot locate the package and
performs no such check.