diff --git a/example/addons/tiltfive/T5Interface.gd b/example/addons/tiltfive/T5Interface.gd index cb1b539..f6ffb50 100644 --- a/example/addons/tiltfive/T5Interface.gd +++ b/example/addons/tiltfive/T5Interface.gd @@ -12,8 +12,33 @@ extends Node ## in editor. This will allow the Godot editor to request ## action information from the interface. +enum GameboardType { + LE = TiltFiveXRInterface.LE_GAMEBOARD, + XE = TiltFiveXRInterface.XE_GAMEBOARD, + XE_Raised = TiltFiveXRInterface.XE_RAISED_GAMEBOARD, + Unknown = TiltFiveXRInterface.NO_GAMEBOARD_SET +} + +# State of a set of glasses. +class GlassesState: + var available := false + var attempting_to_reserve := false + var reserved := false + var glasses_scene : Node + var gameboard_type := GameboardType.Unknown + func can_attempt_to_reserve(): + return available and (not attempting_to_reserve) and (not reserved) + + +# Dictionary maps glasses_id -> GlassesState +var glasses_dict: Dictionary + var tilt_five_xr_interface: TiltFiveXRInterface +var t5_manager : T5ManagerBase: + set(value): + t5_manager = value + func get_tilt_five_xr_interface() -> TiltFiveXRInterface: return tilt_five_xr_interface @@ -51,11 +76,94 @@ func _enter_tree(): tilt_five_xr_interface.application_version = ProjectSettings.get_setting_with_override("xr/tilt_five/application_version") XRServer.add_interface(tilt_five_xr_interface) + tilt_five_xr_interface.glasses_event.connect(_on_glasses_event) + func _exit_tree(): if tilt_five_xr_interface: + tilt_five_xr_interface.glasses_event.disconnect(_on_glasses_event) if tilt_five_xr_interface.is_initialized(): tilt_five_xr_interface.uninitialize() - + XRServer.remove_interface(tilt_five_xr_interface) tilt_five_xr_interface = null + +func _ready(): + if not (t5_manager or Engine.is_editor_hint()): + push_error("T5Manager is not set in T5Interface") + return + if !tilt_five_xr_interface.is_initialized(): + tilt_five_xr_interface.initialize() + +func _start_display(glasses_id : StringName, glasses_scene : Node): + var viewport := t5_manager.get_glasses_scene_viewport(glasses_scene) + var xr_origin := t5_manager.get_glasses_scene_origin(glasses_scene) + tilt_five_xr_interface.start_display(glasses_id, viewport, xr_origin) + var t5_camera := t5_manager.get_glasses_scene_camera(glasses_scene) + if t5_camera: + t5_camera.tracker = "/user/%s/head" % glasses_id + for idx in range(4): + var controller = t5_manager.get_glasses_scene_wand(glasses_scene, idx) + if not controller: break + controller.tracker = "/user/%s/wand_%d" % [glasses_id, idx + 1] + +func _process_glasses(): + for glasses_id in glasses_dict: + var glasses_state = glasses_dict.get(glasses_id) as GlassesState + if glasses_state.can_attempt_to_reserve() and t5_manager.should_use_glasses(glasses_id): + glasses_state.attempting_to_reserve = true + tilt_five_xr_interface.reserve_glasses(glasses_id, t5_manager.get_glasses_display_name(glasses_id)) + +func _on_glasses_event(glasses_id, event_num): + var glasses_state = glasses_dict.get(glasses_id) as GlassesState + if not glasses_state: + glasses_state = GlassesState.new() + glasses_dict[glasses_id] = glasses_state + match event_num: + TiltFiveXRInterface.E_AVAILABLE: + print_verbose(glasses_id, " E_AVAILABLE") + glasses_state.available = true + _process_glasses() + + TiltFiveXRInterface.E_UNAVAILABLE: + print_verbose(glasses_id, " E_UNAVAILABLE") + glasses_state.available = false + if glasses_state.attempting_to_reserve: + glasses_state.attempting_to_reserve = false + _process_glasses() + + TiltFiveXRInterface.E_RESERVED: + print_verbose(glasses_id, " E_RESERVED") + glasses_state.reserved = true + glasses_state.attempting_to_reserve = false + + var glasses_scene = t5_manager.create_glasses_scene(glasses_id) + + # instance our scene + if glasses_scene: + glasses_state.glasses_scene = glasses_scene + _start_display(glasses_id, glasses_scene) + + TiltFiveXRInterface.E_DROPPED: + print_verbose(glasses_id, " E_DROPPED") + glasses_state.reserved = false + + var glasses_scene = glasses_state.glasses_scene + if glasses_scene: + t5_manager.release_glasses_scene(glasses_scene) + glasses_state.glasses_scene = null + tilt_five_xr_interface.stop_display(glasses_id) + + TiltFiveXRInterface.E_TRACKING: + var gbt = tilt_five_xr_interface.get_gameboard_type(glasses_id) + if glasses_state.gameboard_type != gbt: + glasses_state.gameboard_type = gbt + if glasses_state.glasses_scene: + t5_manager.set_glasses_scene_gameboard_type(glasses_state.glasses_scene, glasses_state.gameboard_type) + print_verbose(glasses_id, " E_TRACKING, Gameboard size = ", tilt_five_xr_interface.get_gameboard_extents(gbt)) + + TiltFiveXRInterface.E_NOT_TRACKING: + print_verbose(glasses_id, " E_NOT_TRACKING") + + _: + print_verbose(glasses_id, " - unknown event: ", event_num) diff --git a/example/addons/tiltfive/T5Manager.gd b/example/addons/tiltfive/T5Manager.gd index d85b797..05d1711 100644 --- a/example/addons/tiltfive/T5Manager.gd +++ b/example/addons/tiltfive/T5Manager.gd @@ -1,4 +1,4 @@ -class_name T5Manager extends Node +class_name T5Manager extends T5ManagerBase ## The T5Manager node should be added to your ## main scene and will manage when glasses @@ -6,144 +6,45 @@ class_name T5Manager extends Node ## ## This should be persistent. -signal glasses_available(glasses_id : String) -signal glasses_reserved(glasses_id : String) -signal glasses_dropped(glasses_id : String) +signal glasses_scene_was_added(glasses : T5GlassesBase) +signal glasses_scene_will_be_removed(glasses : T5GlassesBase) const xr_origin_node := ^"Origin" const xr_camera_node := ^"Origin/Camera" -const wand_node_list := [^"Origin/Wand_1", ^"Origin/Wand_2", ^"Origin/Wand_3", ^"Origin/Wand_4"] +const wand_node_list := [^"Origin/Wand_1", ^"Origin/Wand_2"] -var tilt_five_xr_interface: TiltFiveXRInterface - -@export var automatically_start : bool = true @export var glasses_scene : PackedScene = preload("res://addons/tiltfive/scenes/T5GlassesBase.tscn") -var reserved_glasses: Dictionary - # We'll add our glasses scenes as children of this node var glasses_node: Node3D -# Called when the manager is loaded and added to our scene -func _enter_tree(): - tilt_five_xr_interface = T5Interface.get_tilt_five_xr_interface() - - if tilt_five_xr_interface: - tilt_five_xr_interface.glasses_event.connect(on_glasses_event) - -# Called when the manager is removed -func _exit_tree(): - if tilt_five_xr_interface: - tilt_five_xr_interface.glasses_event.disconnect(on_glasses_event) - - tilt_five_xr_interface = null - -# Called when our scene is fully setup -func _ready(): - # Create a node on our parent under which we create our glasses scenes +func _ready(): glasses_node = Node3D.new() glasses_node.name = "TiltFiveGlasses" get_parent().add_child.call_deferred(glasses_node) - if not tilt_five_xr_interface: - return - - if automatically_start and !tilt_five_xr_interface.is_initialized(): - tilt_five_xr_interface.initialize() - -func start_service() -> bool: - if not tilt_five_xr_interface: - return false - - return tilt_five_xr_interface.initialize() - -func has_reserved_glasses() -> bool: - for glasses in reserved_glasses: - if reserved_glasses[glasses]: - return true - return false - -func reserve_glasses(glasses_id : StringName, display_name := "") -> void: - if not reserved_glasses.has(glasses_id): - print_verbose("Warning: Tilt Five glasses id ", glasses_id, " does not exist") - return - if reserved_glasses[glasses_id]: - print_verbose("Warning: Tilt Five glasses ", glasses_id, " already reserved") - return - if display_name.length() == 0: - display_name = ProjectSettings.get_setting_with_override("xr/tilt_five/default_display_name") - tilt_five_xr_interface.reserve_glasses(glasses_id, display_name) - -func start_display(glasses_id : StringName, viewport : SubViewport): - var xr_origin = viewport.get_node(xr_origin_node) - tilt_five_xr_interface.start_display(glasses_id, viewport, xr_origin) - var t5_camera := viewport.get_node_or_null(xr_camera_node) as T5Camera3D - if t5_camera: - t5_camera.tracker = "/user/%s/head" % glasses_id - for idx in 4: - var controller = viewport.get_node_or_null(wand_node_list[idx]) as T5Controller3D - if controller: - controller.tracker = "/user/%s/wand_%d" % [glasses_id, idx + 1] - -func node_name_from_glasses_id(glasses_id: String) -> String: - return "Glasses_" + glasses_id.replace("-", "_") - -func on_glasses_event(glasses_id, event_num): - match event_num: - TiltFiveXRInterface.E_AVAILABLE: - print_verbose(glasses_id, " E_AVAILABLE") - if not reserved_glasses.has(glasses_id): - reserved_glasses[glasses_id] = false - - # If we're managing our glasses scene, reserve our glasses - if glasses_scene: - reserve_glasses(glasses_id) - - # Let others who are interested know - glasses_available.emit(glasses_id) - - TiltFiveXRInterface.E_UNAVAILABLE: - print_verbose(glasses_id, " E_UNAVAILABLE") - - # Let others who are interested know - reserved_glasses.erase(glasses_id) - - TiltFiveXRInterface.E_RESERVED: - print_verbose(glasses_id, " E_RESERVED") - - reserved_glasses[glasses_id] = true - - # If we're managing our glasses scene, instance our scene - if glasses_scene: - var gview = glasses_scene.instantiate() - gview.name = node_name_from_glasses_id(glasses_id) - glasses_node.add_child(gview) - start_display(glasses_id, gview) - - # Let others who are interested know - glasses_reserved.emit(glasses_id) - - TiltFiveXRInterface.E_DROPPED: - print_verbose(glasses_id, " E_DROPPED") - - var node_name = node_name_from_glasses_id(glasses_id) - var gview = glasses_node.get_node_or_null(node_name) - if gview: - tilt_five_xr_interface.stop_display(glasses_id) - glasses_node.remove_child(gview) - gview.queue_free() - - if reserved_glasses.get(glasses_id, false): - reserved_glasses[glasses_id] = false - glasses_dropped.emit(glasses_id) - - TiltFiveXRInterface.E_TRACKING: - var gbt = tilt_five_xr_interface.get_gameboard_type(glasses_id) - print_verbose(glasses_id, " E_TRACKING, Gameboard size = ", tilt_five_xr_interface.get_gameboard_extents(gbt)) - - TiltFiveXRInterface.E_NOT_TRACKING: - print_verbose(glasses_id, " E_NOT_TRACKING") - - _: - print_verbose(glasses_id, " - unknown event: ", event_num) - +func create_glasses_scene(glasses_id : String) -> Node: + var gview = glasses_scene.instantiate() + glasses_node.add_child(gview) + glasses_scene_was_added.emit(gview) + return gview + +func release_glasses_scene(glasses_scene : Node) -> void: + glasses_scene_will_be_removed.emit(glasses_scene) + glasses_node.remove_child(glasses_scene) + glasses_scene.queue_free() + +func get_glasses_scene_viewport(glasses_scene : Node) -> SubViewport: + return glasses_scene as SubViewport + +func get_glasses_scene_origin(glasses_scene : Node) -> T5Origin3D: + return glasses_scene.get_node(xr_origin_node) + +func get_glasses_scene_camera(glasses_scene : Node) -> Camera3D: + return glasses_scene.get_node(xr_camera_node) + +func get_glasses_scene_wand(glasses_scene : Node, wand_num : int) -> T5Controller3D: + if wand_num < wand_node_list.size(): + return glasses_scene.get_node_or_null(wand_node_list[wand_num]) as T5Controller3D + return null + diff --git a/example/addons/tiltfive/T5ManagerBase.gd b/example/addons/tiltfive/T5ManagerBase.gd new file mode 100644 index 0000000..c4d3a40 --- /dev/null +++ b/example/addons/tiltfive/T5ManagerBase.gd @@ -0,0 +1,55 @@ +class_name T5ManagerBase extends Node + +## The T5ManagerBase derived node should be added to your +## These functions must be overridden +## +## create_glasses_scene +## release_glasses_scene +## get_glasses_scene_viewport +## get_glasses_scene_origin +## get_glasses_scene_camera +## get_glasses_scene_wand +## +## The derived node should be persistent. + +# Called when the manager is loaded and added to our scene +func _enter_tree(): + T5Interface.t5_manager = self + +# Called when the manager is removed +func _exit_tree(): + T5Interface.t5_manager = null + +func should_use_glasses(glasses_id : String) -> bool: + return true + +func get_glasses_display_name(glasses_id : String) -> String: + return ProjectSettings.get_setting_with_override("xr/tilt_five/default_display_name") + +func create_glasses_scene(glasses_id : String) -> Node: + push_error("create_glasses_scene not implemented in T5ManagerBase derived class") + return null + +func release_glasses_scene(glasses_scene : Node) -> void: + push_error("destroy_glasses_scene not implemented in T5ManagerBase derived class") + +func get_glasses_scene_viewport(glasses_scene : Node) -> SubViewport: + push_error("get_glasses_scene_viewport not implemented in T5ManagerBase derived class") + return null + +func get_glasses_scene_origin(glasses_scene : Node) -> T5Origin3D: + push_error("get_glasses_scene_origin not implemented in T5ManagerBase derived class") + return null + +func get_glasses_scene_camera(glasses_scene : Node) -> Camera3D: + push_error("get_glasses_scene_camera not implemented in T5ManagerBase derived class") + return null + +func get_glasses_scene_wand(glasses_scene : Node, wand_num : int) -> T5Controller3D: + push_error("get_glasses_scene_wand not implemented in T5ManagerBase derived class") + return null + +func set_glasses_scene_gameboard_type(glasses_scene : Node, gameboard_type : T5Interface.GameboardType) -> void: + pass + + diff --git a/example/addons/tiltfive/scenes/T5GlassesBase.gd b/example/addons/tiltfive/scenes/T5GlassesBase.gd index 9726117..4804d01 100644 --- a/example/addons/tiltfive/scenes/T5GlassesBase.gd +++ b/example/addons/tiltfive/scenes/T5GlassesBase.gd @@ -1,7 +1,9 @@ -extends SubViewport +class_name T5GlassesBase extends SubViewport @onready var wand = $Origin/Wand_1 +var glasses_id : String + func _process(_delta): if wand: var wand_pose : XRPose = wand.get_pose() diff --git a/example/addons/tiltfive/scenes/T5GlassesBase.tscn b/example/addons/tiltfive/scenes/T5GlassesBase.tscn index 2fff38b..5da1db5 100644 --- a/example/addons/tiltfive/scenes/T5GlassesBase.tscn +++ b/example/addons/tiltfive/scenes/T5GlassesBase.tscn @@ -1,11 +1,11 @@ [gd_scene load_steps=2 format=3 uid="uid://je0clrdu7o76"] -[ext_resource type="Script" path="res://addons/tiltfive/scenes/T5GlassesBase.gd" id="1_ra3f6"] +[ext_resource type="Script" path="res://addons/tiltfive/scenes/T5GlassesBase.gd" id="1_tymyw"] [node name="T5GlassesBase" type="SubViewport"] size = Vector2i(2, 2) render_target_update_mode = 0 -script = ExtResource("1_ra3f6") +script = ExtResource("1_tymyw") [node name="Origin" type="T5Origin3D" parent="."] diff --git a/example/main.gd b/example/main.gd index ea4a90d..880e6ec 100644 --- a/example/main.gd +++ b/example/main.gd @@ -1,2 +1,7 @@ extends Node3D +func _on_t5_manager_glasses_scene_was_added(glasses): + print("Scene ", glasses.name, " added") + +func _on_t5_manager_glasses_scene_will_be_removed(glasses): + print("Scene ", glasses.name, " removed") diff --git a/example/main.tscn b/example/main.tscn index 2cfdf95..910bb38 100644 --- a/example/main.tscn +++ b/example/main.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=17 format=3 uid="uid://ckbe6draoen0x"] -[ext_resource type="Script" path="res://main.gd" id="1_ptcg0"] +[ext_resource type="Script" path="res://main.gd" id="1_xvgge"] [ext_resource type="Script" path="res://addons/tiltfive/T5Manager.gd" id="2_dibvp"] [ext_resource type="Script" path="res://addons/tiltfive/scenes/helpers/SelectableBody.gd" id="3_jespo"] [ext_resource type="PackedScene" uid="uid://dl3mv76qkuscf" path="res://T5Glasses.tscn" id="3_xx6p7"] @@ -35,7 +35,7 @@ albedo_color = Color(0.862745, 0, 0.0235294, 1) albedo_color = Color(0.741176, 0, 0.686275, 1) [node name="Main" type="Node3D"] -script = ExtResource("1_ptcg0") +script = ExtResource("1_xvgge") [node name="T5Manager" type="Node" parent="."] script = ExtResource("2_dibvp") @@ -100,3 +100,6 @@ surface_material_override/0 = SubResource("StandardMaterial3D_qrhlq") [node name="SpectatorCam" type="Camera3D" parent="."] transform = Transform3D(0.670983, -0.138786, 0.728368, 0, 0.982326, 0.187176, -0.741472, -0.125592, 0.659125, 14.0459, 4.9572, 12.9908) cull_mask = 3 + +[connection signal="glasses_scene_was_added" from="T5Manager" to="." method="_on_t5_manager_glasses_scene_was_added"] +[connection signal="glasses_scene_will_be_removed" from="T5Manager" to="." method="_on_t5_manager_glasses_scene_will_be_removed"]