Skip to content

Commit 4234c28

Browse files
committed
tracker: add a button for setting aethervr as the openxr runtime
1 parent c461091 commit 4234c28

4 files changed

Lines changed: 113 additions & 29 deletions

File tree

tracker/aethervr/camera_capture.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(self, on_frame):
1616

1717
def capture_images(self):
1818
capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
19-
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 960)
19+
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 860)
2020
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
2121

2222
if not capture.isOpened():

tracker/aethervr/gui.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
QComboBox,
1414
QFormLayout,
1515
QGroupBox,
16+
QMessageBox,
1617
)
1718

18-
from PySide6.QtCore import QEvent, Qt, QSize, QEvent, QRect
19+
from PySide6.QtCore import QEvent, Qt, QSize, QEvent, QRect, QTimer
1920
from PySide6.QtGui import QPaintEvent, QColor, QPainter, QBrush, QImage
2021
from mediapipe import solutions
2122
import numpy as np
@@ -25,6 +26,7 @@
2526
from aethervr.tracking_state import TrackingState
2627
from aethervr.input_state import ControllerButton
2728
from aethervr.runtime_connection import RuntimeConnection
29+
from aethervr.system_openxr_config import SystemOpenXRConfig
2830

2931

3032
STYLESHEET = """
@@ -45,15 +47,16 @@
4547

4648
class Window(QMainWindow):
4749

48-
def __init__(self, config: Config, connection: RuntimeConnection):
50+
def __init__(self, config: Config, connection: RuntimeConnection, system_openxr_config: SystemOpenXRConfig):
4951
super().__init__()
5052
self.config = config
5153
self.connection = connection
54+
self.system_openxr_config = system_openxr_config
5255

5356
self.camera_view = None
5457

5558
self.setWindowTitle("AetherVR Tracker")
56-
self.resize(QSize(1080, 720))
59+
self.resize(QSize(1280, 720))
5760

5861
layout = QVBoxLayout()
5962
layout.setContentsMargins(0, 0, 0, 0)
@@ -77,16 +80,12 @@ def create_horizontal_widget(self):
7780
layout = QHBoxLayout()
7881
layout.setContentsMargins(0, 0, 0, 0)
7982
layout.setSpacing(0)
80-
layout.addWidget(ConfigPanel(self.config))
83+
layout.addWidget(ConfigPanel(self.config, self.system_openxr_config))
8184
layout.addWidget(separator)
8285
layout.addWidget(self.camera_view)
8386
widget.setLayout(layout)
8487

85-
size_policy = QSizePolicy(
86-
QSizePolicy.Policy.Preferred,
87-
QSizePolicy.Policy.Preferred,
88-
)
89-
88+
size_policy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
9089
size_policy.setHorizontalStretch(1)
9190
size_policy.setVerticalStretch(1)
9291
widget.setSizePolicy(size_policy)
@@ -102,11 +101,12 @@ def update_camera_overlay(self, tracking_state: TrackingState):
102101

103102
class ConfigPanel(QWidget):
104103

105-
def __init__(self, config: Config):
104+
def __init__(self, config: Config, system_openxr_config: SystemOpenXRConfig):
106105
super().__init__()
107106
self.config = config
108107

109108
layout = QVBoxLayout()
109+
layout.addWidget(OpenXRConfigGroup(system_openxr_config))
110110
layout.addWidget(self.build_controller_group("Left Controller", config.left_controller_config))
111111
layout.addWidget(self.build_controller_group("Right Controller", config.right_controller_config))
112112
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
@@ -126,6 +126,47 @@ def build_controller_group(self, label: str, config: ControllerConfig):
126126
return group
127127

128128

129+
class OpenXRConfigGroup(QGroupBox):
130+
131+
def __init__(self, system_openxr_config: SystemOpenXRConfig):
132+
super().__init__("OpenXR")
133+
self.system_openxr_config = system_openxr_config
134+
135+
self.current_label = QLabel()
136+
137+
self.set_button = QPushButton("Set AetherVR as OpenXR Runtime")
138+
self.set_button.clicked.connect(self._activate_aethervr)
139+
140+
layout = QFormLayout()
141+
layout.addRow(QLabel("Current OpenXR Runtime:"), self.current_label)
142+
layout.addRow(self.set_button)
143+
self.setLayout(layout)
144+
145+
self.timer = QTimer(self)
146+
self.timer.timeout.connect(self._refresh)
147+
self.timer.start(2000)
148+
149+
self._refresh()
150+
151+
def _activate_aethervr(self):
152+
if self.system_openxr_config.activate_aethervr():
153+
self._refresh()
154+
else:
155+
QMessageBox.critical(
156+
self,
157+
"Error",
158+
"Failed to set AetherVR as the system OpenXR runtime. "
159+
+ "Please make sure you're running AetherVR as an administrator.",
160+
)
161+
162+
def _refresh(self):
163+
active_runtime = self.system_openxr_config.active_runtime_name()
164+
is_aethervr = self.system_openxr_config.is_aethervr(active_runtime)
165+
166+
self.current_label.setText("None" if active_runtime is None else active_runtime)
167+
self.set_button.setEnabled(not is_aethervr)
168+
169+
129170
class ButtonBindingDropdown(QComboBox):
130171

131172
def __init__(self, config: ControllerConfig, gesture: Gesture):
@@ -275,11 +316,11 @@ def set_state(self, connected: bool):
275316

276317
class GUI:
277318

278-
def __init__(self, config: Config, connection: RuntimeConnection):
319+
def __init__(self, config: Config, connection: RuntimeConnection, system_openxr_config: SystemOpenXRConfig):
279320
self.app = QApplication(sys.argv)
280321
self.app.setStyleSheet(STYLESHEET)
281322

282-
self.window = Window(config, connection)
323+
self.window = Window(config, connection, system_openxr_config)
283324
self.window.show()
284325

285326
def update_camera_frame(self, frame):
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from pathlib import Path
2+
import winreg
3+
import json
4+
import sys
5+
6+
7+
class SystemOpenXRConfig:
8+
_REG_KEY_NAME = "SOFTWARE\\Khronos\\OpenXR\\1"
9+
_REG_VALUE_NAME = "ActiveRuntime"
10+
_AETHERVR_MANIFEST_NAME = "openxr_runtime.json"
11+
12+
def active_runtime_name(self):
13+
try:
14+
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
15+
key = winreg.OpenKeyEx(reg, SystemOpenXRConfig._REG_KEY_NAME, 0, winreg.KEY_QUERY_VALUE)
16+
current_manifest_path, _ = winreg.QueryValueEx(key, SystemOpenXRConfig._REG_VALUE_NAME)
17+
winreg.CloseKey(key)
18+
19+
with open(current_manifest_path) as f:
20+
current_manifest = json.load(f)
21+
22+
return current_manifest["runtime"]["name"]
23+
except Exception:
24+
return None
25+
26+
def is_aethervr(self, runtime_name):
27+
return runtime_name == "AetherVR"
28+
29+
def activate_aethervr(self):
30+
path = self._path_to_aethervr_manifest()
31+
32+
try:
33+
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
34+
key = winreg.CreateKeyEx(reg, SystemOpenXRConfig._REG_KEY_NAME, 0, winreg.KEY_SET_VALUE)
35+
winreg.SetValueEx(key, SystemOpenXRConfig._REG_VALUE_NAME, 0, winreg.REG_SZ, str(path))
36+
winreg.CloseKey(key)
37+
winreg.CloseKey(reg)
38+
39+
return True
40+
except Exception:
41+
return False
42+
43+
def _path_to_aethervr_manifest(self):
44+
if getattr(sys, "frozen", False):
45+
package_root = Path(sys.executable).parent
46+
return package_root / SystemOpenXRConfig._AETHERVR_MANIFEST_NAME
47+
else:
48+
project_root = Path(__file__).parents[2]
49+
build_dir = project_root / "openxr_runtime" / "out" / "x86_64-windows-msvc-debug"
50+
return project_root / build_dir / SystemOpenXRConfig._AETHERVR_MANIFEST_NAME

tracker/aethervr_tracker.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from aethervr.input_state import InputState
88
from aethervr.gesture_detector import GestureDetector
99
from aethervr.config import Config, ControllerConfig
10-
import numpy as np
10+
from aethervr.system_openxr_config import SystemOpenXRConfig
1111
import time
1212

1313

@@ -25,23 +25,16 @@ def __init__(self):
2525
self.camera_capture = CameraCapture(self.on_frame)
2626
self.connection = RuntimeConnection(38057)
2727

28-
self.head_tracker = HeadTracker(
29-
self.on_head_tracking_results,
30-
)
31-
32-
self.hand_tracker = HandTracker(
33-
self.head_tracker,
34-
self.on_hand_tracking_results,
35-
)
36-
37-
self.gesture_detector = GestureDetector(
38-
self.config, self.tracking_state, self.input_state
39-
)
28+
self.head_tracker = HeadTracker(self.on_head_tracking_results)
29+
self.hand_tracker = HandTracker(self.head_tracker, self.on_hand_tracking_results)
30+
self.gesture_detector = GestureDetector(self.config, self.tracking_state, self.input_state)
4031

4132
self.last_head_tracking_result = time.time()
4233
self.last_hand_tracking_result = time.time()
4334

44-
self.gui = GUI(self.config, self.connection)
35+
self.system_openxr_config = SystemOpenXRConfig()
36+
37+
self.gui = GUI(self.config, self.connection, self.system_openxr_config)
4538
self.gui.run()
4639

4740
def on_frame(self, frame):
@@ -63,7 +56,7 @@ def on_head_tracking_results(self, state: HeadState):
6356
self.input_state.headset_state.position = state.position
6457
self.input_state.headset_state.pitch = state.pitch
6558
self.input_state.headset_state.yaw = state.yaw
66-
59+
6760
self.connection.update_headset_state(self.input_state.headset_state)
6861

6962
def on_hand_tracking_results(self, left_state: HandState, right_state: HandState):
@@ -72,7 +65,7 @@ def on_hand_tracking_results(self, left_state: HandState, right_state: HandState
7265

7366
left_controller_state = self.input_state.left_controller_state
7467
right_controller_state = self.input_state.right_controller_state
75-
68+
7669
if left_state.visible:
7770
left_controller_state.position = left_state.position
7871
left_controller_state.orientation = left_state.orientation

0 commit comments

Comments
 (0)