How to Efficiently Update Partial Vertex Information of a Mesh?

Topic Description

Is there a way to **directly access the vertex data buffer of a mesh**, modify only the specific vertex indices in-place, and then refresh/commit the changes? I need to update only a small subset of vertices (approximately 200 out of 50,000+) at high frequency (~30 fps), but the current `UsdGeom.Mesh.GetPointsAttr().Set()` method requires passing the complete vertex array, causing significant performance overhead.

Isaac Sim Version

5.1.0

Detailed Description

I’m developing a simulation scenario that requires updating partial vertex positions of a mesh at very high frequency (approximately 30 fps). In my use case:

- **Mesh size**: Contains over **20,000 vertices** (typically 50,000+ vertices)

- **Update pattern**: Only about **200 vertices** need to be updated per frame (less than 1% of total vertices)

- **Update frequency**: Updates occur every frame at ~30 fps

- **Current method**: Using `UsdGeom.Mesh.GetPointsAttr().Set()` which requires passing the complete vertex array

The current implementation forces me to:

1. Retrieve all vertices using `Get()` (even though I only need to modify ~200)

2. Convert the entire numpy array to a `Gf.Vec3f` list (50,000+ conversions per frame)

3. Call `Set()` with the complete array (triggering validation and notifications for all vertices)

This approach creates significant performance overhead, especially the conversion step which becomes a bottleneck at high update frequencies.

**Question:** Is there a way to **directly access the vertex data buffer**, modify only the specific vertex indices in-place, and then refresh/commit the changes? This would eliminate the need to retrieve, convert, and set the entire vertex array when only a small subset needs updating.

Steps to Reproduce

1. Create or load a mesh with a large number of vertices (e.g., 50,000+ vertices)

2. Identify a small subset of vertices to update (e.g., 200 vertices)

3. Attempt to update only those vertices using the current API:

```python

from pxr import UsdGeom, Gf

import omni.usd

import numpy as np

# Get mesh and vertex attributes

stage = omni.usd.get_context().get_stage()

target_prim = stage.GetPrimAtPath(“/World/ground_plane2/ground_plane2/Grid”)

target_mesh = UsdGeom.Mesh(target_prim)

target_points_attr = target_mesh.GetPointsAttr()

# Get all current vertices (local coordinates)

target_local_points = target_points_attr.Get()

target_local_vertices = np.array([(p[0], p[1], p[2]) for p in target_local_points])

# Only want to update partial vertices (e.g., swept_vertex_indices)

swept_vertex_indices = np.array([100, 200, 300, …]) # Only update vertices at these indices

new_local_vertices = … # New vertex positions (only for updated vertices)

# Update partial vertices into the full array

target_local_vertices[swept_vertex_indices] = new_local_vertices

# Problem: Must convert and set the entire array, even though only a few vertices are updated

new_target_points = [Gf.Vec3f(v[0], v[1], v[2]) for v in target_local_vertices]

target_points_attr.Set(new_target_points) # Full update of all vertices

```

4. Observe performance overhead from:

  • Retrieving all 50,000+ vertices when only 200 need updating

  • Converting 50,000+ numpy arrays to `Gf.Vec3f` objects

  • Setting the entire array back, triggering validation for all vertices

What I’ve Tried

Before posting this question, I’ve searched through the existing Isaac Sim and USD APIs:

1. **Standard USD API**: The `UsdGeom.Mesh.GetPointsAttr()` returns a `VtArray`, which is copy-on-write, but the standard workflow still requires:

  • `Get()` to retrieve the entire array

  • Modify elements in the array

  • `Set()` to write back the entire array

2. **Isaac Sim Deformable API**: I found `DeformablePrim.set_simulation_mesh_nodal_positions(new_positions, indices)` method that accepts indices. However:

  • The `indices` parameter refers to **environment indices** (for batch operations across multiple deformable meshes), not vertex indices

  • Each call still requires passing **all vertices** of the selected environments’ meshes

  • This API is specific to deformable meshes (physics-simulated) and not applicable to standard USD meshes

  • It does not support partial vertex updates within a single mesh

3. **No Direct Buffer Access Found**: I couldn’t find any API methods like:

  • `GetWritableBuffer()`

  • `SetIndices()` / `SetPartial()`

  • `Refresh()` / `Commit()` / `Invalidate()`

All existing code examples in the Isaac Sim codebase use the full `Get()` and `Set()` pattern for mesh vertex updates.

### Related Issues

None found. This appears to be a feature request / optimization question rather than a bug.

### Additional Context

Desired API (Hypothetical)

If direct buffer access were available, the implementation would look like:

```python

# Desired API for direct buffer access (hypothetical)

# Get a writable view of the vertex buffer

vertex_buffer = target_points_attr.GetWritableBuffer() # Returns numpy array or similar

# Modify specific indices directly

vertex_buffer[swept_vertex_indices] = new_local_vertices

# Commit/refresh the changes

target_points_attr.Refresh() # or Commit(), Invalidate(), etc.

```

**Complete Code Example:**

```python

from pxr import UsdGeom, Gf

import omni.usd

import numpy as np

import time

def update_mesh_vertices_partial(

target_mesh_path: str,

swept_vertex_indices: np.ndarray,

new_world_vertices: np.ndarray):

"""

Update partial vertices of a mesh



Parameters:

    target_mesh_path: Prim path of the mesh

    swept_vertex_indices: Array of vertex indices to update

    new_world_vertices: New world coordinate vertices (only for updated vertices)

"""

stage = omni.usd.get_context().get_stage()

target_prim = stage.GetPrimAtPath(target_mesh_path)

target_mesh = UsdGeom.Mesh(target_prim)

target_points_attr = target_mesh.GetPointsAttr()



*# Get all current vertices (local coordinates)*

target_local_points = target_points_attr.Get()

target_local_vertices = np.array(\[(p\[0\], p\[1\], p\[2\]) for p in target_local_points\])



*# Get world-to-local transformation matrix*

target_world_transform = omni.usd.get_world_transform_matrix(target_prim)

target_world_transform_matrix = np.array(target_world_transform).reshape(4, 4)

target_world_to_local = np.linalg.inv(target_world_transform_matrix)



*# Convert new world coordinates to local coordinates*

homogeneous_world = np.column_stack(\[

    new_world_vertices, 

    np.ones(len(new_world_vertices))

\])

homogeneous_local = (target_world_to_local @ homogeneous_world.T).T

new_local_vertices = homogeneous_local\[:, :3\]



*# Update partial vertices into the full array*

target_local_vertices\[swept_vertex_indices\] = new_local_vertices



*# Performance bottleneck: Must convert the entire array to Gf.Vec3f format*

t0 = time.time()

new_target_points = \[Gf.Vec3f(v\[0\], v\[1\], v\[2\]) for v in target_local_vertices\]

conversion_time = time.time() - t0



*# Performance bottleneck: Must set all vertices*

t0 = time.time()

target_points_attr.Set(new_target_points)

set_time = time.time() - t0



print(f"Updated {len(swept_vertex_indices)} vertices out of {len(target_local_vertices)}")

print(f"  Conversion time: {conversion_time\*1000:.2f} ms")

print(f"  Set time: {set_time\*1000:.2f} ms")

print(f"  Total time: {(conversion_time + set_time)\*1000:.2f} ms")

This is a good, well-scoped question, but it will likely get more targeted help in the OpenUSD area where users and engineers focus on low-level UsdGeom.Mesh authoring and performance patterns.​

Would you mind reposting this in the Universal Scene Description (OpenUSD) category here so that the USD experts can chime in?

When you repost, you can keep the same technical details (partial vertex updates, GetPointsAttr().Set(), desired buffer-style API) and mention that you are using Isaac Sim 5.1.0, but the core of the question is about efficient USD mesh point updates rather than an Isaac Sim–specific feature.

Got it, I am actually working on open-USD api trying to figure out a way and recompile the Isaac-sim. Thanks for advise.