Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XR demo for binding modifier logic #1137

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions xr/openxr_binding_modifier_demo/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
5 changes: 5 additions & 0 deletions xr/openxr_binding_modifier_demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Godot 4+ specific ignores
.godot/
/android/

.editorconfig
65 changes: 65 additions & 0 deletions xr/openxr_binding_modifier_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# XR Binding Modifier demo

This is a demo for an OpenXR project where we show how to use the binding modifier feature in the action map.

Language: GDScript

Renderer: Compatibility

Check out this demo on the asset library: https://godotengine.org/asset-library/asset/0000

## How does it work?

OpenXR has introduced a system called binding modifiers that allow you to add additional logic to the action map.
Currently there are only two modifiers available but more will likely be coming.

**Warning:** Binding modifiers are optional features that need to be enabled and may not be available on all platforms.

## Local Floor Reference Space

This demo uses the local floor reference space so the player is centered by default looking at the information displays.

If your player isn't standing in the correct spot, try a system recenter.
This is different per runtime, for instance:
- On Quest (including with SteamLink) hold the Meta button for 3 seconds to initiate a recenter.
- On SteamVR open the in headset SteamVR menu and choose the recenter option from the menu.

## Action map

This project does not use the default action map but instead configures an action map that just contains the actions required for this example to work. This so we remove any clutter and just focus on the functionality being demonstrated.

The actions defined in the action map are solely needed to demonstrate the different modifiers and uses the `grip_pose` to position the controllers.

### Analog Threshold Modifier

This is a modifier that works on boolean inputs that are driven by analog controls such as the trigger or grip button (on some controllers).
With this modifier you can change the default values at which the input toggles from `true` to `false` or back.

![Screenshot](screenshots/analog_binding_modifier.png)

**Note:** This modifier is created on the individual bindings using the modifier button next to each binding.

### DPad Modifier

The DPad extension splits common inputs like thumbsticks and trackpads into a DPad like input.
When using the DPad inputs you should not also bind the original thumbstick or trackpad input.
Optionally you can also add a modifier to further control this behavior.

![Screenshot](screenshots/dpad_modifier.png)

**Note:** This modifier is created for the interaction profile with the modifier button on the right hand side.

## Running on PCVR

This project can be run as normal for PCVR. Ensure that an OpenXR runtime has been installed.
This project has been tested with the Oculus client and SteamVR OpenXR runtimes.
Note that Godot currently can't run using the WMR OpenXR runtime. Install SteamVR with WMR support.

## Running on standalone VR

You must install the Android build templates and OpenXR loader plugin and configure an export template for your device.
Please follow [the instructions for deploying on Android in the manual](https://docs.godotengine.org/en/stable/tutorials/xr/deploying_to_android.html).

## Screenshots

![Screenshot](screenshots/binding_modifier_demo.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions xr/openxr_binding_modifier_demo/assets/pattern.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://rek0t7kubpx4"
path.s3tc="res://.godot/imported/pattern.png-cf6f03dfd1cdd4bc35da3414e912103d.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}

[deps]

source_file="res://assets/pattern.png"
dest_files=["res://.godot/imported/pattern.png-cf6f03dfd1cdd4bc35da3414e912103d.s3tc.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
37 changes: 37 additions & 0 deletions xr/openxr_binding_modifier_demo/controller_state.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
extends Control

@export var controller : XRController3D

@onready var trigger_input_node = $VBoxContainer/TriggerInput/HSlider
@onready var trigger_click_node = $VBoxContainer/TriggerInput/CheckBox
@onready var on_threshold_node = $VBoxContainer/Thresholds/OnThreshold
@onready var off_threshold_node = $VBoxContainer/Thresholds/OffThreshold

@onready var dpad_up_node = $VBoxContainer/DPadState/Up
@onready var dpad_down_node = $VBoxContainer/DPadState/Down
@onready var dpad_left_node = $VBoxContainer/DPadState/Left
@onready var dpad_right_node = $VBoxContainer/DPadState/Right

var off_trigger_threshold = 1.0
var on_trigger_threshold = 0.0

func _process(_delta):
if controller:
var trigger_input = controller.get_float("trigger")
trigger_input_node.value = trigger_input

var trigger_click = controller.is_button_pressed("trigger_click")
trigger_click_node.button_pressed = trigger_click

if trigger_click:
off_trigger_threshold = min(off_trigger_threshold, trigger_input)
else:
on_trigger_threshold = max(on_trigger_threshold, trigger_input)

on_threshold_node.text = "On: %0.2f" % on_trigger_threshold
off_threshold_node.text = "Off: %0.2f" % off_trigger_threshold

dpad_up_node.button_pressed = controller.is_button_pressed("up")
dpad_down_node.button_pressed = controller.is_button_pressed("down")
dpad_left_node.button_pressed = controller.is_button_pressed("left")
dpad_right_node.button_pressed = controller.is_button_pressed("right")
1 change: 1 addition & 0 deletions xr/openxr_binding_modifier_demo/controller_state.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://b7rteq0k83idy
93 changes: 93 additions & 0 deletions xr/openxr_binding_modifier_demo/controller_state.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
[gd_scene load_steps=2 format=3 uid="uid://c6h2mtlrvrp5v"]

[ext_resource type="Script" uid="uid://b7rteq0k83idy" path="res://controller_state.gd" id="1_dwfbl"]

[node name="ControllerState" type="Control"]
custom_minimum_size = Vector2(512, 512)
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("1_dwfbl")

[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -58.5
offset_top = -20.0
offset_right = 58.5
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2

[node name="ThresholdHeader" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
alignment = 1

[node name="Label" type="Label" parent="VBoxContainer/ThresholdHeader"]
layout_mode = 2
text = "Threshold Info:"

[node name="TriggerInput" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2

[node name="Label" type="Label" parent="VBoxContainer/TriggerInput"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "Trigger:"

[node name="HSlider" type="HSlider" parent="VBoxContainer/TriggerInput"]
custom_minimum_size = Vector2(350, 0)
layout_mode = 2
max_value = 1.0
step = 0.01

[node name="CheckBox" type="CheckBox" parent="VBoxContainer/TriggerInput"]
layout_mode = 2

[node name="Thresholds" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
alignment = 1

[node name="OnThreshold" type="Label" parent="VBoxContainer/Thresholds"]
layout_mode = 2
text = "On: 0.0"

[node name="OffThreshold" type="Label" parent="VBoxContainer/Thresholds"]
layout_mode = 2
text = "Off: 0.0"

[node name="DPadHeader" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
alignment = 1

[node name="Label" type="Label" parent="VBoxContainer/DPadHeader"]
layout_mode = 2
text = "D-pad Info:"

[node name="DPadState" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
alignment = 1

[node name="Up" type="CheckBox" parent="VBoxContainer/DPadState"]
layout_mode = 2
text = "Up"

[node name="Down" type="CheckBox" parent="VBoxContainer/DPadState"]
layout_mode = 2
text = "Down
"

[node name="Left" type="CheckBox" parent="VBoxContainer/DPadState"]
layout_mode = 2
text = "Left
"

[node name="Right" type="CheckBox" parent="VBoxContainer/DPadState"]
layout_mode = 2
text = "Right
"
1 change: 1 addition & 0 deletions xr/openxr_binding_modifier_demo/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions xr/openxr_binding_modifier_demo/icon.svg.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://cesuwxv0lx34c"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
89 changes: 89 additions & 0 deletions xr/openxr_binding_modifier_demo/main.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
[gd_scene load_steps=10 format=3 uid="uid://c6ec3t6mbd08d"]

[ext_resource type="PackedScene" uid="uid://c6h2mtlrvrp5v" path="res://controller_state.tscn" id="1_h2yge"]
[ext_resource type="Script" uid="uid://dytx8naceu3j6" path="res://start_vr.gd" id="1_ig7tw"]
[ext_resource type="Texture2D" uid="uid://rek0t7kubpx4" path="res://assets/pattern.png" id="2_1bvp3"]

[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_ig7tw"]
sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)

[sub_resource type="Sky" id="Sky_0xm2m"]
sky_material = SubResource("ProceduralSkyMaterial_ig7tw")

[sub_resource type="Environment" id="Environment_h2yge"]
background_mode = 2
sky = SubResource("Sky_0xm2m")
tonemap_mode = 2

[sub_resource type="BoxMesh" id="BoxMesh_ig7tw"]
size = Vector3(0.1, 0.1, 0.1)

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lquwl"]
albedo_color = Color(0.47788, 0.47788, 0.47788, 1)
albedo_texture = ExtResource("2_1bvp3")

[sub_resource type="PlaneMesh" id="PlaneMesh_1bvp3"]
material = SubResource("StandardMaterial3D_lquwl")

[node name="Main" type="Node3D"]

[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_h2yge")

[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0)
shadow_enabled = true

[node name="XROrigin3D" type="XROrigin3D" parent="."]

[node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0)

[node name="LeftHand" type="XRController3D" parent="XROrigin3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 1, -0.5)
tracker = &"left_hand"
pose = &"grip"
show_when_tracked = true

[node name="Test" type="MeshInstance3D" parent="XROrigin3D/LeftHand"]
mesh = SubResource("BoxMesh_ig7tw")

[node name="SubViewport" type="SubViewport" parent="XROrigin3D/LeftHand"]
disable_3d = true
render_target_update_mode = 4

[node name="ControllerState" parent="XROrigin3D/LeftHand/SubViewport" node_paths=PackedStringArray("controller") instance=ExtResource("1_h2yge")]
controller = NodePath("../..")

[node name="RightHand" type="XRController3D" parent="XROrigin3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 1, -0.5)
tracker = &"right_hand"
pose = &"grip"
show_when_tracked = true

[node name="Test" type="MeshInstance3D" parent="XROrigin3D/RightHand"]
mesh = SubResource("BoxMesh_ig7tw")

[node name="SubViewport" type="SubViewport" parent="XROrigin3D/RightHand"]
disable_3d = true
render_target_update_mode = 4

[node name="ControllerState" parent="XROrigin3D/RightHand/SubViewport" node_paths=PackedStringArray("controller") instance=ExtResource("1_h2yge")]
controller = NodePath("../..")

[node name="LeftControllerState" type="OpenXRCompositionLayerQuad" parent="XROrigin3D" node_paths=PackedStringArray("layer_viewport")]
transform = Transform3D(0.766044, 0, 0.642788, 0, 1, 0, -0.642788, 0, 0.766044, -0.5, 1, -1)
layer_viewport = NodePath("../LeftHand/SubViewport")
quad_size = Vector2(0.4, 0.4)

[node name="RightControllerState" type="OpenXRCompositionLayerQuad" parent="XROrigin3D" node_paths=PackedStringArray("layer_viewport")]
transform = Transform3D(0.866025, 0, -0.5, 0, 1, 0, 0.5, 0, 0.866025, 0.5, 1, -1)
layer_viewport = NodePath("../RightHand/SubViewport")
quad_size = Vector2(0.4, 0.4)

[node name="Floor" type="MeshInstance3D" parent="."]
mesh = SubResource("PlaneMesh_1bvp3")

[node name="StartVR" type="Node3D" parent="."]
script = ExtResource("1_ig7tw")
Loading