init
This commit is contained in:
222
addons/godot-xr-tools/user_settings/user_settings.gd
Normal file
222
addons/godot-xr-tools/user_settings/user_settings.gd
Normal file
@@ -0,0 +1,222 @@
|
||||
extends Node
|
||||
|
||||
|
||||
## Emitted when the WebXR primary is changed (either by the user or auto detected).
|
||||
signal webxr_primary_changed (value)
|
||||
|
||||
|
||||
enum WebXRPrimary {
|
||||
AUTO,
|
||||
THUMBSTICK,
|
||||
TRACKPAD,
|
||||
}
|
||||
|
||||
|
||||
@export_group("Input")
|
||||
|
||||
## User setting for snap-turn
|
||||
@export var snap_turning : bool = true
|
||||
|
||||
## User setting for y axis dead zone
|
||||
@export var y_axis_dead_zone : float = 0.1
|
||||
|
||||
## User setting for y axis dead zone
|
||||
@export var x_axis_dead_zone : float = 0.2
|
||||
|
||||
## Used to control rumble like volume
|
||||
@export_range(0.0, 1.0, 0.05) var haptics_scale := 1.0
|
||||
|
||||
@export_group("Player")
|
||||
|
||||
## User setting for player height
|
||||
@export var player_height : float = 1.85: set = set_player_height
|
||||
|
||||
@export_group("WebXR")
|
||||
|
||||
## User setting for WebXR primary
|
||||
@export var webxr_primary : WebXRPrimary = WebXRPrimary.AUTO: set = set_webxr_primary
|
||||
|
||||
|
||||
## Settings file name to persist user settings
|
||||
var settings_file_name : String = "user://xtools_user_settings.json"
|
||||
|
||||
## Records the first input to generate input (thumbstick or trackpad).
|
||||
var webxr_auto_primary := 0
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
var webxr_interface = XRServer.find_interface("WebXR")
|
||||
if webxr_interface:
|
||||
XRServer.tracker_added.connect(self._on_webxr_tracker_added)
|
||||
|
||||
_load()
|
||||
|
||||
|
||||
## Reset to default values
|
||||
func reset_to_defaults() -> void:
|
||||
# Reset to defaults.
|
||||
# Where applicable we obtain our project settings
|
||||
snap_turning = XRTools.get_default_snap_turning()
|
||||
y_axis_dead_zone = XRTools.get_y_axis_dead_zone()
|
||||
x_axis_dead_zone = XRTools.get_x_axis_dead_zone()
|
||||
player_height = XRTools.get_player_standard_height()
|
||||
webxr_primary = WebXRPrimary.AUTO
|
||||
webxr_auto_primary = 0
|
||||
haptics_scale = XRToolsRumbleManager.get_default_haptics_scale()
|
||||
|
||||
## Set the player height property
|
||||
func set_player_height(new_value : float) -> void:
|
||||
player_height = clamp(new_value, 1.0, 2.5)
|
||||
|
||||
## Set the WebXR primary
|
||||
func set_webxr_primary(new_value : WebXRPrimary) -> void:
|
||||
webxr_primary = new_value
|
||||
if webxr_primary == WebXRPrimary.AUTO:
|
||||
if webxr_auto_primary == 0:
|
||||
# Don't emit the signal yet, wait until we detect which to use.
|
||||
pass
|
||||
else:
|
||||
webxr_primary_changed.emit(webxr_auto_primary)
|
||||
else:
|
||||
webxr_primary_changed.emit(webxr_primary)
|
||||
|
||||
|
||||
## Gets the WebXR primary (taking into account auto detection).
|
||||
func get_real_webxr_primary() -> WebXRPrimary:
|
||||
if webxr_primary == WebXRPrimary.AUTO:
|
||||
return webxr_auto_primary
|
||||
return webxr_primary
|
||||
|
||||
|
||||
## Save the settings to file
|
||||
func save() -> void:
|
||||
# Convert the settings to a dictionary
|
||||
var settings := {
|
||||
"input" : {
|
||||
"default_snap_turning" : snap_turning,
|
||||
"y_axis_dead_zone" : y_axis_dead_zone,
|
||||
"x_axis_dead_zone" : x_axis_dead_zone,
|
||||
"haptics_scale": haptics_scale
|
||||
},
|
||||
"player" : {
|
||||
"height" : player_height
|
||||
},
|
||||
"webxr" : {
|
||||
"webxr_primary" : webxr_primary,
|
||||
}
|
||||
}
|
||||
|
||||
# Convert the settings dictionary to text
|
||||
var settings_text := JSON.stringify(settings)
|
||||
|
||||
# Attempt to open the settings file for writing
|
||||
var file := FileAccess.open(settings_file_name, FileAccess.WRITE)
|
||||
if not file:
|
||||
push_warning("Unable to write to %s" % settings_file_name)
|
||||
return
|
||||
|
||||
# Write the settings text to the file
|
||||
file.store_line(settings_text)
|
||||
file.close()
|
||||
|
||||
|
||||
## Get the action associated with a WebXR primary choice
|
||||
func get_webxr_primary_action(primary : WebXRPrimary) -> String:
|
||||
match primary:
|
||||
WebXRPrimary.THUMBSTICK:
|
||||
return "thumbstick"
|
||||
|
||||
WebXRPrimary.TRACKPAD:
|
||||
return "trackpad"
|
||||
|
||||
_:
|
||||
return "auto"
|
||||
|
||||
|
||||
## Load the settings from file
|
||||
func _load() -> void:
|
||||
# First reset our values
|
||||
reset_to_defaults()
|
||||
|
||||
# Skip if no settings file found
|
||||
if !FileAccess.file_exists(settings_file_name):
|
||||
return
|
||||
|
||||
# Attempt to open the settings file for reading
|
||||
var file := FileAccess.open(settings_file_name, FileAccess.READ)
|
||||
if not file:
|
||||
push_warning("Unable to read from %s" % settings_file_name)
|
||||
return
|
||||
|
||||
# Read the settings text
|
||||
var settings_text := file.get_as_text()
|
||||
file.close()
|
||||
|
||||
# Parse the settings text and verify it's a dictionary
|
||||
var settings_raw = JSON.parse_string(settings_text)
|
||||
if typeof(settings_raw) != TYPE_DICTIONARY:
|
||||
push_warning("Settings file %s is corrupt" % settings_file_name)
|
||||
return
|
||||
|
||||
# Parse our input settings
|
||||
var settings : Dictionary = settings_raw
|
||||
if settings.has("input"):
|
||||
var input : Dictionary = settings["input"]
|
||||
if input.has("default_snap_turning"):
|
||||
snap_turning = input["default_snap_turning"]
|
||||
if input.has("y_axis_dead_zone"):
|
||||
y_axis_dead_zone = input["y_axis_dead_zone"]
|
||||
if input.has("x_axis_dead_zone"):
|
||||
x_axis_dead_zone = input["x_axis_dead_zone"]
|
||||
if input.has("haptics_scale"):
|
||||
haptics_scale = input["haptics_scale"]
|
||||
|
||||
# Parse our player settings
|
||||
if settings.has("player"):
|
||||
var player : Dictionary = settings["player"]
|
||||
if player.has("height"):
|
||||
player_height = player["height"]
|
||||
|
||||
# Parse our WebXR settings
|
||||
if settings.has("webxr"):
|
||||
var webxr : Dictionary = settings["webxr"]
|
||||
if webxr.has("webxr_primary"):
|
||||
webxr_primary = webxr["webxr_primary"]
|
||||
|
||||
|
||||
## Used to connect to tracker events when using WebXR.
|
||||
func _on_webxr_tracker_added(tracker_name: StringName, _type: int) -> void:
|
||||
if tracker_name == &"left_hand" or tracker_name == &"right_hand":
|
||||
var tracker := XRServer.get_tracker(tracker_name)
|
||||
tracker.input_vector2_changed.connect(self._on_webxr_vector2_changed)
|
||||
|
||||
|
||||
## Used to auto detect which "primary" input gets used first.
|
||||
func _on_webxr_vector2_changed(name: String, _vector: Vector2) -> void:
|
||||
if webxr_auto_primary == 0:
|
||||
if name == "thumbstick":
|
||||
webxr_auto_primary = WebXRPrimary.THUMBSTICK
|
||||
elif name == "trackpad":
|
||||
webxr_auto_primary = WebXRPrimary.TRACKPAD
|
||||
|
||||
if webxr_auto_primary != 0:
|
||||
# Let the developer know which one is chosen.
|
||||
webxr_primary_changed.emit(webxr_auto_primary)
|
||||
|
||||
## Helper function to remap input vector with deadzone values
|
||||
func get_adjusted_vector2(p_controller, p_input_action):
|
||||
var vector = Vector2.ZERO
|
||||
var original_vector = p_controller.get_vector2(p_input_action)
|
||||
|
||||
if abs(original_vector.y) > y_axis_dead_zone:
|
||||
vector.y = remap(abs(original_vector.y), y_axis_dead_zone, 1, 0, 1)
|
||||
if original_vector.y < 0:
|
||||
vector.y *= -1
|
||||
|
||||
if abs(original_vector.x) > x_axis_dead_zone:
|
||||
vector.x = remap(abs(original_vector.x), x_axis_dead_zone, 1, 0, 1)
|
||||
if original_vector.x < 0:
|
||||
vector.x *= -1
|
||||
|
||||
return vector
|
||||
1
addons/godot-xr-tools/user_settings/user_settings.gd.uid
Normal file
1
addons/godot-xr-tools/user_settings/user_settings.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bqgb8i74tm0t
|
||||
74
addons/godot-xr-tools/user_settings/user_settings_ui.gd
Normal file
74
addons/godot-xr-tools/user_settings/user_settings_ui.gd
Normal file
@@ -0,0 +1,74 @@
|
||||
extends TabContainer
|
||||
|
||||
signal player_height_changed(new_height)
|
||||
|
||||
@onready var snap_turning_button = $Input/InputVBox/SnapTurning/SnapTurningCB
|
||||
@onready var haptics_scale_slider = $Input/InputVBox/HapticsScale/HapticsScaleSlider
|
||||
@onready var y_deadzone_slider = $Input/InputVBox/yAxisDeadZone/yAxisDeadZoneSlider
|
||||
@onready var x_deadzone_slider = $Input/InputVBox/xAxisDeadZone/xAxisDeadZoneSlider
|
||||
@onready var player_height_slider = $Player/PlayerVBox/PlayerHeight/PlayerHeightSlider
|
||||
@onready var webxr_primary_button = $WebXR/WebXRVBox/WebXR/WebXRPrimary
|
||||
|
||||
func _update():
|
||||
# Input
|
||||
snap_turning_button.button_pressed = XRToolsUserSettings.snap_turning
|
||||
y_deadzone_slider.value = XRToolsUserSettings.y_axis_dead_zone
|
||||
x_deadzone_slider.value = XRToolsUserSettings.x_axis_dead_zone
|
||||
haptics_scale_slider.value = XRToolsUserSettings.haptics_scale
|
||||
|
||||
# Player
|
||||
player_height_slider.value = XRToolsUserSettings.player_height
|
||||
|
||||
# WebXR
|
||||
webxr_primary_button.selected = XRToolsUserSettings.webxr_primary
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
var webxr_interface = XRServer.find_interface("WebXR")
|
||||
set_tab_hidden(2, webxr_interface == null)
|
||||
|
||||
if XRToolsUserSettings:
|
||||
_update()
|
||||
else:
|
||||
$Save/Button.disabled = true
|
||||
|
||||
|
||||
func _on_Save_pressed():
|
||||
if XRToolsUserSettings:
|
||||
# Save
|
||||
XRToolsUserSettings.save()
|
||||
|
||||
|
||||
func _on_Reset_pressed():
|
||||
if XRToolsUserSettings:
|
||||
XRToolsUserSettings.reset_to_defaults()
|
||||
_update()
|
||||
emit_signal("player_height_changed", XRToolsUserSettings.player_height)
|
||||
|
||||
|
||||
# Input settings changed
|
||||
func _on_SnapTurningCB_pressed():
|
||||
XRToolsUserSettings.snap_turning = snap_turning_button.button_pressed
|
||||
|
||||
|
||||
# Player settings changed
|
||||
func _on_PlayerHeightSlider_drag_ended(_value_changed):
|
||||
XRToolsUserSettings.player_height = player_height_slider.value
|
||||
emit_signal("player_height_changed", XRToolsUserSettings.player_height)
|
||||
|
||||
|
||||
func _on_web_xr_primary_item_selected(index: int) -> void:
|
||||
XRToolsUserSettings.webxr_primary = index
|
||||
|
||||
|
||||
func _on_y_axis_dead_zone_slider_value_changed(value):
|
||||
XRToolsUserSettings.y_axis_dead_zone = y_deadzone_slider.value
|
||||
|
||||
|
||||
func _on_x_axis_dead_zone_slider_value_changed(value):
|
||||
XRToolsUserSettings.x_axis_dead_zone = x_deadzone_slider.value
|
||||
|
||||
|
||||
func _on_haptics_scale_slider_value_changed(value):
|
||||
XRToolsUserSettings.haptics_scale = value
|
||||
@@ -0,0 +1 @@
|
||||
uid://1kkudbch46t0
|
||||
199
addons/godot-xr-tools/user_settings/user_settings_ui.tscn
Normal file
199
addons/godot-xr-tools/user_settings/user_settings_ui.tscn
Normal file
@@ -0,0 +1,199 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://ytsxet2k47lj"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://1kkudbch46t0" path="res://addons/godot-xr-tools/user_settings/user_settings_ui.gd" id="1"]
|
||||
|
||||
[node name="UserSettingsUI" type="TabContainer"]
|
||||
offset_right = 214.0
|
||||
offset_bottom = 126.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 12
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Input" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="InputVBox" type="VBoxContainer" parent="Input"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SnapTurning" type="HBoxContainer" parent="Input/InputVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Input/InputVBox/SnapTurning"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Snap turning:"
|
||||
|
||||
[node name="SnapTurningCB" type="CheckBox" parent="Input/InputVBox/SnapTurning"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="yAxisDeadZone" type="HBoxContainer" parent="Input/InputVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Input/InputVBox/yAxisDeadZone"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Y axis dead zone"
|
||||
|
||||
[node name="yAxisDeadZoneSlider" type="HSlider" parent="Input/InputVBox/yAxisDeadZone"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
max_value = 0.5
|
||||
step = 0.01
|
||||
value = 0.1
|
||||
|
||||
[node name="xAxisDeadZone" type="HBoxContainer" parent="Input/InputVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Input/InputVBox/xAxisDeadZone"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "X axis dead zone"
|
||||
|
||||
[node name="xAxisDeadZoneSlider" type="HSlider" parent="Input/InputVBox/xAxisDeadZone"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
max_value = 0.5
|
||||
step = 0.01
|
||||
value = 0.2
|
||||
|
||||
[node name="HapticsScale" type="HBoxContainer" parent="Input/InputVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Input/InputVBox/HapticsScale"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Haptics Scale"
|
||||
|
||||
[node name="HapticsScaleSlider" type="HSlider" parent="Input/InputVBox/HapticsScale"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
max_value = 1.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Input/InputVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="Input/InputVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Save" type="Button" parent="Input/InputVBox/Buttons"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Apply"
|
||||
|
||||
[node name="Reset" type="Button" parent="Input/InputVBox/Buttons"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Reset"
|
||||
|
||||
[node name="Player" type="MarginContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="PlayerVBox" type="VBoxContainer" parent="Player"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PlayerHeight" type="HBoxContainer" parent="Player/PlayerVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Player/PlayerVBox/PlayerHeight"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Height adjust:"
|
||||
|
||||
[node name="PlayerHeightSlider" type="HSlider" parent="Player/PlayerVBox/PlayerHeight"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
min_value = 1.0
|
||||
max_value = 2.5
|
||||
step = 0.05
|
||||
value = 1.5
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Player/PlayerVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="Player/PlayerVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Save" type="Button" parent="Player/PlayerVBox/Buttons"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Apply"
|
||||
|
||||
[node name="Reset" type="Button" parent="Player/PlayerVBox/Buttons"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Reset"
|
||||
|
||||
[node name="WebXR" type="MarginContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="WebXRVBox" type="VBoxContainer" parent="WebXR"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="WebXR" type="HBoxContainer" parent="WebXR/WebXRVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="WebXR/WebXRVBox/WebXR"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "WebXR primary:"
|
||||
|
||||
[node name="WebXRPrimary" type="OptionButton" parent="WebXR/WebXRVBox/WebXR"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
item_count = 3
|
||||
selected = 0
|
||||
popup/item_0/text = "Auto"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Thumbstick"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Trackpad"
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="WebXR/WebXRVBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="WebXR/WebXRVBox"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Save" type="Button" parent="WebXR/WebXRVBox/Buttons"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Apply"
|
||||
|
||||
[node name="Reset" type="Button" parent="WebXR/WebXRVBox/Buttons"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "Reset"
|
||||
|
||||
[connection signal="pressed" from="Input/InputVBox/SnapTurning/SnapTurningCB" to="." method="_on_SnapTurningCB_pressed"]
|
||||
[connection signal="value_changed" from="Input/InputVBox/yAxisDeadZone/yAxisDeadZoneSlider" to="." method="_on_y_axis_dead_zone_slider_value_changed"]
|
||||
[connection signal="value_changed" from="Input/InputVBox/xAxisDeadZone/xAxisDeadZoneSlider" to="." method="_on_x_axis_dead_zone_slider_value_changed"]
|
||||
[connection signal="value_changed" from="Input/InputVBox/HapticsScale/HapticsScaleSlider" to="." method="_on_haptics_scale_slider_value_changed"]
|
||||
[connection signal="pressed" from="Input/InputVBox/Buttons/Save" to="." method="_on_Save_pressed"]
|
||||
[connection signal="pressed" from="Input/InputVBox/Buttons/Reset" to="." method="_on_Reset_pressed"]
|
||||
[connection signal="drag_ended" from="Player/PlayerVBox/PlayerHeight/PlayerHeightSlider" to="." method="_on_PlayerHeightSlider_drag_ended"]
|
||||
[connection signal="pressed" from="Player/PlayerVBox/Buttons/Save" to="." method="_on_Save_pressed"]
|
||||
[connection signal="pressed" from="Player/PlayerVBox/Buttons/Reset" to="." method="_on_Reset_pressed"]
|
||||
[connection signal="item_selected" from="WebXR/WebXRVBox/WebXR/WebXRPrimary" to="." method="_on_PlayerHeightSlider_drag_ended"]
|
||||
[connection signal="pressed" from="WebXR/WebXRVBox/Buttons/Save" to="." method="_on_Save_pressed"]
|
||||
[connection signal="pressed" from="WebXR/WebXRVBox/Buttons/Reset" to="." method="_on_Reset_pressed"]
|
||||
Reference in New Issue
Block a user