From ad4bb4595111beed3acf73ecad2b3f128857402b Mon Sep 17 00:00:00 2001 From: "Marek S. Lukasiewicz" Date: Mon, 3 Mar 2025 10:01:54 +0100 Subject: [PATCH] Add fallback camera with mouse controls --- project/aircraft/aircraft.gd | 7 ++++ project/aircraft/aircraft.tscn | 10 ++++- project/aircraft/fallback_input.gd | 58 ++++++++++++++++++++++++++ project/aircraft/fallback_input.gd.uid | 1 + project/main.tscn | 2 + project/start_vr.gd | 2 + 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 project/aircraft/fallback_input.gd create mode 100644 project/aircraft/fallback_input.gd.uid diff --git a/project/aircraft/aircraft.gd b/project/aircraft/aircraft.gd index f2e0921..fec7cca 100644 --- a/project/aircraft/aircraft.gd +++ b/project/aircraft/aircraft.gd @@ -1,5 +1,7 @@ extends Node3D +signal use_fallback(active: bool) + ## Rotation of cyclic stick model for full control deflection, in degrees @export var range_cyclic: float = 30 ## Main rotor speed, in revolutions per minute @@ -16,6 +18,7 @@ var _rotor_azimuth: float = 0 @onready var tracker_mount_platform: Node3D = $AttitudeRoot/PilotEyes/PilotFloor/TrackerMountPlatform @onready var xr_origin: XROrigin3D = $AttitudeRoot/PilotEyes/PilotFloor/XROrigin3D @onready var tracker: XRController3D = $AttitudeRoot/PilotEyes/PilotFloor/XROrigin3D/ViveTracker +@onready var xr_camera: XRCamera3D = $AttitudeRoot/PilotEyes/PilotFloor/XROrigin3D/XRCamera3D @onready var bone_cg: int = skeleton.find_bone("BodyCG") @onready var bone_cyclic: int = skeleton.find_bone("Cyclic") @@ -53,3 +56,7 @@ func _process(delta: float) -> void: xr_origin.transform = tracker_mount.transform * tracker.transform.affine_inverse() else: xr_origin.transform = Transform3D.IDENTITY + +func _on_vr_setup_failed() -> void: + xr_camera.current = false + use_fallback.emit(true) diff --git a/project/aircraft/aircraft.tscn b/project/aircraft/aircraft.tscn index 455427b..0a9a2fc 100644 --- a/project/aircraft/aircraft.tscn +++ b/project/aircraft/aircraft.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=6 format=3 uid="uid://bj1s0g7ixjw71"] +[gd_scene load_steps=7 format=3 uid="uid://bj1s0g7ixjw71"] [ext_resource type="Script" uid="uid://cx30pr7kn4c74" path="res://aircraft/aircraft.gd" id="1_l4uib"] [ext_resource type="PackedScene" uid="uid://cux4tju0ovvly" path="res://assets/mi2/Mi-2.glb" id="1_mrxe8"] [ext_resource type="PackedScene" uid="uid://fmygcraoturj" path="res://reference_axes.tscn" id="3_2bi7g"] [ext_resource type="PackedScene" uid="uid://cis4s43ubuynp" path="res://instruments.tscn" id="3_5w717"] +[ext_resource type="Script" uid="uid://bt32fse84itrp" path="res://aircraft/fallback_input.gd" id="3_gf6ud"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mrxe8"] @@ -22,6 +23,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.4, 0) [node name="PilotEyes" type="Node3D" parent="AttitudeRoot"] editor_description = "Target position for Local XR tracking" transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0.214, -0.7, 1.594) +script = ExtResource("3_gf6ud") [node name="PilotFloor" type="Node3D" parent="AttitudeRoot/PilotEyes"] editor_description = "Required for floor tracking @@ -44,6 +46,7 @@ visible = false [node name="XROrigin3D" type="XROrigin3D" parent="AttitudeRoot/PilotEyes/PilotFloor"] [node name="XRCamera3D" type="XRCamera3D" parent="AttitudeRoot/PilotEyes/PilotFloor/XROrigin3D"] +current = true [node name="ViveTracker" type="XRController3D" parent="AttitudeRoot/PilotEyes/PilotFloor/XROrigin3D"] tracker = &"/user/vive_tracker_htcx/role/camera" @@ -52,9 +55,14 @@ tracker = &"/user/vive_tracker_htcx/role/camera" transform = Transform3D(0.25, 0, 0, 0, 0.25, 0, 0, 0, 0.25, 0, 0, 0) visible = false +[node name="FallbackCamera" type="Camera3D" parent="AttitudeRoot/PilotEyes"] +transform = Transform3D(1, 0, 0, 0, 0.980955, 0.194234, 0, -0.194234, 0.980955, 0, 0, 0) + [node name="Instruments" parent="AttitudeRoot" instance=ExtResource("3_5w717")] transform = Transform3D(-0.5, -1.27582e-08, 7.43353e-08, 4.33325e-10, 0.4923, 0.0874084, -7.5421e-08, 0.0874084, -0.4923, 0.00724897, -1.1581, 2.40391) [node name="MarshConnector" type="MarshConnector" parent="."] +[connection signal="use_fallback" from="." to="AttitudeRoot/PilotEyes" method="_on_aircraft_use_fallback"] + [editable path="Mi-2"] diff --git a/project/aircraft/fallback_input.gd b/project/aircraft/fallback_input.gd new file mode 100644 index 0000000..a48e11d --- /dev/null +++ b/project/aircraft/fallback_input.gd @@ -0,0 +1,58 @@ +# Based on Godot Engine Third Person Shooter Demo, used under MIT License +# Copyright (c) 2018-2021 Juan Linietsky, Godot Engine contributors +# https://github.com/godotengine/tps-demo/blob/master/player/player_input.gd + +extends Node3D + +const CAMERA_MOUSE_ROTATION_SPEED := 0.001 +const CAMERA_X_ROT_MIN := deg_to_rad(-85) +const CAMERA_X_ROT_MAX := deg_to_rad(70) + +var active: bool = false +var initial_rotation: Vector3 + +@onready var camera_base = $"." +@onready var camera = $FallbackCamera + +func _ready() -> void: + set_process(false) + set_process_input(false) + initial_rotation = camera.rotation + +func _on_aircraft_use_fallback(active_in: bool) -> void: + if active_in: + set_process(true) + set_process_input(true) + camera.make_current() + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + else: + set_process(false) + set_process_input(false) + camera.current = false + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) # default value + + # Reset the rotation + camera_base.rotation = Vector3.ZERO + camera.rotation = initial_rotation + + active = active_in + + +func _input(event): + # Make mouse aiming speed resolution-independent + # (required when using the `canvas_items` stretch mode). + var scale_factor: float = min( + (float(get_viewport().size.x) / get_viewport().get_visible_rect().size.x), + (float(get_viewport().size.y) / get_viewport().get_visible_rect().size.y) + ) + + if event is InputEventMouseMotion: + var camera_speed_this_frame = CAMERA_MOUSE_ROTATION_SPEED + rotate_camera(event.relative * camera_speed_this_frame * scale_factor) + + +func rotate_camera(move): + camera_base.rotate_y(-move.x) + # After relative transforms, camera needs to be renormalized. + camera_base.orthonormalize() + camera.rotation.x = clamp(camera.rotation.x - move.y, CAMERA_X_ROT_MIN, CAMERA_X_ROT_MAX) diff --git a/project/aircraft/fallback_input.gd.uid b/project/aircraft/fallback_input.gd.uid new file mode 100644 index 0000000..5db95c6 --- /dev/null +++ b/project/aircraft/fallback_input.gd.uid @@ -0,0 +1 @@ +uid://bt32fse84itrp diff --git a/project/main.tscn b/project/main.tscn index f185af3..1f92149 100644 --- a/project/main.tscn +++ b/project/main.tscn @@ -28,3 +28,5 @@ shadow_enabled = true [node name="StartVR" type="Node3D" parent="."] script = ExtResource("1_ig7tw") + +[connection signal="setup_failed" from="StartVR" to="Aircraft" method="_on_vr_setup_failed"] diff --git a/project/start_vr.gd b/project/start_vr.gd index f1d77fe..597c4b5 100644 --- a/project/start_vr.gd +++ b/project/start_vr.gd @@ -3,6 +3,7 @@ extends Node3D signal focus_lost signal focus_gained signal pose_recentered +signal setup_failed @export var maximum_refresh_rate : int = 90 @@ -40,6 +41,7 @@ func _ready() -> void: print("OpenXR not instantiated!") # Allow the game to run without VR #get_tree().quit() + setup_failed.emit() func _on_tracker_added(tracker_name: StringName, type: int) -> void: