Skip to content

Commit 42c1ce2

Browse files
rburingdsnopek
andcommitted

File tree

9 files changed

+287
-2
lines changed

9 files changed

+287
-2
lines changed

.github/dist/footer.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ <h2>Unavailable demos</h2>
3232
<li><code>mono/*</code>: Not available yet (requires Mono-enabled HTML5 build).</li>
3333
<li><code>networking/*</code>: Doesn't make sense to be hosted on a static host, as the server must be hosted on the same origin due to the browser's same-origin policy.</li>
3434
<li><code>plugins/*</code>: Only effective within the editor.</li>
35-
<li><code>xr/*</code>: Not functional on the web platform, as these demos are not designed for WebXR.</li>
35+
<li><code>xr/openxr_*</code>: Not functional on the web platform, as these demos are not designed for WebXR.</li>
3636
</ul>
3737
</body>
3838
</html>

.github/workflows/export_web.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ jobs:
6060
mono/ \
6161
networking/ \
6262
plugins/ \
63-
xr/
63+
xr/openxr_character_centric_movement \
64+
xr/openxr_composition_layers \
65+
xr/openxr_hand_tracking_demo \
66+
xr/openxr_origin_centric_movement
6467
6568
for panorama in 3d/material_testers/backgrounds/*.hdr; do
6669
# Decrease the resolution to get below the 100 MB PCK size limit.
@@ -88,6 +91,11 @@ jobs:
8891
# Enable ETC2 texture importing, which is disabled by default (but required for web exports to work on mobile platforms).
8992
echo "[rendering]\n\ntextures/vram_compression/import_etc2_astc=true" >> project.godot
9093
94+
# Enable WebXR Polyfill and WebXR Layers Polyfill for the WebXR demo.
95+
if [ "$demo" == "xr/webxr/" ]
96+
sed -i 's~^html/head_include=""$~html/head_include="<script src=\\"https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.min.js\\"></script>\n<script>\nvar polyfill = new WebXRPolyfill();\n</script>\n<script src=\\"https://cdn.jsdelivr.net/npm/webxr-layers-polyfill@latest/build/webxr-layers-polyfill.min.js\\"></script>\n<script>\nvar layersPolyfill = new WebXRLayersPolyfill();\n</script>"~g' export_presets.cfg
97+
fi
98+
9199
godot --verbose --headless --export-release "Web" "$BASEDIR/.github/dist/$demo/index.html"
92100
93101
# Replace the WASM file with a symbolic link to avoid duplicating files in the pushed branch.

xr/webxr/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Godot 4+ specific ignores
2+
.godot/
3+
/android/

xr/webxr/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# WebXR demo
2+
3+
This is a minimalist demo of WebXR rendering and controller support.
4+
5+
Language: GDScript
6+
7+
Renderer: Compatibility

xr/webxr/icon.svg

+1
Loading

xr/webxr/icon.svg.import

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[remap]
2+
3+
importer="texture"
4+
type="CompressedTexture2D"
5+
uid="uid://b8qswdbhoi3ks"
6+
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
7+
metadata={
8+
"vram_texture": false
9+
}
10+
11+
[deps]
12+
13+
source_file="res://icon.svg"
14+
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
15+
16+
[params]
17+
18+
compress/mode=0
19+
compress/high_quality=false
20+
compress/lossy_quality=0.7
21+
compress/hdr_compression=1
22+
compress/normal_map=0
23+
compress/channel_pack=0
24+
mipmaps/generate=false
25+
mipmaps/limit=-1
26+
roughness/mode=0
27+
roughness/src_normal=""
28+
process/fix_alpha_border=true
29+
process/premult_alpha=false
30+
process/normal_map_invert_y=false
31+
process/hdr_as_srgb=false
32+
process/hdr_clamp_exposure=false
33+
process/size_limit=0
34+
detect_3d/compress_to=1
35+
svg/scale=1.0
36+
editor/scale_with_editor_scale=false
37+
editor/convert_colors_with_editor_theme=false

xr/webxr/main.gd

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
extends Node3D
2+
3+
var webxr_interface: XRInterface
4+
var vr_supported: bool = false
5+
6+
7+
func _ready() -> void:
8+
$CanvasLayer/EnterVRButton.pressed.connect(self._on_enter_vr_button_pressed)
9+
10+
webxr_interface = XRServer.find_interface("WebXR")
11+
if webxr_interface:
12+
# WebXR uses a lot of asynchronous callbacks, so we connect to various
13+
# signals in order to receive them.
14+
webxr_interface.session_supported.connect(self._webxr_session_supported)
15+
webxr_interface.session_started.connect(self._webxr_session_started)
16+
webxr_interface.session_ended.connect(self._webxr_session_ended)
17+
webxr_interface.session_failed.connect(self._webxr_session_failed)
18+
19+
webxr_interface.select.connect(self._webxr_on_select)
20+
webxr_interface.selectstart.connect(self._webxr_on_select_start)
21+
webxr_interface.selectend.connect(self._webxr_on_select_end)
22+
23+
webxr_interface.squeeze.connect(self._webxr_on_squeeze)
24+
webxr_interface.squeezestart.connect(self._webxr_on_squeeze_start)
25+
webxr_interface.squeezeend.connect(self._webxr_on_squeeze_end)
26+
27+
# This returns immediately - our _webxr_session_supported() method
28+
# (which we connected to the "session_supported" signal above) will
29+
# be called sometime later to let us know if it's supported or not.
30+
webxr_interface.is_session_supported("immersive-vr")
31+
32+
$XROrigin3D/LeftController.button_pressed.connect(self._on_left_controller_button_pressed)
33+
$XROrigin3D/LeftController.button_released.connect(self._on_left_controller_button_released)
34+
35+
36+
func _webxr_session_supported(session_mode: String, supported: bool) -> void:
37+
if session_mode == 'immersive-vr':
38+
vr_supported = supported
39+
40+
41+
func _on_enter_vr_button_pressed() -> void:
42+
if not vr_supported:
43+
OS.alert("Your browser doesn't support VR")
44+
return
45+
46+
# We want an immersive VR session, as opposed to AR ('immersive-ar') or a
47+
# simple 3DoF viewer ('viewer').
48+
webxr_interface.session_mode = 'immersive-vr'
49+
# 'bounded-floor' is room scale, 'local-floor' is a standing or sitting
50+
# experience (it puts you 1.6m above the ground if you have 3DoF headset),
51+
# whereas as 'local' puts you down at the XROrigin3D.
52+
# This list means it'll first try to request 'bounded-floor', then
53+
# fallback on 'local-floor' and ultimately 'local', if nothing else is
54+
# supported.
55+
webxr_interface.requested_reference_space_types = 'bounded-floor, local-floor, local'
56+
# In order to use 'local-floor' or 'bounded-floor' we must also
57+
# mark the features as required or optional.
58+
webxr_interface.required_features = 'local-floor'
59+
webxr_interface.optional_features = 'bounded-floor'
60+
61+
# This will return false if we're unable to even request the session,
62+
# however, it can still fail asynchronously later in the process, so we
63+
# only know if it's really succeeded or failed when our
64+
# _webxr_session_started() or _webxr_session_failed() methods are called.
65+
if not webxr_interface.initialize():
66+
OS.alert("Failed to initialize WebXR")
67+
return
68+
69+
70+
func _webxr_session_started() -> void:
71+
$CanvasLayer.visible = false
72+
# This tells Godot to start rendering to the headset.
73+
get_viewport().use_xr = true
74+
# This will be the reference space type you ultimately got, out of the
75+
# types that you requested above. This is useful if you want the game to
76+
# work a little differently in 'bounded-floor' versus 'local-floor'.
77+
print ("Reference space type: " + webxr_interface.reference_space_type)
78+
# This will be the list of features that were successfully enabled
79+
# (except on browsers that don't support this property).
80+
print("Enabled features: ", webxr_interface.enabled_features)
81+
82+
83+
func _webxr_session_ended() -> void:
84+
$CanvasLayer.visible = true
85+
# If the user exits immersive mode, then we tell Godot to render to the web
86+
# page again.
87+
get_viewport().use_xr = false
88+
89+
90+
func _webxr_session_failed(message: String) -> void:
91+
OS.alert("Failed to initialize: " + message)
92+
93+
94+
func _on_left_controller_button_pressed(button: String) -> void:
95+
print ("Button pressed: " + button)
96+
97+
98+
func _on_left_controller_button_released(button: String) -> void:
99+
print ("Button release: " + button)
100+
101+
102+
func _process(_delta: float) -> void:
103+
var thumbstick_vector: Vector2 = $XROrigin3D/LeftController.get_vector2("thumbstick")
104+
if thumbstick_vector != Vector2.ZERO:
105+
print ("Left thumbstick position: " + str(thumbstick_vector))
106+
107+
108+
func _webxr_on_select(input_source_id: int) -> void:
109+
print("Select: " + str(input_source_id))
110+
111+
var tracker: XRPositionalTracker = webxr_interface.get_input_source_tracker(input_source_id)
112+
var xform = tracker.get_pose('default').transform
113+
print (xform.origin)
114+
115+
116+
func _webxr_on_select_start(input_source_id: int) -> void:
117+
print("Select Start: " + str(input_source_id))
118+
119+
120+
func _webxr_on_select_end(input_source_id: int) -> void:
121+
print("Select End: " + str(input_source_id))
122+
123+
124+
func _webxr_on_squeeze(input_source_id: int) -> void:
125+
print("Squeeze: " + str(input_source_id))
126+
127+
128+
func _webxr_on_squeeze_start(input_source_id: int) -> void:
129+
print("Squeeze Start: " + str(input_source_id))
130+
131+
132+
func _webxr_on_squeeze_end(input_source_id: int) -> void:
133+
print("Squeeze End: " + str(input_source_id))

xr/webxr/main.tscn

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[gd_scene load_steps=7 format=3 uid="uid://dismxfxe7wvdn"]
2+
3+
[ext_resource type="Script" path="res://main.gd" id="1_ig7tw"]
4+
5+
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_lins3"]
6+
sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
7+
ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
8+
9+
[sub_resource type="Sky" id="Sky_wiqav"]
10+
sky_material = SubResource("ProceduralSkyMaterial_lins3")
11+
12+
[sub_resource type="Environment" id="Environment_6ff2h"]
13+
background_mode = 2
14+
sky = SubResource("Sky_wiqav")
15+
tonemap_mode = 2
16+
17+
[sub_resource type="BoxMesh" id="BoxMesh_gv5m4"]
18+
size = Vector3(0.1, 0.1, 0.1)
19+
20+
[sub_resource type="BoxMesh" id="BoxMesh_f3sb7"]
21+
size = Vector3(0.1, 0.1, 0.1)
22+
23+
[node name="Main" type="Node3D"]
24+
script = ExtResource("1_ig7tw")
25+
26+
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
27+
environment = SubResource("Environment_6ff2h")
28+
29+
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
30+
transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0)
31+
shadow_enabled = true
32+
33+
[node name="XROrigin3D" type="XROrigin3D" parent="."]
34+
35+
[node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"]
36+
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7, 0)
37+
38+
[node name="LeftController" type="XRController3D" parent="XROrigin3D"]
39+
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 1, 0)
40+
tracker = &"left_hand"
41+
42+
[node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/LeftController"]
43+
mesh = SubResource("BoxMesh_gv5m4")
44+
45+
[node name="RightController" type="XRController3D" parent="XROrigin3D"]
46+
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 1, 0)
47+
tracker = &"right_hand"
48+
49+
[node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/RightController"]
50+
mesh = SubResource("BoxMesh_f3sb7")
51+
52+
[node name="CanvasLayer" type="CanvasLayer" parent="."]
53+
54+
[node name="EnterVRButton" type="Button" parent="CanvasLayer"]
55+
anchors_preset = 8
56+
anchor_left = 0.5
57+
anchor_top = 0.5
58+
anchor_right = 0.5
59+
anchor_bottom = 0.5
60+
offset_left = -50.0
61+
offset_top = -25.0
62+
offset_right = 50.0
63+
offset_bottom = 25.0
64+
grow_horizontal = 2
65+
grow_vertical = 2
66+
text = "Enter VR"

xr/webxr/project.godot

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; Engine configuration file.
2+
; It's best edited using the editor UI and not directly,
3+
; since the parameters that go here are not all obvious.
4+
;
5+
; Format:
6+
; [section] ; section goes between []
7+
; param=value ; assign values to parameters
8+
9+
config_version=5
10+
11+
[application]
12+
13+
config/name="WebXR demo"
14+
run/main_scene="res://main.tscn"
15+
config/features=PackedStringArray("4.3", "GL Compatibility")
16+
config/icon="res://icon.svg"
17+
18+
[physics]
19+
20+
common/enable_object_picking=false
21+
22+
[rendering]
23+
24+
renderer/rendering_method="gl_compatibility"
25+
renderer/rendering_method.mobile="gl_compatibility"
26+
textures/vram_compression/import_etc2_astc=true
27+
28+
[xr]
29+
30+
shaders/enabled=true

0 commit comments

Comments
 (0)