Changelog#
All notable changes to ovphysx are documented in this file.
[0.4] - 2026-05-27#
Breaking Changes#
gpu_indexreplaced byactive_cuda_gpusonovphysx_create_args. Theint32_t gpu_indexfield is removed. Useovphysx_string_t active_cuda_gpusinstead: a comma-separated string of CUDA device ordinals. Single-GPU usage:args.active_cuda_gpus = ovphysx_cstr("2")(C),PhysX(active_cuda_gpus="2")(Python),createArgs.setActiveCudaGpus("2")(C++). Default (empty) selects GPU 0, matching the oldgpu_index=0default. Multi-GPU patterns"0,1,...,N-1"(all GPUs, round-robin) and"1,2,...,N-1"(all except first) are now supported. The C++CreateArgs::setGpuIndex(int32_t)is replaced bysetActiveCudaGpus(const std::string&).Pre-loaded Carbonite PhysX stacks are rejected. ovphysx can share a process with other OV libraries through namespaced USD reuse, but it no longer reuses a pre-existing
omni.physx.plugin/IPhysxSimulationstack. If another Carbonite user already registered PhysX plugins beforeovphysx_create_instance(), creation fails with a clear error. Conflicting pre-set/physics/cudaDeviceor/physics/suppressReadbackvalues now fail instead of being silently accepted.DirectGPU mode is now opt-in instead of auto-enabled for GPU instances. Previously,
ovphysx_create_instance(device=GPU)unconditionally set/physics/suppressReadback=trueand/physics/suppressFabricUpdate=true(via two paths: a startup-timeCarboniteLoader::setStartupSuppressReadbackwrite before PhysX plugins loaded, and a per-instance write at GPU bootstrap), which routes PhysX intoeENABLE_DIRECT_GPU_APImode. DirectGPU is incompatible with contact modification (per PhysX SDK guidance: contact-modify requires a CPU roundtrip), so any scene relying oneMODIFY_CONTACTS– including the surface-velocity / conveyor pattern, custom contact callbacks, and contact-shaping filter rules – silently failed to form kinematic-vs-dynamic broadphase pairs in GPU mode. Both write paths are removed: ovphysx no longer touches/physics/suppressReadbackor/physics/suppressFabricUpdateat any point in its lifecycle. The previously-publicCarboniteLoader::setStartupSuppressReadbackstatic method is removed (internal API; no public callers). Hosts that want DirectGPU’s tensor-pipeline performance (e.g. Isaac Lab) opt in by either (a) setting/physics/suppressReadback=truevia Carbonite settings beforeovphysx_create_instanceif they have direct ISettings access, or (b) passingovphysx_config_entry_carbonite("/physics/suppressReadback", "true")increate_args.config_entries(Python:PhysXConfig(carbonite_overrides={"/physics/suppressReadback": True})). At startup ovphysx now logs a[CarboniteLoader] /physics/suppressReadback=...INFO line so misconfigurations are visible. Seeovphysx_create_argsininclude/ovphysx/ovphysx_types.hfor the full trade-off documentation. Migration: GPU users who relied on suppressReadback being auto-enabled must set it explicitly via one of the routes above. Perf note: DirectGPU is the faster simulation path for tensor-pipeline workloads, so existing GPU consumers will see slower per-step times until they opt in – the change here is correctness (contact-modify scenes now work in GPU mode), not a perf regression of the new default. Users who need contact-modify in GPU mode now get correct behavior with no code change.ovphysx’s Pythonimportnow auto-registers ovphysx’s namespaced USD schema/plugin path by appending it toOV_PXR_PLUGINPATH_2511once at module load. Eliminates a class of silent-schema-drop bugs in mixed-process apps (import ovphysx; import ovrtxor vice versa) where USD’sPlugRegistryis populated lazily on first stage open and never re-scans, so any subsystem that didn’t get its plugin path published before that moment has its applied schemas silently dropped. The implicit registration inCarboniteLoadercontinues to cover the ovphysx-only-in-process case; this Python-side change is purely additive. The auto-call is pure-Python (env-var append only) and does not trigger native loading – libcarb / USD /_bindingsremain deferred to first native-attribute access (PhysX,ContactBinding, …). Failures (e.g. partially-installed checkout with noplugins/usddirectory) are surfaced as a logged warning rather than raised soimport ovphysxdoes not hard-fail. The existing publicregister_schema_paths()is unchanged and remains idempotent (already-called guard); apps that called it explicitly continue to work and the explicit call becomes a cheap no-op on success. C/C++ apps are unaffected – still useovphysx_register_schema_paths()explicitly. Migration: none required. Apps that need an env-var-pure import canos.environ.pop("OV_PXR_PLUGINPATH_2511")afterimport ovphysx.
Added#
Clone events from
ovstage_clone_subtree()are consumed by ovphysx during step ingest. When an ovstage Stage is attached,physx.step()consumesOVSTAGE_EVENT_CLONEevents surfaced byovstage_query_changes()and drives PhysX-side replication via the clone plugin — same plugin the existingovphysx_clone()entrypoint calls, just routed through ovstage instead of an explicit C / Python call. The standalone clone APIs (ovphysx_clone()C,PhysX::clone()C++,PhysX.clone()Python) remain fully supported for callers that don’t have an attached Stage or that want the async multi-target convenience; both paths funnel into the sameovphysx_clone_replicate_internalhelper. Known limitation: clones that arrive after the bridge’s first successfulpublish_outbound(i.e. onceensure_publish_initializedhas bound a non-zeroRIGID_BODY_POSEprim list) simulate on the PhysX side but their poses are NOT published back to ovstage — the publish prim list is frozen at first publish. Issue a[OvstageBridge] clone-after-publish:warning per affected target identifies this case in logs. Workarounds: clone everything before the first non-emptyphysx.step(), or detach + re-attach the stage to rebuild the publish bindings. Refresh-on-clone is tracked as a follow-up because it requiresovstage_unregister_consumer+ re-registration through the bridge state machine.ovphysx_attach_stage()/ovphysx_detach_stage()for ovstage consumer integration. Pair anovstage_instance_t*with an ovphysx instance andphysx.step()becomes the canonical “consume control attributes from ovstage and publish poses back” step: the OvstageBridge pulls dirtydrive:force,physics:mass,drive:velocity,drive:position_target(Tensor route),physxSurfaceVelocity:surfaceVelocity,physics:gravityMagnitude,physics:gravityDirection(Fabric route), and clone events (ovstage_query_changes) ahead of integrate; afterfetchResultsit publishesRIGID_BODY_POSEasxformOp:transform(float64 row-major mat4) to ovstage’s output-buffer contract. Init-only attach (rejects re-attach withOVPHYSX_API_ERROR); idempotent detach. Python:PhysX.attach_stage(stage)auto-unwraps viaStage.handle().Cross-device staged reads AND writes in
ovphysx_read_tensor_binding/ovphysx_write_tensor_binding/ovphysx_write_tensor_binding_masked. A CPU-declaredsrc/dst/mask/indextensor now works against a GPU-bound binding, and a GPU-declared tensor works against a CPU-bound binding: the call transparently allocates a staging buffer on the binding’s device (or host) and routes throughIOptionalCuda::memcpyDtoH/memcpyHtoD. Reads stage-and-copy after the simView read; writes copy-and-stage before the simView write. Same-device combinations are unchanged. Cross-GPU (different ordinals) still returnsOVPHYSX_API_DEVICE_MISMATCH.Schema path pre-registration API for multi-subsystem USD processes. New C API
ovphysx_register_schema_paths()and Python helperovphysx.register_schema_paths()append ovphysx’s namespaced USD schema/plugin root toOV_PXR_PLUGINPATH_2511before ovphysx initialization. Use this with peer subsystem equivalents such asovrtx_register_schema_paths()before the first USD stage open or schema-registry access.Safer behavior when loading into a process with another Carbonite-owning library. Previously, ovphysx would blindly load its own USD-dependent plugin stack on top of any framework already bootstrapped in the process.
loadUsdDependentPlugins’usdrt::population::IUtils-availability guard is now narrowed to fire only in full-host mode (Kit / Isaac Sim, i.e.IPhysxSimulationalready registered) rather than any-usdrt-user, so partial hosts that provideIUtilsbut not the physics-schema plugins now trigger ovphysx’s ownomni.usdphysics.pluginload instead of silently skipping it. In-process coexistence remains subject to ABI compatibility of the shared deps that both libraries link against (Fabric interface version, Carbonite version, USD build-package); when those don’t line up, the new drift diagnostics (seeOVPHYSX_COEXIST_DIAGNOSTICSandconfig.toml build_packagebelow, plus the refuse-branch inCarboniteLoader::initialize()under Changed or Fixed) convert the failure into a named, actionable error instead of a silent plugin-resolution cascade.OVPHYSX_COEXIST_DIAGNOSTICS=1diagnostic env var. Set to enable stderr-only diagnostic logging of the plugin-load and Fabric-acquire paths insideovphysx_create_instance– useful when triaging a “Dependency: [omni::physics::schema::IUsdPhysics v1.1] failed to be resolved” cascade or “Fabric interfaces unavailable” error in a coexistence context. Logs the Framework pointer, plugin-registry contents,omni.physicsschema.pluginregistration state, and the Fabric interface-version list as seen by ovphysx. Unset: no behavior change.OVPHYSX_COEXIST_REFUSE=1opt-out env var. Restores the legacy fail-fast-at-load behavior for users who would rather not attempt coexistence with another Carbonite-owning library. With the default coexistence flip (see Changed or Fixed below),OVPHYSX_COEXIST_REFUSE=1is the explicit off-ramp: ovphysx detects a foreign Carbonite framework and refuses to initialize with a sharp error message instead of proceeding. Default unset: ovphysx proceeds with coexistence and lets per-phase interface probes name any version skew at the failing plugin.Per-phase plugin-interface probes after
loadPlugins().CarboniteLoader::initialize()now runstryAcquireInterface<>against the primary interface of each plugin loaded in the foundation and early-PhysX phases (carb.dictionary,carb.settings,carb.tokens,carb.tasking,carb.filesystem,omni.physx.foundation). If any returns null – because a foreign-host plugin advertises an incompatible major/minor or because the plugin failed to load – ovphysx fails fast with a single error line that names every plugin that did not satisfy ovphysx’s expected interface version. Standalone ovphysx is unaffected. The probe converts what was previously an opaquenullptrcascade two layers down into a named, actionable error at load time, mirroring the existingUsdVersionCheckpattern for USD.USD build-package drift diagnostic in
config.toml.config.tomlnow records the exact USD packman package identifier ovphysx was linked against (e.g.build_package = "0.25.11.kit.2-gl.19811", auto-populated at build time from the resolved_build/target-deps/usd/releasesymlink). At runtime,UsdVersionCheckresolves the loaded USD’s canonical path, extracts its packman id, and compares. If the two differ – e.g. ovphysx built against0.25.11.kit.2but another library preloaded0.25.11.kit.1– a single warning line names both ids and points at Carbonite-plugin-resolution failure as the probable downstream symptom. Same-build case is silent.New
--devschemaflag forwards to ovruntime for local physics schema builds.Multi-GPU scene distribution. Pass
active_cuda_gpus="0,1,2"(all N GPUs) oractive_cuda_gpus="1,2"(all except GPU 0) to distribute scenes round-robin across the specified devices. Internally maps to/physics/sceneMultiGPUMode = eAlloreSkipFirst. Other patterns (e.g."0,2"on a 4-GPU machine) returnOVPHYSX_API_INVALID_ARGUMENTwith a descriptive error; arbitrary subset support can be added in a future release without API changes.TensorBindingsAPI: standalone rigid body read-only queries -
RIGID_BODY_ACCELERATION([N,6]),RIGID_BODY_INV_MASS([N]), andRIGID_BODY_INV_INERTIA([N,9]). The enum values use previously unused slots afterRIGID_BODY_WRENCH; existing tensor enum values were not reordered.Articulation kinematic update API. New C
ovphysx_update_articulations_kinematic(), PythonPhysX.update_articulations_kinematic(), and experimental C++PhysX::updateArticulationsKinematic()recompute articulation link poses from current DOF positions. In GPU mode the first call may perform the standard DirectGPU warmup step; after warmup, the FK refresh does not run a normal simulation step or collision/contact work.TensorBinding row metadata for tensor bindings. Python
TensorBinding.prim_pathsand Covphysx_tensor_binding_get_prim_paths()return the resolved USD prim path for each rigid-body tensor row and the articulation root prim path for each articulation row.Python tensor-binding empty-match guard.
PhysX.create_tensor_binding(..., raise_if_empty=True)now raisesValueErrorwhen the binding would match zero prims. The default remainsFalse, preserving zero-count bindings for optional scene elements.Python binding lifetime warnings.
TensorBindingandContactBindingnow emitResourceWarningif garbage collection cleans them up without an explicitdestroy()or context-manager exit. This does not affect normal read/write paths; create bindings once outside simulation loops and reuse or destroy them explicitly.ContactBinding detailed contact/friction reads. Added
sensor_paths,filter_paths,max_contact_data_count, Covphysx_contact_binding_get_sensor_paths(),ovphysx_contact_binding_get_filter_paths(),ovphysx_get_contact_binding_capacity(),ovphysx_read_contact_data(),ovphysx_read_friction_data(), and matching Python methods. The API uses flat[C,*]buffers with[S,F]count/start-index tensors. Detailed reads require filtered bindings (filters_per_sensor > 0); unfiltered bindings remain valid for aggregate net-force reads.Experimental C++
CreateArgstype andPhysX::create(out, CreateArgs).CreateArgsis a safe C++ wrapper forovphysx_create_argsthat default-constructs toOVPHYSX_CREATE_ARGS_DEFAULTand exposes setters fordevice,active_cuda_gpus,bundled_deps_path, and config entries. Replaces the previouscreate(PhysX&, config_entries, count)overload.OmniPVD recording support via typed config. Two new config entries enable
.ovdrecording:omnipvd_output_enabled(bool) andomnipvd_ovd_recording_directory(string). Both must be set at instance creation viaPhysXConfig(Python) orconfig_entriesonovphysx_create_args(C/C++). These entries were previously available only viacarbonite_overrides; they now have first-class typed fields. See OmniPVD Recording tutorial.
Changed or Fixed#
ContactBinding device-mismatch errors now explain the DirectGPU opt-in. When a CPU-backed ContactBinding receives a CUDA output tensor, the error now points out that
device="gpu"enables GPU dynamics only and that TensorAPI/ContactBinding CUDA views require/physics/suppressReadback=truebefore instance creation. Behavior is unchanged.ovphysx startup now appends its namespaced USD schema path when
OV_PXR_PLUGINPATH_2511is already set. PreviouslyCarboniteLoaderonly set the variable when it was empty, so pre-existing ovrtx or application paths could prevent ovphysx’s own schema root from being published. Startup now shares the same append/dedupe logic asovphysx_register_schema_paths()and does not modifyPXR_PLUGINPATH_NAME. Startup and explicit pre-registration fail fast if ovphysx cannot find an existingplugins/usddirectory, rather than publishing a missing path that USD silently ignores.Improved automatic GPU selection on multi-GPU systems. When the physics device is not set explicitly, ovphysx now prefers the GPU already in use by the renderer, and otherwise picks the discrete GPU with the most memory. Previously device 0 was always chosen, which could result in physics running on a different GPU than the renderer on multi-GPU workstations. Users on single-GPU machines are unaffected; users on multi-GPU systems may see a different device selected than before.
ovphysx_clone()now preservesxformOp:scalefrom source prims. Previously, clones of prims with non-unit scale appeared at incorrect world-space size. The fix reads the source’s scale (supporting bothdouble3andfloat3attributes), writes it to each target, includes it inxformOpOrder, and incorporates it into thelocalMatrix.Contact binding now matches runtime-cloned sensor bodies. Runtime clones created with
ovphysx_clone()can exist only in Fabric/usdrt, where copied USD API-schema metadata may not be visible. Contact binding now keeps the strictPhysxContactReportAPIrequirement for real USD prims while allowing clone-only runtime paths to validate through their PhysX actor pointer, so cloned environments produce the same contact-binding rows as rigid-body tensor bindings.ovphysx_step_sync()now callsensure_physics_attached()before stepping, matching the asyncovphysx_step()path. Previously, the firststep_syncafteradd_usdwould run against an unattached stage — features like OmniPVD recording finalization silently failed.Config entries are now applied before PhysX plugin loading so that settings read during
createPhysics()(e.g., OmniPVD recording) are in place at initialization time.TensorBindingsAPI shape property tensors now accept CPU buffers in GPU simulations. Rigid-body and articulation material/contact/rest-offset tensors are PhysX parameter tensors and remain CPU-resident even when state tensors use CUDA buffers.
ovphysx wheel bootstrap no longer sets
PYTHONHOME. Fixes mis-resolution ofsys.prefixin child processes that spawn venv interpreters (Windows; e.g. rerun.io).Unified build flags.
build.sh/build.batnow accept the same flags as ovruntime:-c/--clean,-x/--rebuild,-d/--debug,-r/--release(default),-t/--target <name>,-g/--generate. Note: default is now release-only (was debug+release);-tis now--target <name>(use--test-venvfor the old test-venv behavior).create_articulation_view()andcreate_rigid_body_view()pattern matching extended to reach nested bodies. Patterns now support**for recursive descent and — by default — match a named final component against descendants at any depth so patterns like/World/envs/env_*/Robot/<name>keep working on deeper hierarchies. Views dedup by backing PhysX pointer. Set/physics/tensors/recursiveLeafPatternMatchtofalseto restore strict per-level matching. Migration: existing patterns may pick up additional prims at deeper levels. If that changes behavior, either set the Carbonite flag tofalseor switch to explicit absolute paths./ovphysx/latest/on GitHub Pages now serves the docs directly instead of redirecting to/ovphysx/<version>/. This fixes deep links such as the PyPI Changelog URL (/latest/changelog.html) which previously 404’d. Bookmarks to/latest/continue to work; bookmarks to versioned URLs are unchanged.
Removed#
Environment variables
OVPHYSX_ROOT,OVPHYSX_BIN_DIR,OVPHYSX_PLUGINS_DIRremoved. All runtime paths are now derived fromOVPHYSX_LIB(the single dev-mode override for the shared library path). Migration: replace any of the removed env vars withOVPHYSX_LIBpointing at the shared library.ovphysx_set_shutting_down()removed. Full teardown now happens automatically when the last instance is destroyed viaovphysx_destroy_instance()(C/C++) orrelease()(Python). Migration: remove anyovphysx_set_shutting_down()calls.Legacy Python TensorAPI compatibility removed.
ovphysx.tensorsis no longer shipped or exported. Migration: replaceovphysx.tensorsimports with TensorBindingsAPI: usephysx.create_tensor_binding()to create bindings, thenbinding.read()/binding.write()for data access.
[0.3] - 2026-03-31#
Breaking Changes#
Settings API replaced with typed config system. The old string-based settings (
settings_keys/settings_values/settings_countonovphysx_create_args;ovphysx_set_global_setting()/ovphysx_get_global_setting(); PythonPhysX(settings=...),set_setting(),get_setting(); C++PhysX::setSetting()/getSetting()) are all removed. Migration: C — useovphysx_config_entry_tarray onconfig_entries/config_entry_countwith builders fromovphysx_config.h, runtimeovphysx_set_global_config()and typed gettersovphysx_get_global_config_bool/int32/float/string(). Python — usePhysX(config=PhysXConfig(num_threads=4)), runtimeset_config_bool()/set_config_int32()and matching getters. Arbitrary Carbonite paths remain accessible viaovphysx_config_entry_carbonite()(C/C++) orPhysXConfig(carbonite_overrides={...})(Python).OVPHYSX_CONFIG_CUDA_DEVICEremoved fromovphysx_config_int32_t. Migration: useactive_cuda_gpusonovphysx_create_args(C) orPhysX(active_cuda_gpus=...)(Python). Setting/physics/cudaDeviceviacarbonite_overridesnow 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). Migration: these settings remain accessible viacarbonite_overrides.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. Migration: code using symbolic names is unaffected; code using raw integer values must update.Error handling switched from inline error strings to thread-local
get_last_error()query.ovphysx_result_tandovphysx_enqueue_result_tno longer have anerrorfield. Migration: on failure, callovphysx_get_last_error()on the same thread to retrieve the error message (valid until the next ovphysx API call on that thread). Forwait_op, iterateerror_op_indicesand callovphysx_get_last_op_error()per failed index, thenovphysx_destroy_wait_result(&result). Removed:ovphysx_destroy_error(),ovphysx_destroy_errors(). Python API is unaffected (errors are raised as exceptions).ContactEventHeader.stageIdtype changed fromlongtoint64_tfor cross-platform ABI stability. This changes the struct layout on Windows.ovphysx_get_contact_report()C signature changed: now returns typed pointers (const ovphysx_contact_event_header_t**,const ovphysx_contact_point_t**) instead ofconst void**, and gained 2 new parameters (out_friction_anchors,out_num_friction_anchors). Migration: update pointer types and append, NULL, NULLfor friction anchor parameters. C++ callers are unaffected (new params default tonullptr).Removed
ovphysx_articulation_get_dof_count,_body_count,_joint_count,_is_fixed_base,_fixed_tendon_count,_spatial_tendon_count. Migration: useovphysx_get_articulation_metadata(handle, binding, &meta). PythonTensorBindingproperties are unchanged.ovphysx_clone()parameterparent_positions_xyzreplaced withparent_transforms(7 floats per target: px, py, pz, qx, qy, qz, qw). Migration: replaceparent_positions_xyz=[x, y, z]withparent_transforms=[px, py, pz, 0, 0, 0, 1](append identity quaternion).Removed
ovphysx_set_clone_env_root()(C) andset_clone_env_root()(Python). Migration: no replacement needed — the firstclone(),simulate(), orwarmup_gpu()call triggers stage attach lazily.ovphysx_device_tenum reordered:OVPHYSX_DEVICE_AUTO = 0(was 2) so zero-initialized args default to AUTO. Migration: code using symbolic names is unaffected; code using raw integers must update (0=AUTO, 1=GPU, 2=CPU).OVPHYSX_TENSOR_ARTICULATION_CORIOLIS_FORCE_F32renamed toARTICULATION_CORIOLIS_AND_CENTRIFUGAL_FORCE(enum value 72 unchanged). Migration: update references to the old name.Scene query enum constants disambiguated:
OVPHYSX_SCENE_QUERY_CLOSEST→OVPHYSX_SCENE_QUERY_MODE_CLOSEST(and_ANY,_ALL);OVPHYSX_SCENE_QUERY_SPHERE→OVPHYSX_SCENE_QUERY_GEOMETRY_SPHERE(and_BOX,_SHAPE). Migration: update to the new constant names.Python enum cleanup. All
OVPHYSX_TENSOR_*_F32bare-int constants removed (useTensorType.*members);OVPHYSX_API_*,OVPHYSX_LOG_*,OVPHYSX_DEVICE_*constants removed (useApiStatus.*,LogLevel.*,DeviceType.*);OVPHYSX_OP_INDEX_ALLrenamed toOP_INDEX_ALL;kDLCPU,kDLCUDA,kDLInt,kDLUInt,kDLFloatremoved (useDLDeviceType.kDLCPU/DLDataTypeCode.kDLFloat).Removed
ovphysx_finalize(). Full Carbonite framework teardown now happens automatically when the last instance is destroyed. Migration: remove anyovphysx_finalize()calls.
Added#
ovphysx_articulation_metadata_tstruct andovphysx_get_articulation_metadata()— fills 6 scalar topology fields in one call, one lock acquire, one stream fence.OVPHYSX_CONFIG_SCENE_MULTI_GPU_MODE(int32) config entry for/physics/sceneMultiGPUMode.ConfigBool,ConfigInt32,ConfigFloat,ConfigStringIntEnums in Python for typed config key access.PhysXConfigdataclass in Python for typed initialization config.Remote USD loading:
add_usd()now accepts remote URIs (omniverse://, S3, Azure Blob). Use HTTPS virtual-hosted S3 URLs. Newovphysx_configure_s3()/configure_s3()andovphysx_configure_azure_sas()/configure_azure_sas()for credential setup.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).TensorBindingsAPI: dynamics query tensors (read-only) — Jacobian, generalized mass matrix, Coriolis + centrifugal forces, gravity compensation, link incoming joint force, DOF projected joint forces.
TensorBindingsAPI: standalone rigid body properties — mass, inertia, COM pose (read/write with indexed/masked write support). Articulation body inverse mass and inverse inertia (read-only).
TensorBindingsAPI: fixed tendon properties — stiffness, damping, limit stiffness, limits, rest length, offset (read/write, indexed, masked).
TensorBindingsAPI: spatial tendon properties — stiffness, damping, limit stiffness, offset (read/write, indexed, masked).
TensorBindingsAPI: shape-level tensors —
RIGID_BODY_SHAPE_FRICTION_AND_RESTITUTION([N,S,3]),RIGID_BODY_CONTACT_OFFSET([N,S]),RIGID_BODY_REST_OFFSET([N,S]), and articulation equivalents. All support read, indexed write, and masked write.Articulation metadata name queries:
ovphysx_articulation_get_dof_names,get_body_names,get_joint_names.Contact binding API:
ovphysx_create_contact_binding/destroy/get_spec/read_net_forces/read_force_matrix. PythonContactBindingclass for reading contact forces via DLPack tensors.PhysX object interop:
ovphysx_get_physx_ptr()returns raw PhysX SDK pointers by USD prim path and type enum. SDK ships PhysX headers underinclude/physx/. See PhysX Interop tutorial.Contact report API:
ovphysx_get_contact_report()exposes per-step contact data as typed C struct pointers. PythonPhysX.get_contact_report()returns a dict with ctypes arrays.Scene query API:
ovphysx_raycast(),ovphysx_sweep(),ovphysx_overlap()— raycast, geometry sweep, and overlap queries. Supports CLOSEST/ANY/ALL hit modes with sphere, box, and arbitrary-shape geometry.Synchronous stepping:
ovphysx_step_sync()/step_sync()combines step + wait into one call.ovphysx_step_n_sync()/step_n_sync()batches N steps.OVPHYSX_API_BUFFER_TOO_SMALL = 6status code.CMake package config:
find_package(ovphysx)providesovphysx::ovphysxtarget and Windowsovphysx_copy_runtime_dlls()helper.Visual sample using Rerun for rigid body visualization (Rendering Handoff).
Linux aarch64 support.
Changed or Fixed#
Physics scene is now parsed lazily on the first
simulate()call instead of duringadd_usd(). This avoids GPU buffer corruption whenclone()adds environments after the initial load. Correct sequence:add_usd()→clone()→warmup_gpu()/step().A GPU and CUDA installation is no longer required for 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 now validate dtype is float32 before dispatching.
ContactBinding.destroy()now checks status and raises on failure; validates parent SDK is alive.create_contact_bindingnow validatessensor_patternsis non-empty andfilter_patternslength.Wheel metadata corrected:
Root-Is-Purelib: false, OS classifiers set to Linux and Windows.Fixed shutdown crash where stepper tasks could access freed CUDA context.
Fixed process-exit crashes caused by non-deterministic DLL/SO unload ordering.
ovphysx_destroy_instance()now performs full teardown when the last instance is destroyed.ovphysx_remove_usd()now validatesusd_handleand returnsOVPHYSX_API_NOT_FOUNDfor unknown handles.
Removed#
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.