You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

167 lines
5.4 KiB

extends Node3D
## Main entry point for G1 Teleop Quest 3 app.
## Two-phase startup:
## CONFIG phase: Dark VR environment with start screen UI panel
## AR phase: Passthrough mixed reality with body tracking
enum Phase { CONFIG, AR }
@onready var body_tracker: Node = $BodyTracker
@onready var teleop_client: Node = $TeleopClient
@onready var xr_origin: XROrigin3D = $XROrigin3D
@onready var xr_camera: XRCamera3D = $XROrigin3D/XRCamera3D
@onready var webcam_quad: MeshInstance3D = $XROrigin3D/XRCamera3D/WebcamQuad
@onready var start_screen: Node3D = $XROrigin3D/StartScreen
@onready var left_controller: XRController3D = $XROrigin3D/LeftController
@onready var right_controller: XRController3D = $XROrigin3D/RightController
@onready var vr_pointer: Node3D = $VRUIPointer
var xr_interface: XRInterface
var xr_is_focused: bool = false
var current_phase: Phase = Phase.CONFIG
var _panel_positioned: bool = false
func _ready() -> void:
# Hide webcam quad and start screen until positioned
webcam_quad.visible = false
start_screen.visible = false
# Initialize OpenXR interface
xr_interface = XRServer.find_interface("OpenXR")
if xr_interface and xr_interface.is_initialized():
print("[Main] OpenXR already initialized")
_on_openxr_ready()
elif xr_interface:
xr_interface.connect("session_begun", _on_openxr_session_begun)
xr_interface.connect("session_focussed", _on_openxr_focused)
xr_interface.connect("session_stopping", _on_openxr_stopping)
if not xr_interface.initialize():
printerr("[Main] Failed to initialize OpenXR")
get_tree().quit()
return
print("[Main] OpenXR initialized, waiting for session")
else:
printerr("[Main] OpenXR interface not found. Is the plugin enabled?")
get_tree().quit()
return
# Enable XR on the viewport
get_viewport().use_xr = true
# Connect start screen signals
start_screen.connect_requested.connect(_on_connect_requested)
start_screen.launch_ar_requested.connect(_on_launch_ar_requested)
# Connect teleop client connection state to start screen
teleop_client.connection_state_changed.connect(_on_connection_state_changed)
# Wire webcam frames (can happen anytime we're connected)
teleop_client.webcam_frame_received.connect(webcam_quad._on_webcam_frame)
# Setup VR pointer with references to controllers and XR origin
vr_pointer.setup(xr_origin, xr_camera, left_controller, right_controller)
# Setup body tracker visualization
body_tracker.setup(xr_origin)
print("[Main] Starting in CONFIG phase")
func _process(_delta: float) -> void:
if not _panel_positioned and current_phase == Phase.CONFIG:
# Wait until we have valid head tracking data to position the panel
var hmd := XRServer.get_hmd_transform()
if hmd.origin != Vector3.ZERO and hmd.origin.y > 0.3:
_position_panel_in_front_of_user(hmd)
_panel_positioned = true
func _position_panel_in_front_of_user(hmd: Transform3D) -> void:
# Place the panel 1.2m in front of the user at their eye height
var forward := -hmd.basis.z
forward.y = 0 # Project onto horizontal plane
if forward.length() < 0.01:
forward = Vector3(0, 0, -1)
forward = forward.normalized()
var panel_pos := hmd.origin + forward * 1.2
panel_pos.y = hmd.origin.y # Same height as eyes
start_screen.global_position = panel_pos
# Face the panel toward the user
# look_at() points -Z at target, but QuadMesh front face is +Z, so rotate 180
start_screen.look_at(hmd.origin, Vector3.UP)
start_screen.rotate_y(PI)
start_screen.visible = true
print("[Main] Panel positioned at %s (user at %s)" % [panel_pos, hmd.origin])
func _on_openxr_session_begun() -> void:
print("[Main] OpenXR session begun")
_on_openxr_ready()
func _on_openxr_ready() -> void:
# In CONFIG phase, we stay in opaque/dark VR mode
# Passthrough is only enabled when user clicks "Launch AR"
if current_phase == Phase.AR:
_enable_passthrough()
func _on_openxr_focused() -> void:
xr_is_focused = true
print("[Main] OpenXR session focused")
func _on_openxr_stopping() -> void:
xr_is_focused = false
print("[Main] OpenXR session stopping")
func _on_connect_requested(host: String, port: int) -> void:
print("[Main] Connect requested: %s:%d" % [host, port])
# If already connected, disconnect first
if teleop_client.is_connected:
teleop_client.disconnect_from_server()
return
teleop_client.server_host = host
teleop_client.server_port = port
teleop_client.connect_to_server()
func _on_connection_state_changed(connected: bool) -> void:
start_screen.set_connected(connected)
func _on_launch_ar_requested() -> void:
if current_phase == Phase.AR:
return
print("[Main] Launching AR mode")
current_phase = Phase.AR
# Enable passthrough
_enable_passthrough()
# Wire body tracker to teleop client
body_tracker.tracking_data_ready.connect(teleop_client._on_tracking_data)
# Show webcam quad, hide start screen
webcam_quad.visible = true
start_screen.hide_screen()
func _enable_passthrough() -> void:
var openxr = xr_interface as OpenXRInterface
if openxr:
var modes = openxr.get_supported_environment_blend_modes()
print("[Main] Supported blend modes: ", modes)
if XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND in modes:
openxr.set_environment_blend_mode(XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND)
print("[Main] Passthrough enabled (alpha blend)")
get_viewport().transparent_bg = true
RenderingServer.set_default_clear_color(Color(0, 0, 0, 0))
else:
print("[Main] Alpha blend not supported, using opaque mode")