Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions examples/cloudxr_mujoco_teleop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ works with any CloudXR / OpenXR-compatible HMD.

| Example | What it shows |
|---------|---------------|
| `visualize_poses_mujoco_example.py` | HMD + left/right controller as textured mocap bodies. |
| `visualize_hands_mujoco_example.py` | Both hands as 26-joint skeletons (spheres + capsule bones), with the HMD as optional context. |
| `visualize_poses_mujoco_example` | HMD + left/right controller as textured mocap bodies. |
| `visualize_hands_mujoco_example` | Both hands as 26-joint skeletons (spheres + capsule bones), with the HMD as optional context. |

## Prerequisites

Expand All @@ -24,9 +24,11 @@ Assumes IsaacTeleop is already built and installed in this tree.
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
On first launch, `uv run` fetches this example's Python dependencies
(`mujoco`, `numpy`, `scipy` — see `pyproject.toml`) into an isolated
environment.
Install the example once; this pulls its Python dependencies
(`mujoco`, `numpy`, `scipy` — see `pyproject.toml`):
```bash
uv pip install -e ./examples/cloudxr_mujoco_teleop
```

2. **CloudXR environment loaded**, so OpenXR resolves to the CloudXR
runtime. Source the setup script that ships with your CloudXR install:
Expand All @@ -41,12 +43,12 @@ limit, no recording.

## Examples

### `visualize_poses_mujoco_example.py`
### `visualize_poses_mujoco_example`

HMD + left + right controller, each rendered as a textured mesh mocap body.

```bash
uv run visualize_poses_mujoco_example.py
python -m isaacteleop_examples.cloudxr_mujoco_teleop.visualize_poses_mujoco_example
```

| Flag | Default | Description |
Expand All @@ -55,15 +57,15 @@ uv run visualize_poses_mujoco_example.py
| `--pose {grip,aim}` | `grip` | Which controller pose drives the mesh — natural "hold" pose or the aim ray. |
| `--debug` | off | Print `mocap_pos` for all three devices once per second. |

### `visualize_hands_mujoco_example.py`
### `visualize_hands_mujoco_example`

Both hands as 26-joint skeletons drawn directly into `viewer.user_scn` each
frame (left = cyan, right = warm orange) — avoids paying for 52 mocap bodies
in the model. The HMD is shown as context using the same mesh + texture as
the poses example.

```bash
uv run visualize_hands_mujoco_example.py
python -m isaacteleop_examples.cloudxr_mujoco_teleop.visualize_hands_mujoco_example
```

| Flag | Default | Description |
Expand Down Expand Up @@ -92,16 +94,17 @@ HMD will be visible.
```
cloudxr_mujoco_teleop/
├── README.md
├── pyproject.toml # uv dependency declaration
├── visualize_poses_mujoco_example.py
├── visualize_hands_mujoco_example.py
└── vive_assets/
├── generic_hmd.obj # HMD mesh (~500 KB)
├── generic_hmd_color.png # HMD texture
├── vive_focus3_controller_left.obj
├── vive_focus3_controller_left_color.png
├── vive_focus3_controller_right.obj
└── vive_focus3_controller_right_color.png
├── pyproject.toml # the distribution
└── python/isaacteleop_examples/cloudxr_mujoco_teleop/
├── visualize_poses_mujoco_example.py
├── visualize_hands_mujoco_example.py
└── vive_assets/
├── generic_hmd.obj # HMD mesh (~500 KB)
├── generic_hmd_color.png # HMD texture
├── vive_focus3_controller_left.obj
├── vive_focus3_controller_left_color.png
├── vive_focus3_controller_right.obj
└── vive_focus3_controller_right_color.png
```

The bundled meshes are a generic HMD and the Vive Focus 3 controllers.
Expand Down
19 changes: 18 additions & 1 deletion examples/cloudxr_mujoco_teleop/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 HTC CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "cloudxr-mujoco-teleop-example"
# The dist name mirrors the import path, so an installed example claims no bare
# top-level name in site-packages.
name = "isaacteleop-examples-cloudxr-mujoco-teleop"
version = "0.1.0"
description = "Visualize OpenXR / CloudXR headset + controller poses in MuJoCo."
requires-python = ">=3.11,<3.14"
Expand All @@ -12,3 +18,14 @@ dependencies = [
"numpy>=1.22.2",
"scipy>=1.10",
]

# `isaacteleop_examples` is a PEP 420 namespace shared by every example dist and
# must stay without an __init__.py. `only-include` + `sources`, not `packages`:
# the latter keeps only the last path component and would root the wheel at a
# bare top-level `cloudxr_mujoco_teleop/`.
#
# vive_assets/ lives inside the package because both scripts resolve it from
# __file__, so the meshes must travel with the modules.
[tool.hatch.build.targets.wheel]
only-include = ["python/isaacteleop_examples/cloudxr_mujoco_teleop"]
sources = ["python"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 HTC CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
Loading