This commit is contained in:
sharpoff
2026-01-14 13:01:37 +09:00
commit 49d7e22132
660 changed files with 41243 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
@tool
extends Node3D
## XR Tools Loading Screen
##
## The loading screen is shown while the player is waiting
## while we load in a new scene.
## As the player may start in any location and likely hasn't
## put their HMD on yet when the game first starts, we place
## our splash screen far away and make it over sized.
##
## Note that we made this a tool script so you can test the
## progress bar. We show this at a different distance to create
## a nice depth effect.
##
## Note also that our background is pitch black.
## User pressed the continue
signal continue_pressed
## If true, the screen follows the camera
@export var follow_camera : bool = true: set = set_follow_camera
## Curve for following the camera
@export var follow_speed : Curve
## Splash screen texture
@export var splash_screen : Texture2D: set = set_splash_screen
## Progress bar
@export_range(0.0, 1.0, 0.01) var progress : float = 0.5: set = set_progress_bar
## If true, the contine message is shown, if false the progress bar is visible.
@export var enable_press_to_continue : bool = false: set = set_enable_press_to_continue
# Camera to track
var _camera : XRCamera3D
# Splash screen material
var _splash_screen_material : StandardMaterial3D
# Progress material
var _progress_material : ShaderMaterial
func _ready():
# Get materials
_splash_screen_material = $SplashScreen.get_surface_override_material(0)
_progress_material = $ProgressBar.mesh.surface_get_material(0)
# Perform initial update
_update_splash_screen()
_update_progress_bar()
_update_enable_press_to_continue()
_update_follow_camera()
func _process(delta):
# Skip if in editor
if Engine.is_editor_hint():
return
# Skip if no camera to track
if !_camera:
return
# Get the camera direction (horizontal only)
var camera_dir := _camera.global_transform.basis.z
camera_dir.y = 0.0
camera_dir = camera_dir.normalized()
# Get the loading screen direction
var loading_screen_dir := global_transform.basis.z
# Get the angle
var angle := loading_screen_dir.signed_angle_to(camera_dir, Vector3.UP)
if angle == 0:
return
# Do rotation based on the curve
global_transform.basis = global_transform.basis.rotated(
Vector3.UP * sign(angle),
follow_speed.sample_baked(abs(angle) / PI) * delta
).orthonormalized()
## Set the camera to track
func set_camera(p_camera : XRCamera3D) -> void:
_camera = p_camera
_update_follow_camera()
## Set the follow_camera property
func set_follow_camera(p_enabled : bool) -> void:
follow_camera = p_enabled
_update_follow_camera()
## Set the splash_screen texture property
func set_splash_screen(p_splash_screen : Texture2D) -> void:
splash_screen = p_splash_screen
_update_splash_screen()
## Set the progress property
func set_progress_bar(p_progress : float) -> void:
progress = p_progress
_update_progress_bar()
## Set the enable_press_to_continue property
func set_enable_press_to_continue(p_enable : bool) -> void:
enable_press_to_continue = p_enable
_update_enable_press_to_continue()
func _update_follow_camera():
if _camera and !Engine.is_editor_hint():
set_process(follow_camera)
func _update_splash_screen():
if _splash_screen_material:
_splash_screen_material.albedo_texture = splash_screen
func _update_progress_bar():
if _progress_material:
_progress_material.set_shader_parameter("progress", progress)
func _update_enable_press_to_continue():
if is_inside_tree():
$ProgressBar.visible = !enable_press_to_continue
$PressToContinue.visible = enable_press_to_continue
$PressToContinue/HoldButton.enabled = enable_press_to_continue
func _on_HoldButton_pressed():
emit_signal("continue_pressed")

View File

@@ -0,0 +1 @@
uid://bhi5ee6eerov0

View File

@@ -0,0 +1,65 @@
[gd_scene load_steps=13 format=3 uid="uid://bqumugyvkct4r"]
[ext_resource type="Script" uid="uid://bhi5ee6eerov0" path="res://addons/godot-xr-tools/staging/loading_screen.gd" id="2"]
[ext_resource type="Shader" uid="uid://4i0pwdtfmtsv" path="res://addons/godot-xr-tools/staging/loading_screen/loading_screen_shader.tres" id="3"]
[ext_resource type="Texture2D" uid="uid://clbtsf0ahb3fm" path="res://addons/godot-xr-tools/assets/misc/progress_bar.png" id="4"]
[ext_resource type="Texture2D" uid="uid://ocyj01x5mtt7" path="res://addons/godot-xr-tools/assets/misc/Hold trigger to continue.png" id="5"]
[ext_resource type="PackedScene" uid="uid://cymteydkxagpp" path="res://addons/godot-xr-tools/misc/hold_button.tscn" id="6"]
[sub_resource type="Curve" id="21"]
_limits = [0.0, 3.14, 0.0, 1.0]
_data = [Vector2(0.00207039, 0), 0.0, 1.00884, 0, 0, Vector2(1, 3.14), 0.313348, 0.0, 0, 0]
point_count = 2
[sub_resource type="PlaneMesh" id="2"]
size = Vector2(80, 60)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1o2lp"]
transparency = 1
shading_mode = 0
[sub_resource type="ShaderMaterial" id="18"]
render_priority = 0
shader = ExtResource("3")
shader_parameter/bar_color = Color(1, 1, 1, 1)
shader_parameter/bar_texture = ExtResource("4")
shader_parameter/cutout = Vector3(4.85, 0.33, 0)
shader_parameter/progress = 0.5
[sub_resource type="PlaneMesh" id="3"]
material = SubResource("18")
size = Vector2(10, 1)
[sub_resource type="QuadMesh" id="19"]
size = Vector2(9.25, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_k8dyo"]
transparency = 1
shading_mode = 0
albedo_texture = ExtResource("5")
[node name="LoadingScreen" type="Node3D"]
script = ExtResource("2")
follow_speed = SubResource("21")
[node name="SplashScreen" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 15, -50)
mesh = SubResource("2")
surface_material_override/0 = SubResource("StandardMaterial3D_1o2lp")
[node name="ProgressBar" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, -10)
mesh = SubResource("3")
[node name="PressToContinue" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 0, -10)
visible = false
mesh = SubResource("19")
surface_material_override/0 = SubResource("StandardMaterial3D_k8dyo")
[node name="HoldButton" parent="PressToContinue" instance=ExtResource("6")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.55766, 0, 0)
hold_time = 0.5
color = Color(0, 0.717647, 1, 1)
[connection signal="pressed" from="PressToContinue/HoldButton" to="." method="_on_HoldButton_pressed"]

View File

@@ -0,0 +1,186 @@
[gd_resource type="VisualShader" load_steps=16 format=3 uid="uid://4i0pwdtfmtsv"]
[sub_resource type="VisualShaderNodeCompare" id="5"]
output_port_for_preview = 0
default_input_values = [0, Vector2(0, 0), 1, Vector2(0, 0), 2, 1e-05]
type = 3
function = 4
condition = 1
[sub_resource type="VisualShaderNodeFloatParameter" id="6"]
parameter_name = "progress"
[sub_resource type="VisualShaderNodeVectorCompose" id="7"]
[sub_resource type="VisualShaderNodeFloatOp" id="8"]
output_port_for_preview = 0
operator = 6
[sub_resource type="VisualShaderNodeColorParameter" id="9"]
parameter_name = "bar_color"
[sub_resource type="VisualShaderNodeTexture2DParameter" id="17"]
parameter_name = "bar_texture"
texture_type = 1
[sub_resource type="VisualShaderNodeVectorOp" id="18"]
operator = 2
[sub_resource type="VisualShaderNodeFloatOp" id="19"]
operator = 2
[sub_resource type="VisualShaderNodeTexture" id="VisualShaderNodeTexture_hpnli"]
output_port_for_preview = 0
expanded_output_ports = [0]
source = 5
texture_type = 1
[sub_resource type="VisualShaderNodeVec3Parameter" id="VisualShaderNodeVec3Parameter_fhc2j"]
parameter_name = "cutout"
default_value_enabled = true
default_value = Vector3(4.85, 0.33, 0)
[sub_resource type="VisualShaderNodeInput" id="10"]
input_name = "uv"
[sub_resource type="VisualShaderNodeVectorOp" id="11"]
default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(10, 1, 0)]
operator = 2
[sub_resource type="VisualShaderNodeVectorOp" id="12"]
default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(5, 0.5, 0)]
operator = 1
[sub_resource type="VisualShaderNodeVectorFunc" id="13"]
function = 4
[sub_resource type="VisualShaderNodeCompare" id="15"]
output_port_for_preview = 0
default_input_values = [0, Vector2(0, 0), 1, Vector2(4.85, 0.33), 2, 1e-05]
type = 3
function = 2
condition = 1
[resource]
code = "shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx, unshaded;
uniform vec4 bar_color : source_color;
uniform sampler2D bar_texture : source_color;
uniform vec3 cutout = vec3(4.850000, 0.330000, 0.000000);
uniform float progress;
void fragment() {
// ColorParameter:15
vec4 n_out15p0 = bar_color;
vec4 n_out19p0;
// Texture2D:19
n_out19p0 = texture(bar_texture, UV);
float n_out19p4 = n_out19p0.a;
// VectorOp:17
vec3 n_out17p0 = vec3(n_out15p0.xyz) * vec3(n_out19p0.xyz);
// Input:3
vec2 n_out3p0 = UV;
// VectorOp:4
vec3 n_in4p1 = vec3(10.00000, 1.00000, 0.00000);
vec3 n_out4p0 = vec3(n_out3p0, 0.0) * n_in4p1;
// VectorOp:5
vec3 n_in5p1 = vec3(5.00000, 0.50000, 0.00000);
vec3 n_out5p0 = n_out4p0 - n_in5p1;
// VectorFunc:6
vec3 n_out6p0 = abs(n_out5p0);
// Vector3Parameter:20
vec3 n_out20p0 = cutout;
bool n_out8p0;
// Compare:8
{
bvec2 _bv = greaterThan(vec2(n_out6p0.xy), vec2(n_out20p0.xy));
n_out8p0 = any(_bv);
}
// FloatParameter:12
float n_out12p0 = progress;
// VectorCompose:13
float n_in13p1 = 0.00000;
float n_in13p2 = 0.00000;
vec3 n_out13p0 = vec3(n_out12p0, n_in13p1, n_in13p2);
bool n_out11p0;
// Compare:11
{
bvec2 _bv = lessThan(n_out3p0, vec2(n_out13p0.xy));
n_out11p0 = any(_bv);
}
// FloatOp:14
float n_out14p0 = max((n_out8p0 ? 1.0 : 0.0), (n_out11p0 ? 1.0 : 0.0));
// FloatOp:18
float n_out18p0 = n_out19p4 * n_out14p0;
// Output:0
ALBEDO = n_out17p0;
ALPHA = n_out18p0;
}
"
graph_offset = Vector2(-744.269, 307.118)
flags/unshaded = true
nodes/fragment/0/position = Vector2(1220, 440)
nodes/fragment/3/node = SubResource("10")
nodes/fragment/3/position = Vector2(-1020, 580)
nodes/fragment/4/node = SubResource("11")
nodes/fragment/4/position = Vector2(-620, 400)
nodes/fragment/5/node = SubResource("12")
nodes/fragment/5/position = Vector2(-440, 400)
nodes/fragment/6/node = SubResource("13")
nodes/fragment/6/position = Vector2(-240, 420)
nodes/fragment/8/node = SubResource("15")
nodes/fragment/8/position = Vector2(240, 460)
nodes/fragment/11/node = SubResource("5")
nodes/fragment/11/position = Vector2(240, 860)
nodes/fragment/12/node = SubResource("6")
nodes/fragment/12/position = Vector2(-640, 980)
nodes/fragment/13/node = SubResource("7")
nodes/fragment/13/position = Vector2(-280, 960)
nodes/fragment/14/node = SubResource("8")
nodes/fragment/14/position = Vector2(480, 680)
nodes/fragment/15/node = SubResource("9")
nodes/fragment/15/position = Vector2(580, -40)
nodes/fragment/16/node = SubResource("17")
nodes/fragment/16/position = Vector2(40, -100)
nodes/fragment/17/node = SubResource("18")
nodes/fragment/17/position = Vector2(980, 80)
nodes/fragment/18/node = SubResource("19")
nodes/fragment/18/position = Vector2(940, 500)
nodes/fragment/19/node = SubResource("VisualShaderNodeTexture_hpnli")
nodes/fragment/19/position = Vector2(480, 260)
nodes/fragment/20/node = SubResource("VisualShaderNodeVec3Parameter_fhc2j")
nodes/fragment/20/position = Vector2(-280, 600)
nodes/fragment/connections = PackedInt32Array(3, 0, 4, 0, 4, 0, 5, 0, 5, 0, 6, 0, 6, 0, 8, 0, 3, 0, 11, 0, 13, 0, 11, 1, 12, 0, 13, 0, 8, 0, 14, 0, 11, 0, 14, 1, 15, 0, 17, 0, 17, 0, 0, 0, 18, 0, 0, 1, 14, 0, 18, 1, 16, 0, 19, 2, 19, 4, 18, 0, 19, 0, 17, 1, 20, 0, 8, 1)

View File

@@ -0,0 +1,187 @@
[gd_resource type="VisualShader" load_steps=16 format=3]
[sub_resource type="VisualShaderNodeCompare" id="5"]
output_port_for_preview = 0
default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(0, 0, 0), 2, 1e-05]
type = 3
function = 4
condition = 1
[sub_resource type="VisualShaderNodeFloatParameter" id="6"]
parameter_name = "progress"
[sub_resource type="VisualShaderNodeVectorCompose" id="7"]
[sub_resource type="VisualShaderNodeFloatOp" id="8"]
output_port_for_preview = 0
operator = 6
[sub_resource type="VisualShaderNodeColorParameter" id="9"]
parameter_name = "bar_color"
[sub_resource type="VisualShaderNodeTexture2DParameter" id="17"]
parameter_name = "bar_texture"
texture_type = 1
[sub_resource type="VisualShaderNodeVectorOp" id="18"]
operator = 2
[sub_resource type="VisualShaderNodeFloatOp" id="19"]
operator = 2
[sub_resource type="VisualShaderNodeTexture" id="VisualShaderNodeTexture_hpnli"]
output_port_for_preview = 0
expanded_output_ports = [0]
source = 5
texture_type = 1
[sub_resource type="VisualShaderNodeVec3Parameter" id="VisualShaderNodeVec3Parameter_fhc2j"]
parameter_name = "cutout"
default_value_enabled = true
default_value = Vector3(4.85, 0.33, 0)
[sub_resource type="VisualShaderNodeInput" id="10"]
input_name = "uv"
[sub_resource type="VisualShaderNodeVectorOp" id="11"]
default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(10, 1, 0)]
operator = 2
[sub_resource type="VisualShaderNodeVectorOp" id="12"]
default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(5, 0.5, 0)]
operator = 1
[sub_resource type="VisualShaderNodeVectorFunc" id="13"]
function = 4
[sub_resource type="VisualShaderNodeCompare" id="15"]
output_port_for_preview = 0
default_input_values = [0, Vector3(0, 0, 0), 1, Vector3(4.85, 0.33, 0), 2, 1e-05]
type = 3
function = 2
condition = 1
[resource]
code = "shader_type spatial;
render_mode unshaded;
uniform vec4 bar_color : source_color;
uniform sampler2D bar_texture : source_color;
uniform vec3 cutout = vec3(4.850000, 0.330000, 0.000000);
uniform float progress;
void fragment() {
// ColorParameter:15
vec4 n_out15p0 = bar_color;
vec4 n_out19p0;
// Texture2D:19
n_out19p0 = texture(bar_texture, UV);
float n_out19p4 = n_out19p0.a;
// VectorOp:17
vec3 n_out17p0 = vec3(n_out15p0.xyz) * vec3(n_out19p0.xyz);
// Input:3
vec2 n_out3p0 = UV;
// VectorOp:4
vec3 n_in4p1 = vec3(10.00000, 1.00000, 0.00000);
vec3 n_out4p0 = vec3(n_out3p0, 0.0) * n_in4p1;
// VectorOp:5
vec3 n_in5p1 = vec3(5.00000, 0.50000, 0.00000);
vec3 n_out5p0 = n_out4p0 - n_in5p1;
// VectorFunc:6
vec3 n_out6p0 = abs(n_out5p0);
// Vector3Parameter:20
vec3 n_out20p0 = cutout;
bool n_out8p0;
// Compare:8
{
bvec3 _bv = greaterThan(n_out6p0, n_out20p0);
n_out8p0 = any(_bv);
}
// FloatParameter:12
float n_out12p0 = progress;
// VectorCompose:13
float n_in13p1 = 0.00000;
float n_in13p2 = 0.00000;
vec3 n_out13p0 = vec3(n_out12p0, n_in13p1, n_in13p2);
bool n_out11p0;
// Compare:11
{
bvec3 _bv = lessThan(vec3(n_out3p0, 0.0), n_out13p0);
n_out11p0 = any(_bv);
}
// FloatOp:14
float n_out14p0 = max((n_out8p0 ? 1.0 : 0.0), (n_out11p0 ? 1.0 : 0.0));
// FloatOp:18
float n_out18p0 = n_out19p4 * n_out14p0;
// Output:0
ALBEDO = n_out17p0;
ALPHA = n_out18p0;
}
"
graph_offset = Vector2(-744.269, 307.118)
flags/unshaded = true
nodes/fragment/0/position = Vector2(1220, 440)
nodes/fragment/3/node = SubResource("10")
nodes/fragment/3/position = Vector2(-1020, 580)
nodes/fragment/4/node = SubResource("11")
nodes/fragment/4/position = Vector2(-620, 400)
nodes/fragment/5/node = SubResource("12")
nodes/fragment/5/position = Vector2(-440, 400)
nodes/fragment/6/node = SubResource("13")
nodes/fragment/6/position = Vector2(-240, 420)
nodes/fragment/8/node = SubResource("15")
nodes/fragment/8/position = Vector2(240, 460)
nodes/fragment/11/node = SubResource("5")
nodes/fragment/11/position = Vector2(240, 860)
nodes/fragment/12/node = SubResource("6")
nodes/fragment/12/position = Vector2(-640, 980)
nodes/fragment/13/node = SubResource("7")
nodes/fragment/13/position = Vector2(-280, 960)
nodes/fragment/14/node = SubResource("8")
nodes/fragment/14/position = Vector2(480, 680)
nodes/fragment/15/node = SubResource("9")
nodes/fragment/15/position = Vector2(580, -40)
nodes/fragment/16/node = SubResource("17")
nodes/fragment/16/position = Vector2(40, -100)
nodes/fragment/17/node = SubResource("18")
nodes/fragment/17/position = Vector2(980, 80)
nodes/fragment/18/node = SubResource("19")
nodes/fragment/18/position = Vector2(940, 500)
nodes/fragment/19/node = SubResource("VisualShaderNodeTexture_hpnli")
nodes/fragment/19/position = Vector2(480, 260)
nodes/fragment/20/node = SubResource("VisualShaderNodeVec3Parameter_fhc2j")
nodes/fragment/20/position = Vector2(-280, 600)
nodes/fragment/connections = PackedInt32Array(3, 0, 4, 0, 4, 0, 5, 0, 5, 0, 6, 0, 6, 0, 8, 0, 3, 0, 11, 0, 13, 0, 11, 1, 12, 0, 13, 0, 8, 0, 14, 0, 11, 0, 14, 1, 15, 0, 17, 0, 17, 0, 0, 0, 18, 0, 0, 1, 14, 0, 18, 1, 16, 0, 19, 2, 19, 4, 18, 0, 19, 0, 17, 1, 20, 0, 8, 1)

View File

@@ -0,0 +1,212 @@
@tool
class_name XRToolsSceneBase
extends Node3D
## XR Tools Scene Base Class
##
## This is our base scene for all our levels. It ensures that we have all bits
## in place to load our scene into our staging scene.
##
## Developers can customize scene transitions by extending from this class and
## overriding the [method scene_loaded] behavior.
## This signal is used to request the staging transition to the main-menu
## scene. Developers should use [method exit_to_main_menu] rather than
## emitting this signal directly.
signal request_exit_to_main_menu
## This signal is used to request the staging transition to the specified
## scene. Developers should use [method load_scene] rather than emitting
## this signal directly.
##
## The [param user_data] parameter is passed through staging to the new scenes.
signal request_load_scene(p_scene_path, user_data)
## This signal is used to request the staging reload this scene. Developers
## should use [method reset_scene] rather than emitting this signal directly.
##
## The [param user_data] parameter is passed through staging to the new scenes.
signal request_reset_scene(user_data)
## This signal is used to request the staging quit the XR experience. Developers
## should use [method quit] rather than emitting this signal directly.
signal request_quit
# This file contains methods with parameters that are unused, however they are
# documented and intended to be overridden in derived classes. As such unused
# parameter warnings need to be disabled.
#
# warning-ignore:unused_parameter
# gdlint:disable=unused-argument
## Interface
func _ready() -> void:
pass
# Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsSceneBase"
## This method center the player on the [param p_transform] transform.
func center_player_on(p_transform : Transform3D):
# In order to center our player so the players feet are at the location
# indicated by p_transform, and having our player looking in the required
# direction, we must offset this transform using the cameras transform.
# So we get our current camera transform in local space
var camera_transform = $XROrigin3D/XRCamera3D.transform
# We obtain our view direction and zero out our height
var view_direction = camera_transform.basis.z
view_direction.y = 0
# Now create the transform that we will use to offset our input with
var transform : Transform3D
transform = transform.looking_at(-view_direction, Vector3.UP)
transform.origin = camera_transform.origin
transform.origin.y = 0
# And now update our origin point
$XROrigin3D.global_transform = (p_transform * transform.inverse()).orthonormalized()
# If we have a player body, we need to set its starting position too.
var player_body : XRToolsPlayerBody = XRToolsPlayerBody.find_instance($XROrigin3D)
if player_body:
player_body.global_transform = p_transform
## This method is called when the scene is loaded, but before it becomes visible.
##
## The [param user_data] parameter is an optional parameter passed in when the
## scene is loaded - usually from the previous scene. By default the
## user_data can be a [String] spawn-point node-name, [Vector3], [Transform3D],
## an object with a 'get_spawn_position' method, or null to spawn at the scenes
## [XROrigin3D] location.
##
## Advanced scene-transition functionality can be implemented by overriding this
## method and calling the super() with any desired spawn transform. This could
## come from a field of an advanced user_data class-object, or from a game-state
## singleton.
func scene_loaded(user_data = null):
# Called after scene is loaded
# Make sure our camera becomes the current camera
$XROrigin3D/XRCamera3D.current = true
$XROrigin3D.current = true
# Start by assuming the user_data contains spawn position information.
var spawn_position = user_data
# If the user_data is an object with a 'get_spawn_position' method then
# call it (with this [XRToolsSceneBase] allowing it to inspect the scene
# if necessary) and use the return value as the spawn position information.
if typeof(user_data) == TYPE_OBJECT and user_data.has_method("get_spawn_position"):
spawn_position = user_data.get_spawn_position(self)
# Get the spawn [Transform3D] by inspecting the spawn position value for
# standard types of spawn position information:
# - null to use the standard XROrigin3D location
# - String name of a Node3D to spawn at
# - Vector3 to spawn at
# - Transform3D to spawn at
var spawn_transform : Transform3D = $XROrigin3D.global_transform
match typeof(spawn_position):
TYPE_STRING: # Name of Node3D to spawn at
var node = find_child(spawn_position)
if node is Node3D:
spawn_transform = node.global_transform
TYPE_VECTOR3: # Vector3 to spawn at (rotation comes from XROrigin3D)
spawn_transform.origin = spawn_position
TYPE_TRANSFORM3D: # Transform3D spawn location
spawn_transform = spawn_position
# Center the player on the spawn location
center_player_on(spawn_transform)
## This method is called when the scene becomes fully visible to the user.
##
## The [param user_data] parameter is an optional parameter passed in when the
## scene is loaded - usually from the previous scene.
func scene_visible(user_data = null):
# Called after the scene becomes fully visible
pass
## This method is called before the start of transition from this scene to a
## new scene.
##
## The [param user_data] parameter is an optional parameter passed in when the
## scene transition is requested.
func scene_pre_exiting(user_data = null):
# Called before we start fading out and removing our scene
pass
## This method is called immediately before this scene is unloaded.
##
##
## The [param user_data] parameter is an optional parameter passed in when the
## scene transition is requested.
func scene_exiting(user_data = null):
# called right before we remove this scene
pass
## Transition to the main menu scene
##
## This function is used to transition to the main menu scene. The default
## implementation sends the [signal request_exit_to_main_menu].
##
## Custom scene classes can override this function to add their logic, but
## should usually call this super method.
func exit_to_main_menu() -> void:
emit_signal("request_exit_to_main_menu")
## This function is used to transition to the specified scene. The default
## implementation sends the [signal request_load_scene].
##
## Custom scene classes can override this function to add their logic, but
## should usually call this super method.
##
## The [param user_data] parameter is passed to the new scene, and can be used
## to relay information through the transition. The default behavior of
## [method scene_loaded] will attempt to interpret it as a spawn-point for the
## player as node-name, Vector3, or Transform3D.
##
## See [method scene_loaded] for options to provide advanced scene-transition
## functionality.
func load_scene(p_scene_path : String, user_data = null) -> void:
emit_signal("request_load_scene", p_scene_path, user_data)
## This function is used to reset the current scene. The default
## implementation sends the [signal request_reset_scene] which triggers
## a reload of the current scene.
##
## Custom scene classes can override this method to implement faster reset
## logic than is performed by the brute-force scene-reload performed by
## staging.
##
## Any [param user_data] provided is passed into the new scene.
func reset_scene(user_data = null) -> void:
emit_signal("request_reset_scene", user_data)
## This function is used to quit the XR experience. The default
## implementation sends the [signal request_quit] which triggers
## the XR experience to end.
##
## Custom scene classes can override this method to add their logic.
func quit() -> void:
emit_signal("request_quit")

View File

@@ -0,0 +1 @@
uid://177gygw2xqqu

View File

@@ -0,0 +1,22 @@
[gd_scene load_steps=2 format=3 uid="uid://qbmx03iibuuu"]
[ext_resource type="Script" uid="uid://177gygw2xqqu" path="res://addons/godot-xr-tools/staging/scene_base.gd" id="1"]
[node name="SceneBase" type="Node3D"]
script = ExtResource("1")
[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)
far = 300.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 = &"aim"
[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 = &"aim"

View File

@@ -0,0 +1,309 @@
@tool
class_name XRToolsStaging
extends Node3D
## XR Tools Staging Class
##
## When creating a game with multiple levels where you want to
## make use of background loading and have some nice structure
## in place, the Staging scene can be used as a base to handle
## all the startup and scene switching code.
## Just inherit this scene, set it up and make the resulting
## scene your startup scene.
##
## As different XR runtimes need slightly different setups you'll
## need to add the appropriate ARVROrigin setup to your scene.
## When using the OpenXR plugin this is as simple as adding the
## FPController script as a child node.
##
## Furthermore this scene has our loading screen and an anchor
## point into which we load the actual scene we wish the user
## to interact with. You can configure the first scene to load
## and kick off your game by setting the Main Scene property.
##
## If you are creating a game with a single level you may wish to
## simplify things. Check out the demo included in the source
## repository for the OpenXR plugin and then use the techniques
## explained in individual demos found here.
## This signal is emitted when the current scene starts to be unloaded. The
## [param scene] parameter is the path of the current scene, and the
## [param user_data] parameter is the optional data passed from the
## current scene to the next.
signal scene_exiting(scene, user_data)
## This signal is emitted when the old scene has been unloaded and the user
## is fading into the loading scene. The [param user_data] parameter is the
## optional data provided by the old scene.
signal switching_to_loading_scene(user_data)
## This signal is emitted when the new scene has been loaded before it becomes
## visible. The [param scene] parameter is the path of the new scene, and the
## [param user_data] parameter is the optional data passed from the old scene
## to the new scene.
signal scene_loaded(scene, user_data)
## This signal is emitted when the new scene has become fully visible to the
## player. The [param scene] parameter is the path of the new scene, and the
## [param user_data] parameter is the optional data passed from the old scene
## to the new scene.
signal scene_visible(scene, user_data)
## This signal is invoked when the XR experience starts.
signal xr_started
## This signal is invoked when the XR experience ends. This usually occurs when
## the player removes the headset. The game may want to react by pausing until
## the player puts the headset back on and the [signal xr_started] signal is
## emitted.
signal xr_ended
## Main scene file
@export_file('*.tscn') var main_scene : String
## If true, the player is prompted to continue
@export var prompt_for_continue : bool = true
## The current scene
var current_scene : XRToolsSceneBase
## The current scene path
var current_scene_path : String
# Tween for fading
var _tween : Tween
## The [XROrigin3D] node used while staging
@onready var xr_origin : XROrigin3D = XRHelpers.get_xr_origin(self)
## The [XRCamera3D] node used while staging
@onready var xr_camera : XRCamera3D = XRHelpers.get_xr_camera(self)
func _ready():
# Do not initialise if in the editor
if Engine.is_editor_hint():
return
# Specify the camera to track
if xr_camera:
$LoadingScreen.set_camera(xr_camera)
# We start by loading our main level scene
load_scene(main_scene)
# Verifies our staging has a valid configuration.
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
# Report missing XR Origin
var test_origin : XROrigin3D = XRHelpers.get_xr_origin(self)
if !test_origin:
warnings.append("No XROrigin3D node found, please add one")
# Report missing XR Camera
var test_camera : XRCamera3D = XRHelpers.get_xr_camera(self)
if !test_camera:
warnings.append("No XRCamera3D node found, please add one to your XROrigin3D node")
# Report main scene not specified
if main_scene == "":
warnings.append("No main scene selected")
# Report main scene invalid
if !FileAccess.file_exists(main_scene):
warnings.append("Main scene doesn't exist")
# Return warnings
return warnings
# Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsStaging"
## This function loads the [param p_scene_path] scene file.
##
## The [param user_data] parameter contains optional data passed from the old
## scene to the new scene.
##
## See [method XRToolsSceneBase.scene_loaded] for details on how to implement
## advanced scene-switching.
func load_scene(p_scene_path : String, user_data = null) -> void:
# Do not load if in the editor
if Engine.is_editor_hint():
return
if !xr_origin:
return
if !xr_camera:
return
# Start the threaded loading of the scene. If the scene is already cached
# then this will finish immediately with THREAD_LOAD_LOADED
ResourceLoader.load_threaded_request(p_scene_path)
# If a current scene is visible then fade it out and unload it.
if current_scene:
# Report pre-exiting and remove the scene signals
current_scene.scene_pre_exiting(user_data)
_remove_signals(current_scene)
# Fade to black
if _tween:
_tween.kill()
_tween = get_tree().create_tween()
_tween.tween_method(set_fade, 0.0, 1.0, 1.0)
await _tween.finished
# Now we remove our scene
emit_signal("scene_exiting", current_scene, user_data)
current_scene.scene_exiting(user_data)
$Scene.remove_child(current_scene)
current_scene.queue_free()
current_scene = null
# If a continue-prompt is desired or the new scene has not finished
# loading, then switch to the loading screen.
if prompt_for_continue or \
ResourceLoader.load_threaded_get_status(p_scene_path) != ResourceLoader.THREAD_LOAD_LOADED:
# Make our loading screen visible again and reset some stuff
xr_origin.set_process_internal(true)
xr_origin.current = true
xr_camera.current = true
$LoadingScreen.progress = 0.0
$LoadingScreen.enable_press_to_continue = false
$LoadingScreen.follow_camera = true
$LoadingScreen.visible = true
switching_to_loading_scene.emit(user_data)
# Fade to visible
if _tween:
_tween.kill()
_tween = get_tree().create_tween()
_tween.tween_method(set_fade, 1.0, 0.0, 1.0)
await _tween.finished
# If the loading screen is visible then show the progress and optionally
# wait for the continue. Once done fade out the loading screen.
if $LoadingScreen.visible:
# Loop waiting for the scene to load
var res : ResourceLoader.ThreadLoadStatus
while true:
var progress := []
res = ResourceLoader.load_threaded_get_status(p_scene_path, progress)
if res != ResourceLoader.THREAD_LOAD_IN_PROGRESS:
break
$LoadingScreen.progress = progress[0]
await get_tree().create_timer(0.1).timeout
# Handle load error
if res != ResourceLoader.THREAD_LOAD_LOADED:
# Report the error to the log and console
push_error("Error ", res, " loading resource ", p_scene_path)
# Halt if running in the debugger
# gdlint:ignore=expression-not-assigned
breakpoint
# Terminate with a non-zero error code to indicate failure
get_tree().quit(1)
# Wait for user to be ready
if prompt_for_continue:
$LoadingScreen.enable_press_to_continue = true
await $LoadingScreen.continue_pressed
# Fade to black
if _tween:
_tween.kill()
_tween = get_tree().create_tween()
_tween.tween_method(set_fade, 0.0, 1.0, 1.0)
await _tween.finished
# Hide our loading screen
$LoadingScreen.follow_camera = false
$LoadingScreen.visible = false
xr_origin.set_process_internal(false)
# Get the loaded scene
var new_scene : PackedScene = ResourceLoader.load_threaded_get(p_scene_path)
# Setup our new scene
current_scene = new_scene.instantiate()
current_scene_path = p_scene_path
$Scene.add_child(current_scene)
_add_signals(current_scene)
# We create a small delay here to give tracking some time to update our nodes...
await get_tree().create_timer(0.1).timeout
current_scene.scene_loaded(user_data)
scene_loaded.emit(current_scene, user_data)
# Fade to visible
if _tween:
_tween.kill()
_tween = get_tree().create_tween()
_tween.tween_method(set_fade, 1.0, 0.0, 1.0)
await _tween.finished
# Report new scene visible
current_scene.scene_visible(user_data)
scene_visible.emit(current_scene, user_data)
## This method sets the fade-alpha for scene transitions. The [param p_value]
## parameter must be in the range [0.0 - 1.0].
##
## Our fade object allows us to black out the screen for transitions.
## Note that our AABB is set to HUGE so it should always be rendered
## unless hidden.
func set_fade(p_value : float):
XRToolsFade.set_fade("staging", Color(0, 0, 0, p_value))
func _add_signals(p_scene : XRToolsSceneBase):
p_scene.connect("request_exit_to_main_menu", _on_exit_to_main_menu)
p_scene.connect("request_load_scene", _on_load_scene)
p_scene.connect("request_reset_scene", _on_reset_scene)
p_scene.connect("request_quit", _on_quit)
func _remove_signals(p_scene : XRToolsSceneBase):
p_scene.disconnect("request_exit_to_main_menu", _on_exit_to_main_menu)
p_scene.disconnect("request_load_scene", _on_load_scene)
p_scene.disconnect("request_reset_scene", _on_reset_scene)
p_scene.disconnect("request_quit", _on_quit)
func _on_exit_to_main_menu():
load_scene(main_scene)
func _on_load_scene(p_scene_path : String, user_data):
load_scene(p_scene_path, user_data)
func _on_reset_scene(user_data):
load_scene(current_scene_path, user_data)
func _on_quit():
$StartXR.end_xr()
func _on_StartXR_xr_started():
emit_signal("xr_started")
func _on_StartXR_xr_ended():
emit_signal("xr_ended")

View File

@@ -0,0 +1 @@
uid://ralokdfu8o3n

View File

@@ -0,0 +1,38 @@
[gd_scene load_steps=7 format=3 uid="uid://bnqnnnet4dw12"]
[ext_resource type="Script" uid="uid://ralokdfu8o3n" path="res://addons/godot-xr-tools/staging/staging.gd" id="1"]
[ext_resource type="PackedScene" uid="uid://bqumugyvkct4r" path="res://addons/godot-xr-tools/staging/loading_screen.tscn" id="2"]
[ext_resource type="PackedScene" uid="uid://wtpox7m5vu2b" path="res://addons/godot-xr-tools/effects/fade.tscn" id="2_htugy"]
[ext_resource type="Environment" uid="uid://ckiwtcdsam7ed" path="res://addons/godot-xr-tools/staging/staging_env.tres" id="3_40x3a"]
[ext_resource type="PackedScene" uid="uid://btknduttnmoxf" path="res://addons/godot-xr-tools/misc/vr_common_shader_cache.tscn" id="5"]
[ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="6_balvx"]
[node name="Staging" type="Node3D"]
script = ExtResource("1")
[node name="Fade" parent="." instance=ExtResource("2_htugy")]
[node name="XROrigin3D" type="XROrigin3D" parent="."]
[node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"]
environment = ExtResource("3_40x3a")
[node name="VRCommonShaderCache" parent="XROrigin3D/XRCamera3D" instance=ExtResource("5")]
[node name="LeftHandController" type="XRController3D" parent="XROrigin3D"]
tracker = &"left_hand"
pose = &"aim"
[node name="RightHandController" type="XRController3D" parent="XROrigin3D"]
tracker = &"right_hand"
pose = &"aim"
[node name="LoadingScreen" parent="." instance=ExtResource("2")]
progress = 0.0
[node name="Scene" type="Node3D" parent="."]
[node name="StartXR" parent="." instance=ExtResource("6_balvx")]
[connection signal="xr_ended" from="StartXR" to="." method="_on_StartXR_xr_ended"]
[connection signal="xr_started" from="StartXR" to="." method="_on_StartXR_xr_started"]

View File

@@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=3 uid="uid://ckiwtcdsam7ed"]
[sub_resource type="Sky" id="1"]
[resource]
background_mode = 1
sky = SubResource("1")