Tensor Bindings – Read and Write Simulation Data#
Deprecated since ovphysx 0.6. The tensor-binding API this tutorial teaches is superseded by the session read/write API —
ovphysx_read/ovphysx_writein C,PhysX.read/PhysX.writein Python. Bindings keep working through the deprecation window but are not the recommended path for new code. The runnable session samples aretests/c_samples/output_read_c/andtests/python_samples/output_read.py. Refer to Migrating to the Session Read/Write API.
This tutorial shows how to read and write simulation data through tensor bindings after you attach an ovstage-populated scene. You learn how to use path patterns to bind multiple physics objects in one call, including runtime-only clone paths.
Migrating to the session read/write API#
The session API replaces a binding’s single packed TensorType with an (object type, attribute name) pair passed to ovphysx_query + ovphysx_read / ovphysx_write. Two model
changes matter when porting:
Packed columns split into per-attribute columns.
RIGID_BODY_POSE(N, 7)becomes two attributes —position(N, 3)andorientation(N, 4);RIGID_BODY_VELOCITY(N, 6)becomeslinearVelocity(N, 3)andangularVelocity(N, 3).Angular units are per-axis, not per column. An
ARTICULATION_JOINTjoint row holds one value per unlocked reduced-coordinate DOF axis, so the unit ofjointPosition,jointVelocity,jointPositionTarget, andjointVelocityTargetfollows the axis kind, not the column: an angular axis (revolute, or a D6 rotational DOF) is always in degrees, and a prismatic axis stays in the stage’s base length unit (no conversion). The joint’s axis kind fixes this — authoring aJointStateAPIdoes not change it. A binding value on an angular axis must be scaled to degrees, but scaling a prismatic axis by 57.3× is a bug — so convert by the axis kind you authored in USD (no per-row attribute reports it back), not by blanket-converting the column. Refer to ovstage integration for the full per-axis rule. Everything else, including the body / link / rootangularVelocitycolumns, is the engine’s native radians with no conversion — scaling those by 57.3× is likewise a bug.
Tensor binding to session API mapping
This table maps each tensor-binding TensorType to its session object type and
attributes:
Tensor binding |
Session object type |
Session attribute(s) |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This is the common subset. The full attribute vocabulary is the OVPHYSX_ATTR_* macros in
ovphysx_types.h, and which (object type, attribute) pairs are writable is queryable at
runtime through ovphysx_writability.
Prerequisites#
Complete the Hello World tutorial.
A USD scene that contains physics-enabled prims matching your binding pattern. This tutorial uses
links_chain_sample.usda, which ships with every package underovphysx/samples/data/in the wheel,<sdk-root>/samples/data/in the C/C++ SDK, andtests/data/in a repository checkout.
For the physics concepts behind the quantities these tensors expose — rigid bodies, articulations, joints and drives, deformables — and how to author them in USD, refer to the Simulation Setup pages, starting with Rigid Bodies and Articulations.
Code Language#
Python#
This complete sample attaches links_chain_sample.usda, creates a DOF
velocity-target binding, a link-pose binding, and an optional rigid-body pose
binding from path patterns, writes velocity targets, steps, and reads link
poses back:
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# NOTE: this sample demonstrates the deprecated tensor-binding API. New code should use the
# session read/write API (PhysX.read / PhysX.write).
# @implements REQ-PYTHON-SAMPLE-001
# @covers AC-1 AC-2
# NOTE: This file is included verbatim in documentation via literalinclude.
#!/usr/bin/env python3
"""
Tensor bindings sample demonstrating simulation data exchange.
.. deprecated:: 0.6.0
The tensor-binding API shown here is deprecated in favor of the session read/write
API (``PhysX.read`` / ``PhysX.write``). This sample is retained as the deprecated-API
example and is removed with the API.
This sample demonstrates:
1. Loading a USD scene into an ovstage Stage
2. Creating tensor bindings for data exchange
3. Writing control inputs using tensor API
4. Running extended simulation
5. Reading physics outputs using tensor API
"""
import math
from pathlib import Path
import numpy as np
import ovphysx
from ovphysx import PhysX
from ovphysx.types import TensorType
_physx_schemas_registered = False
def attach_scene(physx, usd_path, stage_name):
import ovstage
if not ovstage.population.available():
raise RuntimeError("ovstage population bridge is unavailable")
# ovphysx ships its PhysX USD schemas as codeless resources and does not register
# them itself. Register them with ovstage once, before the first population
# call in the process. The Newton USD schema (pip package newton-usd-schemas)
# is registered alongside so authored newton:* attributes reach the parser.
global _physx_schemas_registered
if not _physx_schemas_registered:
ovstage.population.register_usd_schemas(
[str(ovphysx.codeless_schema_root()), str(ovphysx.newton_schema_root())]
)
_physx_schemas_registered = True
stage = ovstage.Stage(stage_name)
ordinal = 1
try:
ovstage.population.open_usd(stage, str(usd_path), ordinal=ordinal, domains=ovstage.PopulationDomain.PHYSICS)
stage.advance_write_floor(ordinal=ordinal).wait()
physx.attach_ovstage(stage, read_ordinal=ordinal)
return stage
except Exception:
stage.destroy()
raise
def main():
PhysX.set_cpu_mode(True)
physx = PhysX()
stage = None
velocity_target_binding = None
link_pose_binding = None
optional_pose_binding = None
try:
# Prefer package data so a copied sample works. Fall back to the checked-in
# sample's adjacent data directory when package data is absent.
usd_path = (
Path(ovphysx.__file__).resolve().parent
/ "samples"
/ "data"
/ "links_chain_sample.usda"
)
if not usd_path.is_file():
usd_path = Path(__file__).resolve().parent.parent / "data" / "links_chain_sample.usda"
if not usd_path.is_file():
raise FileNotFoundError(f"ovphysx sample data is missing: {usd_path}")
print(f"Loading USD scene through ovstage: {usd_path}")
stage = attach_scene(physx, usd_path, "ovphysx-tensor-bindings-sample")
physx.wait_all()
print("Creating tensor binding for DOF velocity targets...")
velocity_target_binding = physx.create_tensor_binding(
pattern="/World/articulation/articulationLink*",
tensor_type=TensorType.ARTICULATION_DOF_VELOCITY_TARGET,
)
print(f" DOF count: {velocity_target_binding.shape[1]}")
print("Creating tensor binding for link poses...")
link_pose_binding = physx.create_tensor_binding(
pattern="/World/articulation/articulationLink*",
tensor_type=TensorType.ARTICULATION_LINK_POSE,
)
print(f" Link count: {link_pose_binding.shape[1]}, Pose dims: {link_pose_binding.shape[2]}")
optional_pose_binding = physx.create_tensor_binding(
pattern="/World/optionalRigidBodies/*",
tensor_type=TensorType.RIGID_BODY_POSE,
)
if optional_pose_binding.count == 0:
print(" Optional rigid body pose binding is empty")
optional_pose_binding.destroy()
optional_pose_binding = None
num_dofs = velocity_target_binding.shape[1]
velocity_targets = np.zeros(velocity_target_binding.shape, dtype=np.float32)
for i in range(num_dofs):
velocity_targets[0, i] = 25.0 if i % 2 == 0 else -25.0
print("Setting DOF velocity targets (alternating +/-25 rad/s)...")
velocity_target_binding.write(velocity_targets)
print(f" Velocity targets: {velocity_targets[0, :5]}... (first 5 DOFs)")
print("\nRunning 1000 simulation steps...")
link_poses = np.zeros(link_pose_binding.shape, dtype=np.float32)
link_count = link_pose_binding.shape[1]
if link_count < 2:
raise RuntimeError(f"Fixture must expose more than one articulation link, got {link_count}")
# Link 0 is the fixed root. This linear fixture's last link is its moving chain tip.
link_index_to_print = link_count - 1
initial_printed_position = None
max_abs_position_delta = 0.0
motion_tolerance = 1.0e-3
dt = 0.01
for i in range(1000):
physx.step(dt)
physx.wait_all()
if i % 100 == 0 or i == 999:
link_pose_binding.read(link_poses)
displayed_pose = link_poses[0, link_index_to_print]
if not np.all(np.isfinite(displayed_pose)):
raise RuntimeError(
f"Selected link {link_index_to_print} has a non-finite pose at step {i}: {displayed_pose}"
)
position = displayed_pose[0:3]
if initial_printed_position is None:
initial_printed_position = position.copy()
else:
# This checks that the displayed link is not static. It does not
# require motion after the chain settles.
position_delta = float(np.max(np.abs(position - initial_printed_position)))
max_abs_position_delta = max(max_abs_position_delta, position_delta)
px, py, pz = position
qx, qy, qz, qw = displayed_pose[3:7]
roll_x_rad = math.atan2(
2.0 * (qw * qx + qy * qz), 1.0 - 2.0 * (qx * qx + qy * qy)
)
deg_x = roll_x_rad * 180.0 / math.pi
print(
f" Step {i:4d}, link {link_index_to_print}: "
f"pos=({px:.6f}, {py:.6f}, {pz:.6f}), "
f"quat(xyzw)=({qx:.6f}, {qy:.6f}, {qz:.6f}, {qw:.6f}), "
f"rotation_x={deg_x:.2f} deg"
)
if max_abs_position_delta <= motion_tolerance:
raise RuntimeError(
f"Printed link {link_index_to_print} max position delta {max_abs_position_delta:.6f} "
f"did not exceed motion tolerance {motion_tolerance:.6f}"
)
print(
f"\nCompleted 1000 simulation steps successfully! "
f"Link {link_index_to_print} max position delta: {max_abs_position_delta:.6f}"
)
finally:
if optional_pose_binding is not None:
optional_pose_binding.destroy()
if velocity_target_binding is not None:
velocity_target_binding.destroy()
if link_pose_binding is not None:
link_pose_binding.destroy()
if stage is not None:
physx.detach_ovstage()
stage.destroy()
physx.destroy()
print("Cleanup complete")
if __name__ == "__main__":
main()
C#
Create tensor bindings, write control targets, step, and read back state:
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// NOTE: this sample demonstrates the deprecated tensor-binding API. New code should use the
// session read/write API (ovphysx_read / ovphysx_write).
// NOTE: This file is included verbatim in the documentation via literalinclude.
#include <ovphysx/ovphysx.h>
#include <ovphysx/ovphysx_types.h>
#include "ovstage_sample.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
#error "This file must be compiled as C, not C++"
#endif
#define NUM_LINKS 15
#define NUM_JOINTS 14
static ovphysx_sample_stage_attachment_t g_stage_attachment;
typedef struct TensorBuffer {
DLTensor tensor;
void* data;
int64_t* shape;
} TensorBuffer;
static int check_result(ovphysx_result_t result, const char* context) {
if (result.status != OVPHYSX_API_SUCCESS) {
fprintf(stderr, "ERROR in %s: ", context);
ovphysx_string_t err = ovphysx_get_last_error();
if (err.ptr && err.length > 0) {
fprintf(stderr, "%.*s\n", (int)err.length, err.ptr);
} else {
fprintf(stderr, "status=%d\n", (int)result.status);
}
return 0;
}
return 1;
}
static void destroy_tensor(TensorBuffer* t) {
if (!t) {
return;
}
free(t->data);
free(t->shape);
t->data = NULL;
t->shape = NULL;
}
static TensorBuffer make_tensor_f32_2d(size_t rows, size_t cols) {
TensorBuffer t;
memset(&t, 0, sizeof(TensorBuffer));
t.data = calloc(rows * cols, sizeof(float));
t.shape = (int64_t*)malloc(sizeof(int64_t) * 2);
t.shape[0] = (int64_t)rows;
t.shape[1] = (int64_t)cols;
t.tensor.data = t.data;
t.tensor.ndim = 2;
t.tensor.shape = t.shape;
t.tensor.strides = NULL;
t.tensor.byte_offset = 0;
t.tensor.dtype.code = kDLFloat;
t.tensor.dtype.bits = 32;
t.tensor.dtype.lanes = 1;
t.tensor.device.device_type = kDLCPU;
t.tensor.device.device_id = 0;
return t;
}
static TensorBuffer make_tensor_f32_3d(size_t dim0, size_t dim1, size_t dim2) {
TensorBuffer t;
memset(&t, 0, sizeof(TensorBuffer));
t.data = calloc(dim0 * dim1 * dim2, sizeof(float));
t.shape = (int64_t*)malloc(sizeof(int64_t) * 3);
t.shape[0] = (int64_t)dim0;
t.shape[1] = (int64_t)dim1;
t.shape[2] = (int64_t)dim2;
t.tensor.data = t.data;
t.tensor.ndim = 3;
t.tensor.shape = t.shape;
t.tensor.strides = NULL;
t.tensor.byte_offset = 0;
t.tensor.dtype.code = kDLFloat;
t.tensor.dtype.bits = 32;
t.tensor.dtype.lanes = 1;
t.tensor.device.device_type = kDLCPU;
t.tensor.device.device_id = 0;
return t;
}
static int wait_op(ovphysx_handle_t handle, ovphysx_op_index_t op_index, const char* context) {
ovphysx_op_wait_result_t wait_result = {0};
ovphysx_result_t result = ovphysx_wait_op(handle, op_index, 10ULL * 1000 * 1000 * 1000, &wait_result);
int has_errors = (wait_result.num_errors > 0);
ovphysx_destroy_wait_result(&wait_result);
if (has_errors) {
fprintf(stderr, "ERROR in %s: async operation failed\n", context);
return 0;
}
if (result.status != OVPHYSX_API_SUCCESS) {
fprintf(stderr, "ERROR in %s: wait failed (status=%d)\n", context, (int)result.status);
return 0;
}
return 1;
}
static int destroy_instance_and_shutdown(ovphysx_handle_t handle) {
ovphysx_sample_destroy_stage(handle, &g_stage_attachment);
ovphysx_destroy_instance(handle);
ovphysx_shutdown();
return 1;
}
static int run(void) {
printf("=== ovphysx Articulation Control (C API - Tensor Binding) ===\n");
ovphysx_result_t result = ovphysx_initialize();
if (!check_result(result, "initialize")) {
return 1;
}
// 1. Create instance
ovphysx_handle_t handle = 0;
ovphysx_create_args args = OVPHYSX_CREATE_ARGS_DEFAULT;
result = ovphysx_create_instance(&args, &handle);
if (!check_result(result, "create_instance")) {
ovphysx_shutdown();
return 1;
}
printf("Instance created.\n");
// 2. Populate ovstage from USD and attach it
memset(&g_stage_attachment, 0, sizeof(g_stage_attachment));
if (!ovphysx_sample_attach_usd_with_ovstage(
handle, OVPHYSX_TEST_DATA "/links_chain_sample.usda", &g_stage_attachment)) {
fprintf(stderr, "Failed to attach ovstage scene\n");
return destroy_instance_and_shutdown(handle);
}
printf("USD scene loaded.\n");
// 3. Create tensor bindings.
// 3a. DOF velocity target binding, used to write control targets.
ovphysx_tensor_binding_handle_t dof_target_binding = 0;
ovphysx_tensor_binding_desc_t dof_target_desc = {
.pattern = OVPHYSX_LITERAL("/World/articulation"),
.tensor_type = OVPHYSX_TENSOR_ARTICULATION_DOF_VELOCITY_TARGET_F32
};
result = ovphysx_create_tensor_binding(handle, &dof_target_desc, &dof_target_binding);
if (!check_result(result, "create DOF target binding")) {
return destroy_instance_and_shutdown(handle);
}
// 3b. Articulation link pose binding, used to read the simulated state.
ovphysx_tensor_binding_handle_t link_pose_binding = 0;
ovphysx_tensor_binding_desc_t link_pose_desc = {
.pattern = OVPHYSX_LITERAL("/World/articulation"),
.tensor_type = OVPHYSX_TENSOR_ARTICULATION_LINK_POSE_F32
};
result = ovphysx_create_tensor_binding(handle, &link_pose_desc, &link_pose_binding);
if (!check_result(result, "create articulation link pose binding")) {
return destroy_instance_and_shutdown(handle);
}
printf("Tensor bindings created.\n");
// 4. Query binding specs and allocate tensors
ovphysx_tensor_spec_t dof_spec, link_pose_spec;
result = ovphysx_get_tensor_binding_spec(handle, dof_target_binding, &dof_spec);
if (!check_result(result, "get_tensor_binding_spec (dof target)")) {
return destroy_instance_and_shutdown(handle);
}
result = ovphysx_get_tensor_binding_spec(handle, link_pose_binding, &link_pose_spec);
if (!check_result(result, "get_tensor_binding_spec (link pose)")) {
return destroy_instance_and_shutdown(handle);
}
printf("\nBinding specs:\n");
printf(" Articulation DOFs: shape=[%lld, %lld], ndim=%d\n",
(long long)dof_spec.shape[0], (long long)dof_spec.shape[1], dof_spec.ndim);
printf(" Articulation link poses: shape=[%lld, %lld, %lld], ndim=%d\n",
(long long)link_pose_spec.shape[0],
(long long)link_pose_spec.shape[1],
(long long)link_pose_spec.shape[2],
link_pose_spec.ndim);
// Allocate CPU tensors matching the reported specs.
const size_t dof_count = (size_t)dof_spec.shape[0];
const size_t dof_components = (size_t)dof_spec.shape[1];
const size_t link_pose_batch = (size_t)link_pose_spec.shape[0];
const size_t link_count = (size_t)link_pose_spec.shape[1];
const size_t link_pose_components = (size_t)link_pose_spec.shape[2];
TensorBuffer dof_target_tensor = make_tensor_f32_2d(dof_count, dof_components);
TensorBuffer link_pose_tensor = make_tensor_f32_3d(link_pose_batch, link_count, link_pose_components);
// 5. Set initial DOF velocity targets and simulate
printf("\n=== Setting initial DOF velocity targets ===\n");
float* dof_target_data = (float*)dof_target_tensor.data;
for (size_t i = 0; i < dof_count * dof_components; i++) {
dof_target_data[i] = 0.0f;
}
printf("\n=== Writing initial DOF velocity targets ===\n");
result = ovphysx_write_tensor_binding(handle, dof_target_binding, &dof_target_tensor.tensor, NULL);
if (!check_result(result, "write initial DOF targets")) {
destroy_tensor(&dof_target_tensor);
destroy_tensor(&link_pose_tensor);
return destroy_instance_and_shutdown(handle);
}
// 6. Simulation loop
const float dt = 1.0f / 60.0f;
const size_t link_index_to_print = (link_count > 0) ? (link_count - 1) : 0;
printf("Running 120 simulation steps...\n");
for (int step = 0; step < 120; ++step) {
// Every 50 steps flip the target velocity sign, and alternate the
// direction per DOF so neighbouring joints drive against each other.
if (step % 50 == 0) {
float target_vel = ((step / 50) % 2 == 0) ? 50.0f : -50.0f;
for (size_t i = 0; i < dof_count * dof_components; ++i) {
dof_target_data[i] = (i % 2 == 0) ? target_vel : -target_vel;
}
result = ovphysx_write_tensor_binding(handle, dof_target_binding, &dof_target_tensor.tensor, NULL);
if (!check_result(result, "write DOF targets")) {
destroy_tensor(&dof_target_tensor);
destroy_tensor(&link_pose_tensor);
return destroy_instance_and_shutdown(handle);
}
}
ovphysx_enqueue_result_t step_result = ovphysx_step(handle, dt);
if (step_result.status != OVPHYSX_API_SUCCESS) {
fprintf(stderr, "ERROR in step enqueue (status=%d)\n", (int)step_result.status);
{
ovphysx_string_t err = ovphysx_get_last_error();
if (err.ptr && err.length > 0)
fprintf(stderr, " %.*s\n", (int)err.length, err.ptr);
}
destroy_tensor(&dof_target_tensor);
destroy_tensor(&link_pose_tensor);
return destroy_instance_and_shutdown(handle);
}
if (!wait_op(handle, step_result.op_index, "step")) {
destroy_tensor(&dof_target_tensor);
destroy_tensor(&link_pose_tensor);
return destroy_instance_and_shutdown(handle);
}
// Read and print the link poses every 30 steps.
if (step % 30 == 0) {
result = ovphysx_read_tensor_binding(handle, link_pose_binding, &link_pose_tensor.tensor);
if (!check_result(result, "read articulation link poses")) {
destroy_tensor(&dof_target_tensor);
destroy_tensor(&link_pose_tensor);
return destroy_instance_and_shutdown(handle);
}
const float* link_pose_data = (const float*)link_pose_tensor.data;
size_t articulation_index = 0;
size_t link_pose_offset = (articulation_index * link_count + link_index_to_print) * link_pose_components;
printf("Step %3d | Link %zu pos=(%.3f, %.3f, %.3f) quat=(%.3f, %.3f, %.3f, %.3f)\n",
step,
link_index_to_print,
link_pose_data[link_pose_offset + 0],
link_pose_data[link_pose_offset + 1],
link_pose_data[link_pose_offset + 2],
link_pose_data[link_pose_offset + 3],
link_pose_data[link_pose_offset + 4],
link_pose_data[link_pose_offset + 5],
link_pose_data[link_pose_offset + 6]);
}
}
printf("\n=== Cleanup ===\n");
destroy_tensor(&dof_target_tensor);
destroy_tensor(&link_pose_tensor);
ovphysx_destroy_tensor_binding(handle, dof_target_binding);
ovphysx_destroy_tensor_binding(handle, link_pose_binding);
printf("=== Articulation control sample completed successfully ===\n");
ovphysx_sample_destroy_stage(handle, &g_stage_attachment);
ovphysx_destroy_instance(handle);
ovphysx_shutdown();
printf("Cleanup complete\n");
return 0;
}
int main(void) {
int rc = run();
return rc;
}
For GPU tensor bindings with CUDA, refer to tensor_bindings_gpu_c/ in the samples
directory. GPU dynamics are enabled by default (physxScene:enableGPUDynamics
defaults to true); set it to false to opt into CPU dynamics. For maximum
performance in tensor-heavy loops, GPU dynamics alone is not enough: enable
DirectGPU TensorAPI before creating the PhysX instance with
/physics/suppressReadback=true. Refer to
Warmup and Determinism.
Empty Optional Bindings#
A tensor binding that matches zero physics objects is valid. This is useful when absence
is a legitimate result for the current scene, such as optional assets or broad
inspection queries. Empty bindings remain zero-count views; if topology changes
and matching physics objects are added or recreated, destroy the old binding and create a
new one. For optional queries, keep the default raise_if_empty=False and
check binding.count before allocating or reading tensors. Use
raise_if_empty=True only when zero matches are a configuration error for your
application.
Point-instancer limitation. TensorBindingsAPI does not expose per-instance rows for rigid bodies created by
UsdGeom.PointInstancer. With the defaultraise_if_empty=False, a rigid-body binding that targets only the point instancer has count zero; the opt-inraise_if_empty=Truemode raises instead. Use the ovstage output read API for simulated instance readback. For control, author the point instancer’spositions,orientations,velocities, andangularVelocitiesarrays through ovstage and pass those control ordinals toupdate_from_ovstage(). Use standalone rigid-body prims when per-body tensor bindings are required.
Binding Lifetime#
Tensor bindings are views of the physics objects realized for the current stage.
Create them after loading USD and reuse them across simulation steps. A normal
step() or step_sync() does not invalidate a binding.
Do not keep cached bindings across application-owned topology changes. Before
reset(), before removing USD data that contains bound objects, or before
loading or reparsing a stage so bound objects are destroyed and recreated,
destroy cached bindings when practical. If a stale binding survives one of those
lifecycle operations, only destroy it; do not read or write through it. Create a
replacement binding after the operation completes. In reset-heavy episode code,
the reset path should clear cached bindings because that path is where the
application changes the stage.
step() is asynchronous: in-stream tensor reads and writes do not need extra
synchronization, but out-of-stream consumers must call wait_op() or
wait_all() before reading results. Refer to the
Execution Model for details.
Tensor Type Reference#
Use this table to pre-allocate tensors without probing binding.shape at runtime.
Python callers can also inspect binding.spec for the DLPack dtype and layout
returned by ovphysx_get_tensor_binding_spec(). Allocate buffers from
binding.shape and binding.dtype; most tensor types are float32, but runtime
bool bindings such as TensorType.RIGID_BODY_DISABLE_SIMULATION,
TensorType.RIGID_BODY_DISABLE_GRAVITY, and
TensorType.ARTICULATION_BODY_DISABLE_GRAVITY report uint8, as does the
read-only enum binding TensorType.ARTICULATION_DOF_DRIVE_TYPE.
Inspect binding.native_device before choosing where to allocate. It returns a
Python-owned DLDevice: CPU-only property bindings report kDLCPU even in a
DirectGPU scene, while other bindings follow their native TensorAPI view. That
view is CUDA for DirectGPU and CPU otherwise, even when the scene uses GPU
dynamics. In C, query the same value with
ovphysx_get_tensor_binding_native_device(). This is the no-staging device;
the query does not change existing read/write behavior.
The standalone rigid-body property, articulation DOF/body property, shape property, and deformable-material tables in Tensor Type Reference are CPU-only. Their data, index, and mask buffers must be host-resident even when the simulation runs on GPU; CUDA and CUDA-managed buffers are rejected rather than copied to host. Fixed and spatial tendon property tensors use the simulation device instead.
Symbols:
N: rigid body count in the bindingA: articulation count in the bindingL: max link count across matched articulationsD: max DOF count across matched articulationsT: max tendon count across matched articulations (fixed or spatial, depending on type)M: generalized coordinate count —numDofsfor fixed-base,numDofs + 6for floating-base articulationsS: max collision shape count per body/link in the bindingR,C: Jacobian shape fromgetJacobianShape()— fixed-base:R=(L-1)*6, C=D; floating-base:R=(L-1)*6+6, C=D+6B: volume deformable body count in the bindingV: max simulation node count across matched volume deformablesVr: max rest node count across matched volume deformablesE: max simulation element count across matched volume deformables (tetrahedra, K=4)F: max collision element count across matched volume deformables; K =getNumNodesPerElement()(4 for tetmesh)P: deformable material count in the bindingBs: surface deformable body count in the bindingVs: max simulation node count across matched surface deformablesEs: max simulation element count across matched surface deformables (triangles, K=3)
Rigid Body State
These constants expose per-body simulation state on a rigid-body binding:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
|
World-frame pose; writes teleport with |
|
|
2D |
yes |
yes |
|
World-frame linear and angular velocity |
|
|
2D |
yes |
no |
|
World-frame linear and angular acceleration |
|
|
2D |
no |
yes |
|
Write-only force at center of mass (control input) |
|
|
2D |
no |
yes |
|
Write-only wrench-at-position in world frame |
Rigid Body Properties (standalone, non-articulated bodies)
These constants expose mass, inertia, and runtime flags on standalone rigid bodies:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
1D |
yes |
yes |
mass scalar |
Scalar mass per rigid body |
|
|
1D |
yes |
no |
inverse mass scalar |
Computed from mass; read-only |
|
|
2D |
yes |
yes |
row-major 3x3 |
Inertia tensor in body frame |
|
|
2D |
yes |
no |
row-major 3x3 |
Computed from inertia; read-only |
|
|
2D |
yes |
yes |
|
COM local pose in body frame |
|
|
1D |
yes |
yes |
uint8 flag |
Nonzero disables simulation at runtime |
|
|
1D |
yes |
yes |
uint8 flag |
Nonzero disables gravity at runtime; live PhysX flags only |
Rigid body property tensors in this table are CPU tensors even when the simulation is running on GPU. State tensors such as pose, velocity, acceleration, force, and wrench use the binding’s native TensorAPI view; query the binding device rather than inferring it from GPU dynamics.
For Python bindings, binding.prim_paths returns row metadata only; tensor
reads and writes keep using the tabulated shapes. Rigid-body bindings return one
rigid-body object path per row. Articulation bindings return one articulation
root object path per A row; link names remain available through
binding.body_names.
Rigid Body Shape Properties
These constants expose per-collision-shape material and offset values on a rigid-body binding:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
3D |
yes |
yes |
|
Per-shape material properties |
|
|
2D |
yes |
yes |
offset scalar per shape |
Distance at which contacts are generated |
|
|
2D |
yes |
yes |
offset scalar per shape |
Rest separation between shapes |
Shape property tensors in this table are CPU tensors even when the simulation is running on GPU.
Volume Deformable Body State
Symbols: B = volume deformable body count, V = max simulation nodes, Vr = max rest nodes, E = max simulation elements (tetrahedra, K=4), F = max collision elements (triangles, K=3).
These constants expose simulation and rest mesh state on volume deformable bodies:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
3D |
yes |
yes |
|
Simulation mesh node positions |
|
|
3D |
yes |
yes |
|
Simulation mesh node velocities |
|
|
3D |
yes |
yes |
|
Simulation mesh kinematic targets |
|
|
3D |
yes |
no |
|
Rest mesh node positions |
|
|
3D |
yes |
no |
int32 node indices |
Tetrahedral simulation element connectivity |
|
|
3D |
yes |
no |
int32 node indices |
Collision element connectivity; K=4 for volume tetmesh |
Volume deformable body tensors require DirectGPU mode. Enable
/physics/suppressReadback=true before constructing the PhysX instance.
Surface Deformable Body State
Symbols: Bs = surface deformable body count, Vs = max simulation nodes, Vr = max rest nodes, Es = max simulation elements (triangles, K=3).
These constants expose simulation and rest mesh state on surface deformable bodies:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
3D |
yes |
yes |
|
Simulation mesh node positions |
|
|
3D |
yes |
yes |
|
Simulation mesh node velocities |
|
|
3D |
yes |
no |
|
Rest mesh node positions |
|
|
3D |
yes |
no |
int32 node indices |
Triangular simulation element connectivity |
Surface deformable body tensors require DirectGPU mode. Enable
/physics/suppressReadback=true before constructing the PhysX instance.
Deformable Material Properties
These constants expose per-material friction and elasticity values on a deformable-material binding:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
1D |
yes |
yes |
scalar |
Dynamic friction per deformable material |
|
|
1D |
yes |
yes |
scalar |
Young’s modulus per deformable material |
|
|
1D |
yes |
yes |
scalar |
Poisson’s ratio per deformable material |
Deformable material property tensors in this table are CPU tensors even when the simulation is running on GPU.
Articulation Root State
These constants expose root-body pose, velocity, and center-of-mass values, one row per articulation:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
|
Root body transform in the exposed view world frame |
|
|
2D |
yes |
yes |
|
Root body velocity per articulation |
|
|
2D |
yes |
no |
|
Articulation COM in the exposed view world frame; subspace origin removed |
|
|
2D |
yes |
no |
|
Articulation COM in the root link’s center-of-mass (mass) frame, not its actor/prim frame |
Articulation Link State
These constants expose per-link state and the write-only external wrench:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
3D |
yes |
no |
|
Per-link pose; padded links are zero |
|
|
3D |
yes |
no |
|
Per-link velocity; read-only |
|
|
3D |
yes |
no |
|
Per-link linear and angular acceleration; read-only |
|
|
3D |
no |
yes |
|
Write-only per-link external wrench |
Articulation DOF State and Control
These constants expose joint-space state and the drive targets that control it, in articulation DOF order:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
joint position scalar per DOF |
Joint-space position in articulation DOF order |
|
|
2D |
yes |
yes |
joint velocity scalar per DOF |
Joint-space velocity in articulation DOF order |
|
|
2D |
yes |
yes |
target position scalar per DOF |
Position-control targets |
|
|
2D |
yes |
yes |
target velocity scalar per DOF |
Velocity-control targets |
|
|
2D |
yes |
yes |
actuation scalar per DOF |
Readback is from staging buffer; can differ from solver-applied force |
Articulation DOF Properties
These constants expose the per-DOF drive gains, limits, and clamps:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
stiffness scalar per DOF |
PD position-control stiffness |
|
|
2D |
yes |
yes |
damping scalar per DOF |
PD velocity-control damping |
|
|
3D |
yes |
yes |
|
Joint position limits |
|
|
2D |
yes |
yes |
max velocity scalar per DOF |
Per-DOF velocity clamp |
|
|
2D |
yes |
yes |
max force scalar per DOF |
Per-DOF force/torque clamp |
|
|
2D |
yes |
yes |
armature scalar per DOF |
Added inertia at each DOF |
|
|
3D |
yes |
yes |
|
Friction coefficients at each DOF |
|
|
2D |
yes |
no |
uint8 per DOF |
|
Articulation Body Properties
These constants expose per-link mass, inertia, and gravity flags:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
mass scalar per link |
Scalar mass per articulation link |
|
|
3D |
yes |
yes |
|
COM local pose in body frame per link |
|
|
3D |
yes |
yes |
row-major 3x3 |
Inertia tensor in COM frame per link |
|
|
2D |
yes |
no |
inverse mass scalar per link |
Computed from mass; read-only |
|
|
3D |
yes |
no |
row-major 3x3 |
Computed from inertia; read-only |
|
|
2D |
yes |
yes |
uint8 flag per link |
Nonzero disables gravity per link at runtime; padded link columns ignored on write |
Articulation Shape Properties
These constants expose per-collision-shape material and offset values on an articulation binding:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
3D |
yes |
yes |
|
Per-shape material properties per link |
|
|
2D |
yes |
yes |
offset scalar per shape |
Distance at which contacts are generated |
|
|
2D |
yes |
yes |
offset scalar per shape |
Rest separation between shapes |
Shape property tensors in this table are CPU tensors even when the simulation is running on GPU.
Articulation Inverse Dynamics Queries (read-only)
These read-only constants expose the derived dynamics quantities computed by the solver:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
3D |
yes |
no |
row-major |
Shape from |
|
|
3D |
yes |
no |
row-major square |
Generalized mass matrix; shape from |
|
|
2D |
yes |
no |
force scalar per generalized coordinate |
Combined Coriolis and centrifugal forces |
|
|
2D |
yes |
no |
force scalar per generalized coordinate |
Gravity compensation forces |
|
|
3D |
yes |
no |
|
Incoming joint force and torque per link |
|
|
2D |
yes |
no |
scalar per DOF |
Projected joint forces |
The generalized joint coordinates in these inverse dynamics tensors use the direction
authored by each USD joint relationship: the sign is positive when body0 is
the articulation parent and negative when body1 is the parent. If S_dof is
the diagonal matrix of those signs, use T=S_dof for a fixed base and
T=diag(I6,S_dof) for a floating base. The returned values are
J=J_physx*T, M=T*M_physx*T, c=T*c_physx, and g=T*g_physx. The packed
centroidal result follows [A|b]=[A_physx*T|b_physx], so its six root columns
and bias column are unchanged. Angular generalized-coordinate dimensions use
radians and receive no degree conversion; prismatic and floating-translation
dimensions retain their linear units.
Fixed Tendon Properties
These constants expose per-tendon gains, limits, and lengths on articulations that author fixed tendons:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
stiffness scalar per tendon |
Requires articulation with fixed tendons |
|
|
2D |
yes |
yes |
damping scalar per tendon |
Requires articulation with fixed tendons |
|
|
2D |
yes |
yes |
limit stiffness scalar per tendon |
Requires articulation with fixed tendons |
|
|
3D |
yes |
yes |
|
Fixed tendon position limits |
|
|
2D |
yes |
yes |
rest length scalar per tendon |
Requires articulation with fixed tendons |
|
|
2D |
yes |
yes |
offset scalar per tendon |
Requires articulation with fixed tendons |
Spatial Tendon Properties
These constants expose per-tendon gains and offsets on articulations that author spatial tendons:
Constant |
Shape |
Dimensionality |
Read |
Write |
Component layout |
Behavioral note |
|---|---|---|---|---|---|---|
|
|
2D |
yes |
yes |
stiffness scalar per tendon |
Requires articulation with spatial tendons |
|
|
2D |
yes |
yes |
damping scalar per tendon |
Requires articulation with spatial tendons |
|
|
2D |
yes |
yes |
limit stiffness scalar per tendon |
Requires articulation with spatial tendons |
|
|
2D |
yes |
yes |
offset scalar per tendon |
Requires articulation with spatial tendons |
For canonical enum definitions and low-level semantics, refer to include/ovphysx/ovphysx_types.h.
Result#
After this tutorial, you can create tensor bindings, push batched simulation inputs, and read back batched results. For new code, prefer the session read/write API — refer to Migrating to the Session Read/Write API.