Python API Reference#

High-level Python API for the ovphysx library.

Stream-Ordered Execution Model#

All operations in this API are stream-ordered, meaning they execute in submission order as if on a single queue. This provides sequential consistency:

  • Operations appear to complete in submission order

  • Writes from operation N are visible to operation N+1

  • You don’t need explicit synchronization between dependent operations

  • Independent operations may execute concurrently internally for performance

Example (no explicit waits needed between dependent operations):

Listing 1 Stream-ordered operation sequence#
 from ovphysx.types import ObjectScope, SimObjectType

 def step_and_read(physx, stage, initial_ordinal, from_ordinal, to_ordinal, dt):
     physx.attach_ovstage(stage, read_ordinal=initial_ordinal)
     # After the application authors later ovstage edits:
     physx.update_from_ovstage(from_ordinal, to_ordinal)
     physx.step(dt)  # Sees the drained stage edits
     with physx.read(
         SimObjectType.RIGID_BODY, ["position"], scope=ObjectScope.ALL
     ) as result:  # Reads current state
         for group in result.groups:
             ...  # use group.tensors (native CPU/CUDA columns)

Use wait_op() when:

  • Before accessing results outside the stream (e.g., reading data on CPU/GPU)

  • To ensure operations complete before program exit

  • For explicit synchronization points in your application

Thread Safety#

  • PhysX instances share the underlying omni.physx runtime. Serialize simulation, stage mutation, and binding creation across instances.

  • Only one instance may own a live ovstage attach in a process. A peer attach attempt raises RuntimeError and leaves the owner’s stage and bindings unchanged. Detach the owner before attaching another instance.

  • A single instance is NOT thread-safe. Use external synchronization if calling from multiple threads.

  • ctypes releases the GIL during native calls, so concurrent step() and PhysX.read() / PhysX.write() from different threads is a data race. See the developer guide threading section for the recommended pattern.

Core Classes#

class ovphysx.api.PhysX(
*,
config: PhysXConfig | None = None,
ignore_version_mismatch: bool = False,
active_cuda_gpus: str | None = None,
)#

Bases: object

High-level wrapper around the C API using ctypes.

attach_ovstage(stage, *, read_ordinal: int = 1) → None#

Attach an ovstage Stage as the orchestration data surface.

Attach performs the initial scene parse at read_ordinal. After the producer authors later ovstage edits, call update_from_ovstage() with only those subsequent ordinals. Tensor bindings remain available as a perf escape hatch.

Parameters:
  • stage – An ovstage.Stage or a raw ovstage_instance_t* handle.

  • read_ordinal – Caller-owned ovstage ordinal at which selected physics data is sealed. Must be non-zero. 0 is reserved as the runtime skip-cursor sentinel. The application owns ordinal advancement. Defaults to 1. open_usd() / population does not seal data, so call advance_write_floor() first. Attachment fails if the initial articulation/joint schema scan cannot read that ordinal.

Preconditions:
  • Instance must be valid.

  • Not already attached to a Stage.

  • No other instance owns the process-wide live ovstage attach.

  • Selected physics data must be sealed at read_ordinal.

  • read_ordinal must be non-zero.

  • The application registered ovphysx’s codeless PhysX schemas with ovstage before the first population in the process (ovstage.population.register_usd_schemas([str(ovphysx.codeless_schema_root())])). Population drops every Physx* API it cannot resolve, so an unregistered stage carries none of the asset’s PhysX settings. This call verifies the registration and raises RuntimeError naming the missing call when population ran without it (for the rest of the process; the Carbonite setting /ovphysx/schemas/requireRegistration = false downgrades this to a warning).

  • Scenes that author Newton newton:* attributes need the Newton USD schema (pip install newton-usd-schemas) registered in the same call (ovphysx.newton_schema_root()); population drops those attributes otherwise. Before the native attach this call checks, once per process, that the installed package was registered before the first population and emits a RuntimeWarning when it was not; a warning promoted to an error therefore leaves the instance detached. When the package is not installed it logs a warning on the ovphysx logger instead (nothing is wrong for scenes without newton:* attributes, and suites that promote warnings to errors stay unaffected). The check probes only once USD has built its schema definitions (a procedurally authored stage attached before any USD population is left alone, so the check never registers a schema on the application’s behalf), and it recognizes the Newton schema family registered from any complete copy. The setting /ovphysx/schemas/warnMissingNewtonSchema = false (PhysXConfig(carbonite_overrides=...), "false" accepted) silences the check, for a registration ovstage cannot observe.

Lifetime:
  • stage must outlive the attachment because ovphysx captures and dereferences its native pointer until detach. This wrapper holds a reference to stage for the duration of the attachment, so a Stage created inline (attach_ovstage(ovstage.Stage(...))) stays alive. The reference is dropped by detach_ovstage() and destroy().

Errors:
  • Raises ValueError if read_ordinal is 0.

  • Raises RuntimeError if already attached, another instance owns the live process-wide attach, stage is null, or the runtime attach fails. Instance remains unattached on failure.

clone(
source_path: str,
target_paths: list[str],
anchor_transforms: list[tuple[float, float, float, float, float, float, float]] | None = None,
env_ids: list[int] | None = None,
) → int#

Clone a prim hierarchy to create multiple runtime physics copies.

Creates physics-optimized clones in the runtime representation for high-performance simulation, backed by the PhysX SDK replicator so cloned articulations are real articulations. The source prim must exist in the loaded stage and have physics properties. Replication executes inline. The returned operation index is already complete, so subsequent operations see the clone immediately and wait_op() returns immediately.

This is the clone entrypoint for both standalone callers and callers that populate the scene through an ovstage Stage attached via attach_ovstage(). Replication runs in the internal representation only (USD untouched).

Cross-environment collision filtering can optionally use PhysX environment ids, controlled by the /ovphysx/clone/useEnvIds setting (default: on). When enabled and the scene runs GPU dynamics + GPU broadphase, each cloned environment gets a distinct environment id so copies in different environments do not collide. The source environment is included: its bodies are created holding environment id 0 (assigned as the attach parses them; clones get 1..N), so co-located clones (anchor_transforms=None) are collision-isolated from the source as well. Environment ids do not provide collision isolation in CPU mode. Give CPU clones spatially disjoint anchor_transforms. Otherwise all copies share one collision space. The runtime logs a warning when env ids are requested but GPU dynamics or GPU broadphase is unavailable. Call ovphysx.enable_python_logging() to receive it on the ovphysx Python logger. Like all carbonite settings, useEnvIds is per-process (shared by every ovphysx instance in the process), so set it consistently before attaching.

When one logical environment is assembled from SEVERAL clone calls (e.g. an IsaacLab ClonePlan cloning one source row at a time: first /env0/Robot to every environment, then /env0/Object), pass env_ids so objects that share an environment share an environment id. With env_ids=None each call numbers its copies afresh, so /env1/Robot and /env1/Object cloned by different calls would land on different ids and never collide with each other:

env_ids = [0, 1]  # same ids in every call -> same logical environments
physx.clone("/env0/Robot", ["/env1/Robot", "/env2/Robot"], env_ids=env_ids)
physx.clone("/env0/Object", ["/env1/Object", "/env2/Object"], env_ids=env_ids)
Parameters:
  • source_path – USD path of the source prim hierarchy to clone (e.g., “/World/env0”)

  • target_paths – Runtime physics-object paths for the cloned hierarchies (e.g., [“/World/env1”, “/World/env2”])

  • anchor_transforms – Optional list of (px, py, pz, qx, qy, qz, qw) transforms giving the absolute world pose of each target subtree root. Entry i anchors the exact subtree at target_paths[i]. Position is followed by quaternion rotation (imaginary-first, matching tensor API convention). Identity rotation = (0, 0, 0, 1). Must have the same length as target_paths. Descendants keep their poses relative to the source subtree root: target_object_world = anchor_transforms[i] * inverse(source_root_world) * source_object_world. Pass None to co-locate every copy on the source. Co-location is collision-isolated only under GPU dynamics + GPU broadphase. In CPU mode, provide spatially disjoint transforms to avoid cross-environment collisions.

  • env_ids – Optional logical environment id per target (list of int, same length as target_paths, each 0 <= id < 0x00FFFFFF, because PhysX supports at most 1<<24 environments and the runtime id is env_ids[i] + 1). Stable across calls: the same id always maps to the same runtime environment, so clones from different calls that share an id collide with each other and stay isolated from every other environment (engages under GPU dynamics + GPU broadphase, like all env-id filtering). Pass None for automatic per-call numbering (each call’s copies get fresh ids past every previous call’s).

Returns:

op_index (can be used with wait_op() for explicit synchronization)

Raises:
  • ValueError – If paths are invalid, anchor_transforms has the wrong length or contains an invalid pose, or env_ids has the wrong length or contains an invalid id.

  • RuntimeError – If clone fails to queue, if no USD scene is loaded, or if clone() is called after warmup() or the first step() / step_sync(). Cloning after warmup corrupts simulation state on GPU and is rejected in all modes for API consistency. To recover, call reset_stage(), wait for it to complete, then reload the source scene or reattach its ovstage before cloning again.

Preconditions:
  • A USD stage is loaded and source_path exists.

  • target_paths are unique and do not already exist.

  • warmup() has not been called and no step() / step_sync() has run since the current stage was attached.

Side effects:
  • Creates live PhysX objects keyed by the target paths. No USD or runtime-stage prims are authored.

Ownership/Lifetime:
  • Clones remain valid until reset_stage().

Threading:
  • Do not call concurrently on the same instance without external sync.

Errors:
  • Raises ValueError for invalid inputs.

  • Raises RuntimeError on internal failure, including duplicate-target and after-step/after-warmup ordering violations.

create_contact_binding(
sensor_patterns: list[str],
filter_patterns: list[str] | None = None,
filters_per_sensor: int = 0,
max_contact_data_count: int = 0,
) → ContactBinding#

Create a contact binding for reading aggregate and detailed contact tensors.

Returns DLPack-compatible tensors of net forces [S, 3] or force matrices [S, F, 3]. Detailed contact and friction data are exposed as flat [C, ...] buffers plus [S, F] count/start-index tensors via ContactBinding.read_contact_data() and ContactBinding.read_friction_data().

A sensor is a set of rigid bodies matched by a physics-object path pattern. A filter is a second set of bodies whose contacts with each sensor you want to measure. Patterns include authored USD objects and runtime-only clones.

Contact reporting is opt-in: every authored USD prim matched by sensor_patterns must have PhysxContactReportAPI applied, on the prim named as the sensor itself (not a parent body or child collider). A matched prim without the schema is dropped from the binding, and if that leaves no sensors this call raises RuntimeError. Filter prims need no extra schema, and runtime-only clones inherit contact reporting from the source actor.

The binding must be created before the first simulation step whose contacts you want to observe. Call ContactBinding.read_net_forces() or ContactBinding.read_force_matrix() after a successful PhysX.step(), PhysX.step_sync(), or PhysX.step_n_sync() call. Before the first step, both return all-zeros tensors.

Result tensor shapes after step:
  • net forces: [S, 3] where S = matched sensor count

  • force matrix: [S, F, 3] where F = matched filter count per sensor

  • detailed data: flat [C, 1] or [C, 3] buffers indexed by counts and start_indices with shape [S, F]

Use ContactBinding.sensor_paths and ContactBinding.filter_paths to map rows and columns back to resolved physics-object paths.

Example:

import torch

def read_contact_forces(physx):
    with physx.create_contact_binding(
        sensor_patterns=["/World/robot_0/ee"],
        filter_patterns=["/World/obstacles/box"],
        filters_per_sensor=1,
        max_contact_data_count=256,
    ) as binding:
        # Call this after a successful simulation step.
        forces = torch.zeros(
            (binding.sensor_count, 3), device="cuda"
        )
        binding.read_net_forces(forces)
        return forces
Parameters:
  • sensor_patterns – Physics-object path patterns for sensor bodies. A single path component may be at most 4096 characters long; a longer one raises RuntimeError (also for filter_patterns).

  • filter_patterns – Flat list of physics-object path patterns for filters. Total length must equal len(sensor_patterns) * filters_per_sensor. Pass None with filters_per_sensor=0 to get contacts with all bodies.

  • filters_per_sensor – Number of filter patterns per sensor (same for all sensors).

  • max_contact_data_count – Max raw contact pairs to track in the native backend. Also caps the detailed contact/friction flat-buffer reads. Detailed reads require this value and filters_per_sensor to be positive.

create_sdf_view(
pattern: str,
max_query_points: int,
) → SdfView#

Create an SDF shape view for evaluating signed distance fields.

Requires a GPU instance. CPU SDF evaluation is not implemented.

Parameters:
  • pattern – USD-style object-path glob matching SDF collision shapes, including runtime-only clones. A single path component may be at most 4096 characters long; a longer one raises RuntimeError.

  • max_query_points – Number of query points per shape per call. Query tensors passed to SdfView.evaluate must have Q equal to this.

Returns:

SdfView with count == number of matched shapes.

Example:

sdf = physx.create_sdf_view("/World/Mesh*", max_query_points=64)
# Query/output tensors must be on the CUDA device (SDF eval is GPU-only).
pts = torch.zeros((sdf.count, 64, 3), dtype=torch.float32, device="cuda")
out = torch.zeros((sdf.count, 64, 4), dtype=torch.float32, device="cuda")
sdf.evaluate(pts, out)
sdf.destroy()
create_tensor_binding(
pattern: str = None,
prim_paths: list[str] = None,
tensor_type: int = TensorType.RIGID_BODY_POSE,
*,
raise_if_empty: bool = False,
) → TensorBinding#

Create tensor binding for bulk physics data access (synchronous).

Deprecated since version 0.6.0: The tensor-binding API is deprecated. Use PhysX.read() for reads and PhysX.write() for writes.

A tensor binding connects physics objects (by path pattern or explicit paths) to a tensor type, including authored USD objects and runtime-only clones.

Parameters:
  • pattern – Physics-object path glob pattern (e.g., “/World/robot*”, “/World/env[N]/robot”). A single path component (the text between two slashes; a parenthesized group counts as one component even if it contains a slash) may be at most 4096 characters long; a longer component is rejected with RuntimeError. Mutually exclusive with prim_paths.

  • prim_paths – Explicit list of physics-object paths. Mutually exclusive with pattern.

  • tensor_type – Tensor type enum value (TensorType.*).

  • raise_if_empty – If True, raise ValueError when the binding matches zero physics objects. The default keeps empty bindings valid. Prefer it for optional or broad queries and check binding.count.

Returns:

TensorBinding object for reading/writing tensor data.

Raises:
  • ValueError – If neither pattern nor prim_paths is provided, both are, or raise_if_empty is true and no physics objects match.

  • RuntimeError – If binding creation fails.

Examples:

import numpy as np
from ovphysx import TensorType

def use_tensor_bindings(physx):
    # Optional broad queries can be empty.
    with physx.create_tensor_binding(
        "/World/robot*", tensor_type=TensorType.RIGID_BODY_POSE
    ) as binding:
        if binding.count:
            poses = np.zeros(
                binding.shape, dtype=np.dtype(str(binding.dtype))
            )
            binding.read(poses)

    binding = physx.create_tensor_binding(
        prim_paths=["/World/env1/robot", "/World/env2/robot"],
        tensor_type=TensorType.ARTICULATION_DOF_POSITION_TARGET,
    )
    targets = np.zeros(
        binding.shape, dtype=np.dtype(str(binding.dtype))
    )
    binding.write(targets)
    binding.destroy()
Preconditions:
  • Exactly one of pattern or prim_paths must be provided.

  • A USD stage is loaded.

Side effects:
  • Allocates native binding resources.

Ownership/Lifetime:
  • Returned TensorBinding owns native resources until destroy().

  • Use binding.shape and binding.dtype (or binding.spec) for layout and binding.native_device for no-staging placement. Most tensor types are float32, but some types such as RIGID_BODY_DISABLE_SIMULATION are not.

  • The binding is tied to the current stage topology. Reuse it across steps, but do not keep it across reset_stage(), removing USD data that contains bound objects, or replacing/reparsing the stage so bound objects are destroyed and recreated. Destroy cached bindings before those lifecycle operations when practical. If a stale binding survives, only destroy it. Create replacements after the operation completes.

Diagnostics:
  • Pattern bindings can intentionally match zero physics objects, so expected TensorAPI no-match diagnostics are quieted on the simulation view used to create that binding.

  • Explicit prim_paths keep the default error-level no-match diagnostics for typo detection. To detect partial misses programmatically, compare the requested prim_paths with the resolved binding.prim_paths returned after creation.

Threading:
  • Do not create bindings concurrently with stage mutation.

Errors:
  • Raises ValueError for invalid arguments.

  • Raises RuntimeError on creation failure.

destroy() → None#

Destroy this PhysX instance.

Preconditions:
  • No other thread is using this instance. Calling destroy() again after terminal destruction is a valid no-op.

Side effects:
  • Releases native resources and unregisters the instance.

Ownership/Lifetime:
  • All tensor bindings and contact bindings created by this instance are automatically released.

  • Drop every output-read Warp array and downstream view before destruction. Destroying with live aliases warns and leaves their read-session resources allocated so their pointers do not dangle.

  • The instance becomes unusable after destruction.

Threading:
  • Do not call concurrently with other operations on this instance.

Errors:
  • Raises RuntimeError without changing the instance if called from a native log callback. Retry after the callback returns.

  • Raises RuntimeError if native destruction reports a failure or process shutdown fails. The instance is already destroyed when either failure is reported, so a later call is an idempotent no-op. If both fail, the process-shutdown error is reported with the native status included.

  • A Python or ctypes exception raised while invoking native destruction leaves ownership intact so the call can be retried.

  • Because this is a checked operation, a cleanup failure raised from a finally block becomes the active exception. Python preserves any in-flight exception as chained context; applications that need different precedence must catch and log cleanup failures explicitly.

detach_ovstage() → None#

Detach the currently-attached ovstage Stage.

Idempotent: calling on an unattached instance is a no-op success. Clears registered interests and output-buffer registrations, so a subsequent attach_ovstage() to a different Stage starts clean. After detach, stage-dependent calls such as update_from_ovstage() and step() fail until a Stage is attached again. Detach invalidates the stage’s tensor, contact, and SDF views. Do not read, write, or evaluate existing bindings or SDF views. Destroy them and create replacements after calling attach_ovstage() and realizing a stage again. If this instance owns an active OmniPVD recording, detach stops and finalizes it. On reattach, capability-only recording is dormant and can start immediately. Configured startup output instead starts a new startup session owned by the reattaching instance. Stop it before starting a late destination.

Errors:
  • Raises RuntimeError on internal failures.

get_attach_handle() → int#

Return the handle identifying this instance’s current attach.

An attach handle is an attach identity, not a USD stage id: it is nonzero for every live attach (including an ovstage attach whose source has no backing USD stage), and a fresh handle is minted per attach. A caller that stores it when it binds can tell “still the attach I bound to” apart from “detached” and from “a different attach that happens to reuse the same stage id”. See ADR-0016.

Returns:

The current attach handle, or 0 when nothing is attached.

Errors:
  • Raises RuntimeError if the instance is invalid.

get_config_bool(key: int) → bool#

Get a boolean config value.

Parameters:

key – Boolean config key (e.g., ovphysx.ConfigBool.DISABLE_CONTACT_PROCESSING).

Returns:

Current boolean value.

get_config_float(key: int) → float#

Get a float config value.

Parameters:

key – Float config key.

Returns:

Current float value.

get_config_int32(key: int) → int#

Get an int32 config value.

Parameters:

key – Int32 config key (e.g., ovphysx.ConfigInt32.NUM_THREADS).

Returns:

Current int32 value.

get_config_string(key: int) → str | None#

Get a string config value.

Parameters:

key – String config key from ovphysx.ConfigString.

Returns:

Current string value, or None if not found.

get_contact_report(
*,
include_friction_anchors: bool = False,
copy: bool = False,
) → dict#

Get per-contact-point event data for the current simulation step.

Use this for custom contact sensors, collision debugging, or per-point force analysis. For aggregate force tensors (net forces or force matrices between sensor/filter body sets), use create_contact_binding() instead.

Warning

With the default copy=False, the returned headers, points, and anchors are zero-copy ctypes views into internal C buffers that are valid only until the next step() or step_sync() call. After the next step the buffers may be reallocated or reused. Accessing the views is undefined behavior (silent data corruption or segfault). Python cannot detect this dangling state.

Pass copy=True to get Python-owned lists of dicts that are safe to retain across simulation steps. This is the recommended mode for RL training loops or any code that holds contact data beyond a single step.

Parameters:
  • include_friction_anchors – If True, also return friction anchor data (position and impulse at each friction anchor point).

  • copy – If True, return Python-owned list[dict] for each section (safe to hold across steps). If False (default), return zero-copy ctypes array views (faster but valid only until the next step()/step_sync()).

Returns a dict with:
  • headers: contact event headers describing each contact pair (actors, colliders, event type). When copy=False, a ctypes array of ContactEventHeader; when copy=True, a list[dict] with the same field names. Length is num_headers.

  • num_headers (int): Number of contact event headers.

  • points: per-contact-point data (position, normal, impulse, separation). When copy=False, a ctypes array of ContactPoint; when copy=True, a list[dict]. Length is num_points.

  • num_points (int): Number of contact point entries.

  • anchors (only if include_friction_anchors=True): friction anchor data. When copy=False, a ctypes array of FrictionAnchor; when copy=True, a list[dict]. Length is num_anchors.

  • num_anchors (int, only if include_friction_anchors=True): Number of friction anchors.

Example (safe across steps, copy=True):

report = physx.get_contact_report(copy=True)
physx.step_sync(dt)  # next step, report still valid
for h in report["headers"]:
    print(h["actor0"], h["numContactData"])
for p in report["points"]:
    print(p["position"], p["normal"], p["impulse"])

Example (zero-copy, copy=False):

report = physx.get_contact_report()
for i in range(report["num_headers"]):
    h = report["headers"][i]
    print(h.actor0, h.numContactData)
# Do NOT call step() before finishing access to report.

Prims must have PhysxContactReportAPI applied in the USD stage for contacts to be reported.

Raises:

RuntimeError – If the call fails.

static get_cpu_mode() → bool#

Return whether process-wide hard CPU-only mode is in effect.

True when PhysX.set_cpu_mode(True) has succeeded, or when OVPHYSX_DISABLE_GPU is active. The environment variable is read live before ovphysx_initialize (and again after shutdown until the next initialize). Initialize latches it for that interval. This is not a query of per-scene USD physxScene:enableGPUDynamics, a CUDA ordinal (active_cuda_gpus), or attach-time resolved dynamics.

Callable at any time. No PhysX instance is required.

get_object_type(prim_path: str) → ObjectType#

Classify an authored USD prim by TensorAPI object type.

See ObjectType for the taxonomy. Paths with no classified simulation object return ObjectType.INVALID with success – the call did not fail, the path just isn’t a known simulation object. Live standalone, custom, and articulation joints at their authored prim paths must not return ObjectType.INVALID.

Raises RuntimeError for invalid input (empty path, embedded NUL byte) or if no stage is attached.

Returns:

One of RIGID_BODY, ARTICULATION, ARTICULATION_LINK, ARTICULATION_ROOT_LINK, ARTICULATION_JOINT, JOINT, CUSTOM_JOINT, or INVALID.

Return type:

ObjectType

get_scene_query_paths_from_ids(ids: tuple | list) → list[str]#

Resolve scene-query hit identity fields to physics-object paths.

ids holds opaque identity handles taken directly from the collision, rigid_body, or material entries of hit dicts returned by raycast(), sweep(), or overlap(). IDs that cannot be resolved (a zero id, an id from an object removed since the query, or no active attach) yield empty strings.

Parameters:

ids – Sequence of int identity handles.

Returns:

Physics-object paths in the same order as ids.

Return type:

list[str]

property handle: int#

The raw ovphysx_handle_t for this instance (read-only).

Use this when passing the handle to C/C++ code that calls the ovphysx C API directly.

Raises:

RuntimeError – If the instance has been destroyed.

is_recording() → bool#

Return True only while this instance owns active sampling.

A peer reports False while another instance owns a startup or late session.

overlap(
geometry_type: SceneQueryGeometryType,
mode: SceneQueryMode = SceneQueryMode.ALL,
**kwargs,
) → list[dict]#

Test geometry overlap against objects in the scene.

For overlap queries, location fields (normal, position, distance, face_index, material) are zeroed. Only object identity is populated.

Parameters:
  • geometry_type – SceneQueryGeometryType.

  • mode – SceneQueryMode (ANY or ALL).

  • **kwargs – Geometry parameters (same as sweep()).

Returns:

List of hit dicts (same format as raycast()).

Raises:

ValueError – If mode is CLOSEST.

query_shared_dictionary(query: int) → int#

Return the opaque pointer to the shared ovstage path dictionary backing a query.

This is NOT an ovphysx-private dictionary: it is the process-shared ovstage dictionary the attached Stage uses, the same one that interned the query’s tokens / prim lists, so a group’s attribute token / prim_list handle resolve through an ovstage.PathDictionary(stage) as well. Returns 0 if unavailable.

raycast(
origin: tuple | list,
direction: tuple | list,
distance: float,
mode: SceneQueryMode = SceneQueryMode.CLOSEST,
both_sides: bool = False,
) → list[dict]#

Cast a ray and return hits.

Parameters:
  • origin – Ray origin [x, y, z].

  • direction – Normalized ray direction [x, y, z].

  • distance – Maximum ray length (>= 0).

  • mode – SceneQueryMode (CLOSEST, ANY, or ALL).

  • both_sides – If True, test both sides of mesh triangles.

Returns:

List of hit dicts. Each dict contains collision, rigid_body, proto_index, normal, position, distance, face_index, material. For ANY mode, hit fields are zeroed.

read(
object_type: SimObjectType,
attribute_names: list[str],
*,
scope: ObjectScope = ObjectScope.ALL,
) → ReadResult#

Read physics output (ADR-0007) for one simulated type as column groups.

Mirrors the ovstage read idiom: open a query over object_type in scope, read the named attribute_names (e.g. ["position", "orientation"]), and return a context-managed ReadResult whose groups is one ReadGroup per typed column. The read is ovstage-native. Attach an ovstage Stage first.

Use it as a context manager: the query + read session stay open for the with block so each group’s interned prim_list / attribute handles are valid. Feed them straight into the ovstage write path (stage.query_from_path_list(group.prim_list)) for a no-repack write-back. Group tensors are warp.array snapshots on the native CPU or CUDA device. They keep their read-session storage alive until the array, its Warp views, and any downstream framework views are dropped, so they are safe to keep past the block.

This is the physics -> app direction. To avoid physics consuming its own output, write the data back into ovstage at ordinals that are never passed to update_from_ovstage(). See the ovstage Integration guide for the ordinal-coupling principle.

Step at least once before reading on DirectGPU. On a DirectGPU scene, simulated state columns for RIGID_BODY, ARTICULATION_LINK, ARTICULATION, and ARTICULATION_JOINT come from PhysX’s direct-GPU API, which sizes its structures during the first simulation step and refuses reads until that step has run. Whole-articulation shape/material columns remain on the CPU, so one result can mix devices. Reading before the first step returns no groups for those types even though the objects exist and a query reports them. Step once, then read. CPU scenes can report authored initial state once buffered scene insertion is complete; a still-pending articulation root or joint partition is omitted normally rather than reported as an error.

Parameters:
  • object_type – Simulated type to read (SimObjectType).

  • attribute_names – Semantic attribute names to read.

  • scope – ALL or ACTIVE (active is single-frame).

Returns:

A ReadResult context manager. result.groups is empty if no objects matched, and, on a DirectGPU scene before the first step, for the direct-GPU-sourced types described above. Readiness is evaluated per scene, so a multi-scene result can contain ready partitions while omitting an unready DirectGPU scene’s partition.

Raises:
  • RuntimeError – on a native error (e.g. no ovstage attached).

  • TypeError – if a returned column carries a device or DLPack dtype this Warp frontend does not support.

read_tokens(
object_type: SimObjectType,
attribute_tokens: list[int],
*,
scope: ObjectScope = ObjectScope.ALL,
) → ReadResult#

Token form of read().

Identical to read() but the attributes are given as interned attribute tokens (e.g. an emitted ReadGroup.attribute, or a token obtained through the C query API) instead of strings, so a token can be fed straight back in with no token-to-string-to-name round-trip. Both forms build the same ovx_string_or_token_t array under the hood.

Parameters:
  • object_type – Simulated type to read (SimObjectType).

  • attribute_tokens – Interned attribute tokens to read.

  • scope – ALL or ACTIVE (active is single-frame).

Returns:

A ReadResult context manager (see read()).

Raises:
  • RuntimeError – on a native error (e.g. no ovstage attached).

  • TypeError – if a returned column carries a device or DLPack dtype this Warp frontend does not support.

reset_stage() → int#

Reset stage to empty (async).

Returns:

op_index (can be used with wait_op() for explicit synchronization)

Example

# Simple usage (stream-ordered) physx.reset_stage() physx.wait_all()

Preconditions:
  • Instance must be valid.

Side effects:
  • Clears the runtime stage.

  • Detaches any attached ovstage Stage (the C runtime calls detach_ovstage internally). Callers must re-attach with attach_ovstage() before any further update_from_ovstage().

Ownership/Lifetime:
  • All TensorBinding, ContactBinding, and SdfView objects for the previous stage become invalid. Destroy cached bindings and SDF views before reset when practical. If a stale handle survives, only destroy it. Create replacement bindings and SDF views after the reset completes.

Threading:
  • Do not call concurrently on the same instance without external sync.

Errors:
  • Raises RuntimeError on failure.

set_config(
entry: ovphysx._bindings.ovphysx_config_entry_t,
) → None#

Set a typed global config entry at runtime (process-global).

Prefer the typed setters (set_config_bool(), set_config_int32(), set_config_float()) for a cleaner API.

Parameters:

entry – Typed config entry (ovphysx_config_entry_t).

set_config_bool(key: int, value: bool) → None#

Set a boolean config value at runtime (process-global).

Parameters:
  • key – Boolean config key (e.g., ConfigBool.DISABLE_CONTACT_PROCESSING).

  • value – Boolean value.

set_config_float(key: int, value: float) → None#

Set a float config value at runtime (process-global).

Parameters:
  • key – Float config key.

  • value – Float value.

set_config_int32(key: int, value: int) → None#

Set an int32 config value at runtime (process-global).

Parameters:
  • key – Int32 config key (e.g., ConfigInt32.NUM_THREADS).

  • value – Int32 value.

static set_cpu_mode(cpu_only: bool) → None#

Force process-wide CPU-only mode.

Call before the first PhysX instance is ever created to keep ovphysx’s own code from touching CUDA. The call requires no active instances. Once set to True successfully, the mode cannot be reversed for this process.

When called before the first instance, True prevents CUDA driver use by ovphysx and makes all PhysX scenes use CPU dynamics regardless of their USD physxScene:enableGPUDynamics settings. Other libraries in the process may still open the driver. A call after an earlier instance was destroyed may succeed, but cannot provide ovphysx’s no-CUDA-touch guarantee or retarget an already-bootstrapped runtime. For CPU-only deployments, setting OVPHYSX_DISABLE_GPU before ovphysx initialization provides the equivalent process-wide policy.

Raises:

RuntimeError – If any PhysX instances are currently active, or if attempting to set False after True has been applied (CPU-only mode is sticky as soon as enabling it succeeds).

start_recording(destination: OmniPvdDestination) → None#

Start a late OmniPVD recording session.

The shared runtime must have recording capability selected before the first instance is created, either explicitly with PhysXConfig(omnipvd_recording_capable=True) or implicitly with omnipvd_output_enabled=True. Once an inactive capable runtime is established, an unconfigured peer may start a late session. A runtime created without capability deliberately rejects late start with INVALID_STATE and an error naming omnipvd_recording_capable. Unsupported platforms retain NOT_IMPLEMENTED.

A failed validation or destination-open attempt may be retried. A start while recording is active is invalid and does not replace its destination. After stop, another FILE or TCP session may be started.

step(dt: float) → int#

Initiate physics step (async, returns op_index).

Simulation time is tracked internally. Each step advances it by dt.

Parameters:

dt – Delta time for this step [s].

Returns:

op_index (can be used with wait_op() for explicit synchronization)

Examples

# Simple usage (stream-ordered) physx.step(0.016) binding.read(output) # Automatically waits for step

# Explicit wait (if accessing results outside stream) op = physx.step(0.016) physx.wait_op(op) # Ensure step completes before external GPU work

Preconditions:
  • A USD stage is loaded if physics content is expected.

Side effects:
  • Advances simulation time and mutates physics state.

Ownership/Lifetime:
  • Returned op_index is single-use and must be waited once if needed.

Threading:
  • Do not call concurrently on the same instance without external sync.

Errors:
  • Raises RuntimeError on failure to enqueue.

step_n_sync(n: int, dt: float) → None#

Run N steps in a single C call, saving (N-1) ctypes round-trips.

Equivalent to calling step_sync(dt) n times, but with only one Python-to-C transition. Simulation time is tracked internally and advanced by n * dt.

Parameters:
  • n – Number of steps to run (must be >= 1).

  • dt – Duration of each step [s].

Raises:

RuntimeError – If any step fails.

step_sync(dt: float) → None#

Step simulation and wait for completion in a single call.

Faster than step() + wait_op() for performance-critical applications like RL training that always wait immediately. Simulation time is tracked internally and advanced by dt.

Parameters:

dt – Delta time [s] for this step.

Raises:

RuntimeError – If the step or wait fails.

stop_recording() → None#

Stop and finalize the OmniPVD recording owned by this instance.

This also stops a startup session owned by the instance that created the shared runtime. Peer instances cannot stop that owner’s session.

sweep(
geometry_type: SceneQueryGeometryType,
direction: tuple | list,
distance: float,
mode: SceneQueryMode = SceneQueryMode.CLOSEST,
both_sides: bool = False,
**kwargs,
) → list[dict]#

Sweep a geometry shape along a direction and return hits.

Parameters:
  • geometry_type – SceneQueryGeometryType.

  • direction – Normalized sweep direction [x, y, z].

  • distance – Maximum sweep distance (>= 0).

  • mode – SceneQueryMode.

  • both_sides – If True, test both sides of mesh triangles.

  • **kwargs –

    Geometry parameters:

    • SPHERE: radius, position

    • BOX: half_extent, position, rotation (xyzw quaternion)

    • SHAPE: prim_path (USD prim path string)

Returns:

List of hit dicts (same format as raycast()).

update_articulations_kinematic() → None#

Update articulation link poses from current joint positions.

This performs a synchronous articulation forward-kinematics update without running a normal simulation step, collision detection, or contact generation. Call it after writing articulation DOF positions and before reading articulation link pose tensors when fresh link poses are needed in the same frame.

In GPU mode, the first kinematic update after loading USD may perform the same automatic DirectGPU warmup step used by tensor reads/writes.

Raises:

RuntimeError – If the update fails.

update_from_ovstage(from_ordinal: int, to_ordinal: int) → None#

Apply committed ovstage edits over the closed range [from_ordinal, to_ordinal].

The application that writes to ovstage owns the ordinal range and calls this after sealing the writes. population.apply_usd_changes() waits for population work but does not seal its ordinal; complete stage.advance_write_floor(ordinal).wait() before this call. ovphysx forwards the range (as ovstage’s own ovstage_ordinal_range_t) to the runtime ovstage change feed and applies the resulting deltas to the simulation.

Ordinals at or below the latest successfully consumed ordinal are skipped. A fully consumed range succeeds as a no-op. An overlapping range applies only its unread suffix. attach_ovstage() consumes its initial read_ordinal, so replaying it does not repeat initial population events. Later authored and sealed population changes are applied normally.

wait_all(*, timeout_ns: int | None = None) → None#

Wait for all pending operations (convenience wrapper for wait_op(ALL)).

Parameters:

timeout_ns – Readiness timeout in nanoseconds, with the same semantics as wait_op().

Preconditions:
  • Instance must be valid.

Side effects:
  • Consumes each completed or failed operation reached before success or timeout.

Threading:
  • Serialize all calls on the same instance externally.

Errors:
  • Raises RuntimeError on failure.

  • Raises TimeoutError if timeout expired (e.g., when polling with timeout_ns=0 and operations are not ready).

wait_op(op_index: int, *, timeout_ns: int | None = None) → None#

Wait for operation(s) to complete.

Parameters:
  • op_index – Operation index to wait for, or OP_INDEX_ALL for all ops

  • timeout_ns – Readiness timeout in nanoseconds. None waits indefinitely, and 0 performs one non-blocking readiness poll. A positive value bounds only the wait for readiness; once an operation is ready, synchronous result finalization may make the total call duration exceed this timeout.

Raises:
  • RuntimeError – If an operation failed or op_index is invalid or already consumed.

  • TimeoutError – If timeout expired (e.g., when polling with timeout_ns=0 and the operation is not ready)

Preconditions:
  • op_index must be valid and not previously consumed.

Side effects:
  • Consumes each completed or failed operation reached up to op_index.

  • An operation still pending when the wait times out is not consumed.

  • An index completed by internal stream synchronization may be acknowledged once; that acknowledgement consumes it.

Ownership/Lifetime:
  • Wait-result storage is released internally.

  • Error strings are borrowed and remain valid until the next API call on the same thread.

Threading:
  • Serialize calls on one PhysX instance externally.

  • Do not wait on the same op_index from multiple threads.

Examples:

def wait_for_operation(physx, op_index):
    # Blocking wait (default)
    physx.wait_op(op_index)

def poll_operation(physx, op_index):
    # Non-blocking poll
    try:
        physx.wait_op(op_index, timeout_ns=0)
    except TimeoutError:
        return False
    return True
warmup() → None#

Explicitly run the warmup step (synchronous).

On first call, runs a minimal simulation step (~1ns) to initialize PhysX structures. Works in both CPU and GPU mode (in GPU mode this also populates DirectGPU buffers).

Normally done automatically on the first tensor read, but calling it explicitly lets you control when the latency occurs.

This function is idempotent. Calling it multiple times has no effect after the first successful call. Warmup state resets after reset_stage() or attaching a new USD stage.

Raises:

RuntimeError – If warmup fails.

Side effects:
  • Advances simulation by a minimal timestep on first call.

Threading:
  • Do not call concurrently with other operations on this instance.

write(
object_type: SimObjectType,
attribute_name: str,
*,
scope: ObjectScope = ObjectScope.ALL,
) → WriteSession#

Open an app -> physics write session for ONE attribute (ADR-0012).

The return direction of read(), and its mirror: the groups cover the same prims in the same order. Each tensor exposes the native residency of the write path. This can differ from the corresponding read when a write uses host staging on a GPU scene. Inspect tensor.device instead of inferring placement from the scene or read result.

One attribute per session, unlike read()’s list: the native group carries no attribute field, so a session that mixed attributes could not label its groups. Writing position and orientation is two sessions.

Every group tensor is a warp.array on its native CPU or CUDA device. Non-empty tensors are MUTABLE VIEWS onto runtime-owned storage. Empty tensors are Warp-owned empty arrays. Fill a non-empty tensor, then WriteSession.commit() its group. Anything left uncommitted when the block exits is discarded rather than published.

An attribute the type does not accept raises here rather than silently writing nothing.

Raises RuntimeError if no ovstage Stage is attached, or if the attribute is not writable for object_type.

class ovphysx.api.TensorBinding(
sdk,
handle: int,
tensor_type: int,
ndim: int,
shape: tuple,
dtype: DLDataType | None = None,
*,
_from_factory: bool = False,
)#

Bases: object

Tensor binding for bulk physics data access via DLPack.

Deprecated since version 0.6.0: The tensor-binding API is deprecated. Use PhysX.read() for reads and PhysX.write() for writes.

A tensor binding connects a physics-object path pattern to a tensor type, enabling efficient bulk read/write for authored USD objects and runtime-only clones (poses, velocities, joint positions, etc.). The shape, ndim, and dtype metadata come from ovphysx_get_tensor_binding_spec(). Use them to allocate compatible buffers instead of assuming every tensor type is float32. Use native_device to choose the no-staging CPU or CUDA device.

CPU-only property bindings cover standalone rigid-body mass/inertia/COM values, articulation DOF/body properties, rigid-body/articulation shape properties, deformable-material properties, and disable-simulation/gravity flags. Fixed and spatial tendon property bindings are not CPU-only. CPU-only property bindings require host-resident tensor, index, and mask buffers, including when the simulation runs on GPU.

This is a synchronous API - operations complete before returning. Bindings are tied to the currently realized physics objects. Reuse them across simulation steps, but do not keep them across reset_stage(), removing USD data that contains bound objects, or replacing/reparsing the stage so bound objects are destroyed and recreated. Destroy cached bindings before those lifecycle operations when practical. If a stale binding survives, only destroy it. Create replacement bindings after the operation completes.

Usage patterns:

  • Context manager (auto-cleanup):

    import numpy as np
    from ovphysx import TensorType
    
    def raise_robot_poses(physx):
        with physx.create_tensor_binding(
            "/World/robot*", tensor_type=TensorType.RIGID_BODY_POSE
        ) as binding:
            poses = np.zeros(binding.shape, dtype=np.dtype(str(binding.dtype)))
            binding.read(poses)
            poses[:, 2] += 0.1  # raise z position
            binding.write(poses)
        # Auto-destroyed here
    
  • Manual (explicit cleanup):

    import numpy as np
    from ovphysx import TensorType
    
    def read_robot_poses(physx):
        binding = physx.create_tensor_binding(
            "/World/robot*", tensor_type=TensorType.RIGID_BODY_POSE
        )
        poses = np.zeros(binding.shape, dtype=np.dtype(str(binding.dtype)))
        binding.read(poses)
        binding.destroy()
        return poses
    
property body_count: int#

Number of links.

property body_names: list[str]#

List of body/link names.

property count: int#

Get number of entities (first dimension of shape).

destroy() → None#

Release binding resources.

Safe to call multiple times. Called automatically on garbage collection or when exiting a context manager.

Preconditions:
  • Binding must not be in use by other threads.

Side effects:
  • Releases native resources and invalidates the binding.

Ownership/Lifetime:
  • After destruction, the binding cannot be used.

Threading:
  • Serialized per binding via an internal lock.

Errors:
  • RuntimeError if destruction fails.

property dof_count: int#

Number of degrees of freedom (DOFs). 0 if not an articulation binding.

property dof_names: list[str]#

List of DOF names (one per DOF).

property dtype: DLDataType#

Get the required DLPack dtype for tensors passed to this binding.

Most bindings use float32. Index bindings (deformable element indices) use int32. The bool bindings (DISABLE_SIMULATION, DISABLE_GRAVITY) use uint8, as does ARTICULATION_DOF_DRIVE_TYPE, which is a per-DOF enum byte rather than a flag. See dtype_name for a compact string form.

property dtype_name: str#

Get the required tensor dtype as a short string such as float32, int32, or uint8.

property fixed_tendon_count: int#

Number of fixed tendons per articulation (0 if none).

Use to decide whether to allocate buffers for fixed tendon property tensors (types 80-85) and to skip tendon code paths when T=0.

property handle: int#

Get the binding handle.

property is_fixed_base: bool#

Whether the articulation has a fixed base.

property joint_count: int#

Number of joints per articulation.

property joint_names: list[str]#

List of joint names.

property native_device: DLDevice#

Get the binding’s native DLPack device.

Deprecated since version 0.6.0: The tensor-binding API is deprecated. Use PhysX.read() for reads and PhysX.write() for writes.

CPU-only property tensors report DLDevice(kDLCPU, 0) even when the scene uses GPU dynamics. Other bindings follow their native TensorAPI view and report DLDevice(kDLCUDA, ordinal) in DirectGPU mode or DLDevice(kDLCPU, 0) otherwise. This property identifies the no-staging device and does not change existing read/write behavior. A CUDA device_id is the process-visible runtime ordinal used by a framework device such as cuda:N, not a physical PCI bus index. Compare device_type.value with a constant such as DLDeviceType.kDLCUDA. This is a live query, not cached metadata: it may wait for pending operations and rejects an invalidated simulation view.

property ndim: int#

Get the number of dimensions reported by ovphysx_get_tensor_binding_spec().

property prim_paths: list[str]#

Resolved physics-object paths in tensor row order.

Rigid-body bindings return one path per rigid-body tensor row. Articulation bindings return one root object path per articulation row. For per-articulation link names, use body_names.

read(tensor) → None#

Read simulation data into a user-provided tensor (synchronous).

The tensor must have matching shape and dtype (shape and dtype). Can be a NumPy array, PyTorch tensor, or any object with __dlpack__ protocol.

When called repeatedly with the same NumPy, PyTorch, Warp, or direct DLTensor buffer object, an internal cache skips DLPack acquisition and attribute chain lookups, giving near-raw-C-call overhead. The numpy writeable guard is preserved on the fast path. Other DLPack providers are reacquired on every call. Callers that want the fast path should reuse the same tensor object with unchanged backing storage across calls. Do not resize or rebind storage between cached calls. The staleness guard rebuilds the cache when it detects a pointer change, but storage mutations that reuse the same pointer violate the cache contract. Direct DLTensor inputs retain their caller-owned descriptor.

Parameters:

tensor – DLPack-compatible tensor with pre-allocated storage matching self.shape. Must use self.dtype. When CUDA is available, CPU/CUDA device mismatches are staged for binding types whose storage follows the simulation device. CPU-only property bindings require a host-resident tensor (kDLCPU or kDLCUDAHost), including on GPU simulations; CUDA and CUDA-managed tensors are rejected rather than staged. Cross-GPU mismatches and CUDA tensors in process-wide CPU-only mode are rejected.

Preconditions:
  • This binding is not destroyed.

  • tensor has matching shape and dtype and uses a supported device.

Side effects:
  • Blocks until data is available and writes into the provided tensor.

Ownership/Lifetime:
  • Caller owns tensor storage and must keep it alive for the duration of the call.

  • Do not mutate the tensor’s backing storage (resize(), set_(), etc.) between cached calls. A staleness guard detects pointer changes for NumPy, PyTorch, and Warp inputs and falls back to the slow path. Direct DLTensor inputs retain their caller-owned descriptor.

Threading:
  • Serialized per binding via an internal lock.

Errors:
  • RuntimeError if read fails (shape mismatch, device mismatch, etc.).

property shape: tuple#

Get tensor shape as tuple reported by ovphysx_get_tensor_binding_spec().

Returns:

Tensor dimensions for this binding. Scalar-property bindings use (N,). Flat state tensors use (N, C). Articulation and deformable mesh tensors use (N, L, C).

sleep(indices=None) → None#

Force rigid bodies in this binding to sleep.

Mirrors PhysX SDK PxRigidDynamic::putToSleep. Symmetric counterpart to wake_up(). Bodies that have RIGID_BODY_DISABLE_SIMULATION set are silently skipped.

Only valid on a rigid-body binding. Articulation bindings raise.

Parameters:

indices – Optional int32 DLPack-compatible tensor of indices into this binding. If None, every body in the binding is put to sleep.

Errors:

RuntimeError if the binding is destroyed, is not a rigid-body binding, has been invalidated by a stage change, or the engine call fails.

property spatial_tendon_count: int#

Number of spatial tendons per articulation (0 if none).

Use to decide whether to allocate buffers for spatial tendon property tensors (types 90-93) and to skip tendon code paths when T=0.

property spec: TensorBindingSpec#

Get a Python-owned tensor spec snapshot for this binding.

property tensor_type: int#

Get the tensor type enum value.

wake_up(indices=None) → None#

Wake rigid bodies in this binding.

Mirrors PhysX SDK PxRigidDynamic::wakeUp. Bodies that still have RIGID_BODY_DISABLE_SIMULATION set are silently skipped (the engine refuses to wake disabled actors).

Typical pair: clear the disable flag on an actor (re-add it to simulation in a sleep state) and then call this so the actor is active for the next step().

Only valid on a rigid-body binding. Articulation bindings raise.

Parameters:

indices – Optional int32 DLPack-compatible tensor of indices into this binding. If None, every body in the binding is woken.

Errors:

RuntimeError if the binding is destroyed, is not a rigid-body binding, has been invalidated by a stage change, or the engine wake call fails.

write(tensor, indices=None, mask=None) → None#

Write data from a user-provided tensor into the simulation (synchronous).

The tensor must have matching shape and dtype (shape and dtype). Can be a NumPy array, PyTorch tensor, or any object with __dlpack__ protocol.

When called repeatedly with the same supported buffer object and no indices/mask, an internal cache skips DLPack acquisition and attribute chain lookups, giving near-raw-C-call overhead. Callers that want this fast path should reuse the same tensor object with unchanged backing storage across calls. See read() for the supported providers and full contract.

Parameters:
  • tensor – DLPack-compatible tensor with data to write, shape matching self.shape. Must use self.dtype. When CUDA is available, CPU/CUDA device mismatches are staged for binding types whose storage follows the simulation device. For CPU-only property bindings, this tensor and optional indices or mask must be host-resident (kDLCPU or kDLCUDAHost), including on GPU simulations; CUDA and CUDA-managed tensors are rejected rather than staged. Cross-GPU mismatches and CUDA tensors in process-wide CPU-only mode are rejected.

  • indices – Optional int32 tensor of indices for partial update. If provided, only the rows at the given indices are written. The tensor argument must still be full shape [N, …] matching the binding spec; only the selected rows are applied. Shape of indices: [K] where K <= N.

  • mask –

    Optional bool/uint8 tensor for masked update. If provided, only elements where mask[i] != 0 are written. Shape: [N] matching the binding’s first dimension. When mask is provided, tensor must be full shape [N, …]. If both mask and indices are provided, mask takes precedence and indices are ignored (with a warning).

    Note: there is no corresponding read(..., mask=...). Reads always return the full [N,…] tensor and callers can index the result themselves. This write-only mask design matches other RL physics APIs such as Newton’s selectionAPI, where masks selectively apply actions but observations are always returned in full.

Preconditions:
  • This binding is not destroyed.

  • tensor matches shape and dtype and uses a supported device.

  • indices (if provided) is int32 and within bounds.

  • mask (if provided) is bool/uint8 with shape [N] on a supported device.

Side effects:
  • Updates simulation state for the bound entities.

Ownership/Lifetime:
  • Caller owns tensor/indices/mask storage and must keep it alive for the call.

Threading:
  • Serialized per binding via an internal lock.

Errors:
  • RuntimeError if write fails (shape mismatch, device mismatch, etc.).

class ovphysx.api.ContactBinding(
sdk,
handle: int,
sensor_count: int,
filter_count: int,
max_contact_data_count: int,
)#

Bases: object

Contact tensor binding backed by IRigidContactView.

Do not instantiate directly. Use PhysX.create_contact_binding() to obtain an instance. The sensor_paths and filter_paths properties expose the row/column metadata for the returned tensors.

destroy() → None#

Release contact binding resources.

Safe to call multiple times. Captures strong references to the SDK and library before the C call to guard against GC ordering issues (Python may collect self._sdk before self if both go out of scope together).

property filter_count: int#

Number of filter bodies per sensor (0 when no filters specified).

property filter_paths: list[list[str]]#

Resolved filter physics-object paths in contact tensor column order.

The outer list is indexed by sensor row and the inner list by filter column. Each inner list is empty for unfiltered contact bindings (i.e. when filter_count == 0).

Returns:

Nested list of shape [sensor_count][filter_count].

Return type:

list[list[str]]

get_other_actor_paths_from_ids(ids_array) → list[str]#

Resolve actor IDs from read_raw_contact_data() to physics-object paths.

ids_array is a 1D int64/uint64 array (numpy / warp / torch with DLPack support) holding actor IDs, from either column of the actor_ids tensor. Both use the same namespace. A column is a strided view and this boundary requires C-contiguous input, so wrap a column slice in np.ascontiguousarray() before passing it. Path strings are copied into Python, so the caller can keep them across subsequent ovphysx calls.

Every non-zero ID is checked against the attached stage first, so an ID whose actor has been removed yields an empty path rather than the path it used to name. Since the caller holds the IDs, that makes the failure explicit: a non-zero ID with an empty path is stale, while ID 0 simply means no actor. The check is as precise as the backend’s notion of existence. On a USD stage a merely deactivated prim still resolves.

Returns:

Physics-object paths in the same order as the input IDs.

Return type:

list[str]

property max_contact_data_count: int#

Flat-buffer capacity for detailed contact and friction reads.

read_contact_data(
contact_forces,
positions,
normals,
separations,
counts,
start_indices,
) → None#

Read detailed contact data into flat buffers.

Expected shapes are [C, 1] for contact_forces and separations, [C, 3] for positions and normals, and [sensor_count, filter_count] for counts and start_indices. C is max_contact_data_count. Both C and filter_count must be positive. Count and start-index tensors may be int32 or uint32. Contact force magnitudes use the timestep from the last successful PhysX.step(), PhysX.step_sync(), or PhysX.step_n_sync() call.

read_force_matrix(output) → None#

Read contact force matrix into output. Expected shape: [sensor_count, filter_count, 3].

The dt for impulse-to-force conversion is taken automatically from the last successful PhysX.step(), PhysX.step_sync(), or PhysX.step_n_sync() call.

read_friction_data(
friction_forces,
friction_points,
counts,
start_indices,
) → None#

Read detailed friction data into flat buffers.

Expected shapes are [C, 3] for friction_forces and friction_points, and [sensor_count, filter_count] for counts and start_indices. C is max_contact_data_count and must be positive, and filter_count must also be positive. Count and start-index tensors may be int32 or uint32. Friction entries are per-anchor. Sum each flat slice to build a pair-level [sensor_count, filter_count, 3] force tensor. Friction forces use the timestep from the last successful PhysX.step(), PhysX.step_sync(), or PhysX.step_n_sync() call.

read_net_forces(output) → None#

Read net contact forces into output. Expected shape: [sensor_count, 3].

The dt for impulse-to-force conversion is taken automatically from the last successful PhysX.step(), PhysX.step_sync(), or PhysX.step_n_sync() call.

read_raw_contact_data(
contact_forces,
positions,
normals,
separations,
sensor_layout,
actor_ids,
) → None#

Read raw (unfiltered) contact data into flat buffers.

Filter-less variant of read_contact_data(). Returns every contact involving each sensor regardless of which other actor it collided with, plus per-contact actor-identity tensors for identifying both the sensor and the contacting body via get_other_actor_paths_from_ids().

Expected shapes are [C, 1] for contact_forces and separations, [C, 3] for positions and normals, [sensor_count, 2] for sensor_layout (column 0 count, column 1 start index), and [C, 2] for actor_ids (column 0 the reporting sensor’s actor, column 1 the actor it contacted). C is max_contact_data_count and must be positive. No filter dimension is required. sensor_layout may be int32 or uint32. actor_ids must be int64 or uint64. Slicing the columns out as views costs no copy. Contact force magnitudes use the timestep from the last successful PhysX.step(), PhysX.step_sync(), or PhysX.step_n_sync() call.

Truncation: when the total contact count for a step exceeds max_contact_data_count, the runtime fills the buffers with as many contacts as fit and emits a logged warning. A sensor’s count reports only the contacts actually written, and its start index is clamped to max_contact_data_count, so [start, start + count) is always an in-range (possibly empty) slice. Increase max_contact_data_count at binding creation if truncation occurs.

Token lifetime: tokens in actor_ids are opaque actor handles, not encoded paths, and are stable while the corresponding actor is alive on the attached stage. After an actor is removed, its token is stale and get_other_actor_paths_from_ids() reports it as an empty path rather than as the path it used to name.

property sensor_count: int#

Number of sensor bodies matched.

property sensor_paths: list[str]#

Resolved sensor physics-object paths in contact tensor row order.

Returns:

One path per sensor row, in the same order as the contact data tensors.

Return type:

list[str]

Codeless Schema Discovery#

ovphysx ships its PhysX USD schemas as codeless resources and never registers them itself; these helpers tell the application where they are so it can register them with the USD runtime it owns (see Physics Schemas).

ovphysx.codeless_schema_root() → Path#

Return the directory holding ovphysx’s codeless PhysX USD schema packages.

The returned directory follows the convention schemas/physx/<module>/resources/ and contains one subdirectory per PhysX schema module ovphysx ships (e.g. PhysxSchema and OmniUsdPhysicsDeformableSchema).

Raises:

FileNotFoundError – If no staged codeless schema tree can be found. Install the ovphysx wheel (pip install ovphysx) or run cmake -P scripts/install.cmake from a source checkout to stage the schemas.

ovphysx.codeless_schema_paths() → list[Path]#

Return the per-module codeless schema resource directories.

Each returned path is a resources directory containing a USD plugInfo.json (Type: resource) and generatedSchema.usda, ready to hand to pxr.Plug.Registry().RegisterPlugins() for use with a stock usd-core runtime.

Raises:

FileNotFoundError – If the staged schema tree is missing or contains no registrable schema packages. See codeless_schema_root().

The Newton USD schema (pip install newton-usd-schemas) is a separate package whose newton:* attributes ovphysx reads as fallbacks for the PhysX spellings; this helper locates the installed package so it can be registered in the same call.

ovphysx.newton_schema_root() → Path#

Return the directory of the installed Newton USD schema (newton-usd-schemas).

Register it with ovstage alongside codeless_schema_root(), before the first population call in the process; the parser reads the schema’s newton:* attributes as fallbacks for the PhysX spellings, and population drops them unless the schema is registered.

Raises:

FileNotFoundError – If the newton-usd-schemas package is not installed in this Python environment. The message carries the install hint.

Logging#

ovphysx.api.set_log_level(level: int) → None#

Set the process-scoped libovphysx source log level threshold.

Messages emitted under the named Carbonite sources omni_physx_sdk, omni.physx, and ovphysx_internal below this level are suppressed for console and callback delivery. Every other process source and channel, including any unnamed source, remains unchanged and is subject only to the callback’s severity and channel filter. Callable at any time, including before instance creation. LogLevel.NONE mutes only the three named sources. It is not a whole-runtime or process mute.

Parameters:

level – Log level threshold (LogLevel.DEFAULT through LogLevel.NONE). LogLevel.DEFAULT restores LogLevel.WARNING.

Raises:
  • ValueError – If level is out of range. No state change is applied.

  • RuntimeError – If the native API rejects the call for another reason, including callback-time reconfiguration.

ovphysx.api.get_log_level() → int#

Get the current process-scoped libovphysx source log level threshold.

Returns:

The current log level (int matching ovphysx_log_level_t constants).

ovphysx.api.enable_default_log_output(enable: bool = True) → None#

Enable or disable Carbonite’s built-in console log output.

By default, Carbonite logs to the console. When a custom callback is set (or enable_python_logging() is active), both the built-in console output and the callback receive messages, which may cause duplicate output.

Call with False to suppress the built-in console output while keeping the callback active. Call with True to re-enable it.

This is independent of callback registration and the libovphysx source log level. It controls Carbonite’s process-global built-in console logger and therefore affects every Carbonite tenant in the process. Multi-tenant hosts should normally own this policy and leave the logger enabled.

Parameters:

enable – True to enable (default), False to disable.

Raises:

RuntimeError – If the native API rejects the call, including when it is made from the active native log callback.

ovphysx.api.flush_log(timeout_ns: int = 1 << 64 - 1) → None#

Wait for callback delivery already accepted before this call.

The barrier covers records already handed to ovphysx by Carbonite. If the host enabled Carbonite asynchronous logging, records still buffered upstream are outside this barrier. Successful native shutdown flushes the upstream buffer before disabling and draining the callback.

Parameters:

timeout_ns – Maximum wait in nanoseconds. Zero polls; 2**64 - 1 waits indefinitely.

Raises:
  • ValueError – If timeout_ns is outside the unsigned 64-bit range.

  • TimeoutError – If the timeout expires.

  • RuntimeError – If native log delivery cannot be flushed.

ovphysx.api.enable_python_logging(
logger_name: str = 'ovphysx',
*,
min_severity: int = LogLevel.VERBOSE,
channel_filter: str | None = None,
) → None#

Route native log messages to Python’s logging module.

Sets the sole C-level callback slot and forwards matching native messages to logging.getLogger(logger_name). Calling this function again replaces any existing native callback, including one installed outside Python. Successful shutdown of the Python process-lifecycle scope, normally when the final PhysX instance is destroyed, also disables this bridge. Enable it again after creating an instance in a new lifecycle scope. Each forwarded LogRecord includes ovphysx_channel and ovphysx_timestamp attributes for the native source and Unix-epoch timestamp seconds.

Call disable_python_logging() to stop forwarding.

Parameters:
  • logger_name – Name of the Python logger to route to (default: “ovphysx”).

  • min_severity – Minimum severity for records observed from the process log stream (default: LogLevel.VERBOSE). Records from libovphysx’s omni_physx_sdk, omni.physx, and ovphysx_internal sources are also subject to set_log_level().

  • channel_filter – Optional comma-separated channel=level rules. The native API copies this string, compares levels case-insensitively, and uses raw channel-prefix matching. The longest matching prefix wins. A later rule wins ties of equal length.

Raises:

RuntimeError – If called from a native log callback, another Python logging transition is in progress, or the native API rejects the severity, filter, or callback configuration.

ovphysx.api.disable_python_logging() → None#

Stop routing native log messages to Python’s logging module.

If enable_python_logging() was not called, this is a no-op.

Raises:

RuntimeError – If called from a native log callback or while another Python logging transition is in progress.

Configuration#

Typed config for ovphysx.

@implements REQ-CAPI-NVTX-001 @covers AC-6

Provides PhysXConfig, a dataclass whose fields map 1:1 to the C typed config enums in ovphysx_types.h. Only non-None fields are applied. The rest keep their Carbonite/PhysX defaults.

Usage:

from ovphysx import PhysX, PhysXConfig

physx = PhysX(config=PhysXConfig(
    disable_contact_processing=True,
    num_threads=4,
    carbonite_overrides={"/physics/updateToUsd": False},
))
class ovphysx.config.OmniPvdDestination(
transport: str,
file_path: str = '',
tcp_address: str = '',
tcp_port: int = 0,
tcp_timeout_ms: int = 0,
)#

Bases: object

Exact destination for a late OmniPVD recording session.

classmethod file(
path: str,
) → OmniPvdDestination#

Record to the exact file path path.

file_path: str = ''#
classmethod tcp(
address: str,
port: int,
*,
timeout_ms: int = 0,
) → OmniPvdDestination#

Connect to a ready TCP listener.

tcp_address: str = ''#
tcp_port: int = 0#
tcp_timeout_ms: int = 0#
transport: str#
class ovphysx.config.PhysXConfig(
disable_contact_processing: bool | None = None,
collision_cone_custom_geometry: bool | None = None,
collision_cylinder_custom_geometry: bool | None = None,
num_threads: int | None = None,
scene_multi_gpu_mode: int | None = None,
omnipvd_output_enabled: bool | None = None,
omnipvd_recording_capable: bool | None = None,
omnipvd_ovd_recording_directory: str | None = None,
omnipvd_transport: str | None = None,
omnipvd_tcp_address: str | None = None,
omnipvd_tcp_port: int | None = None,
omnipvd_tcp_timeout_ms: int | None = None,
nvtx_enabled: bool | None = None,
cooked_collider_cache_dir: str | None = None,
ovstage_read_pool_max_mb: int | None = None,
carbonite_overrides: dict[str, bool | int | float | str] | None = None,
)#

Bases: object

Typed configuration for ovphysx.

All fields default to None (= use Carbonite/PhysX default). Only non-None fields are applied.

Example:

from ovphysx import PhysX, PhysXConfig

physx = PhysX(config=PhysXConfig(
    disable_contact_processing=True,
    num_threads=4,
    carbonite_overrides={"/physics/updateToUsd": False},
))
carbonite_overrides: dict[str, bool | int | float | str] | None = None#
collision_cone_custom_geometry: bool | None = None#
collision_cylinder_custom_geometry: bool | None = None#
cooked_collider_cache_dir: str | None = None#

Directory for the local cooked-collider (UJITSO) cache. Provide this to persist cooked colliders across runs and reuse them on the next launch. If left None, ovphysx cooks to a process-private temp directory that is discarded at shutdown, so nothing persists. Applied when the runtime first starts in a process. Later changes have no effect.

disable_contact_processing: bool | None = None#
num_threads: int | None = None#
nvtx_enabled: bool | None = None#

Emit NVTX ranges for the ovphysx API calls and the PhysX SDK profile zones, for capture with Nsight Systems. Off by default. Must be set before instance creation. Setting OVPHYSX_NVTX=1 in the environment has the same effect.

omnipvd_output_enabled: bool | None = None#

Must be set before instance creation

omnipvd_ovd_recording_directory: str | None = None#

Must be set before instance creation

omnipvd_recording_capable: bool | None = None#

Enables late PhysX.start_recording(). Set before the first instance is created. Process-wide and unset/false by default. omnipvd_output_enabled=True also enables this capability. Otherwise default creation installs no OmniPVD provider.

omnipvd_tcp_address: str | None = None#
omnipvd_tcp_port: int | None = None#
omnipvd_tcp_timeout_ms: int | None = None#

0 uses the OS send timeout and a 3000 ms connect window

omnipvd_transport: str | None = None#

Exact lowercase “file” or “tcp”

ovstage_read_pool_max_mb: int | None = None#

Retention budget in MiB for the per-context device read-buffer pool that backs the ovstage output read (/physics/ovstageReadPoolMaxMB). Bounds the device and pinned-host memory the pool keeps between reads for reuse. 0 or any negative value DISABLES the pool (nothing is retained and every read allocates and frees as if the pool were absent). Default 256 when left None. Does not cap the memory a single read allocates, only what is retained.

scene_multi_gpu_mode: int | None = None#

0=disabled, 1=all GPUs, 2=skip first GPU. Used only when active_cuda_gpus is empty

Types and Enums#

Pure-Python type definitions for ovphysx.

@implements REQ-CAPI-NVTX-001 @covers AC-2

This module contains IntEnum definitions that mirror the C enums in ovphysx/include/ovphysx/ovphysx_types.h. It has zero native dependencies (no ctypes, no shared library loading, no USD) and is safe to import in any Python process regardless of USD version or native library state.

Keeping this module dependency-free is intentional: downstream consumers like IsaacLab can import TensorType without triggering ovphysx’s native bootstrap or USD version checks.

Naming convention: strip the OVPHYSX_TENSOR_ prefix and scalar dtype suffix (_F32 or _S32) from the C enum name. This keeps names unambiguous (ARTICULATION_ vs RIGID_BODY_) and makes the _bindings.py aliases mechanically verifiable.

class ovphysx.types.ApiStatus(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Return codes from the ovphysx C API. Mirrors ovphysx_api_status_t.

BUFFER_TOO_SMALL = 6#
DEVICE_MISMATCH = 7#
END_OF_ITERATION = 9#
ERROR = 1#
GPU_NOT_AVAILABLE = 8#
INVALID_ARGUMENT = 4#
INVALID_STATE = 10#
NOT_FOUND = 5#
NOT_IMPLEMENTED = 3#
SUCCESS = 0#
TIMEOUT = 2#
class ovphysx.types.BindingPrimMode(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Prim selection mode for tensor bindings.

Deprecated since version 0.6.0: The tensor-binding API is deprecated. Use PhysX.read() for reads and PhysX.write() for writes.

Unlike the other enums in this module, BindingPrimMode does not have a named typedef in ovphysx_types.h. The values come from the internal implementation. It is not covered by test_types_sync.py.

CREATE_NEW = 2#
EXISTING_ONLY = 0#
MUST_EXIST = 1#
class ovphysx.types.ConfigBool(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Boolean config keys. Mirrors ovphysx_config_bool_t.

COLLISION_CONE_CUSTOM_GEOMETRY = 1#
COLLISION_CYLINDER_CUSTOM_GEOMETRY = 2#
DISABLE_CONTACT_PROCESSING = 0#
NVTX_ENABLED = 4#
OMNIPVD_OUTPUT_ENABLED = 3#
OMNIPVD_RECORDING_CAPABLE = 5#
class ovphysx.types.ConfigFloat(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Float config keys (reserved). Mirrors ovphysx_config_float_t.

class ovphysx.types.ConfigInt32(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Int32 config keys. Mirrors ovphysx_config_int32_t.

NUM_THREADS = 0#
OMNIPVD_TCP_PORT = 2#
OMNIPVD_TCP_TIMEOUT_MS = 3#
OVSTAGE_READ_POOL_MAX_MB = 4#
SCENE_MULTI_GPU_MODE = 1#
class ovphysx.types.ConfigString(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

String config keys. Mirrors ovphysx_config_string_t.

COOKED_COLLIDER_CACHE_DIRECTORY = 1#
OMNIPVD_OVD_RECORDING_DIRECTORY = 0#
OMNIPVD_TCP_ADDRESS = 3#
OMNIPVD_TRANSPORT = 2#
class ovphysx.types.LogLevel(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Log level for ovphysx output. Mirrors ovphysx_log_level_t.

DEFAULT restores the effective WARNING threshold. DEFAULT and NONE configure logging but are never delivered as callback severities.

DEFAULT = 0#
ERROR = 4#
INFO = 2#
NONE = 5#
VERBOSE = 1#
WARNING = 3#
class ovphysx.types.ObjectScope(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Output query scope. Mirrors ovphysx_object_scope_t.

ACTIVE is single-frame (the active set is recomputed each step). ALL is stable until a structural change.

ACTIVE = 1#
ALL = 0#
class ovphysx.types.ObjectType(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

TensorAPI object classification. Mirrors ovphysx_object_type_t.

Returned by PhysX.get_object_type(). Standalone UsdPhysics joints are JOINT; plugin-registered custom joints are CUSTOM_JOINT (pair with OVPHYSX_PHYSX_TYPE_CUSTOM_JOINT / get_physx_ptr); articulation joints are ARTICULATION_JOINT.

ARTICULATION = 2#
ARTICULATION_JOINT = 5#
CUSTOM_JOINT = 7#
INVALID = 0#
JOINT = 6#
RIGID_BODY = 1#
class ovphysx.types.SceneQueryGeometryType(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Geometry type for sweep/overlap queries. Mirrors ovphysx_scene_query_geometry_type_t.

BOX = 1#
SHAPE = 2#
SPHERE = 0#
class ovphysx.types.SceneQueryMode(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Scene query hit mode. Mirrors ovphysx_scene_query_mode_t.

ALL = 2#
ANY = 1#
CLOSEST = 0#
class ovphysx.types.SimObjectType(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Simulated object type for the physics output read.

Mirrors ovphysx_sim_object_type_t. This is a separate enum domain from the TensorBindings ObjectType. In particular, SimObjectType.ARTICULATION is 9 while ObjectType.ARTICULATION is 2.

ARTICULATION = 9#
ARTICULATION_JOINT = 2#
DEFORMABLE_MATERIAL = 10#
DEFORMABLE_SURFACE = 5#
DEFORMABLE_VOLUME = 4#
FIXED_TENDON = 7#
PARTICLE_SET = 6#
RIGID_BODY = 0#
SPATIAL_TENDON = 8#
VEHICLE_WHEEL = 3#
class ovphysx.types.TensorType(
value,
names=_not_given,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)#

Bases: IntEnum

Tensor type identifiers for TensorBindingsAPI.

Deprecated since version 0.6.0: The tensor-binding API is deprecated. Use PhysX.read() for reads and PhysX.write() for writes.

Values match ovphysx_tensor_type_t in ovphysx_types.h. IntEnum members compare equal to plain ints, so they pass directly to the C API without conversion.

ARTICULATION_BODY_COM_POSE = 61#
ARTICULATION_BODY_DISABLE_GRAVITY = 65#
ARTICULATION_BODY_INERTIA = 62#
ARTICULATION_BODY_INV_INERTIA = 64#
ARTICULATION_BODY_INV_MASS = 63#
ARTICULATION_BODY_MASS = 60#
ARTICULATION_CENTROIDAL_MOMENTUM = 14#
ARTICULATION_CONTACT_OFFSET = 111#
ARTICULATION_CORIOLIS_AND_CENTRIFUGAL_FORCE = 72#
ARTICULATION_DOF_ACTUATION_FORCE = 34#
ARTICULATION_DOF_ARMATURE = 40#
ARTICULATION_DOF_DAMPING = 36#
ARTICULATION_DOF_DRIVE_MODEL = 42#
ARTICULATION_DOF_DRIVE_TYPE = 43#
ARTICULATION_DOF_FRICTION_PROPERTIES = 41#
ARTICULATION_DOF_LIMIT = 37#
ARTICULATION_DOF_MAX_FORCE = 39#
ARTICULATION_DOF_MAX_VELOCITY = 38#
ARTICULATION_DOF_POSITION = 30#
ARTICULATION_DOF_POSITION_TARGET = 32#
ARTICULATION_DOF_PROJECTED_JOINT_FORCE = 75#
ARTICULATION_DOF_STIFFNESS = 35#
ARTICULATION_DOF_VELOCITY = 31#
ARTICULATION_DOF_VELOCITY_TARGET = 33#
ARTICULATION_FIXED_TENDON_DAMPING = 81#
ARTICULATION_FIXED_TENDON_LIMIT = 83#
ARTICULATION_FIXED_TENDON_LIMIT_STIFFNESS = 82#
ARTICULATION_FIXED_TENDON_OFFSET = 85#
ARTICULATION_FIXED_TENDON_REST_LENGTH = 84#
ARTICULATION_FIXED_TENDON_STIFFNESS = 80#
ARTICULATION_GRAVITY_FORCE = 73#
ARTICULATION_JACOBIAN = 70#
ARTICULATION_MASS_CENTER_LOCAL = 13#
ARTICULATION_MASS_CENTER_WORLD = 12#
ARTICULATION_MASS_MATRIX = 71#
ARTICULATION_REST_OFFSET = 112#
ARTICULATION_ROOT_POSE = 10#
ARTICULATION_ROOT_VELOCITY = 11#
ARTICULATION_SHAPE_FRICTION_AND_RESTITUTION = 110#
ARTICULATION_SPATIAL_TENDON_DAMPING = 91#
ARTICULATION_SPATIAL_TENDON_LIMIT_STIFFNESS = 92#
ARTICULATION_SPATIAL_TENDON_OFFSET = 93#
ARTICULATION_SPATIAL_TENDON_STIFFNESS = 90#
DEFORMABLE_COLLISION_ELEMENT_INDICES = 125#
DEFORMABLE_MATERIAL_BENDING_DAMPING = 136#
DEFORMABLE_MATERIAL_BENDING_STIFFNESS = 134#
DEFORMABLE_MATERIAL_DYNAMIC_FRICTION = 130#
DEFORMABLE_MATERIAL_ELASTICITY_DAMPING = 133#
DEFORMABLE_MATERIAL_POISSONS_RATIO = 132#
DEFORMABLE_MATERIAL_THICKNESS = 135#
DEFORMABLE_MATERIAL_YOUNGS_MODULUS = 131#
DEFORMABLE_REST_NODAL_POSITION = 123#
DEFORMABLE_SIM_ELEMENT_INDICES = 124#
DEFORMABLE_SIM_KINEMATIC_TARGET = 122#
DEFORMABLE_SIM_NODAL_POSITION = 120#
DEFORMABLE_SIM_NODAL_VELOCITY = 121#
INVALID = 0#
RIGID_BODY_ACCELERATION = 6#
RIGID_BODY_COM_POSE = 5#
RIGID_BODY_CONTACT_OFFSET = 101#
RIGID_BODY_DISABLE_GRAVITY = 103#
RIGID_BODY_DISABLE_SIMULATION = 9#
RIGID_BODY_FORCE = 50#
RIGID_BODY_INERTIA = 4#
RIGID_BODY_INV_INERTIA = 8#
RIGID_BODY_INV_MASS = 7#
RIGID_BODY_MASS = 3#
RIGID_BODY_POSE = 1#
RIGID_BODY_REST_OFFSET = 102#
RIGID_BODY_SHAPE_FRICTION_AND_RESTITUTION = 100#
RIGID_BODY_VELOCITY = 2#
RIGID_BODY_WRENCH = 51#
SURFACE_DEFORMABLE_REST_POSITION = 143#
SURFACE_DEFORMABLE_SIM_ELEMENT_INDICES = 144#
SURFACE_DEFORMABLE_SIM_POSITION = 140#
SURFACE_DEFORMABLE_SIM_VELOCITY = 141#

DLPack Tensor Structures#

DLPack tensor structures for zero-copy data interchange.

This module provides ctypes wrappers for the vendored DLPack C header, enabling efficient data sharing between the C library and Python without copying.

NOTE: This file is NOT copied from another repo. It is a hand-written Python/ctypes mirror of the C structs defined in ovphysx/dlpack/dlpack.h (which itself is the upstream header from https://github.com/dmlc/dlpack). When the vendored C header is updated, this file must be updated to match.

class ovphysx.dlpack.DLDataType#

Bases: Structure

Descriptor of data type for elements of DLTensor.

TYPE_MAP = {'bfloat16': (4, 16, 1), 'float16': (2, 16, 1), 'float32': (2, 32, 1), 'float32x4': (2, 32, 4), 'float64': (2, 64, 1), 'int16': (0, 16, 1), 'int32': (0, 32, 1), 'int64': (0, 64, 1), 'int8': (0, 8, 1), 'uint16': (1, 16, 1), 'uint32': (1, 32, 1), 'uint64': (1, 64, 1), 'uint8': (1, 8, 1), 'uint8x4': (1, 8, 4)}#
bits#

Structure/Union member

code#

Structure/Union member

lanes#

Structure/Union member

class ovphysx.dlpack.DLDataTypeCode#

Bases: c_ubyte

An integer that encodes the category of DLTensor elements’ data type.

kDLBfloat = 4#
kDLBool = 6#
kDLComplex = 5#
kDLFloat = 2#
kDLFloat4_e2m1fn = 17#
kDLFloat6_e2m3fn = 15#
kDLFloat6_e3m2fn = 16#
kDLFloat8_e3m4 = 7#
kDLFloat8_e4m3 = 8#
kDLFloat8_e4m3b11fnuz = 9#
kDLFloat8_e4m3fn = 10#
kDLFloat8_e4m3fnuz = 11#
kDLFloat8_e5m2 = 12#
kDLFloat8_e5m2fnuz = 13#
kDLFloat8_e8m0fnu = 14#
kDLInt = 0#
kDLOpaqueHandle = 3#
kDLUInt = 1#
class ovphysx.dlpack.DLDevice#

Bases: Structure

Represents the device where DLTensor memory is allocated.

device_id#

Structure/Union member

device_type#

Structure/Union member

class ovphysx.dlpack.DLDeviceType#

Bases: c_long

The enum that encodes the type of the device where DLTensor memory is allocated.

kDLCPU = 1#
kDLCUDA = 2#
kDLCUDAHost = 3#
kDLCUDAManaged = 13#
kDLExtDev = 12#
kDLHexagon = 16#
kDLMAIA = 17#
kDLMetal = 8#
kDLOneAPI = 14#
kDLOpenCL = 4#
kDLROCM = 10#
kDLROCMHost = 11#
kDLTrn = 18#
kDLVPI = 9#
kDLVulkan = 7#
kDLWebGPU = 15#
class ovphysx.dlpack.DLManagedTensor#

Bases: Structure

C structure for managed DLPack tensor.

deleter#

Structure/Union member

dl_tensor#

Structure/Union member

manager_ctx#

Structure/Union member

class ovphysx.dlpack.DLTensor#

Bases: Structure

Plain C Tensor object, does not manage memory.

byte_offset#

Structure/Union member

data#

Structure/Union member

device#

Structure/Union member

dtype#

Structure/Union member

ndim#

Structure/Union member

shape#

Structure/Union member

strides#

Structure/Union member

Contact Report Structures#

ctypes mirrors of the C ABI structs returned by ovphysx.api.PhysX.get_contact_report(); see the C API Reference for full field semantics.

Contact report ctypes structures.

These ctypes.Structure mirrors of the C ABI structs returned by ovphysx_get_contact_report() are used to access per-step contact data in Python without copying. Field layouts must stay in sync with the C definitions in ovphysx/include/ovphysx/ovphysx_types.h.

The module has no native-library dependencies and is safe to import in any Python process. The structures are populated by ctypes against pointers returned from the C API.

class ovphysx.contact_types.ContactEventHeader#

Bases: Structure

Contact event header. Mirrors ovphysx_contact_event_header_t.

actor0#

Structure/Union member

actor1#

Structure/Union member

attachHandle#

Structure/Union member

collider0#

Structure/Union member

collider1#

Structure/Union member

contactDataOffset#

Structure/Union member

frictionAnchorsDataOffset#

Structure/Union member

numContactData#

Structure/Union member

numfrictionAnchorsData#

Structure/Union member

protoIndex0#

Structure/Union member

protoIndex1#

Structure/Union member

type#

Structure/Union member

class ovphysx.contact_types.ContactPoint#

Bases: Structure

Per-contact-point data. Mirrors ovphysx_contact_point_t.

faceIndex0#

Structure/Union member

faceIndex1#

Structure/Union member

impulse#

Structure/Union member

material0#

Structure/Union member

material1#

Structure/Union member

normal#

Structure/Union member

position#

Structure/Union member

separation#

Structure/Union member

class ovphysx.contact_types.FrictionAnchor#

Bases: Structure

Friction anchor data. Mirrors ovphysx_friction_anchor_t.

impulse#

Structure/Union member

position#

Structure/Union member

Physics Utilities#

USD physics authoring utilities.

ovphysx.api.PhysX simulates a stage; this subpackage mostly builds one. Its bulk is a pure-Python layer over pxr that authors the geometry, xform ops and physics API schemas a scene needs, so a caller does not have to spell out a rigid body’s collider, mass and transform by hand for every prim. The one exception is simulation, described at the end.

The helpers come from omni.physx.scripts, whose names mixed camelCase and snake_case. Every name here is snake_case, so setCollider is set_collider() and createAPISchemaPropertyCache is create_api_schema_property_cache(). Aliasing the subpackage on import keeps a ported snippet close to its original shape:

from ovphysx import utils as physicsUtils
physicsUtils.add_rigid_box(stage, "/World/box", position=Gf.Vec3f(0, 0, 5))

Eight camelCase spellings survive as deprecated aliases: setCollider, setRigidBody, removeCollider, removePhysics, removeRigidBodySubtree, hasSchema, createJoint and extractTriangleSurfaceFromTetra. Each forwards to the new spelling and raises a DeprecationWarning naming both, and each is reachable as ovphysx.utils.<name> and by explicit import. None is in __all__, so from ovphysx.utils import * and the rendered documentation show the new spellings only. No other camelCase name is kept.

Keyword parameters are snake_case on the same terms, so setCollider(prim, approximationShape=...) becomes set_collider(prim, approximation_shape=...). Three old spellings stay accepted, being the ones on the helpers above: approximationShape on set_collider() and set_rigid_body(), and schemaName on has_schema(). Each raises a DeprecationWarning naming both spellings; passing both in one call raises TypeError.

Importing this subpackage does not load the ovphysx native library, contact a USD resolver or start a simulation, so it is usable in a process that only authors USD. Diagnostics go through the standard logging module under the ovphysx.utils logger hierarchy.

The authoring helpers need pxr. An authoring process supplies its own stock usd-core, and this subpackage is reached with an explicit import ovphysx.utils so that import ovphysx keeps working for simulation-only users who have no pxr.

pxr is required only once an authoring name is actually touched, not to import this subpackage. Submodules load on first attribute access, so from ovphysx.utils import step_and_write_to_ovstage resolves in a process that has ovstage but no USD at all – which is what the output_read sample does.

ovphysx ships the PhysX schemas codeless, so PhysX API schemas are applied by identifier and their properties authored by name; core UsdPhysics schemas still use their typed bindings. Register the schemas before opening any stage:

from ovphysx.utils import codeless
codeless.register_schemas()

Refer to codeless for the registration rules, which are strict about ordering and fail silently when ignored.

The submodules group the surface by theme, and every name is also re-exported here, so both ovphysx.utils.shapes.add_rigid_box and ovphysx.utils.add_rigid_box resolve:

  • codeless - registration and access for the codeless PhysX schemas

  • schema - schema registry introspection, API schema property snapshot and restore

  • transform - xform op stacks, basis vectors, joint-relative transforms

  • paths - collision-free stage paths for a prim about to be defined

  • shapes - cube, sphere, capsule, cylinder, cone and xform constructors, plain, collider and rigid-body flavored

  • planes - ground planes and sized quad planes

  • joints - joints between two prims or between a prim and the world

  • materials - physics materials and their binding to prims

  • filtering - collision groups and filtered pairs

  • authoring - mass, force, physics scenes and the collider and rigid-body API sets on prims that already exist

  • mesh - procedural meshes, triangle and tetrahedron mesh math

  • particles - particle systems, particle sets, PBD materials

  • deformable - volume and surface deformable bodies and their materials

  • simulation - stepping a running simulation and writing its output back to an attached ovstage Stage

  • constants - shared tokens and limits

ovphysx.utils is the home for any supported ovphysx helper that does not belong in the top-level API, a wider remit than USD authoring alone: simulation is the one such submodule today. A submodule of that kind - one that drives a simulation rather than authoring a stage - must import runtime dependencies such as ovstage, warp and numpy inside its functions rather than at module scope, and reach ovphysx.api either the same way or under if TYPE_CHECKING:, so that import ovphysx.utils keeps needing nothing beyond pxr and the standard library and still loads no native library. Such a helper may need ovstage and a live PhysX instance when called; only the import-time guarantee is subpackage-wide.

Utilities are grouped by submodule. Except for the codeless schema helpers, public names are also available directly from ovphysx.utils.

Codeless schema access#

Not flat re-exported. Import it as from ovphysx.utils import codeless.

Access to ovphysx’s codeless PhysX USD schemas.

ovphysx ships the PhysX and Omni deformable schemas as codeless artifacts: a plugInfo.json and generatedSchema.usda per module. API schemas are applied by their schema identifier and their properties are reached by name:

from ovphysx.utils import codeless

codeless.apply_api(prim, "PhysxRigidBodyAPI")
codeless.set_attr(prim, "physxRigidBody:disableGravity", True)

Multiple-apply schemas take an instance name, which becomes the middle component of the property path:

codeless.apply_api(prim, "PhysxCookedDataAPI", "convexHull")
codeless.set_attr(prim, codeless.instanced_name("physxCookedData", "convexHull", "buffer"), buf)

Registration comes first#

The schemas must be registered with the USD runtime before anything in the process opens a stage or queries the schema registry. USD builds that registry once, on first access, and never rebuilds it, so a late registration is silently ineffective and the process it happened in cannot be repaired: there is no reload, no reset, and a second register_schemas() call reports success while changing nothing. Getting the order wrong costs a restart.

Under ovphysx’s own bundled runtime, import ovphysx handles this by appending to OV_PXR_PLUGINPATH_2511. Under a stock usd-core – which is what pxr resolves to in an authoring process – call register_schemas() as the first thing in the program, or preset PXR_PLUGINPATH_NAME before the process starts. Presetting the environment variable is the only route that also works inside a host that has already initialized USD, because USD reads it while constructing the registry rather than afterwards. Refer to docs/physics_schemas.md.

The helpers here turn USD’s “invalid schema” error into one that names the likely cause.

exception ovphysx.utils.codeless.CodelessSchemaError#

Bases: RuntimeError

A codeless PhysX schema could not be applied, removed or authored.

ovphysx.utils.codeless.apply_api(
prim: pxr.Usd.Prim,
identifier: str,
instance_name: str | None = None,
) → None#

Apply a codeless API schema to a prim by schema identifier.

Parameters:
  • prim – The prim to apply the API to.

  • identifier – The schema identifier, e.g. "PhysxRigidBodyAPI".

  • instance_name – The instance name for a multiple-apply schema, e.g. "convexHull" for PhysxCookedDataAPI. Omit for single-apply schemas.

Raises:

CodelessSchemaError – If the API could not be applied. The message distinguishes the two usual causes – the schemas not being registered, or the identifier not naming a known API schema – from USD refusing to author a schema it knows.

ovphysx.utils.codeless.get_attr(prim: pxr.Usd.Prim, name: str) → pxr.Usd.Attribute#

Return a prim attribute by name, with a readable error when it is missing.

Parameters:
  • prim – The prim to read from.

  • name – The full property name, e.g. "physxRigidBody:disableGravity".

Raises:

CodelessSchemaError – If the prim is invalid or has no such attribute. Usually the owning API was not applied first, or the schemas are unregistered.

ovphysx.utils.codeless.instanced_name(
prefix: str,
instance_name: str,
property_name: str,
) → str#

Build the property name of a multiple-apply schema instance.

For example instanced_name("physxCookedData", "convexHull", "buffer") gives "physxCookedData:convexHull:buffer".

ovphysx.utils.codeless.register_schemas(verify: bool = False)#

Register ovphysx’s codeless PhysX schemas with the active USD runtime.

Intended for authoring processes running on a stock usd-core. Call it before opening any stage or querying the schema registry; USD builds that registry once and a later call cannot repair it, in this process or by any other means short of a restart.

A late call gives no sign of it on its own. RegisterPlugins still reports the plugin roots as registered and Tf.Type.FindByName still resolves the schema types; only applying an API fails, arbitrarily far away. Pass verify to convert that into an immediate error.

Idempotent: USD ignores a plugin root it has already seen.

Parameters:

verify – Confirm the schemas reached the schema registry and raise if they did not. Off by default because the check is not free: the only way to ask is to query the schema registry, which builds it, so a verified call has to be the last plugin registration in the process. Leave it off where another USD component registers plugins after ovphysx.

Returns:

The list of registered Plug.Plugin objects, which is empty when the schemas were already registered.

Raises:
  • FileNotFoundError – If the staged codeless schema tree cannot be found. See ovphysx.codeless_schema_paths().

  • CodelessSchemaError – If verify is set and any of the schema roots did not reach the schema registry, because something built it first or because that root is not staged.

ovphysx.utils.codeless.remove_api(
prim: pxr.Usd.Prim,
identifier: str,
instance_name: str | None = None,
) → bool#

Remove a codeless API schema from a prim by schema identifier.

With the schemas registered, removing an API that was never applied is not an error; USD reports it as a no-op and this returns its result unchanged, so a whole family of mutually exclusive APIs can be stripped without asking which one is there.

The returned bool is USD’s answer to whether it authored the removal on the current edit target, not a promise that the API is gone: an opinion on a stronger layer survives a successful removal on a weaker one. A removal USD refuses on a schema it knows – on a layer that is not editable, for instance – is False too, on the same terms as try_apply_api().

Parameters:
  • prim – The prim to strip.

  • identifier – The schema identifier.

  • instance_name – The instance name for a multiple-apply schema.

Returns:

Whether USD reports the removal as successful.

Raises:

CodelessSchemaError – If the prim is invalid or USD cannot resolve the call at all, on the same terms as try_apply_api().

ovphysx.utils.codeless.schema_is_registered() → bool#

Whether the codeless PhysX schemas are visible to the schema registry.

Every schema root ovphysx ships has to be there: a partial registration – the PhysX schemas without the Omni deformable ones, say – answers False.

Note that calling this builds the schema registry if it does not exist yet, which locks out every later schema plugin registration in the process, ovphysx’s and any other library’s alike. Register first, then query.

ovphysx.utils.codeless.set_attr(prim: pxr.Usd.Prim, name: str, value) → pxr.Usd.Attribute#

Author a prim attribute by name.

The attribute must already be declared, which for a codeless schema means its API was applied first. This is the codeless replacement for the typed api.CreateFooAttr(value) calls.

Parameters:
  • prim – The prim to author on.

  • name – The full property name.

  • value – The value to set.

Returns:

The authored attribute.

Raises:

CodelessSchemaError – If the attribute does not exist or cannot be set.

ovphysx.utils.codeless.set_attrs(prim: pxr.Usd.Prim, values: dict) → None#

Author several attributes, skipping the ones whose value is None.

The authoring helpers expose every schema attribute as an optional keyword defaulting to None, so that an unsupplied attribute keeps its schema fallback instead of being pinned to a value chosen by the helper. This applies that convention in one call.

Parameters:
  • prim – The prim to author on.

  • values – A mapping of full property name to value. None values are skipped.

Raises:

CodelessSchemaError – If the prim is invalid or an attribute cannot be set.

ovphysx.utils.codeless.set_rel(
prim: pxr.Usd.Prim,
name: str,
targets,
) → pxr.Usd.Relationship#

Set a relationship’s targets by name.

Parameters:
  • prim – The prim to author on.

  • name – The full relationship name, e.g. "physxParticle:particleSystem".

  • targets – A single target path or a list of them.

Raises:

CodelessSchemaError – If the prim has no such relationship, or the targets could not be set.

ovphysx.utils.codeless.try_apply_api(
prim: pxr.Usd.Prim,
identifier: str,
instance_name: str | None = None,
) → bool#

Apply a codeless API schema, returning whether USD accepted it.

The bool-returning half of apply_api(), for a caller that already reports a refused application its own way – the deformable helpers warn and return False rather than raising.

A refusal is USD declining to author a schema it knows, as for a prim on a layer that is not editable. USD reports it either by answering False or by raising, and both come back as False here. The two causes below are not refusals: USD cannot resolve the identifier at all, so it has nothing to answer about.

Parameters:
  • prim – The prim to apply the API to.

  • identifier – The schema identifier, e.g. "PhysxRigidBodyAPI".

  • instance_name – The instance name for a multiple-apply schema.

Raises:

CodelessSchemaError – If the prim is invalid or USD cannot resolve the call at all – the schemas not being registered, the identifier not naming a known API schema, or the instance name contradicting the one it does name.

Shape constructors#

Shape constructors: geometry, colliders and rigid bodies in one call.

Every shape comes in three flavors. The plain add_* helper defines the UsdGeom prim with a display color and a translate-orient-scale xform op stack; add_collider_* additionally applies UsdPhysics.CollisionAPI; and add_rigid_* applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI with a density and initial velocities on top of that. Each is a one-line way to put a falling box or a static ramp on a stage.

The physics APIs are applied here directly rather than through set_collider(), so a shape carries a bare collision API and no mesh approximation: these are all analytic UsdGeom shapes, for which an approximation has nothing to describe. Reach for set_collider instead when the prim is a mesh or already exists.

ovphysx.utils.shapes.add_box(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
size: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cube to the stage.

Parameters:
  • stage – The Usd.Stage to add cube.

  • path – The desired cube path.

  • size – The size of the cube.

  • position – The position where the cube should be placed in stage.

  • orientation – The cube orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_capsule(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Capsule to the stage.

The extent authored here is the one UsdGeom.Capsule’s own schema computation produces, so it follows axis and reaches height / 2 + radius along it – the cylindrical section plus a hemispherical cap at each end – and radius across it.

Parameters:
  • stage – The Usd.Stage to add capsule.

  • path – The desired capsule path.

  • radius – The radius of the capsule.

  • height – The height of the capsule.

  • axis – The axis of the capsule.

  • position – The position where the capsule should be placed in stage.

  • orientation – The capsule orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_collider_box(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
size: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cube to the stage and add physics collider API to it.

Parameters:
  • stage – The Usd.Stage to add cube.

  • path – The desired cube path.

  • size – The size of the cube.

  • position – The position where the cube should be placed in stage.

  • orientation – The cube orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_collider_capsule(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Capsule to the stage and add physics collider API to it.

Parameters:
  • stage – The Usd.Stage to add capsule.

  • path – The desired capsule path.

  • radius – The radius of the capsule.

  • height – The height of the capsule.

  • axis – The axis of the capsule.

  • position – The position where the capsule should be placed in stage.

  • orientation – The capsule orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_collider_cone(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cone to the stage and add physics collider API to it.

Parameters:
  • stage – The Usd.Stage to add cone.

  • path – The desired cone path.

  • radius – The radius of the cone.

  • height – The height of the cone.

  • axis – The axis of the cone.

  • position – The position where the cone should be placed in stage.

  • orientation – The cone orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_collider_cube(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
size: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cube to the stage and add physics collider API to it.

Parameters:
  • stage – The Usd.Stage to add cube.

  • path – The desired cube path.

  • size – The size of the cube.

  • position – The position where the cube should be placed in stage.

  • orientation – The cube orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_collider_cylinder(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cylinder to the stage and add physics collider API to it.

Parameters:
  • stage – The Usd.Stage to add cylinder.

  • path – The desired cylinder path.

  • radius – The radius of the cylinder.

  • height – The height of the cylinder.

  • axis – The axis of the cylinder.

  • position – The position where the cylinder should be placed in stage.

  • orientation – The cylinder orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_collider_sphere(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Sphere to the stage and add physics collider API to it.

Parameters:
  • stage – The Usd.Stage to add sphere.

  • path – The desired sphere path.

  • radius – The radius of the sphere.

  • position – The position where the sphere should be placed in stage.

  • orientation – The sphere orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_cone(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cone to the stage.

The extent authored here is the one UsdGeom.Cone’s own schema computation produces, so it follows axis and reaches height / 2 along it and radius across it.

Parameters:
  • stage – The Usd.Stage to add cone.

  • path – The desired cone path.

  • radius – The radius of the cone.

  • height – The height of the cone.

  • axis – The axis of the cone.

  • position – The position where the cone should be placed in stage.

  • orientation – The cone orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_cube(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
size: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cube to the stage.

Parameters:
  • stage – The Usd.Stage to add cube.

  • path – The desired cube path.

  • size – The size of the cube.

  • position – The position where the cube should be placed in stage.

  • orientation – The cube orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_cylinder(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cylinder to the stage.

The extent authored here is the one UsdGeom.Cylinder’s own schema computation produces, so it follows axis and reaches height / 2 along it and radius across it.

Parameters:
  • stage – The Usd.Stage to add cylinder.

  • path – The desired cylinder path.

  • radius – The radius of the cylinder.

  • height – The height of the cylinder.

  • axis – The axis of the cylinder.

  • position – The position where the cylinder should be placed in stage.

  • orientation – The cylinder orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_rigid_box(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
size: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
density: float = 1.0,
lin_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
ang_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cube to the stage and add physics rigid body and collider API to it.

A density of 0.0 still applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI; it is not a request for a static collider. Use add_collider_box for that.

Parameters:
  • stage – The Usd.Stage to add cube.

  • path – The desired cube path.

  • size – The size of the cube.

  • position – The position where the cube should be placed in stage.

  • orientation – The cube orientation.

  • color – The color of the mesh.

  • density – The density of the rigid body.

  • lin_velocity – The initial linear velocity of the rigid body.

  • ang_velocity – The initial angular velocity of the rigid body.

ovphysx.utils.shapes.add_rigid_capsule(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
density: float = 1.0,
lin_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
ang_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
) → pxr.Usd.Prim#

Add UsdGeom.Capsule to the stage and add physics rigid body and collider API to it.

A density of 0.0 still applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI; it is not a request for a static collider. Use add_collider_capsule for that.

Parameters:
  • stage – The Usd.Stage to add capsule.

  • path – The desired capsule path.

  • radius – The radius of the capsule.

  • height – The height of the capsule.

  • axis – The axis of the capsule.

  • position – The position where the capsule should be placed in stage.

  • orientation – The capsule orientation.

  • color – The color of the mesh.

  • density – The density of the rigid body.

  • lin_velocity – The initial linear velocity of the rigid body.

  • ang_velocity – The initial angular velocity of the rigid body.

ovphysx.utils.shapes.add_rigid_cone(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
density: float = 1.0,
lin_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
ang_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cone to the stage and add physics rigid body and collider API to it.

A density of 0.0 still applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI; it is not a request for a static collider. Use add_collider_cone for that.

Parameters:
  • stage – The Usd.Stage to add cone.

  • path – The desired cone path.

  • radius – The radius of the cone.

  • height – The height of the cone.

  • axis – The axis of the cone.

  • position – The position where the cone should be placed in stage.

  • orientation – The cone orientation.

  • color – The color of the mesh.

  • density – The density of the rigid body.

  • lin_velocity – The initial linear velocity of the rigid body.

  • ang_velocity – The initial angular velocity of the rigid body.

ovphysx.utils.shapes.add_rigid_cube(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
size: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
density: float = 1.0,
lin_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
ang_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cube to the stage and add physics rigid body and collider API to it.

A density of 0.0 still applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI; it is not a request for a static collider. Use add_collider_cube for that.

Parameters:
  • stage – The Usd.Stage to add cube.

  • path – The desired cube path.

  • size – The size of the cube.

  • position – The position where the cube should be placed in stage.

  • orientation – The cube orientation.

  • color – The color of the mesh.

  • density – The density of the rigid body.

  • lin_velocity – The initial linear velocity of the rigid body.

  • ang_velocity – The initial angular velocity of the rigid body.

ovphysx.utils.shapes.add_rigid_cylinder(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
height: float = 1.0,
axis: str = 'Y',
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
density: float = 1.0,
lin_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
ang_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
) → pxr.Usd.Prim#

Add UsdGeom.Cylinder to the stage and add physics rigid body and collider API to it.

A density of 0.0 still applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI; it is not a request for a static collider. Use add_collider_cylinder for that.

Parameters:
  • stage – The Usd.Stage to add cylinder.

  • path – The desired cylinder path.

  • radius – The radius of the cylinder.

  • height – The height of the cylinder.

  • axis – The axis of the cylinder.

  • position – The position where the cylinder should be placed in stage.

  • orientation – The cylinder orientation.

  • color – The color of the mesh.

  • density – The density of the rigid body.

  • lin_velocity – The initial linear velocity of the rigid body.

  • ang_velocity – The initial angular velocity of the rigid body.

ovphysx.utils.shapes.add_rigid_sphere(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
density: float = 1.0,
lin_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
ang_velocity: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
) → pxr.Usd.Prim#

Add UsdGeom.Sphere to the stage and add physics rigid body and collider API to it.

A density of 0.0 still applies UsdPhysics.RigidBodyAPI and UsdPhysics.MassAPI; it is not a request for a static collider. Use add_collider_sphere for that.

Parameters:
  • stage – The Usd.Stage to add sphere.

  • path – The desired sphere path.

  • radius – The radius of the sphere.

  • position – The position where the sphere should be placed in stage.

  • orientation – The sphere orientation.

  • color – The color of the mesh.

  • density – The density of the rigid body.

  • lin_velocity – The initial linear velocity of the rigid body.

  • ang_velocity – The initial angular velocity of the rigid body.

ovphysx.utils.shapes.add_rigid_xform(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
scale: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add xform to the stage with given transformation and add rigid body API to it.

Parameters:
  • stage – The Usd.Stage to add the xform.

  • path – The desired xform path.

  • position – The position where the xform should be placed in stage.

  • orientation – The xform orientation.

  • scale – The xform scale.

ovphysx.utils.shapes.add_sphere(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
radius: float = 1.0,
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
color: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add UsdGeom.Sphere to the stage.

Parameters:
  • stage – The Usd.Stage to add sphere.

  • path – The desired sphere path.

  • radius – The radius of the sphere.

  • position – The position where the sphere should be placed in stage.

  • orientation – The sphere orientation.

  • color – The color of the mesh.

ovphysx.utils.shapes.add_xform(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
position: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
orientation: pxr.Gf.Quatf = Gf.Quatf(1.0),
scale: pxr.Gf.Vec3f = Gf.Vec3f(1.0),
) → pxr.Usd.Prim#

Add xform to the stage with given transformation.

Parameters:
  • stage – The Usd.Stage to add the xform.

  • path – The desired xform path.

  • position – The position where the xform should be placed in stage.

  • orientation – The xform orientation.

  • scale – The xform scale.

Ground and quad planes#

Ground planes and sized quad planes.

A UsdPhysics.Plane is infinite and renders as nothing, so a usable ground plane is two prims: a mesh for the viewport and a collision prim beside it. add_ground_plane() authors that pair under one Xform; add_quad_plane() and add_cube_ground_plane() instead give the collider the finite extent of the geometry itself, which is what a scene needs when a body must be able to fall off the edge.

ovphysx.utils.planes.add_cube_ground_plane(
stage: pxr.Usd.Stage,
cube_path: str | pxr.Sdf.Path,
axis: str,
size: float,
position: pxr.Gf.Vec3f | pxr.Gf.Vec3d,
color: pxr.Gf.Vec3f,
)#

Add UsdGeom.Cube to the stage to act as a sized plane with thickness. The cube is scaled by a vector Gf.Vec3f(0.01, 1.0, 1.0) depending on the up Axis

size is the cube’s full edge length, which is what UsdGeom.Cube’s own size attribute means, so size=2 reaches [-1, 1] across the two lateral axes. On add_ground_plane() and add_quad_plane() size is a half size instead.

Parameters:
  • stage – The Usd.Stage to add path.

  • cube_path – The desired ground plane path.

  • axis – The up axis - “Y”, “Z”

  • size – The edge length of the cube, before the flattening scale.

  • position – The position where the mesh should be placed in stage.

  • color – The color of the mesh.

ovphysx.utils.planes.add_ground_plane(
stage: pxr.Usd.Stage,
plane_path: str | pxr.Sdf.Path,
axis: str,
size: float,
position: pxr.Gf.Vec3f | pxr.Gf.Vec3d,
color: pxr.Gf.Vec3f,
) → str#

Add ground plane to the stage. Note that it will add a mesh for rendering purpose and UsdPhysics.Plane for collision purpose.

Parameters:
  • stage – The Usd.Stage to add path.

  • plane_path – The desired ground plane path.

  • axis – The up axis - “Y”, “Z”

  • size – The half size of the mesh.

  • position – The position where the mesh should be placed in stage.

  • color – The color of the mesh.

ovphysx.utils.planes.add_plane_collider(
stage: pxr.Usd.Stage,
prim_path: str | pxr.Sdf.Path,
up_axis: str,
)#

Define a guide-purpose UsdGeom.Plane and make it a collider.

An occupied path raises ValueError and is not modified.

Parameters:
  • stage – The Usd.Stage to add the plane.

  • prim_path – The desired plane path.

  • up_axis – The plane’s up axis.

ovphysx.utils.planes.add_quad_plane(
stage: pxr.Usd.Stage,
quad_path: str | pxr.Sdf.Path,
axis: str,
size: float,
position: pxr.Gf.Vec3f | pxr.Gf.Vec3d,
color: pxr.Gf.Vec3f,
)#

Add quad mesh to the stage to act as a sized plane.

Parameters:
  • stage – The Usd.Stage to add path.

  • quad_path – The desired ground plane path.

  • axis – The up axis - “Y”, “Z”

  • size – The half size of the mesh.

  • position – The position where the mesh should be placed in stage.

  • color – The color of the mesh.

Joints#

Joint creation between two prims, or between a prim and the world.

create_joint() computes the two local frames from the bodies’ current world poses, so the joint holds them where they already are; add_joint_fixed() takes those frames from the caller instead. Passing one body anchors the joint to the world.

The PhysX joint types are codeless, so they are defined by type name. They derive from UsdPhysicsJoint, which stock usd-core does bind, so they are then reached through that typed base. Refer to ovphysx.utils.codeless.

ovphysx.utils.joints.add_joint_fixed(
stage: pxr.Usd.Stage,
joint_path: str | pxr.Sdf.Path,
actor0: str | pxr.Sdf.Path,
actor1: str | pxr.Sdf.Path,
local_pos0: pxr.Gf.Vec3f,
local_rot0: pxr.Gf.Quatf,
local_pos1: pxr.Gf.Vec3f,
local_rot1: pxr.Gf.Quatf,
break_force: float,
break_torque: float,
) → pxr.UsdPhysics.FixedJoint#

Add fixed joint to the stage.

Parameters:
  • stage – The Usd.Stage to add the joint.

  • joint_path – The desired joint path.

  • actor0 – The actor0 for the joint.

  • actor1 – The actor1 for the joint.

  • local_pos0 – The joint local position offset from the actor0

  • local_rot0 – The joint local rotation offset from the actor0

  • local_pos1 – The joint local position offset from the actor1

  • local_rot1 – The joint local rotation offset from the actor1

  • break_force – The joint break force.

  • break_torque – The joint break torque.

ovphysx.utils.joints.create_joint(
stage: pxr.Usd.Stage,
joint_type: str,
from_prim: pxr.Usd.Prim,
to_prim: pxr.Usd.Prim,
) → pxr.Usd.Prim#

Create a joint between two prims, with local frames computed from their world poses.

The joint prim is placed under the first writable ancestor of to_prim, so that instanced and prototype prims do not break authoring. Passing only one prim anchors the joint to the world.

Parameters:
  • stage – The Usd.Stage to add the joint.

  • joint_type – One of “Fixed”, “Revolute”, “Prismatic”, “Spherical”, “Distance”, “Gear”, “RackAndPinion”; anything else creates a D6 joint with all axes locked.

  • from_prim – The body0 prim, or None.

  • to_prim – The body1 prim.

ovphysx.utils.joints.create_joints(
stage: pxr.Usd.Stage,
joint_type: str,
paths: List[str | pxr.Sdf.Path],
join_to_parent: bool = False,
) → List[pxr.Usd.Prim]#

Create one joint per path, optionally anchoring each to its parent prim.

Parameters:
  • stage – The Usd.Stage to add the joints.

  • joint_type – The joint type, see create_joint.

  • paths – The body1 prim paths.

  • join_to_parent – Whether to use each prim’s parent as body0.

Physics materials#

Physics materials and their binding to prims.

A physics material is a UsdShade.Material carrying UsdPhysics.MaterialAPI, bound to a collider through the physics purpose of UsdShade.MaterialBindingAPI so that it composes independently of any render material on the same prim. Only the friction, restitution and density values the caller supplies are authored; the rest keep their schema fallback.

ovphysx.utils.materials.add_physics_material_to_prim(
stage: pxr.Usd.Stage,
prim: pxr.Usd.Prim,
material_path: str | pxr.Sdf.Path,
)#

Bind physics material to a given prim.

Parameters:
  • stage – The Usd.Stage to add path.

  • prim – The Usd.Prim where material should have the binding to.

  • material_path – The path of the material.

ovphysx.utils.materials.add_rigid_body_material(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
density=None,
static_friction=None,
dynamic_friction=None,
restitution=None,
) → bool#

Define a physics material, authoring only the attributes that were supplied.

Parameters:
  • stage – The Usd.Stage to add the material.

  • path – The desired material path.

  • density – The material density.

  • static_friction – The static friction coefficient.

  • dynamic_friction – The dynamic friction coefficient.

  • restitution – The restitution coefficient.

ovphysx.utils.materials.ensure_material_on_path(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
) → bool#

Define a UsdShade.Material at path if nothing incompatible is already there.

Parameters:
  • stage – The Usd.Stage to add the material.

  • path – The desired material path.

Collision filtering#

Collision filtering: collision groups and filtered pairs.

Two independent mechanisms decide which colliders are allowed to interact. A UsdPhysics.CollisionGroup holds a collection of colliders and a relationship naming the groups it does not collide with, which is the scalable one. A UsdPhysics.FilteredPairsAPI names individual prims on the prim itself, which is the one to reach for when two specific bodies must ignore each other.

ovphysx.utils.filtering.add_collision_group(stage: pxr.Usd.Stage, path: str | pxr.Sdf.Path)#

Define a collision group with an empty filtered-groups relationship.

Parameters:
  • stage – The Usd.Stage to add the group.

  • path – The desired collision group path.

ovphysx.utils.filtering.add_collision_to_collision_group(
stage: pxr.Usd.Stage,
collision_path: str | pxr.Sdf.Path,
collision_group_path: str | pxr.Sdf.Path,
)#

Add collision path to a collision group include rel.

Parameters:
  • stage – The Usd.Stage to add path.

  • collision_path – Collision path to add.

  • collision_group_path – Collision group prim path.

ovphysx.utils.filtering.add_pair_filter(
stage: pxr.Usd.Stage,
paths: List[str | pxr.Sdf.Path],
)#

Make every prim in paths filter collisions against every other one.

Duplicate entries and a mix of str and Sdf.Path spellings of the same prim are tolerated. Paths are compared by value, so no prim is given a filter against itself.

Parameters:
  • stage – The Usd.Stage holding the prims.

  • paths – The prim paths to filter against each other.

ovphysx.utils.filtering.is_in_collision_group(
stage: pxr.Usd.Stage,
collision_path: str | pxr.Sdf.Path,
collision_group_path: str | pxr.Sdf.Path,
) → bool#

Checks if a collision path belongs to a collision group include rel.

Parameters:
  • stage – The Usd.Stage to add path.

  • collision_path – Collision path to add.

  • collision_group_path – Collision group prim path.

ovphysx.utils.filtering.remove_collision_from_collision_group(
stage: pxr.Usd.Stage,
collision_path: str | pxr.Sdf.Path,
collision_group_path: str | pxr.Sdf.Path,
)#

Remove collision path to a collision group include rel.

Parameters:
  • stage – The Usd.Stage to add path.

  • collision_path – Collision path to add.

  • collision_group_path – Collision group prim path.

ovphysx.utils.filtering.remove_pair_filter(
stage: pxr.Usd.Stage,
paths: List[str | pxr.Sdf.Path],
)#

Undo add_pair_filter for the given paths.

The UsdPhysics.FilteredPairsAPI is removed, and the physics:filteredPairs relationship with it, so a later add_pair_filter starts from no targets.

Parameters:
  • stage – The Usd.Stage holding the prims.

  • paths – The prim paths to stop filtering.

Rigid bodies and colliders on existing prims#

Applying physics to prims that already exist.

Where shapes builds a body and its collider together, the helpers here take a prim a stage already has and give it – or take from it – mass, a force, a physics scene, or a whole API set. The set* and remove* family is the core of that: it applies and strips the collider and rigid-body API sets, and an Xformable is given colliders across its whole subtree and stripped of them the same way.

The two halves are inverses over the API schemas, and only partly over the properties authored under them. Usd.Prim.RemoveAPI and remove_api() drop the apiSchemas entry alone, so remove_physics() and the rigid-body helpers built on it leave every property in place: a prim that has to come out clean needs remove_api_schema_properties() as well – and create_api_schema_property_cache() first, if those values have to survive the round trip. remove_collider() is the deliberate exception, and the helpers that call it inherit what it does: it takes the mesh approximation and cooked-data APIs’ properties with the APIs, since those APIs are mutually exclusive and their properties would otherwise orphan onto a prim re-approximated another way. The collision APIs set_collider() re-applies keep theirs, so a re-approximated collider keeps its contact tuning.

How schemas are reached here#

PhysX APIs are applied by identifier and their properties authored by name, because ovphysx ships the PhysX schemas codeless. Refer to ovphysx.utils.codeless. Core UsdPhysics schemas still use their typed bindings, which are part of stock usd-core.

ovphysx.utils.authoring.add_density(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
value: float,
) → pxr.UsdPhysics.MassAPI#

Add density to given prim. Note that his will apply MassAPI on the prim.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • value – The desired density.

ovphysx.utils.authoring.add_force_torque(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
force: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
torque: pxr.Gf.Vec3f = Gf.Vec3f(0.0),
mode: str = 'acceleration',
is_enabled: bool = True,
is_world_space: bool = False,
) → pxr.Usd.Prim#

Add force/torque to given prim. Note that his will apply PhysxForceAPI on the prim.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • force – The desired force.

  • torque – The desired torque.

  • mode – The force/torque mode, “force” or “acceleration”.

  • is_enabled – Bool defining whether force is enabled or not.

  • is_world_space – Bool defining whether force is applied in world space or body local space.

Returns:

The prim the API was applied to.

ovphysx.utils.authoring.add_mass(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
mass: float = 1.0,
) → pxr.UsdPhysics.MassAPI#

Add mass to given prim. Note that his will apply MassAPI on the prim.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • mass – The desired mass.

ovphysx.utils.authoring.add_physics_scene(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
) → List#

Define a UsdPhysics.Scene, unless a prim already exists at path.

Parameters:
  • stage – The Usd.Stage to add the scene.

  • path – The desired scene path.

Returns:

A list holding the created path, or an empty list if nothing was created.

ovphysx.utils.authoring.clear_local_space_velocities(prim: pxr.Usd.Prim)#

Undo set_local_space_velocities, returning the prim to world-space velocities.

Parameters:

prim – The rigid body prim.

ovphysx.utils.authoring.remove_collider(prim: pxr.Usd.Prim)#

Remove the collision API set from a prim, including mesh approximation and cooked data.

The mesh approximation APIs are stripped from instanceable prims as well as meshes, mirroring set_collider. Every approximation API set_collider can apply is removed, the PhysX-only sdf and sphereFill ones included.

The approximation and cooked-data APIs’ authored properties are removed with the APIs, so nothing is left orphaned.

PhysicsCollisionAPI, PhysxCollisionAPI and PhysicsMeshCollisionAPI keep their properties – physxCollision:contactOffset and physxCollision:restOffset above all – because every set_collider() call re-applies those APIs, so a re-approximated collider keeps its contact tuning. A caller who wants those gone too calls remove_api_schema_properties() for them, as the example in create_api_schema_property_cache() does.

Parameters:

prim – The prim to strip.

ovphysx.utils.authoring.remove_collider_subtree(prim: pxr.Usd.Prim)#

Remove the collision API set from every gprim and instanceable prim in a subtree.

Instanceable prims are visited as well as gprims, mirroring set_collider_subtree; an instanceable prim is not a gprim.

Parameters:

prim – The subtree root.

ovphysx.utils.authoring.remove_physics(prim: pxr.Usd.Prim)#

Remove the rigid body API set from a prim.

Parameters:

prim – The prim to strip.

ovphysx.utils.authoring.remove_rigid_body(prim: pxr.Usd.Prim)#

Undo set_rigid_body, following the same Xformable subtree rule.

Parameters:

prim – The prim to strip.

ovphysx.utils.authoring.remove_rigid_body_subtree(prim: pxr.Usd.Prim)#

Remove the rigid body API from a prim and the collision APIs from its subtree.

Parameters:

prim – The subtree root.

ovphysx.utils.authoring.remove_static_collider(prim: pxr.Usd.Prim)#

Undo set_static_collider.

Parameters:

prim – The subtree root.

ovphysx.utils.authoring.set_collider(
prim: pxr.Usd.Prim,
approximation_shape=UsdPhysics.Tokens.none,
)#

Apply the collision API set to a prim, choosing the mesh approximation APIs to match.

A mesh that is part of a rigid body cannot use the triangle-mesh approximation, so a none request on such a mesh is upgraded to convexHull with a warning.

A prim that is already a collider is left untouched with a warning. A prim whose omni:no_collision attribute is true is left untouched. An authored false value does not opt out of collision.

Parameters:
  • prim – The prim to make a collider.

  • approximation_shape – The mesh approximation token, one of "none", "convexHull", "convexDecomposition", "meshSimplification", "boundingCube", "boundingSphere", "sphereFill" or "sdf". The first six are the UsdPhysics.Tokens values; the last two are PhysX-only and are declared here as ovphysx.utils.TOKEN_SPHERE_FILL and ovphysx.utils.TOKEN_SDF.

ovphysx.utils.authoring.set_collider_subtree(
prim: pxr.Usd.Prim,
approximation_shape=UsdPhysics.Tokens.none,
)#

Apply the collision API set to every gprim and instanceable prim in a subtree.

Parameters:
  • prim – The subtree root.

  • approximation_shape – The mesh approximation token, see set_collider.

ovphysx.utils.authoring.set_local_space_velocities(prim: pxr.Usd.Prim, value: bool)#

Choose whether a rigid body’s authored velocities are read in its own frame.

With this enabled the velocity and angularVelocity attributes are interpreted in the body’s local frame instead of world space.

Parameters:
  • prim – The rigid body prim.

  • value – True to interpret velocities in body local space.

ovphysx.utils.authoring.set_physics(prim: pxr.Usd.Prim, kinematic: bool)#

Apply the rigid body API set to a prim.

If the core rigid-body API is already present, this function completes the API set, enables the body, and updates its kinematic state.

Parameters:
  • prim – The prim to make a rigid body.

  • kinematic – Whether the body is kinematic.

ovphysx.utils.authoring.set_physics_scene_asyncsimrender(
scene_prim: pxr.Usd.Prim,
val: bool = True,
)#

Switch a physics scene between asynchronous and synchronous update, unless it is disabled.

Parameters:
  • scene_prim – The UsdPhysics.Scene prim.

  • val – True for asynchronous, False for synchronous.

ovphysx.utils.authoring.set_rigid_body(
prim: pxr.Usd.Prim,
approximation_shape,
kinematic: bool,
)#

Make a prim a rigid body and give it colliders.

An Xformable gets colliders across its whole subtree, matching how a rigid-body hierarchy is normally authored; anything else is treated as a single collider.

Parameters:
  • prim – The prim to make a rigid body.

  • approximation_shape – The mesh approximation token, see set_collider.

  • kinematic – Whether the body is kinematic.

ovphysx.utils.authoring.set_static_collider(
prim: pxr.Usd.Prim,
approximation_shape=UsdPhysics.Tokens.none,
)#

Give a subtree colliders without making it a rigid body.

Parameters:
  • prim – The subtree root.

  • approximation_shape – The mesh approximation token, see set_collider.

Stage paths#

Stage path uniquification.

Authoring a prim at a path another prim already holds silently redefines that prim, so every helper that defines geometry resolves its path through here first. Both helpers only compute a name – neither touches the stage beyond asking what is already at a path.

ovphysx.utils.paths.get_stage_next_free_path(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
prepend_default_prim: bool,
) → str#

Gets valid path in stage, if the path already exists it will append number.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • prepend_default_prim – Whether prepend default prim path name.

Transforms#

Transform and xform-op helpers.

The physics runtime expects a translate-orient-scale xform op stack, and authoring one correctly by hand is fiddly: an op may already exist at a different precision, and resetXformStack has to survive a rewrite. These helpers encapsulate that.

ovphysx.utils.transform.copy_transform_as_scale_orient_translate(
src: pxr.Usd.Prim | pxr.UsdGeom.Xformable,
dst: pxr.Usd.Prim | pxr.UsdGeom.Xformable,
)#

Copies the local transforms from one Xformable to another as a default scale->orient->translate stack.

Note that:
  • Any skew in the src transform will be lost.

  • A resetXformStack is preserved, but not the XformOps that are ignored due to the reset.

  • The transform attribute precision of added XformOps is set to UsdGeom.XformOp.PrecisionFloat.

  • Obsolete xformOp: namespace attributes in dst are not removed (and cannot be for layers)

Parameters:
  • src – The source prim or Xformable.

  • dst – The destination prim or Xformable.

ovphysx.utils.transform.get_aligned_body_transform(stage, cache, joint, body0base)#

Compute the transform that aligns one joint body onto the other.

Parameters:
  • stage – The Usd.Stage holding the bodies.

  • cache – A UsdGeom.XformCache used for the world transforms.

  • joint – The UsdPhysics.Joint to read.

  • body0base – True to treat body0 as the fixed base, False for body1.

ovphysx.utils.transform.get_axis_aligned_vector(axis, len)#

Build an axis-aligned vector of the given length.

Parameters:
  • axis – The axis name - “X”, “Y” or “Z”.

  • len – The signed length along that axis.

ovphysx.utils.transform.get_basis(up_axis)#

Get the (up, forward, right) basis vectors for a stage’s up axis.

Parameters:

up_axis – The stage up axis - “Y” selects a Y-up basis, anything else selects a Z-up basis.

ovphysx.utils.transform.get_forward_vector(up_axis)#

Get the forward vector for a stage’s up axis.

Parameters:

up_axis – The stage up axis - “Y” or “Z”.

ovphysx.utils.transform.get_translation(prim: pxr.Usd.Prim) → pxr.Gf.Vec3f | pxr.Gf.Vec3d#

Return the translate xform op value from the given prim.

Parameters:

prim – The Usd.Prim to check.

ovphysx.utils.transform.get_unit_scale_factor(stage)#

Get the stage’s units-per-meter scale factor.

Parameters:

stage – The Usd.Stage to query.

ovphysx.utils.transform.get_world_position(stage, path)#

Get the world-space center of a prim’s axis-aligned world bound.

Parameters:
  • stage – The Usd.Stage to query.

  • path – The path of the imageable prim.

ovphysx.utils.transform.set_or_add_orient_op(
xformable: pxr.UsdGeom.Xformable,
orient: pxr.Gf.Quatf | pxr.Gf.Quatd | pxr.Gf.Quath,
) → pxr.UsdGeom.XformOp | None#

Sets or adds the orient XformOp on the input Xformable to provided orient value.

Note that:
  • The precision of an added attribute is UsdGeom.XformOp.PrecisionFloat.

Parameters:
  • xformable – The Xformable to modify.

  • orient – The orient quaternion

Returns:

The set or added XformOp, or None if the prim is not an Xformable. Its scale and translate siblings answer False for that case rather than None.

ovphysx.utils.transform.set_or_add_scale_op(
xformable: pxr.UsdGeom.Xformable,
scale: pxr.Gf.Vec3f | pxr.Gf.Vec3d | pxr.Gf.Vec3h,
) → pxr.UsdGeom.XformOp | Literal[False]#

Sets or adds the scale XformOp on the input Xformable to provided scale value.

Note that:
  • The precision of an added attribute is UsdGeom.XformOp.PrecisionFloat.

Parameters:
  • xformable – The Xformable to modify.

  • scale – The scale vector

Returns:

The set or added XformOp, or False if the prim is not an Xformable.

ovphysx.utils.transform.set_or_add_scale_orient_translate(
xformable: pxr.UsdGeom.Xformable,
scale: pxr.Gf.Vec3f | pxr.Gf.Vec3d | pxr.Gf.Vec3h,
orient: pxr.Gf.Quatf | pxr.Gf.Quatd | pxr.Gf.Quath,
translate: pxr.Gf.Vec3f | pxr.Gf.Vec3d | pxr.Gf.Vec3h,
) → List[pxr.UsdGeom.XformOp] | Literal[False]#

Sets or adds scale, orient, and translate XformOps of xformable.

Note that:
  • The precision of created attributes is UsdGeom.XformOp.PrecisionFloat.

Parameters:
  • xformable – The Xformable to modify.

  • scale – The scale vector

  • orient – The orientation quaternion

  • translate – The translation vector

Returns:

List of set and created xform ops that will be [translate, orient, scale], or False if the prim is not an Xformable.

ovphysx.utils.transform.set_or_add_translate_op(
xformable: pxr.UsdGeom.Xformable,
translate: pxr.Gf.Vec3f | pxr.Gf.Vec3d | pxr.Gf.Vec3h,
) → pxr.UsdGeom.XformOp | Literal[False]#

Sets or adds the translate XformOp on the input Xformable to provided translate value.

Note that:
  • The precision of an added attribute is UsdGeom.XformOp.PrecisionFloat.

Parameters:
  • xformable – The Xformable to modify.

  • translate – The translate vector

Returns:

The set or added XformOp, or False if the prim is not an Xformable.

ovphysx.utils.transform.setup_transform_as_scale_orient_translate(
xformable: pxr.Usd.Prim | pxr.UsdGeom.Xformable,
)#

Changes the local transform (ops) to the physics default scale->orient->translate stack.

Note that:
  • Any skew in the transform will be lost.

  • A resetXformStack is preserved, but not the XformOps that are ignored due to the reset.

  • The transform attribute precision is set to UsdGeom.XformOp.PrecisionFloat.

  • Obsolete xformOp: namespace attributes are not removed (and cannot be for layers)

Parameters:

xformable – The prim or Xformable to modify.

Mesh construction and tetrahedral meshes#

Procedural mesh construction and triangle / tetrahedron mesh math.

Two groups of helper live here: constructors that define a UsdGeom.Mesh for a primitive shape, and pure geometry routines over point and index lists that carry no USD state.

create_tetra_voxels yields an empty mesh for a non-positive integer voxel_dim rather than raising.

ovphysx.utils.mesh.calculate_tetra_volume(a, b, c, d)#

Compute the signed volume of a tetrahedron.

Parameters:
  • a – The four tetrahedron corner points.

  • b – The four tetrahedron corner points.

  • c – The four tetrahedron corner points.

  • d – The four tetrahedron corner points.

ovphysx.utils.mesh.compute_bounding_box_diagonal(points) → float#

Gets diagonal length of given point bounds.

Parameters:

points – The input points. Any iterable of indexable three-component points, e.g. Gf.Vec3f, tuples or lists.

Raises:

ValueError – If points is empty.

ovphysx.utils.mesh.convert_tetra_to_triangle_soup(points_in, indices_in)#

Expand every tetrahedron into its four unshared triangles.

Parameters:
  • points_in – The tet mesh points.

  • indices_in – The tet mesh vertex indices, four per tetrahedron.

ovphysx.utils.mesh.create_mesh(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
points: List[pxr.Gf.Vec3f] | List[pxr.Gf.Vec3d],
normals: List[pxr.Gf.Vec3f] | List[pxr.Gf.Vec3d],
indices: List[int],
vertex_counts: List[int],
) → pxr.UsdGeom.Mesh#

Create UsdGeom.Mesh from given points, normals, indices and face counts.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • points – The input points.

  • normals – The input normals.

  • indices – The indices for faces.

  • vertex_counts – Face counts.

ovphysx.utils.mesh.create_mesh_concave(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
half_size: float,
) → pxr.UsdGeom.Mesh#

Create UsdGeom.Mesh that represents a concave mesh.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • half_size – The half size of the mesh.

ovphysx.utils.mesh.create_mesh_cone(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
height: float,
radius: float,
tesselation: int = 32,
) → pxr.UsdGeom.Mesh#

Create UsdGeom.Mesh that represents a cone mesh.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • height – The height of the cone.

  • radius – The radius of the cone.

  • tesselation – The tesselation of the cone mesh.

ovphysx.utils.mesh.create_mesh_cube(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
half_size: float,
) → pxr.UsdGeom.Mesh#

Create UsdGeom.Mesh that represents a cube mesh.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • half_size – The half size of the cube.

ovphysx.utils.mesh.create_mesh_cylinder(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
height: float,
radius: float,
tesselation: int = 32,
) → pxr.UsdGeom.Mesh#

Create UsdGeom.Mesh that represents a cylinder mesh.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • height – The height of the cylinder.

  • radius – The radius of the cylinder.

  • tesselation – The tesselation of the cylinder mesh.

ovphysx.utils.mesh.create_mesh_square_axis(
stage: pxr.Usd.Stage,
path: str | pxr.Sdf.Path,
axis: str,
half_size: float,
) → pxr.UsdGeom.Mesh#

Create UsdGeom.Mesh that represents a square.

Parameters:
  • stage – The Usd.Stage to add path.

  • path – The desired path to create.

  • axis – The up axis “Y”, “Z”.

  • half_size – The half size of the square.

ovphysx.utils.mesh.create_tetra_voxel_box(voxel_dim)#

Build a unit-cube tetrahedral mesh centered on the origin.

A non-positive integer grid resolution yields an empty mesh.

Parameters:

voxel_dim – The voxel grid resolution.

ovphysx.utils.mesh.create_tetra_voxel_sphere(voxel_dim)#

Build a unit-diameter sphere tetrahedral mesh centered on the origin.

A non-positive integer grid resolution yields an empty mesh.

Parameters:

voxel_dim – The voxel grid resolution.

ovphysx.utils.mesh.create_tetra_voxels(voxel_dim, occupancy_filter_func)#

Build a tetrahedral mesh from an occupancy-filtered voxel grid.

Every occupied voxel is split into the five tetrahedra of cube_tetrahedra. Alternate cubes are mirrored per axis, and a cube mirrored an odd number of times has its tetrahedra re-wound, so neighbouring cubes share faces.

Parameters:
  • voxel_dim – The grid resolution, used for all three axes. A non-positive integer yields an empty mesh.

  • occupancy_filter_func – Called as (x, y, z, dimx, dimy, dimz); returns True for an occupied voxel.

ovphysx.utils.mesh.create_triangle_mesh_cube(dim: int)#

Build the surface triangle mesh of a voxelized unit cube.

A non-positive integer grid resolution yields an empty mesh.

Parameters:

dim – The voxel grid resolution.

ovphysx.utils.mesh.create_triangle_mesh_square(dimx: int, dimy: int, scale: float = 1.0)#

Creates points and vertex data for a regular-grid flat triangle mesh square.

A non-positive integer grid dimension yields an empty mesh.

Parameters:
  • dimx – Mesh-vertex resolution in X

  • dimy – Mesh-vertex resolution in Y

  • scale – Uniform scale applied to vertices

Returns:

The vertex and index data

Return type:

points, indices

ovphysx.utils.mesh.cube_tetrahedra()#

Return the five-tetrahedron decomposition of a unit cube.

ovphysx.utils.mesh.extract_triangle_surface_from_tetra(tetra_points, tetra_indices)#

Extract the outer surface triangles of a tetrahedral mesh.

Occurrences of each face are counted: a face that appears more than once is interior and is discarded, and the faces appearing exactly once form the surface.

Parameters:
  • tetra_points – The tet mesh points.

  • tetra_indices – The tet mesh vertex indices, four per tetrahedron.

ovphysx.utils.mesh.fixup_tetra_mesh_volumes(points, indices)#

Return tet indices with inverted tetrahedra re-wound to positive volume.

Parameters:
  • points – The tet mesh points.

  • indices – The tet mesh vertex indices, four per tetrahedron.

ovphysx.utils.mesh.triangulate_mesh(mesh: pxr.UsdGeom.Mesh) → List[int]#

Fan-triangulate a mesh’s faces into a flat triangle index list.

Parameters:

mesh – The UsdGeom.Mesh to read.

ovphysx.utils.mesh.verify_tetra_mesh(points, indices)#

Check a tet mesh for index-count, out-of-range and inverted-volume errors.

Problems are logged as warnings; the first one found stops the check. An index below zero is out of range on the same terms as one past the end.

Parameters:
  • points – The tet mesh points.

  • indices – The tet mesh vertex indices, four per tetrahedron.

Particles#

Particle system, particle set and PBD material authoring.

A particle simulation needs a PhysxParticleSystem prim, a set prim carrying PhysxParticleSetAPI that points back at it, and usually a PBD material. The helpers here author all three, plus the optional feature APIs (anisotropy, smoothing, isosurface, diffuse particles) and the sampling API.

Every schema attribute is an optional keyword that defaults to None and is only authored when supplied, so an unset attribute keeps the schema fallback rather than being pinned to a value chosen here.

The PhysX schemas are codeless, so these helpers apply APIs by identifier and author properties by name; add_physx_particle_system() answers with the Usd.Prim it defined. Refer to ovphysx.utils.codeless.

ovphysx.utils.particles.add_pbd_material_viscous(p)#

Applies a viscous-fluid PBD material preset to a material prim.

The surface tension and viscosity presets are in SI, so they are converted to the stage’s units before being authored.

Parameters:

p – The UsdShade.Material prim.

ovphysx.utils.particles.add_pbd_material_water(p)#

Applies a water PBD material preset to a material prim.

The surface tension and viscosity presets are in SI, so they are converted to the stage’s units before being authored.

Parameters:

p – The UsdShade.Material prim.

ovphysx.utils.particles.add_pbd_particle_material(
stage,
path,
friction=None,
particle_friction_scale=None,
damping=None,
viscosity=None,
vorticity_confinement=None,
surface_tension=None,
cohesion=None,
adhesion=None,
particle_adhesion_scale=None,
adhesion_offset_scale=None,
gravity_scale=None,
density=None,
cfl_coefficient=None,
)#

Applies the PhysxPBDMaterialAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to UsdShade.Material to which the material API should be applied to

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.particles.add_physx_diffuse_particles(
stage,
path,
enabled=None,
max_diffuse_particle_multiplier=None,
threshold=None,
lifetime=None,
air_drag=None,
bubble_drag=None,
buoyancy=None,
kinetic_energy_weight=None,
pressure_weight=None,
divergence_weight=None,
collision_decay=None,
use_accurate_velocity=None,
)#

Applies the PhysxDiffuseParticlesAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to the prim to which the diffuse particle API should be applied to

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.particles.add_physx_particle_anisotropy(
stage,
path,
enabled=None,
scale=None,
min=None,
max=None,
)#

Applies the PhysxParticleAnisotropyAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to the prim to which the anisotropy API should be applied to

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.particles.add_physx_particle_isosurface(
stage,
path,
enabled=None,
max_vertices=None,
max_triangles=None,
max_subgrids=None,
grid_spacing=None,
surface_distance=None,
grid_filtering_passes=None,
grid_smoothing_radius=None,
num_mesh_smoothing_passes=None,
num_mesh_normal_smoothing_passes=None,
)#

Applies the PhysxParticleIsosurfaceAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to the prim to which the isosurface API should be applied to

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.particles.add_physx_particle_smoothing(stage, path, enabled=None, strength=None)#

Applies the PhysxParticleSmoothingAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to the prim to which the smoothing API should be applied to

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.particles.add_physx_particle_system(
stage,
particle_system_path,
particle_system_enabled=None,
simulation_owner=None,
contact_offset=None,
rest_offset=None,
particle_contact_offset=None,
solid_rest_offset=None,
fluid_rest_offset=None,
enable_ccd=None,
solver_position_iterations=None,
max_depenetration_velocity=None,
wind=None,
max_neighborhood=None,
neighborhood_scale=None,
max_velocity=None,
global_self_collision_enabled=None,
non_particle_collision_enabled=None,
) → pxr.Usd.Prim | None#

Creates a PhysxParticleSystem prim at particle_system_path on stage.

Parameters:
  • stage – The stage

  • particle_system_path – Path where the system should be created at

  • attributes (... schema) – See USD schema for documentation

An occupied path is rejected with a ValueError rather than the prim that is already there being retyped.

The concrete codeless prim type must be registered before this function mutates the stage.

Returns:

The particle system Usd.Prim, or None if the prim could not be defined.

Raises:

CodelessSchemaError – If PhysxParticleSystem is not registered.

ovphysx.utils.particles.add_physx_particleset_pointinstancer(
stage,
path: str | pxr.Sdf.Path,
positions,
velocities,
particle_system_path,
self_collision,
fluid,
particle_group,
particle_mass,
density,
num_prototypes: int = 1,
prototype_indices: list | None = None,
) → pxr.Usd.Prim#

Creates a particle set based on a UsdGeom.PointInstancer at path on stage.

Parameters:
  • stage – The stage

  • path – Path where the UsdGeom.PointInstancer particle set should be created

  • positions – List of particle positions

  • velocities – List of particle velocities

  • particle_system_path – Path to particle system that simulates the set

  • self_collision – Enable particle-particle collision in the set

  • fluid – Simulate the particle set as fluid

  • particle_group – The particle group, see schema API doc

  • particle_mass – The per-particle mass - total mass of set is num particles * particle_mass

  • density – The density of the particles - is used to compute particle (set) mass if no mass provided

  • num_prototypes – The number of render prototypes to create (children of point instancer)

  • prototype_indices – The prototype indices for the particles (same length as positions). Will default to 0 for all if not provided.

An occupied path is rejected with a ValueError rather than the prim that is already there being retyped.

Returns:

The created UsdGeom.PointInstancer prim

ovphysx.utils.particles.add_physx_particleset_points(
stage,
path,
positions_list,
velocities_list,
widths_list,
particle_system_path,
self_collision,
fluid,
particle_group,
particle_mass,
density,
) → pxr.UsdGeom.Points#

Creates a particle set based on a UsdGeom.Points at path on stage.

Parameters:
  • stage – The stage

  • path – Path where the UsdGeom.Points particle set should be created

  • positions_list – List of particle positions

  • velocities_list – List of particle velocities

  • widths_list – List of particle widths

  • particle_system_path – Path to particle system that simulates the set

  • self_collision – Enable particle-particle collision in the set

  • fluid – Simulate the particle set as fluid

  • particle_group – The particle group, see schema API doc

  • particle_mass – The per-particle mass - total mass of set is num particles * particle_mass

  • density – The density of the particles - is used to compute particle (set) mass if no mass provided

An occupied path is rejected with a ValueError rather than the prim that is already there being retyped.

Returns:

The UsdGeom.Points

ovphysx.utils.particles.configure_particle_set(
particle_set_prim,
particle_system_path,
self_collision,
fluid,
particle_group,
mass=0.0,
density=0.0,
)#

Applies the particle set and mass APIs that turn a points or instancer prim into particles.

Parameters:
  • particle_set_prim – The UsdGeom.Points or UsdGeom.PointInstancer prim.

  • particle_system_path – Path to the particle system that simulates the set.

  • self_collision – Enable particle-particle collision in the set.

  • fluid – Simulate the particle set as fluid.

  • particle_group – The particle group, see schema API doc.

  • mass – The total mass of the set.

  • density – The density used when no mass is provided.

ovphysx.utils.particles.create_particles_grid(
lower,
particle_spacing,
dim_x,
dim_y,
dim_z,
uniform_particle_velocity=Gf.Vec3f(0.0),
) → Tuple[List[pxr.Gf.Vec3f], List[pxr.Gf.Vec3f]]#

Builds a regular grid of particle positions and a matching velocity list.

Parameters:
  • lower – The grid’s minimum corner.

  • particle_spacing – The distance between adjacent particles.

  • dim_x – Particle count along X.

  • dim_y – Particle count along Y.

  • dim_z – Particle count along Z.

  • uniform_particle_velocity – The velocity given to every particle.

Returns:

A (positions, velocities) tuple.

ovphysx.utils.particles.poisson_sample_mesh(stage: pxr.Usd.Stage, prim_path: pxr.Sdf.Path)#

Marks a mesh for volume particle sampling, creating a particle system if the stage has none.

Only authors the sampling request; the runtime performs the sampling when the stage is simulated.

The stage’s first particle system is reused wherever it sits. Creating one defines /World and makes it the stage default prim when the stage has none.

Parameters:
  • stage – The stage.

  • prim_path – Path to the mesh to sample.

Returns:

The path of the particle system that will sample the mesh, or an empty Sdf.Path if no particle system could be obtained.

A stage that has no particle system and whose default particle system path is already held by a prim of another type gets the empty path too, rather than that prim being written over.

Deformables#

Volume and surface deformable body authoring.

A deformable body is spread across several prims and API schemas, and getting the combination wrong fails quietly at parse time rather than loudly at authoring time. These helpers validate the prim type up front, apply the schema set in the order the parser expects, and author the rest-shape attributes.

Two shapes are supported. The set_physics_*_deformable_body pair configures a single prim that is both the simulation and the collision geometry. The create_auto_*_deformable_hierarchy pair builds the multi-prim layout instead, where a root prim carries the body API, a cooking source mesh drives generation of the simulation and collision meshes, and any remaining point-based geometry in the subtree becomes skinned visual geometry with a bind pose.

The deformable schemas are multiple-apply and partly codeless, so they are reached by type name through Usd.Prim.ApplyAPI and by property path rather than through generated Python classes.

The bind-pose writes in the two create_auto_* helpers are the one exception to routing property access through codeless. They use Usd.Prim.CreateAttribute directly, so that a prim whose OmniPhysicsDeformablePoseAPI application USD refuses does not abort the pass over the rest of the subtree; codeless.set_attr would raise there. REQ-PYTHON-UTILS-001, “Where the deformable pose attributes stay raw”, records the reasoning.

ovphysx.utils.deformable.add_auto_deformable_mesh_simplification(
stage,
prim_path: pxr.Sdf.Path,
) → bool#
Add a simplification collision mesh on a prim with PhysxAutoDeformableBodyAPI, and setup the deformable

body correspondingly.

Parameters:
  • stage – The stage

  • prim_path – Path to UsdGeom.Scope/UsdGeom.Xform to which the PhysxAutoDeformableBodyAPI is applied to.

Returns:

True / False that indicates success of schema application

ovphysx.utils.deformable.add_deformable_material(
stage: pxr.Usd.Stage,
path,
density=None,
static_friction=None,
dynamic_friction=None,
youngs_modulus=None,
poissons_ratio=None,
)#

Applies the UsdPhysics.DeformableMaterialAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to UsdShade.Material to which the material API should be applied to.

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.deformable.add_surface_deformable_material(
stage: pxr.Usd.Stage,
path,
density=None,
static_friction=None,
dynamic_friction=None,
youngs_modulus=None,
poissons_ratio=None,
surface_thickness=None,
surface_stretch_stiffness=None,
surface_shear_stiffness=None,
surface_bend_stiffness=None,
)#

Applies the UsdPhysics.SurfaceDeformableMaterialAPI to the prim at path on stage.

Parameters:
  • stage – The stage

  • path – Path to UsdShade.Material to which the material API should be applied to.

  • attributes (... schema) – See USD schema for documentation

Returns:

True if the API apply succeeded.

ovphysx.utils.deformable.create_auto_surface_deformable_hierarchy(
stage: pxr.Usd.Stage,
root_prim_path: str | pxr.Sdf.Path,
simulation_mesh_path: str | pxr.Sdf.Path,
cooking_src_mesh_path: str | pxr.Sdf.Path,
cooking_src_simplification_enabled: bool,
set_visibility_with_guide_purpose: bool = False,
) → bool#

Creates a surface deformable body from a stage hierachy and adds necessary prims and APIs. For single prim deformable bodies, use set_physics_surface_deformable_body on a UsdGeom.Mesh.

Parameters:
  • stage – The stage

  • root_prim_path – Path to valid a UsdGeom.Imageable which cannot be a UsdGeom.Gprim. The UsdPhysics.DeformableBodyAPI is applied to this prim.

  • simulation_mesh_path – Path to where simulation mesh should be created or a valid UsdGeom.Mesh.

  • cooking_src_mesh_path – Path to valid UsdGeom.Mesh that is used in cooking to generate the simulation mesh. May be outside of root_prim_path sub-hierarchy.

  • cooking_src_simplification_enabled – If True, PhysxAutoDeformableMeshSimplificationAPI is applied.

  • set_visibility_with_guide_purpose – If True, the simulation mesh is assigned the guide purpose to hide it from rendering - but only if other GPrims are present under root_prim_path to provide visible geometry.

Returns:

True / False that indicates success of creation.

A PointBased visual prim without points at the default time returns False before the stage is modified.

ovphysx.utils.deformable.create_auto_volume_deformable_hierarchy(
stage: pxr.Usd.Stage,
root_prim_path: str | pxr.Sdf.Path,
simulation_tetmesh_path: str | pxr.Sdf.Path,
collision_tetmesh_path: str | pxr.Sdf.Path,
cooking_src_mesh_path: str | pxr.Sdf.Path,
simulation_hex_mesh_enabled: bool,
cooking_src_simplification_enabled: bool,
set_visibility_with_guide_purpose: bool = False,
) → bool#

Creates a volume deformable body from a stage hierachy and adds necessary prims and APIs. For single prim deformable bodies, use set_physics_volume_deformable_body on a UsdGeom.TetMesh.

Parameters:
  • stage – The stage

  • root_prim_path – Path to valid a UsdGeom.Imageable which cannot be a UsdGeom.Gprim. The UsdPhysics.DeformableBodyAPI is applied to this prim.

  • simulation_tetmesh_path – Path to where simulation mesh should be created or a valid UsdGeom.TetMesh.

  • collision_tetmesh_path – Path to where collision mesh should be created or a valid UsdGeom.TetMesh. CollisionAPI is applied to the collision mesh. May be identical to simulation_tetmesh_path.

  • cooking_src_mesh_path – Path to valid UsdGeom.Mesh that is used in cooking to generate the simulation and collision mesh. May be outside of root_prim_path sub-hierarchy.

  • simulation_hex_mesh_enabled – If True, simulation mesh is generated as a hexahedral mesh.

  • cooking_src_simplification_enabled – If True, PhysxAutoDeformableMeshSimplificationAPI is applied.

  • set_visibility_with_guide_purpose – If True, the simulation and collision meshes are assigned the guide purpose to hide them from rendering - but only if other GPrims are present under root_prim_path to provide visible geometry. If the simulation and collision meshes are the only geometry present and are distinct, then only the simulation mesh is assigned the guide purpose, leaving the collision mesh for visual representation.

Returns:

True / False that indicates success of creation.

A hexahedral mesh API that fails to apply returns False, on the same terms as a failed mesh simplification request. A caller that asked for a hexahedral simulation mesh is never told it succeeded with a tetrahedral one.

A PointBased visual prim without points at the default time returns False before the stage is modified.

ovphysx.utils.deformable.remove_auto_deformable_body(stage, prim_path: pxr.Sdf.Path)#

Removes the auto deformable body API set and its generated sub-component APIs.

Parameters:
  • stage – The stage.

  • prim_path – Path to the prim carrying PhysxAutoDeformableBodyAPI.

ovphysx.utils.deformable.remove_auto_deformable_hexahedral_mesh(stage, prim_path: pxr.Sdf.Path)#

Removes the hexahedral simulation mesh request from an auto deformable body.

Parameters:
  • stage – The stage.

  • prim_path – Path to the prim carrying PhysxAutoDeformableHexahedralMeshAPI.

ovphysx.utils.deformable.remove_auto_deformable_mesh_simplification(
stage,
prim_path: pxr.Sdf.Path,
)#

Removes the cooking-source simplification request from an auto deformable body.

Parameters:
  • stage – The stage.

  • prim_path – Path to the prim carrying PhysxAutoDeformableMeshSimplificationAPI.

ovphysx.utils.deformable.remove_deformable_body(stage, prim_path: pxr.Sdf.Path)#

Removes every deformable body API, and its local properties, from a prim and its subtree.

Both the single-prim and the hierarchical layouts are handled, and the per-instance deformable pose APIs are removed too. A simulation mesh prim that was generated automatically is left in place, since a caller may have taken ownership of it.

The sweep covers the supplied prim and every descendant, at any depth. It does not stop at a prim that resets its xform stack, so it also reaches pose APIs a caller applied outside the range the create_auto_*_deformable_hierarchy helpers author over.

UsdPhysics.CollisionAPI is the exception, and is taken only from a prim carrying a deformable simulation API, at any depth of that same sweep. Every other prim keeps it, a volume hierarchy’s separate collision mesh included. A collision API the caller applied to the simulation geometry goes with the body.

A property is removed from the current edit target, so one whose opinion arrives over a reference or an inherit arc, or from a stronger layer, keeps its resolved value after its API is gone. The API itself does go: the removal composes over such an arc.

Parameters:
  • stage – The stage.

  • prim_path – Path to the deformable body root prim.

ovphysx.utils.deformable.set_physics_surface_deformable_body(
stage,
prim_path: pxr.Sdf.Path,
) → bool#

Setup a surface deformable body based on a UsdGeom.Mesh at prim_path on stage and add necessary prims and APIs. For hierarchical setups use create_auto_surface_deformable_hierarchy.

Parameters:
  • stage – The stage

  • prim_path – Path to UsdGeom.Mesh ‘sim mesh’ to which the UsdPhysics.DeformableBodyAPI is applied to.

Returns:

True / False that indicates success of schema application

Missing default-time points or face topology return False before the stage is modified.

ovphysx.utils.deformable.set_physics_volume_deformable_body(
stage,
prim_path: pxr.Sdf.Path,
) → bool#

Setup a volume deformable body based on a UsdGeom.TetMesh at prim_path on stage and add necessary prims and APIs. For hierarchical setups use create_auto_volume_deformable_hierarchy.

Parameters:
  • stage – The stage

  • prim_path – Path to UsdGeom.TetMesh ‘sim mesh’ to which the UsdPhysics.DeformableBodyAPI is applied to.

Returns:

True / False that indicates success of schema application

Missing default-time points or tetrahedron indices return False before the stage is modified.

Schema introspection#

USD schema introspection and API-schema property manipulation.

These helpers work against the USD schema registry, so they cover schemas that were registered from a codeless plugin as well as compiled ones. That is what makes them useful outside Kit: a caller that only has pxr can still enumerate a PhysX API schema’s properties, snapshot them, and restore them.

How properties are reached#

ovphysx ships the PhysX schemas codeless, so TfType.pythonClass is None for them and every property here is reached through the registry:

ovphysx.utils.schema.ancestor_has_api(name, prim)#

Check whether a prim or any ancestor has an API schema applied.

A prim that resets its xform stack terminates the ascent, because it no longer inherits its ancestors’ transform. The boundary is read with UsdGeom.Xformable.GetResetXformStack().

Parameters:
  • name – The API schema to look for, as a schema identifier string, a typed schema class, or a Tf.Type.

  • prim – The Usd.Prim to start from.

ovphysx.utils.schema.apply_api_schema_property_cache(cache, prim, multiple_api_token=None)#

Restore values captured by create_api_schema_property_cache().

Replays the cache through the generic Usd.Prim property API, so it works for codeless and compiled schemas alike. Apply the owning API schema to the prim first; this only authors properties.

Unset properties – a None attribute value, or None relationship targets – are skipped so that restoring does not author an opinion the snapshot did not have. The relationship test is targets is None and not not targets: an empty target list is how an authored-empty relationship is snapshotted. See create_api_schema_property_cache().

Parameters:
  • cache – The cache to replay.

  • prim – The Usd.Prim to author on.

  • multiple_api_token – The instance name, for a multiple-apply schema.

ovphysx.utils.schema.create_api_schema_property_cache(api, prim)#

Snapshot an API schema’s attribute and relationship values on a prim.

The result is consumed by apply_api_schema_property_cache(), which makes it possible to remove an API schema and restore its authored values later.

Only authored state is recorded. The snapshot holds one entry per property the API declares, and what that entry carries is the property’s authored value or targets, or nothing at all: an attribute that has only its schema fallback, and a relationship with no authored targets, are both snapshotted as unset.

“Authored” is USD’s own sense of the word throughout: an opinion exists somewhere in the prim’s composed property stack, as opposed to the value coming from a schema fallback. An opinion arriving over a reference, an inherit, a specialize, a variant or a weaker sublayer therefore counts as authored and is recorded. The snapshot does not tell a local opinion from a composed one; what it excludes is the fallback.

A relationship needs Usd.Relationship.HasAuthoredTargets for that distinction, because an unauthored relationship and one authored with an empty target list both compose to an empty list. An unauthored one is stored with None targets, as an unauthored attribute is stored with a None value; an authored one is stored with its target list whether or not that list is empty. apply_api_schema_property_cache() skips None targets and not the empty list, so a relationship authored with no targets is replayed and one carrying no opinion is left alone.

The cache exists for remove_api_schema_properties(), not for the removal of the API schema: Usd.Prim.RemoveAPI and ovphysx.utils.codeless.remove_api() drop the prim’s apiSchemas entry and leave every property authored under it in place.

remove_collider() strips the approximation and cooked-data APIs’ properties itself and leaves PhysxCollisionAPI’s, so removing those is the caller’s to ask for and snapshotting them first is how the caller gets them back.

Example:

from pxr import UsdPhysics

from ovphysx.utils import (
    apply_api_schema_property_cache,
    create_api_schema_property_cache,
    remove_api_schema_properties,
    remove_collider,
    set_collider,
)

# prim is a UsdGeom.Mesh set up as a convexHull collider, carrying
# authored physxCollision:contactOffset and physxCollision:restOffset
# and a physxConvexHullCollision:hullVertexLimit.
tuning = create_api_schema_property_cache("PhysxCollisionAPI", prim)

# Takes the hull API's own opinions with it; leaves the two offsets,
# which is why they have to be removed explicitly here.
remove_collider(prim)
remove_api_schema_properties("PhysxCollisionAPI", prim)

set_collider(prim, UsdPhysics.Tokens.convexDecomposition)
apply_api_schema_property_cache(tuning, prim)

The prim ends up approximated as a convex decomposition, carrying the two offsets it started with and no leftover physxConvexHullCollision opinion. The other attributes PhysxCollisionAPI declares stay unauthored, because the snapshot recorded no value for them.

Parameters:
  • api – The API schema type-name string or schema class.

  • prim – The Usd.Prim to read.

Returns:

[attributes, relationships], where each attribute entry is (name, value_type, value) and each relationship entry is (name, targets). The value type is the Sdf.ValueTypeName needed to recreate the property, a codeless schema having no class to name.

value and targets are both None for an unauthored property. targets is an empty list only for a relationship authored with no targets, which is what lets that opinion survive a restore.

ovphysx.utils.schema.create_multiple_api_schema_property_cache(
api,
prim,
api_prefix,
multiple_token,
)#

Snapshot one instance of a multiple-apply API schema’s properties.

Parameters:
  • api – The API schema type-name string or schema class.

  • prim – The Usd.Prim to read.

  • api_prefix – The property namespace prefix, e.g. “physxCookedData”.

  • multiple_token – The instance name.

Returns:

The same shape as create_api_schema_property_cache(), except that names are recorded as prefix:__INSTANCE_NAME__:name templates. apply_api_schema_property_cache() substitutes its multiple_api_token into them, so a snapshot can be replayed onto a different instance. It records authored state only, on the same terms – including None targets for a relationship instance with no authored ones, as against an empty list for one authored with none.

ovphysx.utils.schema.descendant_has_api(name, prim)#

Check whether a prim or any descendant has an API schema applied.

A prim that resets its xform stack terminates the descent, because its subtree no longer inherits the ancestor’s transform. The boundary is read with UsdGeom.Xformable.GetResetXformStack().

Parameters:
  • name – The API schema to look for, as a schema identifier string, a typed schema class, or a Tf.Type.

  • prim – The Usd.Prim to start from.

ovphysx.utils.schema.get_schema_attribute(
schema,
) → List[Tuple[str, pxr.Sdf.AttributeSpec]]#

List (name, spec) pairs for a schema’s attributes.

Parameters:

schema – A schema type-name string or schema class.

Returns:

(bare_property_name, Sdf.AttributeSpec) pairs. Read spec.typeName for the value type.

ovphysx.utils.schema.get_schema_instances(prim, schema_type_name)#

Get the instance names of a multiple-apply schema applied to a prim.

Parameters:
  • prim – The Usd.Prim to inspect.

  • schema_type_name – The multiple-apply schema type name.

ovphysx.utils.schema.get_schema_prim_def(schema)#

Get the registry prim definition for an applied API or concrete schema.

Parameters:

schema – A schema type-name string or schema class.

ovphysx.utils.schema.get_schema_property_names(schema)#

List the property names a schema registers.

Parameters:

schema – A schema type-name string or schema class.

ovphysx.utils.schema.get_schema_relationship(
schema,
) → List[Tuple[str, pxr.Sdf.RelationshipSpec]]#

List (name, spec) pairs for a schema’s relationships.

Parameters:

schema – A schema type-name string or schema class.

Returns:

(bare_property_name, Sdf.RelationshipSpec) pairs. See get_schema_attribute() for why this is a spec and not a class.

ovphysx.utils.schema.get_tf_type_compatible(schema_type_or_type_name)#

Resolve a schema identifier to a TfType, passing a TfType through.

Accepts a schema type-name string (the only form available for a codeless schema), a typed schema class, or an existing Tf.Type.

Parameters:

schema_type_or_type_name – A schema type-name string, schema class, or type.

ovphysx.utils.schema.has_schema(prim, schema_name)#

Check whether a prim has an applied schema, matching by name.

Parameters:
  • prim – The Usd.Prim to check.

  • schema_name – The applied schema name, e.g. “PhysicsRigidBodyAPI”.

ovphysx.utils.schema.remove_api_schema_properties(api, prim)#

Remove every property an API schema defines from a prim.

Parameters:
  • api – The API schema type-name string or schema class.

  • prim – The Usd.Prim to strip.

ovphysx.utils.schema.remove_multiple_api_schema_properties(
api,
prim,
api_prefix,
multiple_token,
)#

Remove one instance of a multiple-apply API schema’s properties.

Parameters:
  • api – The API schema type-name string or schema class.

  • prim – The Usd.Prim to strip.

  • api_prefix – The property namespace prefix, e.g. “physxCookedData”.

  • multiple_token – The instance name.

Simulation#

Stepping a running simulation and writing its output back to ovstage.

Every other submodule here authors a stage; this one drives a running simulation. step_and_write_to_ovstage() performs the whole step-read-write-back loop an application would otherwise spell out by hand, without that workflow becoming part of the core ovphysx.api.PhysX surface.

That makes this the one submodule whose helpers are not pure pxr: they need ovstage when called, and an attached PhysX instance to call them on. ovstage is therefore imported inside the function bodies, so import ovphysx.utils still needs nothing beyond pxr and the standard library and still loads no native library.

class ovphysx.utils.simulation.OvStageOutputCache(physx: PhysX)#

Reusable application-owned state for step_and_write_to_ovstage().

The cache binds to the exact OVStage attachment and lazily owns copies of scales derived from omni:fabric:worldMatrix and point-instancer pose arrays. It also reuses Warp buffers on each producing device. Call refresh() after authored transforms, point-instancer pose arrays, or topology change. Omit the cache from step_and_write_to_ovstage() to read current OVStage values on every call instead.

close() → None#

Release cache-owned wrappers and buffers. Safe to call repeatedly.

refresh() → None#

Discard owned snapshots and device-specific buffers.

ovphysx.utils.simulation.step_and_write_to_ovstage(
physx: PhysX,
*,
dt: float,
output_ordinal: int,
cache: OvStageOutputCache | None = None,
outputs: Mapping[SimObjectType, Sequence[str]] | None = None,
) → int#

Step once and write selected physics output to the attached OVStage.

Fixed rigid-body, articulation-link, and vehicle-wheel poses are combined with scale derived from the current omni:fabric:worldMatrix and written directly to that attribute. The helper never writes omni:xform or omni:resetXformStack. Point-instancer rigid-body poses are written to their native instancer-local positions and orientations arrays; unsimulated slots retain the current OVStage values.

By default the helper reads current OVStage values on every call and keeps no snapshots after returning. Passing an application-owned cache opts into retaining derived scale, point-instancer baselines, Warp buffers, and CUDA events across calls. Persistent borrowed OVStage views are never kept.

Other selected output is written to its shadow sim:<name> attribute. output_ordinal is caller-owned and must never be drained back into physics with ovphysx.api.PhysX.update_from_ovstage().

Parameters:
  • physx – Attached simulation instance to step and read.

  • dt – Simulation time step in seconds.

  • output_ordinal – OVStage ordinal used only for physics output.

  • cache – Optional application-owned snapshots and reusable buffers.

  • outputs – Optional object-type-to-attribute selection. None uses the documented default dynamic output set.

Returns:

Number of OVStage attributes written.

Metadata keys and naming constants#

Constants shared by the authoring helpers.

Collected here rather than duplicated per module.

ovphysx ships the PhysX schemas codeless, so PhysX schema tokens are spelled out here as string literals; refer to ovphysx.utils.codeless.

ovphysx.utils.constants.AXES_INDICES = {'X': 0, 'Y': 1, 'Z': 2}#

Maps an axis name to its index in a 3-vector.

ovphysx.utils.constants.COOKED_DATA_TOKENS = [pxr.UsdPhysics.Tokens.convexHull, pxr.UsdPhysics.Tokens.convexDecomposition, 'triangleMesh']#

The PhysxCookedDataAPI instance names, one per cooked representation. Each is the instance name of a multiple-apply API, so its buffer lives at physxCookedData:<token>:buffer.

The three values are "convexHull", "convexDecomposition" and "triangleMesh". They are spelled out here because the first two are read from UsdPhysics.Tokens, which the documentation build mocks along with the rest of pxr: the rendered value of this list therefore shows a mock placeholder in place of each of the two, beside triangleMesh as a real literal. Do not read the rendered list as the values.

ovphysx.utils.constants.MAX_FLOAT = 3.40282347e+38#

The largest finite value a USD float attribute can hold.

ovphysx.utils.constants.MESH_APPROXIMATIONS = {'sdf': 'PhysxSDFMeshCollisionAPI', 'sphereFill': 'PhysxSphereFillCollisionAPI', pxr.UsdPhysics.Tokens.boundingCube: None, pxr.UsdPhysics.Tokens.boundingSphere: None, pxr.UsdPhysics.Tokens.convexDecomposition: 'PhysxConvexDecompositionCollisionAPI', pxr.UsdPhysics.Tokens.convexHull: 'PhysxConvexHullCollisionAPI', pxr.UsdPhysics.Tokens.meshSimplification: 'PhysxTriangleMeshSimplificationCollisionAPI', pxr.UsdPhysics.Tokens.none: 'PhysxTriangleMeshCollisionAPI'}#

Maps a mesh approximation token to the identifier of the PhysX collision API schema carrying that approximation’s tuning parameters. A None value means the approximation is fully described by the token and needs no extra API. The values are schema identifier strings rather than typed classes because the PhysX schemas are codeless; apply them with ovphysx.utils.codeless.apply_api().

The eight entries are spelled out below, and the keys are the whole of what ovphysx.utils.set_collider() accepts as its approximation_shape:

  • "none" – "PhysxTriangleMeshCollisionAPI"

  • "convexHull" – "PhysxConvexHullCollisionAPI"

  • "convexDecomposition" – "PhysxConvexDecompositionCollisionAPI"

  • "meshSimplification" – "PhysxTriangleMeshSimplificationCollisionAPI"

  • "boundingCube" – None

  • "boundingSphere" – None

  • "sphereFill" – "PhysxSphereFillCollisionAPI"

  • "sdf" – "PhysxSDFMeshCollisionAPI"

Spelled out because six of the eight keys are read from UsdPhysics.Tokens, which the documentation build mocks along with the rest of pxr, so the rendered value of this dict shows a mock placeholder in place of each of those six keys. Do not read the rendered mapping as the mapping.

ovphysx.utils.constants.METADATA_ATTRIBUTE_NAME_LOCALSPACEVELOCITIES = 'physics:localSpaceVelocities'#

The customData dict key that makes the runtime read a rigid body’s authored velocities in the body frame instead of world space.

ovphysx.utils.constants.SCENE_UPDATE_TYPE_ASYNCHRONOUS = 'Asynchronous'#

the scene steps asynchronously.

Type:

physxScene:updateType value

ovphysx.utils.constants.SCENE_UPDATE_TYPE_DISABLED = 'Disabled'#

the scene does not step.

Type:

physxScene:updateType value

ovphysx.utils.constants.SCENE_UPDATE_TYPE_SYNCHRONOUS = 'Synchronous'#

the scene steps in the update thread.

Type:

physxScene:updateType value

ovphysx.utils.constants.TOKEN_SDF = 'sdf'#

Mesh approximation token for a signed-distance-field collider.

ovphysx.utils.constants.TOKEN_SPHERE_FILL = 'sphereFill'#

Mesh approximation token for a sphere-fill collider.

ovphysx.utils.constants.TOKEN_TRIANGLE_MESH = 'triangleMesh'#

Mesh approximation token for an exact triangle mesh collider.

Constants#

ovphysx.OP_INDEX_ALL#

Sentinel value (0xFFFFFFFFFFFFFFFF) passed to wait_op() to wait for all outstanding operations. Equivalent to OVPHYSX_OP_INDEX_ALL in the C API.