-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[OVPHYSX] FrameTransformer sensor #5703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AntoineRichard
merged 23 commits into
isaac-sim:develop
from
AntoineRichard:antoiner/feat/ovphysx_frametransformer
Jun 22, 2026
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
991ec77
Add FrameTransformer to isaaclab_ovphysx.sensors stub
AntoineRichard b6f3c8b
Add OVPhysX frame_transformer scaffolding
AntoineRichard 7e817c3
Add OVPhysX FrameTransformer warp kernels
AntoineRichard 4de3a7c
Add OVPhysX FrameTransformerData
AntoineRichard 5228dab
Add OVPhysX FrameTransformer shell
AntoineRichard 932c8cf
Port debug-vis methods to OVPhysX FT
AntoineRichard 782b9a8
Implement OVPhysX FrameTransformer._initialize_impl
AntoineRichard d451fc4
Clarify _env_wildcardify docstring
AntoineRichard 278f47d
Implement OVPhysX FrameTransformer update loop
AntoineRichard 600bd13
Surface OVPhysX FrameTransformer in factory type stub
AntoineRichard 5ccc41f
Add OVPhysX FrameTransformer test scaffolding
AntoineRichard 8e45e7a
Port FrameTransformer PhysX test suite to OVPhysX
AntoineRichard 8f6de81
Fix CUDA device mismatch in offset_frames test
AntoineRichard dca0036
Add changelog fragment for OVPhysX FrameTransformer
AntoineRichard a7ddb20
Clean up OVPhysX-side state in _invalidate_initialize_callback
AntoineRichard 05b15aa
Wire OVPhysX preset into Isaac-Franka-Cabinet-Direct-v0
AntoineRichard 9558ce0
Polish OVPhysX FrameTransformer port
AntoineRichard 28385d5
Add missing FrameTransformer changelog fragment
AntoineRichard 236dee6
Merge remote-tracking branch 'origin/develop' into antoiner/feat/ovph…
AntoineRichard 2f4a54f
Align OVPhysX FrameTransformer clone discovery
AntoineRichard 9ba47a0
Remove IsaacSim CI marks from OVPhysX tests
AntoineRichard 914359b
Merge remote-tracking branch 'origin/develop' into antoiner/feat/ovph…
AntoineRichard 207a64a
Merge develop into OVPhysX FrameTransformer
AntoineRichard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
source/isaaclab/changelog.d/antoiner-feat-ovphysx-frametransformer.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| Added | ||
| ^^^^^ | ||
|
|
||
| * Added OVPhysX backend dispatch typing for | ||
| :class:`~isaaclab.sensors.FrameTransformer`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
...e/isaaclab_ovphysx/changelog.d/antoiner-feat-ovphysx_frametransformer.minor.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| Added | ||
| ^^^^^ | ||
|
|
||
| * Added :class:`~isaaclab_ovphysx.sensors.FrameTransformer` and | ||
| :class:`~isaaclab_ovphysx.sensors.FrameTransformerData` for OVPhysX | ||
| frame transform sensing. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
source/isaaclab_ovphysx/isaaclab_ovphysx/sensors/frame_transformer/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||
| # All rights reserved. | ||
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| """Sub-module for OVPhysX frame transformer sensor.""" | ||
|
|
||
| from isaaclab.utils.module import lazy_export | ||
|
|
||
| lazy_export() |
12 changes: 12 additions & 0 deletions
12
source/isaaclab_ovphysx/isaaclab_ovphysx/sensors/frame_transformer/__init__.pyi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||
| # All rights reserved. | ||
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| __all__ = [ | ||
| "FrameTransformer", | ||
| "FrameTransformerData", | ||
| ] | ||
|
|
||
| from .frame_transformer import FrameTransformer | ||
| from .frame_transformer_data import FrameTransformerData |
638 changes: 638 additions & 0 deletions
638
source/isaaclab_ovphysx/isaaclab_ovphysx/sensors/frame_transformer/frame_transformer.py
Large diffs are not rendered by default.
Oops, something went wrong.
187 changes: 187 additions & 0 deletions
187
source/isaaclab_ovphysx/isaaclab_ovphysx/sensors/frame_transformer/frame_transformer_data.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||
| # All rights reserved. | ||
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import warp as wp | ||
|
|
||
| from isaaclab.sensors.frame_transformer import BaseFrameTransformerData | ||
| from isaaclab.utils.warp import ProxyArray | ||
|
|
||
| from isaaclab_ovphysx.sensors.kernels import concat_pos_and_quat_to_pose_1d_kernel, concat_pos_and_quat_to_pose_kernel | ||
|
|
||
|
|
||
| class FrameTransformerData(BaseFrameTransformerData): | ||
| """Data container for the OVPhysX frame transformer sensor.""" | ||
|
|
||
| @property | ||
| def target_frame_names(self) -> list[str]: | ||
| """Target frame names (order matches data ordering).""" | ||
| return self._target_frame_names | ||
|
|
||
| @property | ||
| def target_pose_source(self) -> ProxyArray: | ||
| """Pose of target frame(s) relative to source frame [m, unitless]. | ||
|
|
||
| Shape is (num_instances, num_target_frames), dtype = wp.transformf. In torch this resolves to | ||
| (num_instances, num_target_frames, 7). The pose is provided in (x, y, z, qx, qy, qz, qw) format. | ||
| """ | ||
| wp.launch( | ||
| concat_pos_and_quat_to_pose_kernel, | ||
| dim=(self._num_envs, self._num_target_frames), | ||
| inputs=[self._target_pos_source, self._target_quat_source], | ||
| outputs=[self._target_pose_source], | ||
| device=self._device, | ||
| ) | ||
| if self._target_pose_source_ta is None: | ||
| self._target_pose_source_ta = ProxyArray(self._target_pose_source) | ||
| return self._target_pose_source_ta | ||
|
|
||
| @property | ||
| def target_pos_source(self) -> ProxyArray: | ||
| """Position of target frame(s) relative to source frame [m]. | ||
|
|
||
| Shape is (num_instances, num_target_frames), dtype = wp.vec3f. In torch this resolves to | ||
| (num_instances, num_target_frames, 3). | ||
| """ | ||
| if self._target_pos_source_ta is None: | ||
| self._target_pos_source_ta = ProxyArray(self._target_pos_source) | ||
| return self._target_pos_source_ta | ||
|
|
||
| @property | ||
| def target_quat_source(self) -> ProxyArray: | ||
| """Orientation of target frame(s) relative to source frame [unitless]. | ||
|
|
||
| Shape is (num_instances, num_target_frames), dtype = wp.quatf. In torch this resolves to | ||
| (num_instances, num_target_frames, 4). The orientation is provided in (x, y, z, w) format. | ||
| """ | ||
| if self._target_quat_source_ta is None: | ||
| self._target_quat_source_ta = ProxyArray(self._target_quat_source) | ||
| return self._target_quat_source_ta | ||
|
|
||
| @property | ||
| def target_pose_w(self) -> ProxyArray: | ||
| """Pose of target frame(s) after offset in world frame [m, unitless]. | ||
|
|
||
| Shape is (num_instances, num_target_frames), dtype = wp.transformf. In torch this resolves to | ||
| (num_instances, num_target_frames, 7). The pose is provided in (x, y, z, qx, qy, qz, qw) format. | ||
| """ | ||
| wp.launch( | ||
| concat_pos_and_quat_to_pose_kernel, | ||
| dim=(self._num_envs, self._num_target_frames), | ||
| inputs=[self._target_pos_w, self._target_quat_w], | ||
| outputs=[self._target_pose_w], | ||
| device=self._device, | ||
| ) | ||
| if self._target_pose_w_ta is None: | ||
| self._target_pose_w_ta = ProxyArray(self._target_pose_w) | ||
| return self._target_pose_w_ta | ||
|
|
||
| @property | ||
| def target_pos_w(self) -> ProxyArray: | ||
| """Position of target frame(s) after offset in world frame [m]. | ||
|
|
||
| Shape is (num_instances, num_target_frames), dtype = wp.vec3f. In torch this resolves to | ||
| (num_instances, num_target_frames, 3). | ||
| """ | ||
| if self._target_pos_w_ta is None: | ||
| self._target_pos_w_ta = ProxyArray(self._target_pos_w) | ||
| return self._target_pos_w_ta | ||
|
|
||
| @property | ||
| def target_quat_w(self) -> ProxyArray: | ||
| """Orientation of target frame(s) after offset in world frame [unitless]. | ||
|
|
||
| Shape is (num_instances, num_target_frames), dtype = wp.quatf. In torch this resolves to | ||
| (num_instances, num_target_frames, 4). The orientation is provided in (x, y, z, w) format. | ||
| """ | ||
| if self._target_quat_w_ta is None: | ||
| self._target_quat_w_ta = ProxyArray(self._target_quat_w) | ||
| return self._target_quat_w_ta | ||
|
|
||
| @property | ||
| def source_pose_w(self) -> ProxyArray: | ||
| """Pose of source frame after offset in world frame [m, unitless]. | ||
|
|
||
| Shape is (num_instances,), dtype = wp.transformf. In torch this resolves to (num_instances, 7). | ||
| The pose is provided in (x, y, z, qx, qy, qz, qw) format. | ||
| """ | ||
| wp.launch( | ||
| concat_pos_and_quat_to_pose_1d_kernel, | ||
| dim=self._num_envs, | ||
| inputs=[self._source_pos_w, self._source_quat_w], | ||
| outputs=[self._source_pose_w], | ||
| device=self._device, | ||
| ) | ||
| if self._source_pose_w_ta is None: | ||
| self._source_pose_w_ta = ProxyArray(self._source_pose_w) | ||
| return self._source_pose_w_ta | ||
|
|
||
| @property | ||
| def source_pos_w(self) -> ProxyArray: | ||
| """Position of source frame after offset in world frame [m]. | ||
|
|
||
| Shape is (num_instances,), dtype = wp.vec3f. In torch this resolves to (num_instances, 3). | ||
| """ | ||
| if self._source_pos_w_ta is None: | ||
| self._source_pos_w_ta = ProxyArray(self._source_pos_w) | ||
| return self._source_pos_w_ta | ||
|
|
||
| @property | ||
| def source_quat_w(self) -> ProxyArray: | ||
| """Orientation of source frame after offset in world frame [unitless]. | ||
|
|
||
| Shape is (num_instances,), dtype = wp.quatf. In torch this resolves to (num_instances, 4). | ||
| The orientation is provided in (x, y, z, w) format. | ||
| """ | ||
| if self._source_quat_w_ta is None: | ||
| self._source_quat_w_ta = ProxyArray(self._source_quat_w) | ||
| return self._source_quat_w_ta | ||
|
|
||
| def create_buffers( | ||
| self, | ||
| num_envs: int, | ||
| num_target_frames: int, | ||
| target_frame_names: list[str], | ||
| device: str, | ||
| ) -> None: | ||
| """Create internal buffers for sensor data. | ||
|
|
||
| Args: | ||
| num_envs: Number of environments. | ||
| num_target_frames: Number of target frames. | ||
| target_frame_names: Names of target frames. | ||
| device: Device for tensor storage. | ||
| """ | ||
| self._num_envs = num_envs | ||
| self._device = device | ||
| self._num_target_frames = num_target_frames | ||
| self._target_frame_names = target_frame_names | ||
| self._source_pose_w = wp.zeros(num_envs, dtype=wp.transformf, device=device) | ||
| self._source_pos_w = wp.zeros(num_envs, dtype=wp.vec3f, device=device) | ||
| self._source_quat_w = wp.zeros(num_envs, dtype=wp.quatf, device=device) | ||
| self._target_pose_w = wp.zeros((num_envs, num_target_frames), dtype=wp.transformf, device=device) | ||
| self._target_pos_w = wp.zeros((num_envs, num_target_frames), dtype=wp.vec3f, device=device) | ||
| self._target_quat_w = wp.zeros((num_envs, num_target_frames), dtype=wp.quatf, device=device) | ||
| self._target_pose_source = wp.zeros((num_envs, num_target_frames), dtype=wp.transformf, device=device) | ||
| self._target_pos_source = wp.zeros((num_envs, num_target_frames), dtype=wp.vec3f, device=device) | ||
| self._target_quat_source = wp.zeros((num_envs, num_target_frames), dtype=wp.quatf, device=device) | ||
|
|
||
| # Initialize quaternions to identity (w=1). wp.zeros gives (0,0,0,0) not (0,0,0,1). | ||
|
|
||
| wp.to_torch(self._source_quat_w)[:, 3] = 1.0 | ||
| wp.to_torch(self._target_quat_w)[:, :, 3] = 1.0 | ||
| wp.to_torch(self._target_quat_source)[:, :, 3] = 1.0 | ||
|
|
||
| # -- Pinned ProxyArray cache (one per read property, lazily created on first access) | ||
| self._target_pose_source_ta: ProxyArray | None = None | ||
| self._target_pos_source_ta: ProxyArray | None = None | ||
| self._target_quat_source_ta: ProxyArray | None = None | ||
| self._target_pose_w_ta: ProxyArray | None = None | ||
| self._target_pos_w_ta: ProxyArray | None = None | ||
| self._target_quat_w_ta: ProxyArray | None = None | ||
| self._source_pose_w_ta: ProxyArray | None = None | ||
| self._source_pos_w_ta: ProxyArray | None = None | ||
| self._source_quat_w_ta: ProxyArray | None = None |
117 changes: 117 additions & 0 deletions
117
source/isaaclab_ovphysx/isaaclab_ovphysx/sensors/frame_transformer/kernels.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||
| # All rights reserved. | ||
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| """Warp kernels for the OVPhysX frame transformer sensor.""" | ||
|
|
||
| import warp as wp | ||
|
|
||
| # ---- Frame transformer update kernel ---- | ||
|
|
||
|
|
||
| @wp.kernel | ||
| def frame_transformer_update_kernel( | ||
| env_mask: wp.array(dtype=wp.bool), | ||
| raw_transforms: wp.array(dtype=wp.transformf), | ||
| source_raw_indices: wp.array(dtype=wp.int32), | ||
| target_raw_indices: wp.array2d(dtype=wp.int32), | ||
| source_offset_pos: wp.array(dtype=wp.vec3f), | ||
| source_offset_quat: wp.array(dtype=wp.quatf), | ||
| target_offset_pos: wp.array(dtype=wp.vec3f), | ||
| target_offset_quat: wp.array(dtype=wp.quatf), | ||
| source_pos_w: wp.array(dtype=wp.vec3f), | ||
| source_quat_w: wp.array(dtype=wp.quatf), | ||
| target_pos_w: wp.array2d(dtype=wp.vec3f), | ||
| target_quat_w: wp.array2d(dtype=wp.quatf), | ||
| target_pos_source: wp.array2d(dtype=wp.vec3f), | ||
| target_quat_source: wp.array2d(dtype=wp.quatf), | ||
| ): | ||
| """Update frame transformer sensor data from raw PhysX transforms. | ||
|
|
||
| This kernel processes raw transforms from PhysX and computes: | ||
| 1. Source frame pose in world frame (with optional offset) | ||
| 2. Target frame poses in world frame (with optional offsets) | ||
| 3. Target frame poses relative to source frame | ||
|
|
||
| Args: | ||
| raw_transforms: Raw transforms from PhysX view. Shape is (N*M,) where N is num_envs and M is num_bodies. | ||
| source_raw_indices: Indices into raw_transforms for source frame per environment. Shape is (N,). | ||
| target_raw_indices: Indices into raw_transforms for target frames per (env, frame). Shape is (N, M) where M is | ||
| num_target_frames. | ||
| source_offset_pos: Optional position offset for source frame. Shape is (N, 3). | ||
| source_offset_quat: Optional quaternion offset for source frame. Shape is (N, 4). | ||
| target_offset_pos: Optional position offsets for target frames. Shape is (M, 3). | ||
| target_offset_quat: Optional quaternion offsets for target frames. Shape is (M, 4). | ||
| source_pos_w: Output source position in world frame. Shape is (N, 3). | ||
| source_quat_w: Output source quaternion in world frame. Shape is (N, 4). | ||
| target_pos_w: Output target positions in world frame. Shape is (N, M, 3). | ||
| target_quat_w: Output target quaternions in world frame. Shape is (N, M, 4). | ||
| target_pos_source: Output target positions relative to source frame. Shape is (N, M, 3). | ||
| target_quat_source: Output target quaternions relative to source frame. Shape is (N, M, 4). | ||
| """ | ||
| env_id, frame_id = wp.tid() | ||
|
|
||
| if not env_mask[env_id]: | ||
| return | ||
|
|
||
| # Get source frame transform | ||
| source_idx = source_raw_indices[env_id] | ||
| source_tf = raw_transforms[source_idx] | ||
|
|
||
| # Apply source frame offset | ||
| source_offset_tf = wp.transform(source_offset_pos[env_id], source_offset_quat[env_id]) | ||
| source_tf_offset = wp.transform_multiply(source_tf, source_offset_tf) | ||
| source_pos_w[env_id] = wp.transform_get_translation(source_tf_offset) | ||
| source_quat_w[env_id] = wp.transform_get_rotation(source_tf_offset) | ||
|
|
||
| # Get target frame transform | ||
| target_idx = target_raw_indices[env_id, frame_id] | ||
| target_tf = raw_transforms[target_idx] | ||
|
|
||
| # Apply target offset if needed | ||
| target_offset_tf = wp.transform(target_offset_pos[frame_id], target_offset_quat[frame_id]) | ||
| target_tf_offset = wp.transform_multiply(target_tf, target_offset_tf) | ||
| target_pos_w[env_id, frame_id] = wp.transform_get_translation(target_tf_offset) | ||
| target_quat_w[env_id, frame_id] = wp.transform_get_rotation(target_tf_offset) | ||
|
|
||
| # Compute target frame relative to source frame | ||
| source_tf_inv = wp.transform_inverse(source_tf_offset) | ||
| target_relative_tf = wp.transform_multiply(source_tf_inv, target_tf_offset) | ||
| target_pos_source[env_id, frame_id] = wp.transform_get_translation(target_relative_tf) | ||
| target_quat_source[env_id, frame_id] = wp.transform_get_rotation(target_relative_tf) | ||
|
|
||
|
|
||
| # ---- Gather body pose kernel ---- | ||
|
|
||
|
|
||
| @wp.kernel | ||
| def gather_body_pose_kernel( | ||
| env_mask: wp.array(dtype=wp.bool), | ||
| pose_buffer: wp.array(dtype=wp.transformf), | ||
| dst_flat_indices: wp.array(dtype=wp.int32), | ||
| raw_transforms: wp.array(dtype=wp.transformf), | ||
| ): | ||
| """Copy a single body's per-env pose into the flat raw transforms buffer. | ||
|
|
||
| For each env in the launch, copies ``pose_buffer[env]`` into | ||
| ``raw_transforms[dst_flat_indices[env]]``. Skips envs whose ``env_mask`` is False. | ||
|
|
||
| The pose buffer is a view (``wp.array.view(wp.transformf)``) over a | ||
| ``(num_envs, 7)`` ``float32`` array populated by | ||
| ``binding.read(...)`` for a single ``RIGID_BODY_POSE`` tensor binding, | ||
| so it has shape ``(num_envs,)``. One launch per tracked body fills the | ||
| body's slot column in the flat ``raw_transforms`` buffer. | ||
|
|
||
| Args: | ||
| env_mask: Active environment mask, shape ``(num_envs,)``. | ||
| pose_buffer: Per-env world pose [m, dimensionless], shape ``(num_envs,)``, | ||
| dtype ``wp.transformf`` in ``(px, py, pz, qx, qy, qz, qw)`` format. | ||
| dst_flat_indices: Destination slot in ``raw_transforms`` per env, shape ``(num_envs,)``. | ||
| raw_transforms: Destination flat pose buffer [m, dimensionless], shape | ||
| ``(num_envs * num_unique_bodies,)``, dtype ``wp.transformf``. | ||
| """ | ||
| env_id = wp.tid() | ||
| if not env_mask[env_id]: | ||
| return | ||
| raw_transforms[dst_flat_indices[env_id]] = pose_buffer[env_id] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dataannotation does not includeOvPhysxFrameTransformerData__new__was correctly extended with the OVPhysX return-type union, but thedataclass-level annotation still only listsBaseFrameTransformerData | PhysXFrameTransformerData. When users call.dataon an OVPhysX instance, type-checkers will not resolve toOvPhysxFrameTransformerData, losing access to the OVPhysX-specific API surface.