Changelog#

All notable changes to ovphysx are documented in this file.

[0.3] - 2026-03-31#

Breaking changes#

  • Settings API replaced with typed config system. The old string-based settings (settings_keys/settings_values/settings_count on ovphysx_create_args; ovphysx_set_global_setting()/ovphysx_get_global_setting(); Python PhysX(settings=...), set_setting(), get_setting(); C++ PhysX::setSetting()/getSetting()) are all removed. Use instead: C — ovphysx_config_entry_t array on config_entries/config_entry_count with builders from ovphysx_config.h (ovphysx_config_entry_num_threads(), _disable_contact_processing(), etc.), runtime ovphysx_set_global_config() and typed getters ovphysx_get_global_config_bool/int32/float/string(). C++ — PhysX::create(physx, entries, count) with the same ovphysx_config_entry_t builders. Python — PhysX(config=PhysXConfig(num_threads=4)) with the PhysXConfig dataclass, runtime set_config_bool()/set_config_int32() and get_config_bool()/get_config_int32(). Arbitrary Carbonite paths remain accessible via ovphysx_config_entry_carbonite() (C/C++) or PhysXConfig(carbonite_overrides={...}) (Python).

  • OVPHYSX_CONFIG_CUDA_DEVICE removed from ovphysx_config_int32_t and the ovphysx_config_entry_cuda_device() convenience builder. Use the gpu_index parameter on ovphysx_create_args (C) or PhysX(gpu_index=...) (Python) instead. Setting /physics/cudaDevice via carbonite_overrides now raises an error.

  • Removed typed config entries for settings not in Physics Preferences: OVPHYSX_CONFIG_PHYSX_DISPATCHER, OVPHYSX_CONFIG_OMNI_PVD_OUTPUT_ENABLED, OVPHYSX_CONFIG_UJITSO_COLLISION_COOKING (bool); OVPHYSX_CONFIG_PVD_RECORDING_DIRECTORY (string). These settings remain accessible via the carbonite_overrides escape hatch.

  • Log level enum reordered to ascending severity (matches ovrtx and industry convention). New values: OVPHYSX_LOG_VERBOSE=0, OVPHYSX_LOG_INFO=1, OVPHYSX_LOG_WARNING=2 (unchanged), OVPHYSX_LOG_ERROR=3, OVPHYSX_LOG_NONE=4. Code using symbolic names (OVPHYSX_LOG_WARNING, LogLevel.WARNING) is unaffected. Code using raw integer values must update. Python LogLevel IntEnum updated to match.

  • Error handling switched from inline error strings to thread-local get_last_error() query (matches ovrtx pattern). ovphysx_result_t and ovphysx_enqueue_result_t no longer have an error field. On failure, call ovphysx_get_last_error() on the same thread to retrieve the error message (valid until the next ovphysx API call on that thread). ovphysx_op_error_t struct removed; ovphysx_op_wait_result_t now carries error_op_indices (array of failed op indices) instead of errors (array of ovphysx_op_error_t). Call ovphysx_get_last_op_error(op_index) per failed index, then ovphysx_destroy_wait_result(&result) to free the array. Removed: ovphysx_destroy_error(), ovphysx_destroy_errors(). Added: ovphysx_get_last_error(), ovphysx_get_last_op_error(), ovphysx_destroy_wait_result(). Python API is unaffected (errors are raised as exceptions).

  • ContactEventHeader.stageId type changed from long to int64_t for cross-platform ABI stability (long is 4 bytes on Windows, 8 on Linux; int64_t is 8 on both). This changes the struct layout on Windows. Bumps IPhysxSimulation to 5.0 and IPhysicsSimulation to 0.4 in ovruntime.

  • ovphysx_get_contact_report() C signature changed: now returns typed pointers (const ovphysx_contact_event_header_t**, const ovphysx_contact_point_t**) instead of const void**, and gained 2 new parameters (out_friction_anchors, out_num_friction_anchors). Existing C callers must update the pointer types and append , NULL, NULL for the new friction anchor parameters. C++ callers using PhysX::getContactReport() are unaffected (new params default to nullptr).

  • Removed ovphysx_articulation_get_dof_count, ovphysx_articulation_get_body_count, ovphysx_articulation_get_joint_count, ovphysx_articulation_get_is_fixed_base, ovphysx_articulation_get_fixed_tendon_count, ovphysx_articulation_get_spatial_tendon_count. Use the new ovphysx_get_articulation_metadata(handle, binding, &meta) instead. The Python TensorBinding properties (dof_count, body_count, etc.) are unchanged.

  • ovphysx_clone() parameter parent_positions_xyz replaced with parent_transforms (7 floats per target: px, py, pz, qx, qy, qz, qw instead of 3). This allows specifying both position and rotation for clone parent prims. Quaternion convention: imaginary-first, matching the tensor API pose format. Identity rotation = (0, 0, 0, 1). Python: parent_positions_xyz parameter renamed to parent_transforms (7-tuples). Migration: replace parent_positions_xyz=[x, y, z] with parent_transforms=[px, py, pz, 0, 0, 0, 1] (append identity quaternion).

  • Removed ovphysx_set_clone_env_root() (C) and set_clone_env_root() (Python). No longer needed — the first clone(), simulate(), or warmup_gpu() call triggers stage attach lazily. The API ordering simplifies to: add_usd()clone()warmup_gpu()/step().

  • ovphysx_device_t enum reordered: OVPHYSX_DEVICE_AUTO = 0 (was 2) so that zero-initialized ovphysx_create_args defaults to AUTO instead of GPU. Code that passes raw integer device values must update (0=AUTO, 1=GPU, 2=CPU). Code using symbolic names or string device names is unaffected.

  • OVPHYSX_TENSOR_ARTICULATION_CORIOLIS_FORCE_F32 renamed to TensorType.ARTICULATION_CORIOLIS_AND_CENTRIFUGAL_FORCE (enum value 72 unchanged). The old name was misleading — the getter computes both Coriolis and centrifugal terms.

  • Scene query enum constants disambiguated: OVPHYSX_SCENE_QUERY_CLOSESTOVPHYSX_SCENE_QUERY_MODE_CLOSEST (and _ANY, _ALL); OVPHYSX_SCENE_QUERY_SPHEREOVPHYSX_SCENE_QUERY_GEOMETRY_SPHERE (and _BOX, _SHAPE).

  • Python: All OVPHYSX_TENSOR_*_F32 bare-int constants removed. Use TensorType.* members instead (e.g. TensorType.ARTICULATION_ROOT_POSE).

  • Python: OVPHYSX_API_*, OVPHYSX_LOG_*, OVPHYSX_DEVICE_* constants removed. Use ApiStatus.*, LogLevel.*, DeviceType.* IntEnum members instead.

  • Python: OVPHYSX_OP_INDEX_ALL renamed to OP_INDEX_ALL.

  • Python: kDLCPU, kDLCUDA, kDLInt, kDLUInt, kDLFloat removed from the public API. Use DLDeviceType.kDLCPU / DLDataTypeCode.kDLFloat etc. directly.

  • Removed ovphysx_finalize(). Full Carbonite framework teardown now happens automatically when the last instance is destroyed via ovphysx_destroy_instance() (C/C++) or release() (Python). Migration: remove any ovphysx_finalize() calls — they are no longer needed or available.

Added#

  • OVPHYSX_CONFIG_SCENE_MULTI_GPU_MODE (int32) config entry for /physics/sceneMultiGPUMode.

  • ConfigBool, ConfigInt32, ConfigFloat, ConfigString IntEnums in Python types.py for typed config key access.

  • PhysXConfig dataclass in Python config.py for typed initialization config.

  • ovphysx_articulation_metadata_t struct (in ovphysx_types.h) — plain C struct with 6 scalar topology fields.

  • ovphysx_get_articulation_metadata() — fills the struct in one call, one lock acquire, one stream fence. Result is automatically cached by the Python TensorBinding.

  • Remote USD loading: add_usd() now accepts remote URIs (omniverse://, S3 via HTTPS, Azure Blob https://) in addition to local file paths. Powered by the Omniverse Client Library, loaded automatically at startup. Use HTTPS virtual-hosted S3 URLs (e.g. https://bucket.s3.region.amazonaws.com/path/scene.usd).

  • ovphysx_configure_s3() / configure_s3() — configure S3 credentials (host, bucket, region, access key, secret key, optional session token) for loading USD scenes from S3 buckets.

  • ovphysx_configure_azure_sas() / configure_azure_sas() — configure an Azure SAS token for loading USD scenes from Azure Blob Storage.

  • TensorBindingsAPI: DOF property tensors — stiffness, damping, limits, max velocity, max force, armature, friction properties (read/write, indexed, masked).

  • TensorBindingsAPI: body property tensors — mass, center-of-mass pose, inertia tensor (read/write, indexed, masked).

  • TensorBindingsAPI: link acceleration tensor ([N, L, 6], read-only) for linear + angular acceleration per link.

  • TensorBindingsAPI: dynamics query tensors (read-only) — Jacobian, generalized mass matrix, Coriolis + centrifugal forces, gravity compensation forces, link incoming joint force.

  • TensorBindingsAPI: DOF projected joint forces (read-only, [N, D]).

  • TensorBindingsAPI: standalone rigid body properties — mass ([N]), inertia ([N, 9]), center-of-mass pose ([N, 7]), all read/write with indexed and masked write support.

  • TensorBindingsAPI: articulation body inverse mass ([N, L]) and inverse inertia ([N, L, 9]), both read-only.

  • TensorBindingsAPI: fixed tendon properties — stiffness, damping, limit stiffness, limits, rest length, offset (read/write, indexed, masked). Write uses read-back + batch setter internally since PhysX requires all tendon properties to be set together.

  • TensorBindingsAPI: spatial tendon properties — stiffness ([N,T]), damping ([N,T]), limit stiffness ([N,T]), offset ([N,T]) (read/write, indexed, masked). Same read-back + batch setter pattern as fixed tendons.

  • TensorBindingsAPI: shape-level tensor types — RIGID_BODY_SHAPE_FRICTION_AND_RESTITUTION ([N,S,3]), RIGID_BODY_CONTACT_OFFSET ([N,S]), RIGID_BODY_REST_OFFSET ([N,S]), and articulation equivalents (ARTICULATION_SHAPE_FRICTION_AND_RESTITUTION, ARTICULATION_CONTACT_OFFSET, ARTICULATION_REST_OFFSET). All support read, indexed write, and masked write.

  • Articulation metadata name queries: ovphysx_articulation_get_dof_names, get_body_names, get_joint_names (variable-length string arrays; separate from scalar metadata). Scalar topology fields (dof_count, body_count, joint_count, is_fixed_base, fixed_tendon_count, spatial_tendon_count) are now returned in one call via ovphysx_get_articulation_metadata().

  • Contact binding API: ovphysx_create_contact_binding / destroy / get_spec / read_net_forces / read_force_matrix backed by IRigidContactView.

  • Python ContactBinding class for reading contact forces via DLPack tensors.

  • PhysX object interop: ovphysx_get_physx_ptr(handle, prim_path, physx_type, &out_ptr) returns raw PhysX SDK pointers (void*) by USD prim path and type enum (ovphysx_physx_type_t). Covers PxScene, PxRigidActor, PxArticulationLink, PxJoint, PxArticulationReducedCoordinate, PxShape, PxMaterial, and PxArticulationJointReducedCoordinate. The SDK now ships PhysX headers under include/physx/ (ovphysx_PHYSX_INCLUDE_DIR in CMake). See the PhysX Interop tutorial and tests/c_samples/physx_interop_cpp/ for a complete example using setKinematicTarget().

  • Python API: PhysX.get_physx_ptr(prim_path, physx_type) unified accessor.

  • Python API: PhysX.handle read-only property exposing the raw ovphysx_handle_t.

  • Contact report API: ovphysx_get_contact_report() exposes the Omni PhysX runtime’s per-step contact data as typed C struct pointers (ovphysx_contact_event_header_t, ovphysx_contact_point_t). Struct definitions and ovphysx_friction_anchor_t are in ovphysx_types.h. Optional friction anchor output params (out_friction_anchors, out_num_friction_anchors) — pass NULL to skip.

  • Python API: PhysX.get_contact_report() returns a dict with ctypes arrays of ContactEventHeader and ContactPoint structs for zero-copy field access. Optional include_friction_anchors=True adds friction anchor data.

  • Scene query API: ovphysx_raycast(), ovphysx_sweep(), ovphysx_overlap() — raycast, geometry sweep, and overlap queries against the physics scene. Supports CLOSEST/ANY/ALL hit modes with sphere, box, and arbitrary-shape (UsdGeomGPrim) geometry. Hit results stored in internal buffer (valid until next query).

  • Python API: PhysX.raycast(), PhysX.sweep(), PhysX.overlap() scene query methods.

  • SceneQueryMode and SceneQueryGeometryType IntEnums in Python types.py.

  • Synchronous stepping APIs: ovphysx_step_sync() (C) / PhysX.step_sync() (Python) combines step + wait into a single call, bypassing the async event machinery for lower per-step overhead in synchronous use patterns. ovphysx_step_n_sync() / PhysX.step_n_sync() batches N steps in one call, saving (N-1) ctypes round-trips when using decimation > 1.

  • New status code OVPHYSX_API_BUFFER_TOO_SMALL = 6 added to ovphysx_api_status_t.

  • CMake package config (find_package(ovphysx)) provides the imported target ovphysx::ovphysx and a Windows helper ovphysx_copy_runtime_dlls().

  • Visual sample using Rerun to visualize rigid body simulation (Rendering Handoff).

  • Linux aarch64 support.

Changed or fixed#

  • Carbonite host coexistence: ovphysx now detects when a host application (Kit, Isaac Sim) has already bootstrapped Carbonite and loaded the PhysX plugin stack. In this scenario, ovphysx skips its own plugin loading, USD preloading, and version checks, and instead acquires the host’s existing interfaces. Settings are applied with setDefault*() to avoid overwriting host configuration; device-related settings produce a warning on mismatch. The same ovphysx version works in both standalone and host-embedded environments without configuration changes.

  • Physics scene is now parsed lazily on the first simulate() call instead of during add_usd(). This avoids GPU buffer corruption when clone() adds environments after the initial load. Correct usage sequence: add_usd()clone()warmup_gpu()/step().

  • A GPU and CUDA installation is no longer a requirement for using ovphysx with CPU-only simulation.

  • Fixed a fatal TF_DEBUG crash when ovphysx and OVRTX co-loaded in Python due to a collision with USD libraries.

  • Fixed various memory leaks across C, C++, and Python error-handling and cleanup paths.

  • Contact binding read functions (read_net_forces, read_force_matrix) now validate dtype is float32 before dispatching.

  • ContactBinding.destroy() now checks the result status and raises on failure; also validates parent SDK is still alive.

  • create_contact_binding now validates sensor_patterns is non-empty, filters_per_sensor >= 0, and filter_patterns length matches n_sensors * filters_per_sensor.

  • All new tensor type constants and ContactBinding are now exported from the ovphysx package __init__.py.

  • Wheel metadata corrected: Root-Is-Purelib: false, OS classifiers set to Linux and Windows.

  • Fixed doubled path bug in ovphysxConfig.cmake Windows DLL copy helper. Fixed shutdown crash where stepper tasks could access freed CUDA context (OMPE-81088).

  • Fixed process-exit crashes (OMPE-84139) caused by non-deterministic DLL/SO unload ordering at process exit. ovphysx_destroy_instance() now performs full Carbonite framework teardown (OmniClient shutdown + plugin unload in dependency order) when the last instance is destroyed. No separate finalize call is needed – just call release() (Python) or ovphysx_destroy_instance() (C/C++).

  • ovphysx_remove_usd() now validates usd_handle against the instance-owned handle map and returns OVPHYSX_API_NOT_FOUND for unknown handles (Python raises ValueError: Failed to remove USD: USD handle not found).

Removed#

  • AGENTS.md is no longer shipped in the SDK or wheel (monorepo developer doc only).

  • ovphysx.pc (pkg-config file) removed; CMake is the supported integration path.

  • hdStorm (Hydra Storm renderer) removed from SDK; not needed for headless physics simulation.

[0.2] - 2026-03-03#

First released version. No changelog was maintained prior to v0.3.