Gonna need to try using compute shaders for foam because this isn't working
@ -6,10 +6,10 @@
|
|||||||
render_priority = 0
|
render_priority = 0
|
||||||
shader = ExtResource("1_ln7ti")
|
shader = ExtResource("1_ln7ti")
|
||||||
shader_parameter/color = Vector3(0.1, 0.4, 0.8)
|
shader_parameter/color = Vector3(0.1, 0.4, 0.8)
|
||||||
shader_parameter/speed = 1.0
|
shader_parameter/speed = 1.545
|
||||||
shader_parameter/amplitude = 0.2
|
shader_parameter/amplitude = 0.2
|
||||||
shader_parameter/frequency = 1.0
|
shader_parameter/frequency = 1.29
|
||||||
shader_parameter/waveCount = 16
|
shader_parameter/waveCount = 20
|
||||||
shader_parameter/amplitudeScaleFactor = 0.82
|
shader_parameter/amplitudeScaleFactor = 0.77
|
||||||
shader_parameter/frequencyScaleFactor = 1.18
|
shader_parameter/frequencyScaleFactor = 1.225
|
||||||
shader_parameter/dirs = PackedFloat32Array(0.981, -0.588, 0.035, -0.977, -0.809, -0.351, 0.989, -0.24, -0.192, 0.907, -0.479, 0.499, 0.388, -0.97, 0.882, -0.845, 0.246, -0.068, -0.41, -0.202, -0.914, 0.605, -0.618, 0.898, -0.615, -0.508, -0.223, -0.841, 0.396, -0.609, 0.746, 0.129, 0.724, 0.54, -0.936, 0.589, 0.874, -0.715, -0.353, 0.243)
|
shader_parameter/dirs = PackedFloat32Array(0.981, -0.588, 0.035, -0.977, -0.809, -0.351, 0.989, -0.24, -0.192, 0.907, -0.479, 0.499, 0.388, -0.97, 0.882, -0.845, 0.246, -0.068, -0.41, -0.202, -0.914, 0.605, -0.618, 0.898, -0.615, -0.508, -0.223, -0.841, 0.396, -0.609, 0.746, 0.129, 0.724, 0.54, -0.936, 0.589, 0.874, -0.715, -0.353, 0.243)
|
||||||
|
@ -1,25 +1,7 @@
|
|||||||
[gd_resource type="ShaderMaterial" load_steps=4 format=3 uid="uid://doo0lbnv6560s"]
|
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://doo0lbnv6560s"]
|
||||||
|
|
||||||
[ext_resource type="Shader" uid="uid://cs1j12437apag" path="res://Shaders/SinWater.gdshader" id="1_qkwc8"]
|
[ext_resource type="Shader" uid="uid://cs1j12437apag" path="res://Shaders/SinWater.gdshader" id="1_qkwc8"]
|
||||||
|
|
||||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_qwrmt"]
|
|
||||||
frequency = 0.0126
|
|
||||||
fractal_type = 2
|
|
||||||
|
|
||||||
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_lju3n"]
|
|
||||||
seamless = true
|
|
||||||
noise = SubResource("FastNoiseLite_qwrmt")
|
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
render_priority = 0
|
render_priority = 0
|
||||||
shader = ExtResource("1_qkwc8")
|
shader = ExtResource("1_qkwc8")
|
||||||
shader_parameter/color = Vector3(0.05, 0.2, 0.4)
|
|
||||||
shader_parameter/foamHeight = 0.58
|
|
||||||
shader_parameter/speed = 1.355
|
|
||||||
shader_parameter/foamTex = SubResource("NoiseTexture2D_lju3n")
|
|
||||||
shader_parameter/amplitude = 0.085
|
|
||||||
shader_parameter/frequency = 1.0
|
|
||||||
shader_parameter/waveCount = 20
|
|
||||||
shader_parameter/amplitudeScaleFactor = 0.835
|
|
||||||
shader_parameter/frequencyScaleFactor = 1.145
|
|
||||||
shader_parameter/dirs = PackedFloat32Array(0.981, -0.588, 0.035, -0.977, -0.809, -0.351, 0.989, -0.24, -0.192, 0.907, -0.479, 0.499, 0.388, -0.97, 0.882, -0.845, 0.246, -0.068, -0.41, -0.202, -0.914, 0.605, -0.618, 0.898, -0.615, -0.508, -0.223, -0.841, 0.396, -0.609, 0.746, 0.129, 0.724, 0.54, -0.936, 0.589, 0.874, -0.715, -0.353, 0.243)
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://b6hynk4kyhnqt"]
|
[gd_scene load_steps=6 format=3 uid="uid://b6hynk4kyhnqt"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://13xeofn8faqe" path="res://Scripts/CopyTexture.gd" id="1_8ia8i"]
|
[ext_resource type="Script" uid="uid://fmurci638c43" path="res://Scripts/CopyTexture.gd" id="1_8ia8i"]
|
||||||
[ext_resource type="Shader" uid="uid://w33608b0vm7s" path="res://Shaders/SinFoam.gdshader" id="1_tfkfi"]
|
[ext_resource type="Shader" uid="uid://w33608b0vm7s" path="res://Shaders/SinFoam.gdshader" id="1_tfkfi"]
|
||||||
|
[ext_resource type="Material" uid="uid://bv06fr71bp0mr" path="res://Materials/SinFoam.tres" id="2_3m5rl"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bwsw7c6fvii6d" path="res://icon.svg" id="3_to1x8"]
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_3m5rl"]
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_3m5rl"]
|
||||||
resource_local_to_scene = true
|
resource_local_to_scene = true
|
||||||
@ -17,9 +19,12 @@ shader_parameter/amplitudeScaleFactor = 0.835
|
|||||||
shader_parameter/frequencyScaleFactor = 1.145
|
shader_parameter/frequencyScaleFactor = 1.145
|
||||||
shader_parameter/dirs = PackedFloat32Array(0.981, -0.588, 0.035, -0.977, -0.809, -0.351, 0.989, -0.24, -0.192, 0.907, -0.479, 0.499, 0.388, -0.97, 0.882, -0.845, 0.246, -0.068, -0.41, -0.202, -0.914, 0.605, -0.618, 0.898, -0.615, -0.508, -0.223, -0.841, 0.396, -0.609, 0.746, 0.129, 0.724, 0.54, -0.936, 0.589, 0.874, -0.715, -0.353, 0.243)
|
shader_parameter/dirs = PackedFloat32Array(0.981, -0.588, 0.035, -0.977, -0.809, -0.351, 0.989, -0.24, -0.192, 0.907, -0.479, 0.499, 0.388, -0.97, 0.882, -0.845, 0.246, -0.068, -0.41, -0.202, -0.914, 0.605, -0.618, 0.898, -0.615, -0.508, -0.223, -0.841, 0.396, -0.609, 0.746, 0.129, 0.724, 0.54, -0.936, 0.589, 0.874, -0.715, -0.353, 0.243)
|
||||||
|
|
||||||
[node name="FoamTex" type="SubViewport"]
|
[node name="FoamTex" type="SubViewport" node_paths=PackedStringArray("shaderObject")]
|
||||||
render_target_clear_mode = 1
|
render_target_clear_mode = 1
|
||||||
script = ExtResource("1_8ia8i")
|
script = ExtResource("1_8ia8i")
|
||||||
|
shaderObject = NodePath("ColorRect")
|
||||||
|
shader = ExtResource("2_3m5rl")
|
||||||
|
tex = ExtResource("3_to1x8")
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="."]
|
[node name="ColorRect" type="ColorRect" parent="."]
|
||||||
material = SubResource("ShaderMaterial_3m5rl")
|
material = SubResource("ShaderMaterial_3m5rl")
|
||||||
@ -28,7 +33,3 @@ anchor_right = 1.0
|
|||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
|
||||||
offset_right = 40.0
|
|
||||||
offset_bottom = 40.0
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
extends SubViewport
|
extends SubViewport
|
||||||
|
|
||||||
@onready var textureRect: TextureRect = $TextureRect
|
@export var shaderObject: ColorRect
|
||||||
@onready var shader: ShaderMaterial
|
@export var shader: ShaderMaterial
|
||||||
|
|
||||||
|
@export var tex: Texture2D
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
shader = $ColorRect.material
|
var result: Error = ResourceSaver.save(ImageTexture.create_from_image(tex.get_image()), "iconImage")
|
||||||
|
print(result)
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
textureRect.texture = get_texture()
|
await RenderingServer.frame_post_draw
|
||||||
shader.set_shader_parameter("tex", textureRect.texture)
|
var img: Image = get_texture().get_image()
|
||||||
|
tex = ImageTexture.create_from_image(img)
|
||||||
|
shader.set_shader_parameter("tex", tex)
|
||||||
|
@ -1 +1 @@
|
|||||||
uid://13xeofn8faqe
|
uid://fmurci638c43
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
uniform float foamDecayRate = 0.2;
|
uniform float foamDecayRate = 1.0;
|
||||||
uniform float foamGrowth = 0.5;
|
uniform float foamGrowth = 0.05;
|
||||||
uniform float foamHeight = 0.2;
|
uniform float foamHeight = 0.2;
|
||||||
uniform float speed = 1.0;
|
uniform float speed = 1.0;
|
||||||
uniform sampler2D foamTex;
|
uniform sampler2D foamTex;
|
||||||
@ -11,12 +11,13 @@ uniform int waveCount = 5;
|
|||||||
uniform float amplitudeScaleFactor = 0.82;
|
uniform float amplitudeScaleFactor = 0.82;
|
||||||
uniform float frequencyScaleFactor = 1.18;
|
uniform float frequencyScaleFactor = 1.18;
|
||||||
uniform float dirs[40];
|
uniform float dirs[40];
|
||||||
|
//uniform sampler2D tex: hint_screen_texture;
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
//global uniform float DELTA;
|
//global uniform float DELTA;
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
//float brightness = texture(tex, UV).r - (foamDecayRate * DELTA);
|
//float brightness = texture(tex, UV).r - (foamDecayRate * DELTA);
|
||||||
float brightness = texture(tex, UV).r * foamDecayRate;
|
float brightness = clamp(texture(tex, UV).r - foamDecayRate, 0.0, 1.0);
|
||||||
float amp = 1.0;
|
float amp = 1.0;
|
||||||
float freq = 1.0;
|
float freq = 1.0;
|
||||||
float offset = 0.0;
|
float offset = 0.0;
|
||||||
@ -30,4 +31,5 @@ void fragment() {
|
|||||||
//if (offset >= foamHeight * 1.8) brightness += foamGrowth * DELTA;
|
//if (offset >= foamHeight * 1.8) brightness += foamGrowth * DELTA;
|
||||||
if (offset >= foamHeight * 1.8) brightness += foamGrowth;
|
if (offset >= foamHeight * 1.8) brightness += foamGrowth;
|
||||||
COLOR = vec4(brightness, brightness, brightness, 1.0);
|
COLOR = vec4(brightness, brightness, brightness, 1.0);
|
||||||
|
COLOR = texture(tex, UV);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
Contributors
|
|
||||||
============
|
|
||||||
|
|
||||||
The main author of this project is [Bastiaan Olij](https://github.com/BastiaanOlij) who manages the source repository found at:
|
|
||||||
https://github.com/GodotVR/godot-xr-tools
|
|
||||||
|
|
||||||
Other people who have helped out by submitting fixes, enhancements, etc are:
|
|
||||||
- [Florian Jung](https://github.com/Windfisch)
|
|
||||||
- [RMKD](https://github.com/RMKD)
|
|
||||||
- [Alessandro Schillaci](https://github.com/silverslade)
|
|
||||||
- [jtank4](https://github.com/jtank4)
|
|
||||||
- [Malcolm Nixon](https://github.com/malcolmnixon)
|
|
||||||
- [Sam Sarette](https://github.com/lunarcloud)
|
|
||||||
- [Henodude](https://github.com/Henodude)
|
|
||||||
- [Miodrag Sejic](https://github.com/DigitalN8m4r3)
|
|
||||||
- [Carlos Padial](https://github.com/surreal6)
|
|
||||||
- [Julian Todd](https://github.com/goatchurchprime)
|
|
||||||
- [Kai Tödter](https://github.com/toedter)
|
|
||||||
- [Sam Sarette](https://github.com/lunarcloud)
|
|
||||||
|
|
||||||
Want to be on this list? We would love your help.
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018-2023 Bastiaan Olij and Contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,212 +0,0 @@
|
|||||||
# 4.3.3
|
|
||||||
- Fix Viewport2Din3D property forwarding
|
|
||||||
|
|
||||||
# 4.3.2
|
|
||||||
- Move fade logic into effect
|
|
||||||
- Added collision fade support
|
|
||||||
- Added fix for slowly sliding on slopes
|
|
||||||
- Added fix for ground-control preventing jumping over objects
|
|
||||||
- Added property forwarding for Viewport2Din3D
|
|
||||||
- Added fix for open/close poses
|
|
||||||
- Added rumble manager for haptic feedback
|
|
||||||
- Fix unreliable wall-walking collision
|
|
||||||
|
|
||||||
# 4.3.1
|
|
||||||
- Fix saving project when using plugin-tools to set physics layers or enable OpenXR
|
|
||||||
- Fix updating the editor-preview hand-pose
|
|
||||||
- Fix jumping on slopes
|
|
||||||
- Fix material warnings by converting binary .material files to .tres files
|
|
||||||
- Fix staging to use threaded loading while starting the fade
|
|
||||||
- Fix broken world-grab script
|
|
||||||
|
|
||||||
# 4.3.0
|
|
||||||
- Upgraded project to Godot 4.1 as the new minimum version.
|
|
||||||
- Added reporting of stage load errors.
|
|
||||||
- Blend player height changes and prevent the player from standing up under a low ceiling.
|
|
||||||
- **minor-breakage** Added support for swapping held items between hands.
|
|
||||||
- Added jog-in-place movement provider.
|
|
||||||
- Added support for grappling on GridMap instances
|
|
||||||
- **breakage** Added support for two-handed grabbing.
|
|
||||||
- Added support for snapping hands to grab-points.
|
|
||||||
- Added support for world-grab movement.
|
|
||||||
- Fixed editor errors when using hand physics bones.
|
|
||||||
- Added support for climbable grab-points.
|
|
||||||
- Added control of keyboard or gamepad inputs to Viewport2Din3D instances.
|
|
||||||
|
|
||||||
# 4.2.1
|
|
||||||
- Fixed snap-zones showing highlight when disabled.
|
|
||||||
- Fixed pickup leaving target highlighted after picking up.
|
|
||||||
- Fixed collision hands getting stuck too far from the real hands.
|
|
||||||
|
|
||||||
# 4.2.0
|
|
||||||
- Environments can now be set normally in scenes loaded through the staging system.
|
|
||||||
- Fixed issue with not being able to push rigid bodies when colliding with them.
|
|
||||||
- Fixed player movement on slopes.
|
|
||||||
- Fixed lag in finger-poke.
|
|
||||||
- Added initial collision hand support.
|
|
||||||
- Added support for custom materials for 2D in 3D viewport
|
|
||||||
- Updated pointer to support visibility properties and events
|
|
||||||
- Modified virtual keyboard to expose viewport controls and default to unshaded
|
|
||||||
- Cleaned up teleport and added more properties for customization
|
|
||||||
- Modified pickup highlighting to support pickables in snap-zones
|
|
||||||
- Added "UI Objects" layer 23 for viewports to support interaction by pointer and poking
|
|
||||||
- Fixed player scaling issues with crouching and poke
|
|
||||||
- **minor-breakage** Added support for passing user data between staged scenes with default handling for spawn-points
|
|
||||||
- Moved teleport logic to player and added teleport area node
|
|
||||||
- Change pointer event dispatching
|
|
||||||
- Added multi-touch on 2D in 3D viewports and virtual-keyboard
|
|
||||||
- Added option to disable laser-pointers when close to specific bodies/areas
|
|
||||||
|
|
||||||
# 4.1.0
|
|
||||||
- Enhanced grappling to support collision and target layers
|
|
||||||
- Added Godot Editor XR Tools menu for layers and openxr configuration
|
|
||||||
- Improved gliding to support roll-turning while flapping
|
|
||||||
- Added render_target_size_multiplier to StartXR (requires Godot 4.1+)
|
|
||||||
|
|
||||||
# 4.0.0
|
|
||||||
- Conversion to Godot 4
|
|
||||||
- Fixed footstep resource leak and added jump sounds and footstep signal
|
|
||||||
- Added grab-point switching to pickable objects
|
|
||||||
- Added return-to-snap-zone feature
|
|
||||||
|
|
||||||
# 3.4.0
|
|
||||||
- Fixed footstep resource leak and added jump sounds and footstep signal
|
|
||||||
- Added grab-point switching to pickable objects
|
|
||||||
- Added return-to-snap-zone feature
|
|
||||||
|
|
||||||
# 3.3.0
|
|
||||||
- Added reset-scene and scene-control functions to scene-base
|
|
||||||
- Fixed snap-zones stealing objects picked out of other near-by snap-zones
|
|
||||||
- Improved player body so it can be used to child objects to
|
|
||||||
- Updated scene/script default physics layers to match recommendations on website
|
|
||||||
|
|
||||||
# 3.2.0
|
|
||||||
- Minimum supported Godot version set to 3.5
|
|
||||||
- Added glide option for turning with arm-roll
|
|
||||||
- Added physics gravity effects on the player so they can walk around a planet
|
|
||||||
- Added wall-walking movement provider
|
|
||||||
- Cleaned the code to pass gdlint code checks
|
|
||||||
- Modified to work with both WebXR and OpenXR
|
|
||||||
- Added enable property to pickable objects
|
|
||||||
- Added support for snap-on-drop to snap-zones
|
|
||||||
- Added glide options for gaining altitude when flapping arms
|
|
||||||
- Added option to disable snap-turn repeating by setting the delay to 0
|
|
||||||
- Added capability for pointer function to auto-switch between controllers
|
|
||||||
|
|
||||||
# 3.1.0
|
|
||||||
- Improvements to our 2D in 3D viewport for filtering, unshaded, and transparency options
|
|
||||||
- Fixed editor preview system for our 2D in 3D viewport
|
|
||||||
- Use value based grip input with threshold
|
|
||||||
- Improved pointer demo supporting left hand with switching
|
|
||||||
- Enhanced pointer laser visibility options for colliding with targets
|
|
||||||
- Implement poke feature (finger interaction)
|
|
||||||
- Improvements to snap turning
|
|
||||||
- Moved staging solution into plugin so it can be re-used
|
|
||||||
- Allow setting different animations for hands
|
|
||||||
- Added enable/disable to snap-zones
|
|
||||||
- Added XR settings as Godot editor plugin and the ability to load and save the settings
|
|
||||||
- Added crouching movement provider
|
|
||||||
- Modified climbing to use the hand which most recently grabbed the climbing object
|
|
||||||
- Added enable/disable to pickup function
|
|
||||||
- Added ability to override hand material
|
|
||||||
- Added realistic hand models and textures
|
|
||||||
- Added ability to override hand animations
|
|
||||||
- Added additional search functions to find nodes
|
|
||||||
- Added support for viewport 2D in 3D to support 2D scenes instanced in the tree
|
|
||||||
- Added sprinting movement provider
|
|
||||||
- Added support for setting hand-poses when the hand enters an area
|
|
||||||
- Added support for setting grab-points on objects, and the grab-points supporting different hand-poses
|
|
||||||
|
|
||||||
# 3.0.0
|
|
||||||
- Included demo project with test scenes to evaluate features
|
|
||||||
- Standardized class naming convention for all scripts to "XRTools<PascalCaseName>"
|
|
||||||
- Standardized file naming convention to "snake_case_name.ext"
|
|
||||||
- Added many explicit type specifiers in preparation for GDScript 2.0
|
|
||||||
- Renamed some functions to avoid name-collisions with Godot 4.0
|
|
||||||
|
|
||||||
# 2.6.0
|
|
||||||
- Fixed enforcement of direct-movement maximum speed
|
|
||||||
- Added editor icons for all nodes
|
|
||||||
- Added collision bouncing to PlayerBody
|
|
||||||
|
|
||||||
# 2.5.0
|
|
||||||
- Added advanced player height control
|
|
||||||
- Modified climbing to collapse player to a sphere to allow mounting climbed objects
|
|
||||||
- Added crouch movement provider
|
|
||||||
- Added example fall damage detection
|
|
||||||
- Added moving platform support to player body
|
|
||||||
- Fixed player height-clamping to work in player-units
|
|
||||||
- Fixed glide T-pose detection to work in player-units
|
|
||||||
- Fixed jump detection to work in player-units
|
|
||||||
- Added valid-layer checking to teleport movement
|
|
||||||
- Modified hand meshes (blend and glb) to be scaled, so the hand scenes can be 1:1 scaled
|
|
||||||
- Modified hands to scale with world_scale (required for godot-openxr 1.3.0 and later)
|
|
||||||
- Added physics hands with PhysicsBody bones
|
|
||||||
- Fixed disabling of `_process` in XRToolsPickable script
|
|
||||||
|
|
||||||
# 2.4.1
|
|
||||||
- Fixed grab distance
|
|
||||||
- Fixed snap-zone instance drop and free issue
|
|
||||||
- Movement provides react properly when disabled
|
|
||||||
- Hiding grapple target when disabled
|
|
||||||
|
|
||||||
# 2.4.0
|
|
||||||
- Added configuration setting for head height in player body.
|
|
||||||
- Added Function_JumpDetect_movement to detect jumping via the players body and/or arms
|
|
||||||
- Improved responsiveness of snap-turning
|
|
||||||
- Moved flight logic from Function_Direct_movement to Function_Flight_movement
|
|
||||||
- Added option to disable player sliding on slopes
|
|
||||||
- Added support for remote grabbing
|
|
||||||
- Moved turning logic from Function_Direct_movement to Function_Turn_movement
|
|
||||||
- Fixed movement provider servicing so disabled/bypassed providers can report their finished events
|
|
||||||
- Added grappling movement provider
|
|
||||||
- Added snap-zones
|
|
||||||
|
|
||||||
# 2.3.0
|
|
||||||
- Added vignette
|
|
||||||
- Moved player physics into new PlayerBody asset (breaking change)
|
|
||||||
- Moved Function_Direct_movement settings for player physics into PlayerBody
|
|
||||||
- Added Function_Glide_movement to allow the player to glide
|
|
||||||
- Added Function_Jump_movement to allow the player to jump
|
|
||||||
- Added Function_Climb_movement to allow the player to climb
|
|
||||||
- Redid the setup of the hands to make it easier to extend to other gestures
|
|
||||||
- Improved pickup and throwing logic
|
|
||||||
|
|
||||||
# 2.2
|
|
||||||
- Changed default physics layers to make more sense (minor breaking change)
|
|
||||||
- Replaced Center On Node property with PickupCenter node you can place
|
|
||||||
- Made Object_pickable script work by itself and registers as class `XRToolsPickable`
|
|
||||||
- New Object_interactable convenience script that registers as class `XRToolsInteractable` that reacts to our pointer function
|
|
||||||
- Removed ducktype switch from pointer, pointer will use signals over ducktyping automatically (minor breaking change)
|
|
||||||
|
|
||||||
# 2.1
|
|
||||||
- added option to highlight object that can be picked up
|
|
||||||
- added option to snap object to given location (if reset transform is true)
|
|
||||||
- added callback when shader cache has finished
|
|
||||||
- using proper UI for layers
|
|
||||||
- added hand controllers that react on trigger and grip input
|
|
||||||
- fixed delta on move and slide (breaking change!)
|
|
||||||
- letting go of an object now adds angular velocity
|
|
||||||
|
|
||||||
# 2.0
|
|
||||||
- Renamed add on to **godot-xr-tools**
|
|
||||||
- Add enums to our export variables
|
|
||||||
- Add a switch on pickable objects to keep their current positioning when picked up
|
|
||||||
- Move direct movement player collision slightly backwards based on player radius
|
|
||||||
- Added switch between step turning and smooth turning
|
|
||||||
- Fixed sizing issue with teleport
|
|
||||||
- Added option to change pickup range
|
|
||||||
|
|
||||||
# 1.2
|
|
||||||
- Assign button to teleport function and no longer need to set origin
|
|
||||||
- Added pickable object support
|
|
||||||
- Fixed positioning of direct movement collision shape
|
|
||||||
- Added strafe and fly mode for directional
|
|
||||||
- Added ability to enable/disable the movement functions
|
|
||||||
- Added 2D in 3D viewport for UI
|
|
||||||
- Improved throwing by assigning linear velocity
|
|
||||||
|
|
||||||
# 1.1*
|
|
||||||
- previous versions were not tracked
|
|
||||||
|
|
||||||
* Note that version history before 1.2 was not kept and is thus incomplete
|
|
Before Width: | Height: | Size: 14 KiB |
@ -1,35 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://ocyj01x5mtt7"
|
|
||||||
path.s3tc="res://.godot/imported/Hold trigger to continue.png-ce0a3a4de13c262f7015326bad2cb09d.s3tc.ctex"
|
|
||||||
metadata={
|
|
||||||
"imported_formats": ["s3tc_bptc"],
|
|
||||||
"vram_texture": true
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/assets/misc/Hold trigger to continue.png"
|
|
||||||
dest_files=["res://.godot/imported/Hold trigger to continue.png-ce0a3a4de13c262f7015326bad2cb09d.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
|
|
Before Width: | Height: | Size: 698 B |
@ -1,35 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://clbtsf0ahb3fm"
|
|
||||||
path.s3tc="res://.godot/imported/progress_bar.png-2ef3cbffca173889900be004fdeb1700.s3tc.ctex"
|
|
||||||
metadata={
|
|
||||||
"imported_formats": ["s3tc_bptc"],
|
|
||||||
"vram_texture": true
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/assets/misc/progress_bar.png"
|
|
||||||
dest_files=["res://.godot/imported/progress_bar.png-2ef3cbffca173889900be004fdeb1700.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
|
|
@ -1,53 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/audio.svg")
|
|
||||||
class_name XRToolsAreaAudio
|
|
||||||
extends AudioStreamPlayer3D
|
|
||||||
|
|
||||||
|
|
||||||
## XRTools Area Audio
|
|
||||||
##
|
|
||||||
## This node is attached as a child of a Area3D,
|
|
||||||
## since all the interactables are actualy Extensions of the Area3D,
|
|
||||||
## this node will work on those as well
|
|
||||||
|
|
||||||
|
|
||||||
## XRToolsAreaAudioType to associate with this Area Audio
|
|
||||||
@export var area_audio_type : XRToolsAreaAudioType
|
|
||||||
|
|
||||||
@onready var area : Area3D = get_parent()
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsAreaAudio"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready() -> void:
|
|
||||||
# Listen for enter
|
|
||||||
area.body_entered.connect(_on_body_entered)
|
|
||||||
# Listen for exit
|
|
||||||
area.body_exited.connect(_on_body_exited)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_body_entered(_body):
|
|
||||||
if playing:
|
|
||||||
stop()
|
|
||||||
stream = area_audio_type.touch_sound
|
|
||||||
play()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_body_exited(_body):
|
|
||||||
if playing:
|
|
||||||
stop()
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
if !area_audio_type:
|
|
||||||
warnings.append("Area audio type not specified")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://28xyst6bu0wy
|
|
@ -1,8 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://duqehif60vcjg"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://28xyst6bu0wy" path="res://addons/godot-xr-tools/audio/area_audio.gd" id="1_q1jr0"]
|
|
||||||
|
|
||||||
[node name="AreaAudio" type="AudioStreamPlayer3D"]
|
|
||||||
unit_size = 3.0
|
|
||||||
max_distance = 100.0
|
|
||||||
script = ExtResource("1_q1jr0")
|
|
@ -1,28 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/audio.svg")
|
|
||||||
class_name XRToolsAreaAudioType
|
|
||||||
extends Resource
|
|
||||||
|
|
||||||
|
|
||||||
## XRTools Area Audio Type Resource
|
|
||||||
##
|
|
||||||
## This resource defines the audio stream to play when
|
|
||||||
## a objects enters it
|
|
||||||
|
|
||||||
|
|
||||||
## Surface name
|
|
||||||
@export var name : String = ""
|
|
||||||
|
|
||||||
## Optional audio stream to play when the player lands on this surface
|
|
||||||
@export var touch_sound : AudioStream
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
if name == "":
|
|
||||||
warnings.append("Area audio type must have a name")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://d38w1ftal4fs8
|
|
@ -1,79 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/audio.svg")
|
|
||||||
class_name XRToolsPickableAudio
|
|
||||||
extends AudioStreamPlayer3D
|
|
||||||
|
|
||||||
|
|
||||||
## XRTools Pickable Audio
|
|
||||||
##
|
|
||||||
## This node is attached as a child of a Pickable,
|
|
||||||
## it plays audio for drop and hit based on velocity,
|
|
||||||
## along with a audio for when the object is being picked up.
|
|
||||||
|
|
||||||
|
|
||||||
## XRToolsPickableAudioType to associate with this pickable
|
|
||||||
@export var pickable_audio_type : XRToolsPickableAudioType
|
|
||||||
|
|
||||||
## delta throttle is 1/10 of delta
|
|
||||||
@onready var delta_throttle : float = 0.1
|
|
||||||
|
|
||||||
@onready var _pickable : XRToolsPickable = get_parent()
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsPickableAudio"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready() -> void:
|
|
||||||
# Listen for when this object enters a body
|
|
||||||
_pickable.body_entered.connect(_on_body_entered)
|
|
||||||
# Listen for when this object is picked up or dropped
|
|
||||||
_pickable.picked_up.connect(_on_picked_up)
|
|
||||||
_pickable.dropped.connect(_on_dropped)
|
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(_delta):
|
|
||||||
if !_pickable.sleeping:
|
|
||||||
if _pickable.linear_velocity.length() > 5:
|
|
||||||
volume_db = 0
|
|
||||||
else:
|
|
||||||
volume_db -= _pickable.linear_velocity.length() * delta_throttle
|
|
||||||
|
|
||||||
|
|
||||||
# Called when this object is picked up
|
|
||||||
func _on_picked_up(_pickable) -> void:
|
|
||||||
volume_db = 0
|
|
||||||
if playing:
|
|
||||||
stop()
|
|
||||||
stream = pickable_audio_type.grab_sound
|
|
||||||
play()
|
|
||||||
|
|
||||||
|
|
||||||
# Called when this object is dropped
|
|
||||||
func _on_dropped(_pickable) -> void:
|
|
||||||
for body in _pickable.get_colliding_bodies():
|
|
||||||
if playing:
|
|
||||||
stop()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_body_entered(_body):
|
|
||||||
if playing:
|
|
||||||
stop()
|
|
||||||
if _pickable.is_picked_up():
|
|
||||||
stream = pickable_audio_type.hit_sound
|
|
||||||
else:
|
|
||||||
stream = pickable_audio_type.drop_sound
|
|
||||||
play()
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
if !pickable_audio_type:
|
|
||||||
warnings.append("Pickable audio type not specified")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://06682ueq88r3
|
|
@ -1,9 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://bikkxsbo8x7sd"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://06682ueq88r3" path="res://addons/godot-xr-tools/audio/pickable_audio.gd" id="1_cfg1k"]
|
|
||||||
|
|
||||||
[node name="PickableAudio" type="AudioStreamPlayer3D"]
|
|
||||||
unit_size = 3.0
|
|
||||||
max_db = 1.0
|
|
||||||
max_distance = 100.0
|
|
||||||
script = ExtResource("1_cfg1k")
|
|
@ -1,34 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/audio.svg")
|
|
||||||
class_name XRToolsPickableAudioType
|
|
||||||
extends Resource
|
|
||||||
|
|
||||||
|
|
||||||
## XRTools Pickable Audio Type Resource
|
|
||||||
##
|
|
||||||
## This resource defines the audio streams to play when
|
|
||||||
## the pickable is being picked up/ dropped/ hit something while being held
|
|
||||||
|
|
||||||
|
|
||||||
## Surface name
|
|
||||||
@export var name : String = ""
|
|
||||||
|
|
||||||
## Optional audio stream to play when the player picks up the pickable
|
|
||||||
@export var grab_sound : AudioStream
|
|
||||||
|
|
||||||
## Optional audio stream to play when the player drops the pickable
|
|
||||||
@export var drop_sound : AudioStream
|
|
||||||
|
|
||||||
## Optional audio stream to play when the item is beign held by the player
|
|
||||||
@export var hit_sound : AudioStream
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
if name == "":
|
|
||||||
warnings.append("Pickable audio type must have a name")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://b2oab60fned4f
|
|
@ -1,32 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/foot.svg")
|
|
||||||
class_name XRToolsSurfaceAudio
|
|
||||||
extends Node
|
|
||||||
|
|
||||||
|
|
||||||
## XRTools Surface Audio Node
|
|
||||||
##
|
|
||||||
## This node is attached as a child of a StaticObject to give it a surface
|
|
||||||
## audio type. This will cause the XRToolsMovementFootStep to play the correct
|
|
||||||
## foot-step sounds when walking on the object.
|
|
||||||
|
|
||||||
|
|
||||||
## XRToolsSurfaceAudioType to associate with this surface
|
|
||||||
@export var surface_audio_type : XRToolsSurfaceAudioType
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsSurfaceAudio"
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
# Verify the camera
|
|
||||||
if !surface_audio_type:
|
|
||||||
warnings.append("Surface audio type not specified")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://d2qs67hq3o0dh
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://e1eio0jsyrg2"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://d2qs67hq3o0dh" path="res://addons/godot-xr-tools/audio/surface_audio.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="SurfaceAudio" type="Node"]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,41 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/body.svg")
|
|
||||||
class_name XRToolsSurfaceAudioType
|
|
||||||
extends Resource
|
|
||||||
|
|
||||||
|
|
||||||
## XRTools Surface Type Resource
|
|
||||||
##
|
|
||||||
## This resource defines a type of surface, and the audio streams to play when
|
|
||||||
## the user steps on it
|
|
||||||
|
|
||||||
|
|
||||||
## Surface name
|
|
||||||
@export var name : String = ""
|
|
||||||
|
|
||||||
## Optional audio stream to play when the player jumps on this surface
|
|
||||||
@export var jump_sound : AudioStream
|
|
||||||
|
|
||||||
## Optional audio stream to play when the player lands on this surface
|
|
||||||
@export var hit_sound : AudioStream
|
|
||||||
|
|
||||||
## Audio streams to play when the player walks on this surface
|
|
||||||
@export var walk_sounds :Array[AudioStream] = []
|
|
||||||
|
|
||||||
## Walking sound minimum pitch (to randomize steps)
|
|
||||||
@export_range(0.5, 1.0) var walk_pitch_minimum : float = 0.8
|
|
||||||
|
|
||||||
## Walking sound maximum pitch (to randomize steps)
|
|
||||||
@export_range(1.0, 2.0) var walk_pitch_maximum : float = 1.2
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
# Verify the camera
|
|
||||||
if name == "":
|
|
||||||
warnings.append("Surface audio type must have a name")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://diu2m4nunt58r
|
|
@ -1,121 +0,0 @@
|
|||||||
Creative Commons Legal Code
|
|
||||||
|
|
||||||
CC0 1.0 Universal
|
|
||||||
|
|
||||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
|
||||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
|
||||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
|
||||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
|
||||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
|
||||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
|
||||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
|
||||||
HEREUNDER.
|
|
||||||
|
|
||||||
Statement of Purpose
|
|
||||||
|
|
||||||
The laws of most jurisdictions throughout the world automatically confer
|
|
||||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
|
||||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
|
||||||
authorship and/or a database (each, a "Work").
|
|
||||||
|
|
||||||
Certain owners wish to permanently relinquish those rights to a Work for
|
|
||||||
the purpose of contributing to a commons of creative, cultural and
|
|
||||||
scientific works ("Commons") that the public can reliably and without fear
|
|
||||||
of later claims of infringement build upon, modify, incorporate in other
|
|
||||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
|
||||||
and for any purposes, including without limitation commercial purposes.
|
|
||||||
These owners may contribute to the Commons to promote the ideal of a free
|
|
||||||
culture and the further production of creative, cultural and scientific
|
|
||||||
works, or to gain reputation or greater distribution for their Work in
|
|
||||||
part through the use and efforts of others.
|
|
||||||
|
|
||||||
For these and/or other purposes and motivations, and without any
|
|
||||||
expectation of additional consideration or compensation, the person
|
|
||||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
|
||||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
|
||||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
|
||||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
|
||||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
|
||||||
|
|
||||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
|
||||||
protected by copyright and related or neighboring rights ("Copyright and
|
|
||||||
Related Rights"). Copyright and Related Rights include, but are not
|
|
||||||
limited to, the following:
|
|
||||||
|
|
||||||
i. the right to reproduce, adapt, distribute, perform, display,
|
|
||||||
communicate, and translate a Work;
|
|
||||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
|
||||||
iii. publicity and privacy rights pertaining to a person's image or
|
|
||||||
likeness depicted in a Work;
|
|
||||||
iv. rights protecting against unfair competition in regards to a Work,
|
|
||||||
subject to the limitations in paragraph 4(a), below;
|
|
||||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
|
||||||
in a Work;
|
|
||||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
|
||||||
European Parliament and of the Council of 11 March 1996 on the legal
|
|
||||||
protection of databases, and under any national implementation
|
|
||||||
thereof, including any amended or successor version of such
|
|
||||||
directive); and
|
|
||||||
vii. other similar, equivalent or corresponding rights throughout the
|
|
||||||
world based on applicable law or treaty, and any national
|
|
||||||
implementations thereof.
|
|
||||||
|
|
||||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
|
||||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
|
||||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
|
||||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
|
||||||
of action, whether now known or unknown (including existing as well as
|
|
||||||
future claims and causes of action), in the Work (i) in all territories
|
|
||||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
|
||||||
treaty (including future time extensions), (iii) in any current or future
|
|
||||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
|
||||||
including without limitation commercial, advertising or promotional
|
|
||||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
|
||||||
member of the public at large and to the detriment of Affirmer's heirs and
|
|
||||||
successors, fully intending that such Waiver shall not be subject to
|
|
||||||
revocation, rescission, cancellation, termination, or any other legal or
|
|
||||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
|
||||||
as contemplated by Affirmer's express Statement of Purpose.
|
|
||||||
|
|
||||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
|
||||||
be judged legally invalid or ineffective under applicable law, then the
|
|
||||||
Waiver shall be preserved to the maximum extent permitted taking into
|
|
||||||
account Affirmer's express Statement of Purpose. In addition, to the
|
|
||||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
|
||||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
|
||||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
|
||||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
|
||||||
maximum duration provided by applicable law or treaty (including future
|
|
||||||
time extensions), (iii) in any current or future medium and for any number
|
|
||||||
of copies, and (iv) for any purpose whatsoever, including without
|
|
||||||
limitation commercial, advertising or promotional purposes (the
|
|
||||||
"License"). The License shall be deemed effective as of the date CC0 was
|
|
||||||
applied by Affirmer to the Work. Should any part of the License for any
|
|
||||||
reason be judged legally invalid or ineffective under applicable law, such
|
|
||||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
|
||||||
of the License, and in such case Affirmer hereby affirms that he or she
|
|
||||||
will not (i) exercise any of his or her remaining Copyright and Related
|
|
||||||
Rights in the Work or (ii) assert any associated claims and causes of
|
|
||||||
action with respect to the Work, in either case contrary to Affirmer's
|
|
||||||
express Statement of Purpose.
|
|
||||||
|
|
||||||
4. Limitations and Disclaimers.
|
|
||||||
|
|
||||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
|
||||||
surrendered, licensed or otherwise affected by this document.
|
|
||||||
b. Affirmer offers the Work as-is and makes no representations or
|
|
||||||
warranties of any kind concerning the Work, express, implied,
|
|
||||||
statutory or otherwise, including without limitation warranties of
|
|
||||||
title, merchantability, fitness for a particular purpose, non
|
|
||||||
infringement, or the absence of latent or other defects, accuracy, or
|
|
||||||
the present or absence of errors, whether or not discoverable, all to
|
|
||||||
the greatest extent permissible under applicable law.
|
|
||||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
|
||||||
that may apply to the Work or any use thereof, including without
|
|
||||||
limitation any person's Copyright and Related Rights in the Work.
|
|
||||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
|
||||||
consents, permissions or other rights required for any use of the
|
|
||||||
Work.
|
|
||||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
|
||||||
party to this document and has no duty or obligation with respect to
|
|
||||||
this CC0 or use of the Work.
|
|
Before Width: | Height: | Size: 19 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://dhnfbf4p0s74"
|
|
||||||
path="res://.godot/imported/audio.svg-20d7f0b624a1b2ef54f1b4d12970c8d0.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/audio.svg"
|
|
||||||
dest_files=["res://.godot/imported/audio.svg-20d7f0b624a1b2ef54f1b4d12970c8d0.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
|
|
@ -1 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m5.640625.250438c-.75 0-.75 1-.75 1l.0028 1.7433557s-.0028.7623199.7656177.7594124l.7315823-.0027681v.75c-.9987826-.28732-2.3899024-.5553882-3.4887954-1.2601516-.4265336-.1870545-.9793234 1.1200236-.9793234 1.1200236 1.4123763 1.0085676 3.5979104.8866743 3.9681188 1.390128v1.75c-.5 1-1.5 2.75-2 3.5h2.25l1.5-2.75 1.25 2.75c.5 0 1.25 0 2 .000003-.5-1.000003-1.25-2.500006-1.75-3.500003v-1.75c.450387-.5452358 2.25264-.15852 3.39846-.4920078.282315-.5425377.182574-.6845166-.297031-1.5473061-1.253436.698062-1.535576.3459793-3.851429.7893139v-.75h.75c.25 0 .7631105-.245671.760209-.7445791l-.010209-1.7554209c0-.5-.25-1-1.25-1l-2.5.75h2.5v1.25c-.9605198.00114-1.745755-.00241-2.5 0v-1.25l2.5-.75z" fill-opacity=".99608" stroke-width=".762656" transform="translate(.609375 -.000438)"/><g fill="none" stroke="#ff8080" stroke-width="1.5"><path d="m3.7388367 3.4147117c-.9448159 1.3559779-.9992656 2.3269247-.4091983 3.6683904"/><path d="m12.545272 4.8349236c.597942-1.5043684.197251-2.2941014-.8486-3.1240651"/></g></g><g transform="translate(.5 -.000438)"><g fill="#fc7f7f"><path d="m12 11.999519c.55228 0 1 .448243 1 1.000481v1.000483.211639.786437 1.000479.0014h-1v-1c-.202186 0-.336627-.491203-.5-.5-1.452695-.07822-1.5-.503591-1.5-1h1 1v-.500438h-1-1c0-.552238.44772-1.000481 1-1.000481z" stroke-width=".999963"/><path d="m1.999832 12v1c0 .55228.44772 1 1 1-.55228 0-1 .44772-1 1v1h1v-1h1v1h1v-1c0-.55228-.44772-1-1-1 .55228 0 1-.44772 1-1v-1h-1v1h-1v-1z"/><path d="m5.999832 12v1 3h1v-1h1v1h1v-1c-.000834-.17579-.047991-.34825-.13672-.5.088728-.15175.13588-.32421.13672-.5v-1c0-.55228-.44772-1-1-1h-1zm1 1h1v1h-1z"/></g><g fill="#ff8080" stroke="#ff8080" stroke-width=".500002"><path d="m11.721643 12.70092h2"/><path d="m11.694618 13.781362h2"/></g></g></svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://cyg33jxco0rh6"
|
|
||||||
path="res://.godot/imported/body.svg-324e141d452c32f3136ca97c338025b4.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/body.svg"
|
|
||||||
dest_files=["res://.godot/imported/body.svg-324e141d452c32f3136ca97c338025b4.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
|
|
@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
width="16"
|
|
||||||
version="1.1"
|
|
||||||
id="svg40"
|
|
||||||
sodipodi:docname="foot.svg"
|
|
||||||
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs44" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview42"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="22.627417"
|
|
||||||
inkscape:cx="25.654718"
|
|
||||||
inkscape:cy="13.412932"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1051"
|
|
||||||
inkscape:window-x="-9"
|
|
||||||
inkscape:window-y="-9"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg40" />
|
|
||||||
<path
|
|
||||||
style="fill:#fd8080;fill-opacity:1;stroke-width:0.0261891"
|
|
||||||
d="M 10.044819,9.931539 C 9.7799882,9.796189 9.3263332,9.204865 9.1824507,8.8074722 9.1150567,8.6213345 9.1130747,8.5988058 9.1126277,8.0135482 L 9.1121675,7.411198 9.2141197,6.952888 9.3160719,6.4945781 9.3280929,5.3815396 c 0.0083,-0.7685502 0.00101,-1.2913619 -0.02355,-1.6891995 C 9.2520989,2.8428903 9.2589719,1.9354066 9.3196159,1.7019654 9.3950705,1.4115129 9.4953072,1.2147127 9.6248763,1.1026311 l 0.1155004,-0.099912 0.4190263,0.012919 c 0.455795,0.014052 0.654131,0.053122 1.026564,0.2022222 0.54858,0.2196185 1.119659,0.6685668 1.275523,1.0027405 0.173923,0.3728916 0.239826,0.8748577 0.217049,1.6531852 -0.02249,0.7684764 -0.07101,1.0344577 -0.45261,2.4813776 -0.434952,1.6491948 -0.563063,2.0808353 -0.720937,2.4290176 -0.211941,0.4674268 -0.719089,1.0676688 -0.980889,1.1609458 -0.175215,0.06243 -0.339674,0.05776 -0.479284,-0.01359 z"
|
|
||||||
id="path856" />
|
|
||||||
<path
|
|
||||||
style="fill:#fd8080;fill-opacity:1;stroke-width:0.015625"
|
|
||||||
d="M 5.9752428,13.572626 C 5.825332,13.523052 5.6460658,13.38721 5.4790128,13.196601 4.9595621,12.603902 4.7383842,12.122706 4.5220532,11.114631 4.398494,10.538861 4.3604405,10.389768 4.0920785,9.429995 3.8150524,8.4392364 3.7160202,7.6471402 3.7726775,6.875308 3.8288625,6.1099036 3.9164565,5.844507 4.2308909,5.4869814 4.3533561,5.3477332 4.5254668,5.2179453 4.777465,5.0748125 5.38216,4.731351 5.8813362,4.5862455 6.4581809,4.5862455 c 0.231672,0 0.2836625,0.018714 0.4030472,0.1450784 0.1441095,0.1525344 0.1944306,0.2749974 0.2800073,0.6814355 0.036357,0.1726711 0.042378,0.2436393 0.048891,0.5762416 0.00833,0.4254889 -0.010196,0.7501784 -0.066116,1.1586762 -0.044668,0.3262878 -0.052009,0.4580547 -0.06274,1.1260683 -0.010129,0.6305528 0.012227,1.4086795 0.046016,1.6015625 0.032474,0.185381 0.0839,0.338966 0.2124984,0.634623 0.1235691,0.284096 0.1454697,0.352323 0.1871264,0.582953 0.037078,0.205281 0.038925,0.361544 0.0068,0.575496 -0.066421,0.442379 -0.1842582,0.717659 -0.5233293,1.222544 -0.2556581,0.380682 -0.4087131,0.548019 -0.5739267,0.627484 -0.104405,0.05022 -0.1346894,0.05743 -0.2601551,0.06198 -0.078098,0.0028 -0.1595736,-6.61e-4 -0.181058,-0.0078 z"
|
|
||||||
id="path902" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.2 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://bfkcd3fkyahqu"
|
|
||||||
path="res://.godot/imported/foot.svg-9e361563e010aa07be49bfb25fdb6639.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/foot.svg"
|
|
||||||
dest_files=["res://.godot/imported/foot.svg-9e361563e010aa07be49bfb25fdb6639.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
|
|
@ -1 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m12 11.999519c.55228 0 1 .448243 1 1.000481v1.000483.211639.786437 1.000479.0014h-1v-1c-.202186 0-.336627-.491203-.5-.5-1.452695-.07822-1.5-.503591-1.5-1h1 1v-.500438h-1-1c0-.552238.44772-1.000481 1-1.000481z" stroke-width=".999963"/><path d="m1.999832 12v1c0 .55228.44772 1 1 1-.55228 0-1 .44772-1 1v1h1v-1h1v1h1v-1c0-.55228-.44772-1-1-1 .55228 0 1-.44772 1-1v-1h-1v1h-1v-1z"/><path d="m5.999832 12v1 3h1v-1h1v1h1v-1c-.000834-.17579-.047991-.34825-.13672-.5.088728-.15175.13588-.32421.13672-.5v-1c0-.55228-.44772-1-1-1h-1zm1 1h1v1h-1z"/></g><g stroke="#ff8080"><path d="m11.721643 12.70092h2" fill="#ff8080" stroke-width=".500002"/><path d="m11.694618 13.781362h2" fill="#ff8080" stroke-width=".500002"/><circle cx="-5.853771" cy="-6.970115" fill="none" r="3" stroke-width="1.42857" transform="scale(-1)"/><g fill="#ff8080" transform="matrix(1.5998367 0 0 1.4541212 -8.997551 -.227061)"><path d="m11 2.5h4"/><path d="m13 .5v4"/></g></g></svg>
|
|
Before Width: | Height: | Size: 1.0 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://b5vxil50s0ofi"
|
|
||||||
path="res://.godot/imported/function.svg-52c5f936037e0f38a4da2b1e16ae67fe.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/function.svg"
|
|
||||||
dest_files=["res://.godot/imported/function.svg-52c5f936037e0f38a4da2b1e16ae67fe.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
|
|
@ -1 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9.5532719 19.032065c.7515921 0 .8020221-.134249 1.3566411-1.096988 1.187157-1.391415 1.959866-2.115403 1.959866-2.115403.956259-1.186302.899466-1.493116.400204-2.10267-1.836473-.08397-.950366 1.07716-2.399132 1.067713-3.6654836.57138-5.8363537 3.182233-5.8211772 3.07349.2193447-1.571653 5.7194162-3.595593 5.7442212-3.670251.388057-1.167938 1.199163-2.981165 1.47376-3.928695.58339-1.7248573-.838773-1.8000802-1.724674-.5736257l-1.918979 4.8317897c-.0690625-.138458.3155477-4.449876.3998932-5.2980728.1599514-1.608499-2.0933718-1.6944154-2.1769669-.2258024-.0537649.944555-.1115382 6.0881672-.147506 4.8181392-.0302181-1.067011-1.3692069-2.513104-1.5668065-4.2277384-.1944963-1.7989751-2.5014442-1.2032339-1.7843238.6443794.4060542 2.461134.8612438 1.917287 1.3050931 3.81199l-1.5451335-2.033556c-.8083872-1.309984-2.2747721-.419846-1.466385.890138l1.7162803 2.711944c.1090999.892326.3001203 1.520805 1.3544509 2.699388.5134486.832581.8190766.71043 1.5706688.71043z" fill="#fc7f7f" fill-opacity=".99608" stroke-width="1.21993" transform="translate(.267457 -7.900915)"/><g fill="#fc7f7f"><path d="m12 11.999519c.55228 0 1 .448243 1 1.000481v1.000483.211639.786437 1.000479.0014h-1v-1c-.202186 0-.336627-.491203-.5-.5-1.452695-.07822-1.5-.503591-1.5-1h1 1v-.500438h-1-1c0-.552238.44772-1.000481 1-1.000481z" stroke-width=".999963"/><path d="m1.999832 12v1c0 .55228.44772 1 1 1-.55228 0-1 .44772-1 1v1h1v-1h1v1h1v-1c0-.55228-.44772-1-1-1 .55228 0 1-.44772 1-1v-1h-1v1h-1v-1z"/><path d="m5.999832 12v1 3h1v-1h1v1h1v-1c-.000834-.17579-.047991-.34825-.13672-.5.088728-.15175.13588-.32421.13672-.5v-1c0-.55228-.44772-1-1-1h-1zm1 1h1v1h-1z"/></g><g fill="#ff8080" stroke="#ff8080" stroke-width=".500002"><path d="m11.721643 12.70092h2"/><path d="m11.694618 13.781362h2"/></g></svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://beko1qhyybx7e"
|
|
||||||
path="res://.godot/imported/hand.svg-a05486d804ef16320d6cf54e06292b8f.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/hand.svg"
|
|
||||||
dest_files=["res://.godot/imported/hand.svg-a05486d804ef16320d6cf54e06292b8f.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
|
|
@ -1 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke="#ff8080"><path d="m1.4374349 6.2407149c3.327012.02139 9.5648681-.0018 11.4589221-.028063" fill="#ff8080" stroke-width="3"/><g fill="none" stroke-width="2.29484"><path d="m9.324041 2.3242926 4.933526 4.498955"/><path d="m13.437147 5.859164c-1.047728 1.5267675-2.528193 2.753523-3.7347036 4.077989"/></g></g><g transform="translate(.000168)"><g fill="#fc7f7f"><path d="m12 11.999519c.55228 0 1 .448243 1 1.000481v1.000483.211639.786437 1.000479.0014h-1v-1c-.202186 0-.336627-.491203-.5-.5-1.452695-.07822-1.5-.503591-1.5-1h1 1v-.500438h-1-1c0-.552238.44772-1.000481 1-1.000481z" stroke-width=".999963"/><path d="m1.999832 12v1c0 .55228.44772 1 1 1-.55228 0-1 .44772-1 1v1h1v-1h1v1h1v-1c0-.55228-.44772-1-1-1 .55228 0 1-.44772 1-1v-1h-1v1h-1v-1z"/><path d="m5.999832 12v1 3h1v-1h1v1h1v-1c-.000834-.17579-.047991-.34825-.13672-.5.088728-.15175.13588-.32421.13672-.5v-1c0-.55228-.44772-1-1-1h-1zm1 1h1v1h-1z"/></g><g fill="#ff8080" stroke="#ff8080" stroke-width=".500002"><path d="m11.721643 12.70092h2"/><path d="m11.694618 13.781362h2"/></g></g></svg>
|
|
Before Width: | Height: | Size: 1.1 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://04fn15h4x333"
|
|
||||||
path="res://.godot/imported/movement_provider.svg-3c994cf0a3775c20f333be563d69fbf8.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/movement_provider.svg"
|
|
||||||
dest_files=["res://.godot/imported/movement_provider.svg-3c994cf0a3775c20f333be563d69fbf8.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
|
|
@ -1 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m12 11.999519c.55228 0 1 .448243 1 1.000481v1.000483.211639.786437 1.000479.0014h-1v-1c-.202186 0-.336627-.491203-.5-.5-1.452695-.07822-1.5-.503591-1.5-1h1 1v-.500438h-1-1c0-.552238.44772-1.000481 1-1.000481z" stroke-width=".999963"/><path d="m1.999832 12v1c0 .55228.44772 1 1 1-.55228 0-1 .44772-1 1v1h1v-1h1v1h1v-1c0-.55228-.44772-1-1-1 .55228 0 1-.44772 1-1v-1h-1v1h-1v-1z"/><path d="m5.999832 12v1 3h1v-1h1v1h1v-1c-.000834-.17579-.047991-.34825-.13672-.5.088728-.15175.13588-.32421.13672-.5v-1c0-.55228-.44772-1-1-1h-1zm1 1h1v1h-1z"/></g><g stroke="#ff8080"><path d="m11.721643 12.70092h2" fill="#ff8080" stroke-width=".500002"/><path d="m11.694618 13.781362h2" fill="#ff8080" stroke-width=".500002"/><ellipse cx="-8" cy="-5.999998" fill="none" rx="4.285715" ry="4.285713" stroke-width="1.42857" transform="scale(-1)"/></g></svg>
|
|
Before Width: | Height: | Size: 943 B |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://b6gwa6o27pbry"
|
|
||||||
path="res://.godot/imported/node.svg-37d53571b4a4459efefcc791c5402b4f.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/node.svg"
|
|
||||||
dest_files=["res://.godot/imported/node.svg-37d53571b4a4459efefcc791c5402b4f.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
|
|
@ -1 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m12 11.999519c.55228 0 1 .448243 1 1.000481v1.000483.211639.786437 1.000479.0014h-1v-1c-.202186 0-.336627-.491203-.5-.5-1.452695-.07822-1.5-.503591-1.5-1h1 1v-.500438h-1-1c0-.552238.44772-1.000481 1-1.000481z" stroke-width=".999963"/><path d="m1.999832 12v1c0 .55228.44772 1 1 1-.55228 0-1 .44772-1 1v1h1v-1h1v1h1v-1c0-.55228-.44772-1-1-1 .55228 0 1-.44772 1-1v-1h-1v1h-1v-1z"/><path d="m5.999832 12v1 3h1v-1h1v1h1v-1c-.000834-.17579-.047991-.34825-.13672-.5.088728-.15175.13588-.32421.13672-.5v-1c0-.55228-.44772-1-1-1h-1zm1 1h1v1h-1z"/></g><g stroke="#ff8080"><path d="m11.721643 12.70092h2" fill="#ff8080" stroke-width=".500002"/><path d="m11.694618 13.781362h2" fill="#ff8080" stroke-width=".500002"/><path d="m.98844737 8.4010995c1.83521943-3.2693204 4.32032623 3.4590935 5.09543103-.1041898.5122353-2.3548301 1.5512016-4.7636852 1.5512016-7.1393289 0-.44964593.3393333.8692356.4187169 1.3152833.3581573 2.0123893.7090004 4.0047814 1.1634017 6.0086392 1.3138514 5.7939487.1057884.3417346 2.0036324-1.0154361.144766-.1035335.95577 1.0405657 1.098766 1.1092871.938333.4509487 2.146617-.5105464 3.19809-.8075801" fill="none" stroke="#ff8080" stroke-width="1.52176"/></g></svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1,37 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://dsexrsb8t0vi2"
|
|
||||||
path="res://.godot/imported/rumble.svg-104961f6551a931675972887ab17d2bc.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/godot-xr-tools/editor/icons/rumble.svg"
|
|
||||||
dest_files=["res://.godot/imported/rumble.svg-104961f6551a931675972887ab17d2bc.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
|
|
@ -1,79 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsFade
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Fade Script
|
|
||||||
##
|
|
||||||
## This script manages fading the view.
|
|
||||||
|
|
||||||
|
|
||||||
# Dictionary of fade requests
|
|
||||||
var _faders : Dictionary = {}
|
|
||||||
|
|
||||||
# Fade update flag
|
|
||||||
var _update : bool = false
|
|
||||||
|
|
||||||
# Fade mesh
|
|
||||||
var _mesh : MeshInstance3D
|
|
||||||
|
|
||||||
# Fade shader material
|
|
||||||
var _material : ShaderMaterial
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsFade"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the fade node is ready
|
|
||||||
func _ready() -> void:
|
|
||||||
# Add to the fade_mesh group - in the future this should be replaced with
|
|
||||||
# static instances.
|
|
||||||
add_to_group("fade_mesh")
|
|
||||||
|
|
||||||
# Get the mesh and material
|
|
||||||
_mesh = $FadeMesh
|
|
||||||
_material = _mesh.get_surface_override_material(0)
|
|
||||||
|
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
||||||
func _process(_delta : float) -> void:
|
|
||||||
# Skip if nothing to update
|
|
||||||
if not _update:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Calculate the cumulative shade color
|
|
||||||
var fade := Color(1, 1, 1, 0)
|
|
||||||
var show := false
|
|
||||||
for whom in _faders:
|
|
||||||
var color := _faders[whom] as Color
|
|
||||||
fade = fade.blend(color)
|
|
||||||
show = true
|
|
||||||
|
|
||||||
# Set the shader and show if necessary
|
|
||||||
_material.set_shader_parameter("albedo", fade)
|
|
||||||
_mesh.visible = show
|
|
||||||
_update = false
|
|
||||||
|
|
||||||
|
|
||||||
# Set the fade level
|
|
||||||
func set_fade_level(p_whom : Variant, p_color : Color) -> void:
|
|
||||||
# Test if fading is needed
|
|
||||||
if p_color.a > 0:
|
|
||||||
# Set the fade level
|
|
||||||
_faders[p_whom] = p_color
|
|
||||||
_update = true
|
|
||||||
elif _faders.erase(p_whom):
|
|
||||||
# Fade erased
|
|
||||||
_update = true
|
|
||||||
|
|
||||||
|
|
||||||
## Set the fade level on the fade instance
|
|
||||||
static func set_fade(p_whom : Variant, p_color : Color) -> void:
|
|
||||||
# In the future this use of groups should be replaced by static instances.
|
|
||||||
var tree := Engine.get_main_loop() as SceneTree
|
|
||||||
for node in tree.get_nodes_in_group("fade_mesh"):
|
|
||||||
var fade := node as XRToolsFade
|
|
||||||
if fade:
|
|
||||||
fade.set_fade_level(p_whom, p_color)
|
|
@ -1 +0,0 @@
|
|||||||
uid://b5ns7ylgwpge
|
|
@ -1,13 +0,0 @@
|
|||||||
shader_type spatial;
|
|
||||||
render_mode depth_test_disabled, skip_vertex_transform, unshaded, cull_disabled;
|
|
||||||
|
|
||||||
uniform vec4 albedo : source_color;
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
POSITION = vec4(VERTEX.x, -VERTEX.y, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
ALBEDO = albedo.rgb;
|
|
||||||
ALPHA = albedo.a;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
uid://bcxt1xr1pngp5
|
|
@ -1,21 +0,0 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://wtpox7m5vu2b"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://b5ns7ylgwpge" path="res://addons/godot-xr-tools/effects/fade.gd" id="1_6667f"]
|
|
||||||
[ext_resource type="Shader" uid="uid://bcxt1xr1pngp5" path="res://addons/godot-xr-tools/effects/fade.gdshader" id="1_tjen8"]
|
|
||||||
|
|
||||||
[sub_resource type="QuadMesh" id="QuadMesh_aqp6r"]
|
|
||||||
custom_aabb = AABB(-5000, -5000, -5000, 10000, 10000, 10000)
|
|
||||||
size = Vector2(2, 2)
|
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_xjgy8"]
|
|
||||||
render_priority = 50
|
|
||||||
shader = ExtResource("1_tjen8")
|
|
||||||
shader_parameter/albedo = Color(0, 0, 0, 1)
|
|
||||||
|
|
||||||
[node name="Fade" type="Node3D"]
|
|
||||||
script = ExtResource("1_6667f")
|
|
||||||
|
|
||||||
[node name="FadeMesh" type="MeshInstance3D" parent="."]
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("QuadMesh_aqp6r")
|
|
||||||
surface_material_override/0 = SubResource("ShaderMaterial_xjgy8")
|
|
@ -1,195 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsVignette
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
@export var radius : float = 1.0: set = set_radius
|
|
||||||
@export var fade : float = 0.05: set = set_fade
|
|
||||||
@export var steps : int = 32: set = set_steps
|
|
||||||
|
|
||||||
@export var auto_adjust : bool = true: set = set_auto_adjust
|
|
||||||
@export var auto_inner_radius : float = 0.35
|
|
||||||
@export var auto_fade_out_factor : float = 1.5
|
|
||||||
@export var auto_fade_delay : float = 1.0
|
|
||||||
@export var auto_rotation_limit : float = 20.0: set = set_auto_rotation_limit
|
|
||||||
@export var auto_velocity_limit : float = 10.0
|
|
||||||
|
|
||||||
var material : ShaderMaterial = preload("res://addons/godot-xr-tools/effects/vignette.tres")
|
|
||||||
|
|
||||||
var auto_first = true
|
|
||||||
var fade_delay = 0.0
|
|
||||||
var origin_node = null
|
|
||||||
var last_origin_basis : Basis
|
|
||||||
var last_location : Vector3
|
|
||||||
@onready var auto_rotation_limit_rad = deg_to_rad(auto_rotation_limit)
|
|
||||||
|
|
||||||
func set_radius(new_radius : float) -> void:
|
|
||||||
radius = new_radius
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_radius()
|
|
||||||
|
|
||||||
func _update_radius() -> void:
|
|
||||||
if radius < 1.0:
|
|
||||||
if material:
|
|
||||||
material.set_shader_parameter("radius", radius * sqrt(2))
|
|
||||||
$Mesh.visible = true
|
|
||||||
else:
|
|
||||||
$Mesh.visible = false
|
|
||||||
|
|
||||||
func set_fade(new_fade : float) -> void:
|
|
||||||
fade = new_fade
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_fade()
|
|
||||||
|
|
||||||
func _update_fade() -> void:
|
|
||||||
if material:
|
|
||||||
material.set_shader_parameter("fade", fade)
|
|
||||||
|
|
||||||
|
|
||||||
func set_steps(new_steps : int) -> void:
|
|
||||||
steps = new_steps
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_mesh()
|
|
||||||
|
|
||||||
func _update_mesh() -> void:
|
|
||||||
var vertices : PackedVector3Array
|
|
||||||
var indices : PackedInt32Array
|
|
||||||
|
|
||||||
vertices.resize(2 * steps)
|
|
||||||
indices.resize(6 * steps)
|
|
||||||
for i in steps:
|
|
||||||
var v : Vector3 = Vector3.RIGHT.rotated(Vector3.FORWARD, deg_to_rad((360.0 * i) / steps))
|
|
||||||
vertices[i] = v
|
|
||||||
vertices[steps+i] = v * 2.0
|
|
||||||
|
|
||||||
var off = i * 6
|
|
||||||
var i2 = ((i + 1) % steps)
|
|
||||||
indices[off + 0] = steps + i
|
|
||||||
indices[off + 1] = steps + i2
|
|
||||||
indices[off + 2] = i2
|
|
||||||
indices[off + 3] = steps + i
|
|
||||||
indices[off + 4] = i2
|
|
||||||
indices[off + 5] = i
|
|
||||||
|
|
||||||
# update our mesh
|
|
||||||
var arr_mesh = ArrayMesh.new()
|
|
||||||
var arr : Array
|
|
||||||
arr.resize(ArrayMesh.ARRAY_MAX)
|
|
||||||
arr[ArrayMesh.ARRAY_VERTEX] = vertices
|
|
||||||
arr[ArrayMesh.ARRAY_INDEX] = indices
|
|
||||||
arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arr)
|
|
||||||
arr_mesh.custom_aabb = AABB(Vector3(-1.0, -1.0, -1.0), Vector3(1.0, 1.0, 1.0))
|
|
||||||
|
|
||||||
$Mesh.mesh = arr_mesh
|
|
||||||
$Mesh.set_surface_override_material(0, material)
|
|
||||||
|
|
||||||
func set_auto_adjust(new_auto_adjust : bool) -> void:
|
|
||||||
auto_adjust = new_auto_adjust
|
|
||||||
if is_inside_tree() and !Engine.is_editor_hint():
|
|
||||||
_update_auto_adjust()
|
|
||||||
|
|
||||||
func _update_auto_adjust() -> void:
|
|
||||||
# Turn process on if auto adjust is true.
|
|
||||||
# Note we don't turn it off here, we want to finish fading out the vignette if needed
|
|
||||||
if auto_adjust:
|
|
||||||
set_process(true)
|
|
||||||
|
|
||||||
func set_auto_rotation_limit(new_auto_rotation_limit : float) -> void:
|
|
||||||
auto_rotation_limit = new_auto_rotation_limit
|
|
||||||
auto_rotation_limit_rad = deg_to_rad(auto_rotation_limit)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsVignette"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
if !Engine.is_editor_hint():
|
|
||||||
origin_node = XRHelpers.get_xr_origin(self)
|
|
||||||
_update_mesh()
|
|
||||||
_update_radius()
|
|
||||||
_update_fade()
|
|
||||||
_update_auto_adjust()
|
|
||||||
else:
|
|
||||||
set_process(false)
|
|
||||||
|
|
||||||
# Called on process
|
|
||||||
func _process(delta):
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
if !origin_node:
|
|
||||||
return
|
|
||||||
|
|
||||||
if !auto_adjust:
|
|
||||||
# set to true for next time this is enabled
|
|
||||||
auto_first = true
|
|
||||||
|
|
||||||
# We are done, turn off process
|
|
||||||
set_process(false)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
if auto_first:
|
|
||||||
# first time we run process since starting, just record transform
|
|
||||||
last_origin_basis = origin_node.global_transform.basis
|
|
||||||
last_location = global_transform.origin
|
|
||||||
auto_first = false
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get our delta transform
|
|
||||||
var delta_b = origin_node.global_transform.basis * last_origin_basis.inverse()
|
|
||||||
var delta_v = global_transform.origin - last_location
|
|
||||||
|
|
||||||
# Adjust radius based on rotation speed of our origin point (not of head movement).
|
|
||||||
# We convert our delta rotation to a quaterion.
|
|
||||||
# A quaternion represents a rotation around an angle.
|
|
||||||
var q = delta_b.get_rotation_quaternion()
|
|
||||||
|
|
||||||
# We get our angle from our w component and then adjust to get a
|
|
||||||
# rotation speed per second by dividing by delta
|
|
||||||
var angle = (2 * acos(q.w)) / delta
|
|
||||||
|
|
||||||
# Calculate what our radius should be for our rotation speed
|
|
||||||
var target_radius = 1.0
|
|
||||||
if auto_rotation_limit > 0:
|
|
||||||
target_radius = 1.0 - (
|
|
||||||
clamp(angle / auto_rotation_limit_rad, 0.0, 1.0) * (1.0 - auto_inner_radius))
|
|
||||||
|
|
||||||
# Now do the same for speed, this includes players physical speed but there
|
|
||||||
# isn't much we can do there.
|
|
||||||
if auto_velocity_limit > 0:
|
|
||||||
var velocity = delta_v.length() / delta
|
|
||||||
target_radius = min(target_radius, 1.0 - (
|
|
||||||
clamp(velocity / auto_velocity_limit, 0.0, 1.0) * (1.0 - auto_inner_radius)))
|
|
||||||
|
|
||||||
# if our radius is small then our current we apply it
|
|
||||||
if target_radius < radius:
|
|
||||||
set_radius(target_radius)
|
|
||||||
fade_delay = auto_fade_delay
|
|
||||||
elif fade_delay > 0.0:
|
|
||||||
fade_delay -= delta
|
|
||||||
else:
|
|
||||||
set_radius(clamp(radius + delta / auto_fade_out_factor, 0.0, 1.0))
|
|
||||||
|
|
||||||
last_origin_basis = origin_node.global_transform.basis
|
|
||||||
last_location = global_transform.origin
|
|
||||||
|
|
||||||
# This method verifies the vignette has a valid configuration.
|
|
||||||
# Specifically it checks the following:
|
|
||||||
# - XROrigin3D is a parent
|
|
||||||
# - XRCamera3D is our parent
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
# Check the origin node
|
|
||||||
if !XRHelpers.get_xr_origin(self):
|
|
||||||
warnings.append("Parent node must be in a branch from XROrigin3D")
|
|
||||||
|
|
||||||
# check camera node
|
|
||||||
var parent = get_parent()
|
|
||||||
if !parent or !parent is XRCamera3D:
|
|
||||||
warnings.append("Parent node must be an XRCamera3D")
|
|
||||||
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://dj5ensfee24r2
|
|
@ -1,34 +0,0 @@
|
|||||||
shader_type spatial;
|
|
||||||
render_mode depth_test_disabled, skip_vertex_transform, unshaded, cull_disabled;
|
|
||||||
|
|
||||||
uniform vec4 color : source_color = vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
uniform float radius = 1.0;
|
|
||||||
uniform float fade = 0.05;
|
|
||||||
|
|
||||||
varying float dist;
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
vec3 v = VERTEX;
|
|
||||||
dist = length(v);
|
|
||||||
|
|
||||||
// outer ring is 2.0, inner ring is 1.0, so this scales purely the inner ring
|
|
||||||
if (dist < 1.5) {
|
|
||||||
// Adjust by radius
|
|
||||||
dist = radius;
|
|
||||||
v *= dist;
|
|
||||||
|
|
||||||
// We don't know our eye center, projecting a center point in the distance gives us a good enough approximation
|
|
||||||
vec4 eye = PROJECTION_MATRIX * vec4(0.0, 0.0, 100.0, 1.0);
|
|
||||||
|
|
||||||
// and we offset our inner circle
|
|
||||||
v.xy += eye.xy / eye.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// looks like this is broken in Godot 4...
|
|
||||||
POSITION = vec4(v, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
ALBEDO = color.rgb;
|
|
||||||
ALPHA = clamp((dist - radius) / fade, 0.0, 1.0);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
uid://8svyd3fm8mxf
|
|
@ -1,10 +0,0 @@
|
|||||||
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://cesiqdvdfojle"]
|
|
||||||
|
|
||||||
[ext_resource type="Shader" path="res://addons/godot-xr-tools/effects/vignette.gdshader" id="1_x02h0"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
render_priority = 0
|
|
||||||
shader = ExtResource("1_x02h0")
|
|
||||||
shader_parameter/color = Color(0, 0, 0, 1)
|
|
||||||
shader_parameter/radius = 0.2
|
|
||||||
shader_parameter/fade = 0.05
|
|
@ -1,28 +0,0 @@
|
|||||||
[gd_scene load_steps=4 format=4 uid="uid://cc6ngdqie8o8c"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dj5ensfee24r2" path="res://addons/godot-xr-tools/effects/vignette.gd" id="1"]
|
|
||||||
[ext_resource type="Material" uid="uid://cesiqdvdfojle" path="res://addons/godot-xr-tools/effects/vignette.tres" id="2_djtaj"]
|
|
||||||
|
|
||||||
[sub_resource type="ArrayMesh" id="ArrayMesh_yyajy"]
|
|
||||||
_surfaces = [{
|
|
||||||
"aabb": AABB(-2, -2, 0, 4, 4, 1e-05),
|
|
||||||
"format": 34359742465,
|
|
||||||
"index_count": 192,
|
|
||||||
"index_data": PackedByteArray("IAAhAAEAIAABAAAAIQAiAAIAIQACAAEAIgAjAAMAIgADAAIAIwAkAAQAIwAEAAMAJAAlAAUAJAAFAAQAJQAmAAYAJQAGAAUAJgAnAAcAJgAHAAYAJwAoAAgAJwAIAAcAKAApAAkAKAAJAAgAKQAqAAoAKQAKAAkAKgArAAsAKgALAAoAKwAsAAwAKwAMAAsALAAtAA0ALAANAAwALQAuAA4ALQAOAA0ALgAvAA8ALgAPAA4ALwAwABAALwAQAA8AMAAxABEAMAARABAAMQAyABIAMQASABEAMgAzABMAMgATABIAMwA0ABQAMwAUABMANAA1ABUANAAVABQANQA2ABYANQAWABUANgA3ABcANgAXABYANwA4ABgANwAYABcAOAA5ABkAOAAZABgAOQA6ABoAOQAaABkAOgA7ABsAOgAbABoAOwA8ABwAOwAcABsAPAA9AB0APAAdABwAPQA+AB4APQAeAB0APgA/AB8APgAfAB4APwAgAAAAPwAAAB8A"),
|
|
||||||
"primitive": 3,
|
|
||||||
"uv_scale": Vector4(0, 0, 0, 0),
|
|
||||||
"vertex_count": 64,
|
|
||||||
"vertex_data": PackedByteArray("AACAPwAAAAAAAAAAvhR7P8LFR74AAAAAXoNsPxbvw74AAAAAMdtUP9o5Dr8AAAAA8wQ1P/MENb8AAAAA2jkOPzHbVL8AAAAAFe/DPl6DbL8AAAAAxMVHPr4Ue78AAAAALr07swAAgL8AAAAAwsVHvr4Ue78AAAAAFO/Dvl+DbL8AAAAA2TkOvzLbVL8AAAAA8wQ1v/MENb8AAAAAMttUv9k5Dr8AAAAAXoNsvxfvw74AAAAAvxR7v8HFR74AAAAAAACAvy69uzMAAAAAvxR7v73FRz4AAAAAXoNsvxXvwz4AAAAAMNtUv9s5Dj8AAAAA9AQ1v/IENT8AAAAA3TkOvy/bVD8AAAAAGu/Dvl6DbD8AAAAAxsVHvr4Uez8AAAAALt5MMgAAgD8AAAAAyMVHPr4Uez8AAAAAG+/DPl2DbD8AAAAA1zkOPzPbVD8AAAAA8gQ1P/UENT8AAAAAMdtUP9s5Dj8AAAAAX4NsPxXvwz4AAAAAvxR7P7zFRz4AAAAAAAAAQAAAAAAAAAAAvhT7P8LFx74AAAAAXoPsPxbvQ78AAAAAMdvUP9o5jr8AAAAA8wS1P/MEtb8AAAAA2jmOPzHb1L8AAAAAFe9DP16D7L8AAAAAxMXHPr4U+78AAAAALr27swAAAMAAAAAAwsXHvr4U+78AAAAAFO9Dv1+D7L8AAAAA2TmOvzLb1L8AAAAA8wS1v/MEtb8AAAAAMtvUv9k5jr8AAAAAXoPsvxfvQ78AAAAAvxT7v8HFx74AAAAAAAAAwC69OzQAAAAAvxT7v73Fxz4AAAAAXoPsvxXvQz8AAAAAMNvUv9s5jj8AAAAA9AS1v/IEtT8AAAAA3TmOvy/b1D8AAAAAGu9Dv16D7D8AAAAAxsXHvr4U+z8AAAAALt7MMgAAAEAAAAAAyMXHPr4U+z8AAAAAG+9DP12D7D8AAAAA1zmOPzPb1D8AAAAA8gS1P/UEtT8AAAAAMdvUP9s5jj8AAAAAX4PsPxXvQz8AAAAAvxT7P7zFxz4AAAAA")
|
|
||||||
}]
|
|
||||||
custom_aabb = AABB(-1, -1, -1, 1, 1, 1)
|
|
||||||
|
|
||||||
[node name="Vignette" type="Node3D"]
|
|
||||||
script = ExtResource("1")
|
|
||||||
|
|
||||||
[node name="Mesh" type="MeshInstance3D" parent="."]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1)
|
|
||||||
visible = false
|
|
||||||
cast_shadow = 0
|
|
||||||
ignore_occlusion_culling = true
|
|
||||||
mesh = SubResource("ArrayMesh_yyajy")
|
|
||||||
surface_material_override/0 = ExtResource("2_djtaj")
|
|
@ -1,134 +0,0 @@
|
|||||||
class_name XRToolsPointerEvent
|
|
||||||
|
|
||||||
## Types of pointer events
|
|
||||||
enum Type {
|
|
||||||
## Pointer entered target
|
|
||||||
ENTERED,
|
|
||||||
|
|
||||||
## Pointer exited target
|
|
||||||
EXITED,
|
|
||||||
|
|
||||||
## Pointer pressed target
|
|
||||||
PRESSED,
|
|
||||||
|
|
||||||
## Pointer released target
|
|
||||||
RELEASED,
|
|
||||||
|
|
||||||
## Pointer moved on target
|
|
||||||
MOVED
|
|
||||||
}
|
|
||||||
|
|
||||||
## Type of pointer event
|
|
||||||
var event_type : Type
|
|
||||||
|
|
||||||
## Pointer generating event
|
|
||||||
var pointer : Node3D
|
|
||||||
|
|
||||||
## Target of pointer
|
|
||||||
var target : Node3D
|
|
||||||
|
|
||||||
## Point position
|
|
||||||
var position : Vector3
|
|
||||||
|
|
||||||
## Last point position
|
|
||||||
var last_position : Vector3
|
|
||||||
|
|
||||||
|
|
||||||
## Initialize a new instance of the XRToolsPointerEvent class
|
|
||||||
func _init(
|
|
||||||
p_event_type : Type,
|
|
||||||
p_pointer : Node3D,
|
|
||||||
p_target : Node3D,
|
|
||||||
p_position : Vector3,
|
|
||||||
p_last_position : Vector3) -> void:
|
|
||||||
event_type = p_event_type
|
|
||||||
pointer = p_pointer
|
|
||||||
target = p_target
|
|
||||||
position = p_position
|
|
||||||
last_position = p_last_position
|
|
||||||
|
|
||||||
|
|
||||||
## Report a pointer entered event
|
|
||||||
static func entered(
|
|
||||||
pointer : Node3D,
|
|
||||||
target : Node3D,
|
|
||||||
at : Vector3) -> void:
|
|
||||||
report(
|
|
||||||
XRToolsPointerEvent.new(
|
|
||||||
Type.ENTERED,
|
|
||||||
pointer,
|
|
||||||
target,
|
|
||||||
at,
|
|
||||||
at))
|
|
||||||
|
|
||||||
|
|
||||||
## Report pointer moved event
|
|
||||||
static func moved(
|
|
||||||
pointer : Node3D,
|
|
||||||
target : Node3D,
|
|
||||||
to : Vector3,
|
|
||||||
from : Vector3) -> void:
|
|
||||||
report(
|
|
||||||
XRToolsPointerEvent.new(
|
|
||||||
Type.MOVED,
|
|
||||||
pointer,
|
|
||||||
target,
|
|
||||||
to,
|
|
||||||
from))
|
|
||||||
|
|
||||||
|
|
||||||
## Report pointer pressed event
|
|
||||||
static func pressed(
|
|
||||||
pointer : Node3D,
|
|
||||||
target : Node3D,
|
|
||||||
at : Vector3) -> void:
|
|
||||||
report(
|
|
||||||
XRToolsPointerEvent.new(
|
|
||||||
Type.PRESSED,
|
|
||||||
pointer,
|
|
||||||
target,
|
|
||||||
at,
|
|
||||||
at))
|
|
||||||
|
|
||||||
|
|
||||||
## Report pointer released event
|
|
||||||
static func released(
|
|
||||||
pointer : Node3D,
|
|
||||||
target : Node3D,
|
|
||||||
at : Vector3) -> void:
|
|
||||||
report(
|
|
||||||
XRToolsPointerEvent.new(
|
|
||||||
Type.RELEASED,
|
|
||||||
pointer,
|
|
||||||
target,
|
|
||||||
at,
|
|
||||||
at))
|
|
||||||
|
|
||||||
|
|
||||||
## Report a pointer exited event
|
|
||||||
static func exited(
|
|
||||||
pointer : Node3D,
|
|
||||||
target : Node3D,
|
|
||||||
last : Vector3) -> void:
|
|
||||||
report(
|
|
||||||
XRToolsPointerEvent.new(
|
|
||||||
Type.EXITED,
|
|
||||||
pointer,
|
|
||||||
target,
|
|
||||||
last,
|
|
||||||
last))
|
|
||||||
|
|
||||||
|
|
||||||
## Report a pointer event
|
|
||||||
static func report(event : XRToolsPointerEvent) -> void:
|
|
||||||
# Fire event on pointer
|
|
||||||
if is_instance_valid(event.pointer):
|
|
||||||
if event.pointer.has_signal("pointing_event"):
|
|
||||||
event.pointer.emit_signal("pointing_event", event)
|
|
||||||
|
|
||||||
# Fire event/method on the target if it's valid
|
|
||||||
if is_instance_valid(event.target):
|
|
||||||
if event.target.has_signal("pointer_event"):
|
|
||||||
event.target.emit_signal("pointer_event", event)
|
|
||||||
elif event.target.has_method("pointer_event"):
|
|
||||||
event.target.pointer_event(event)
|
|
@ -1 +0,0 @@
|
|||||||
uid://coj6c0xt3vnro
|
|
@ -1,92 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsFallDamage
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Example Fall Damage Detector
|
|
||||||
##
|
|
||||||
## This example script detects the player falling to the ground and
|
|
||||||
## optionally hitting walls.
|
|
||||||
##
|
|
||||||
## It works by tracking the player body velocity to detect velocity
|
|
||||||
## changes (acceleration) exceeding a threshold.
|
|
||||||
##
|
|
||||||
## This doesn't use the usual Acceleration = dV / dT as it doesn't appear
|
|
||||||
## to work too well considering the "instantaneous" nature of the
|
|
||||||
## collision. Additionally all it would end up doing is multiplying the
|
|
||||||
## change in velocity by the physics-frame-rate making it sensitive to
|
|
||||||
## varying physics timing.
|
|
||||||
##
|
|
||||||
## Instead the threshold in terms of delta-velocity makes it easy to work
|
|
||||||
## out natural values. For example if the player falls under regular gravity
|
|
||||||
## (9.81 meters per second^2 for 1 second) then hits the ground, they will have fallen around
|
|
||||||
## 4.9 meters, and will then encounter an instantaneous velocity-change of
|
|
||||||
## 9.81 meters per second.
|
|
||||||
##
|
|
||||||
## This file can handle simple demonstrations, but games will most likely
|
|
||||||
## want to modify it, for example to ignore damage on certain surfaces.
|
|
||||||
|
|
||||||
|
|
||||||
## Signal invoked when the player takes fall damage
|
|
||||||
signal player_fall_damage(damage)
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 1000
|
|
||||||
|
|
||||||
## Ignore damage if player is launched up
|
|
||||||
@export var ignore_launch : bool = true
|
|
||||||
|
|
||||||
## Only take damage on ground
|
|
||||||
@export var ground_only : bool = false
|
|
||||||
|
|
||||||
## Acceleration limit
|
|
||||||
@export var damage_threshold : float = 8.0
|
|
||||||
|
|
||||||
|
|
||||||
## Previous velocity
|
|
||||||
var _previous_velocity : Vector3 = Vector3.ZERO
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsFallDamage"
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
# In Godot 4 we must now manually call our super class ready function
|
|
||||||
super()
|
|
||||||
|
|
||||||
# Set as always active
|
|
||||||
is_active = true
|
|
||||||
|
|
||||||
|
|
||||||
func physics_movement(_delta: float, player_body: XRToolsPlayerBody, disabled: bool):
|
|
||||||
# Skip if not enabled
|
|
||||||
if disabled or !enabled:
|
|
||||||
_previous_velocity = player_body.velocity
|
|
||||||
return
|
|
||||||
|
|
||||||
# Calculate the instantaneous acceleration
|
|
||||||
var accel_vec := player_body.velocity - _previous_velocity
|
|
||||||
_previous_velocity = player_body.velocity
|
|
||||||
|
|
||||||
# Ignore launching the player
|
|
||||||
if ignore_launch:
|
|
||||||
# Forgive "up" acceleration equal to our "up" speed
|
|
||||||
var forgive : float = max(0, min(accel_vec.y, player_body.velocity.y))
|
|
||||||
accel_vec.y -= forgive
|
|
||||||
|
|
||||||
# Handle ground-only collisions
|
|
||||||
if ground_only:
|
|
||||||
# Ignore if not on ground
|
|
||||||
if not player_body.on_ground:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Only consider vertical acceleration
|
|
||||||
accel_vec *= Vector3.UP
|
|
||||||
|
|
||||||
# Detect fall damage
|
|
||||||
var accel := accel_vec.length()
|
|
||||||
if accel > damage_threshold:
|
|
||||||
emit_signal("player_fall_damage", accel)
|
|
@ -1 +0,0 @@
|
|||||||
uid://bpa2l4eu5qmh2
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://d2yejwiwab3wv"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bpa2l4eu5qmh2" path="res://addons/godot-xr-tools/examples/fall_damage.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="FallDamage" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,432 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/function.svg")
|
|
||||||
class_name XRToolsFunctionPickup
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Function Pickup Script
|
|
||||||
##
|
|
||||||
## This script implements picking up of objects. Most pickable
|
|
||||||
## objects are instances of the [XRToolsPickable] class.
|
|
||||||
##
|
|
||||||
## Additionally this script can work in conjunction with the
|
|
||||||
## [XRToolsMovementProvider] class support climbing. Most climbable objects are
|
|
||||||
## instances of the [XRToolsClimbable] class.
|
|
||||||
|
|
||||||
|
|
||||||
## Signal emitted when the pickup picks something up
|
|
||||||
signal has_picked_up(what)
|
|
||||||
|
|
||||||
## Signal emitted when the pickup drops something
|
|
||||||
signal has_dropped
|
|
||||||
|
|
||||||
|
|
||||||
# Default pickup collision mask of 3:pickable and 19:handle
|
|
||||||
const DEFAULT_GRAB_MASK := 0b0000_0000_0000_0100_0000_0000_0000_0100
|
|
||||||
|
|
||||||
# Default pickup collision mask of 3:pickable
|
|
||||||
const DEFAULT_RANGE_MASK := 0b0000_0000_0000_0000_0000_0000_0000_0100
|
|
||||||
|
|
||||||
# Constant for worst-case grab distance
|
|
||||||
const MAX_GRAB_DISTANCE2: float = 1000000.0
|
|
||||||
|
|
||||||
|
|
||||||
## Pickup enabled property
|
|
||||||
@export var enabled : bool = true
|
|
||||||
|
|
||||||
## Grip controller axis
|
|
||||||
@export var pickup_axis_action : String = "grip"
|
|
||||||
|
|
||||||
## Action controller button
|
|
||||||
@export var action_button_action : String = "trigger_click"
|
|
||||||
|
|
||||||
## Grab distance
|
|
||||||
@export var grab_distance : float = 0.3: set = _set_grab_distance
|
|
||||||
|
|
||||||
## Grab collision mask
|
|
||||||
@export_flags_3d_physics \
|
|
||||||
var grab_collision_mask : int = DEFAULT_GRAB_MASK: set = _set_grab_collision_mask
|
|
||||||
|
|
||||||
## If true, ranged-grabbing is enabled
|
|
||||||
@export var ranged_enable : bool = true
|
|
||||||
|
|
||||||
## Ranged-grab distance
|
|
||||||
@export var ranged_distance : float = 5.0: set = _set_ranged_distance
|
|
||||||
|
|
||||||
## Ranged-grab angle
|
|
||||||
@export_range(0.0, 45.0) var ranged_angle : float = 5.0: set = _set_ranged_angle
|
|
||||||
|
|
||||||
## Ranged-grab collision mask
|
|
||||||
@export_flags_3d_physics \
|
|
||||||
var ranged_collision_mask : int = DEFAULT_RANGE_MASK: set = _set_ranged_collision_mask
|
|
||||||
|
|
||||||
## Throw impulse factor
|
|
||||||
@export var impulse_factor : float = 1.0
|
|
||||||
|
|
||||||
## Throw velocity averaging
|
|
||||||
@export var velocity_samples: int = 5
|
|
||||||
|
|
||||||
|
|
||||||
# Public fields
|
|
||||||
var closest_object : Node3D = null
|
|
||||||
var picked_up_object : Node3D = null
|
|
||||||
var picked_up_ranged : bool = false
|
|
||||||
var grip_pressed : bool = false
|
|
||||||
|
|
||||||
# Private fields
|
|
||||||
var _object_in_grab_area := Array()
|
|
||||||
var _object_in_ranged_area := Array()
|
|
||||||
var _velocity_averager := XRToolsVelocityAverager.new(velocity_samples)
|
|
||||||
var _grab_area : Area3D
|
|
||||||
var _grab_collision : CollisionShape3D
|
|
||||||
var _ranged_area : Area3D
|
|
||||||
var _ranged_collision : CollisionShape3D
|
|
||||||
|
|
||||||
|
|
||||||
## Controller
|
|
||||||
@onready var _controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
## Grip threshold (from configuration)
|
|
||||||
@onready var _grip_threshold : float = XRTools.get_grip_threshold()
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsFunctionPickup"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
# Skip creating grab-helpers if in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Create the grab collision shape
|
|
||||||
_grab_collision = CollisionShape3D.new()
|
|
||||||
_grab_collision.set_name("GrabCollisionShape")
|
|
||||||
_grab_collision.shape = SphereShape3D.new()
|
|
||||||
_grab_collision.shape.radius = grab_distance
|
|
||||||
|
|
||||||
# Create the grab area
|
|
||||||
_grab_area = Area3D.new()
|
|
||||||
_grab_area.set_name("GrabArea")
|
|
||||||
_grab_area.collision_layer = 0
|
|
||||||
_grab_area.collision_mask = grab_collision_mask
|
|
||||||
_grab_area.add_child(_grab_collision)
|
|
||||||
_grab_area.area_entered.connect(_on_grab_entered)
|
|
||||||
_grab_area.body_entered.connect(_on_grab_entered)
|
|
||||||
_grab_area.area_exited.connect(_on_grab_exited)
|
|
||||||
_grab_area.body_exited.connect(_on_grab_exited)
|
|
||||||
add_child(_grab_area)
|
|
||||||
|
|
||||||
# Create the ranged collision shape
|
|
||||||
_ranged_collision = CollisionShape3D.new()
|
|
||||||
_ranged_collision.set_name("RangedCollisionShape")
|
|
||||||
_ranged_collision.shape = CylinderShape3D.new()
|
|
||||||
_ranged_collision.transform.basis = Basis(Vector3.RIGHT, PI/2)
|
|
||||||
|
|
||||||
# Create the ranged area
|
|
||||||
_ranged_area = Area3D.new()
|
|
||||||
_ranged_area.set_name("RangedArea")
|
|
||||||
_ranged_area.collision_layer = 0
|
|
||||||
_ranged_area.collision_mask = ranged_collision_mask
|
|
||||||
_ranged_area.add_child(_ranged_collision)
|
|
||||||
_ranged_area.area_entered.connect(_on_ranged_entered)
|
|
||||||
_ranged_area.body_entered.connect(_on_ranged_entered)
|
|
||||||
_ranged_area.area_exited.connect(_on_ranged_exited)
|
|
||||||
_ranged_area.body_exited.connect(_on_ranged_exited)
|
|
||||||
add_child(_ranged_area)
|
|
||||||
|
|
||||||
# Update the colliders
|
|
||||||
_update_colliders()
|
|
||||||
|
|
||||||
# Monitor Grab Button
|
|
||||||
_controller.connect("button_pressed", _on_button_pressed)
|
|
||||||
_controller.connect("button_released", _on_button_released)
|
|
||||||
|
|
||||||
|
|
||||||
# Called on each frame to update the pickup
|
|
||||||
func _process(delta):
|
|
||||||
# Do not process if in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Skip if disabled, or the controller isn't active
|
|
||||||
if !enabled or !_controller.get_is_active():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Handle our grip
|
|
||||||
var grip_value = _controller.get_float(pickup_axis_action)
|
|
||||||
if (grip_pressed and grip_value < (_grip_threshold - 0.1)):
|
|
||||||
grip_pressed = false
|
|
||||||
_on_grip_release()
|
|
||||||
elif (!grip_pressed and grip_value > (_grip_threshold + 0.1)):
|
|
||||||
grip_pressed = true
|
|
||||||
_on_grip_pressed()
|
|
||||||
|
|
||||||
# Calculate average velocity
|
|
||||||
if is_instance_valid(picked_up_object) and picked_up_object.is_picked_up():
|
|
||||||
# Average velocity of picked up object
|
|
||||||
_velocity_averager.add_transform(delta, picked_up_object.global_transform)
|
|
||||||
else:
|
|
||||||
# Average velocity of this pickup
|
|
||||||
_velocity_averager.add_transform(delta, global_transform)
|
|
||||||
|
|
||||||
_update_closest_object()
|
|
||||||
|
|
||||||
|
|
||||||
## Find an [XRToolsFunctionPickup] node.
|
|
||||||
##
|
|
||||||
## This function searches from the specified node for an [XRToolsFunctionPickup]
|
|
||||||
## assuming the node is a sibling of the pickup under an [XRController3D].
|
|
||||||
static func find_instance(node : Node) -> XRToolsFunctionPickup:
|
|
||||||
return XRTools.find_xr_child(
|
|
||||||
XRHelpers.get_xr_controller(node),
|
|
||||||
"*",
|
|
||||||
"XRToolsFunctionPickup") as XRToolsFunctionPickup
|
|
||||||
|
|
||||||
|
|
||||||
## Find the left [XRToolsFunctionPickup] node.
|
|
||||||
##
|
|
||||||
## This function searches from the specified node for the left controller
|
|
||||||
## [XRToolsFunctionPickup] assuming the node is a sibling of the [XOrigin3D].
|
|
||||||
static func find_left(node : Node) -> XRToolsFunctionPickup:
|
|
||||||
return XRTools.find_xr_child(
|
|
||||||
XRHelpers.get_left_controller(node),
|
|
||||||
"*",
|
|
||||||
"XRToolsFunctionPickup") as XRToolsFunctionPickup
|
|
||||||
|
|
||||||
|
|
||||||
## Find the right [XRToolsFunctionPickup] node.
|
|
||||||
##
|
|
||||||
## This function searches from the specified node for the right controller
|
|
||||||
## [XRToolsFunctionPickup] assuming the node is a sibling of the [XROrigin3D].
|
|
||||||
static func find_right(node : Node) -> XRToolsFunctionPickup:
|
|
||||||
return XRTools.find_xr_child(
|
|
||||||
XRHelpers.get_right_controller(node),
|
|
||||||
"*",
|
|
||||||
"XRToolsFunctionPickup") as XRToolsFunctionPickup
|
|
||||||
|
|
||||||
|
|
||||||
## Get the [XRController3D] driving this pickup.
|
|
||||||
func get_controller() -> XRController3D:
|
|
||||||
return _controller
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the grab distance has been modified
|
|
||||||
func _set_grab_distance(new_value: float) -> void:
|
|
||||||
grab_distance = new_value
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_colliders()
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the grab collision mask has been modified
|
|
||||||
func _set_grab_collision_mask(new_value: int) -> void:
|
|
||||||
grab_collision_mask = new_value
|
|
||||||
if is_inside_tree() and _grab_collision:
|
|
||||||
_grab_collision.collision_mask = new_value
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the ranged-grab distance has been modified
|
|
||||||
func _set_ranged_distance(new_value: float) -> void:
|
|
||||||
ranged_distance = new_value
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_colliders()
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the ranged-grab angle has been modified
|
|
||||||
func _set_ranged_angle(new_value: float) -> void:
|
|
||||||
ranged_angle = new_value
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_colliders()
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the ranged-grab collision mask has been modified
|
|
||||||
func _set_ranged_collision_mask(new_value: int) -> void:
|
|
||||||
ranged_collision_mask = new_value
|
|
||||||
if is_inside_tree() and _ranged_collision:
|
|
||||||
_ranged_collision.collision_mask = new_value
|
|
||||||
|
|
||||||
|
|
||||||
# Update the colliders geometry
|
|
||||||
func _update_colliders() -> void:
|
|
||||||
# Update the grab sphere
|
|
||||||
if _grab_collision:
|
|
||||||
_grab_collision.shape.radius = grab_distance
|
|
||||||
|
|
||||||
# Update the ranged-grab cylinder
|
|
||||||
if _ranged_collision:
|
|
||||||
_ranged_collision.shape.radius = tan(deg_to_rad(ranged_angle)) * ranged_distance
|
|
||||||
_ranged_collision.shape.height = ranged_distance
|
|
||||||
_ranged_collision.transform.origin.z = -ranged_distance * 0.5
|
|
||||||
|
|
||||||
|
|
||||||
# Called when an object enters the grab sphere
|
|
||||||
func _on_grab_entered(target: Node3D) -> void:
|
|
||||||
# reject objects which don't support picking up
|
|
||||||
if not target.has_method('pick_up'):
|
|
||||||
return
|
|
||||||
|
|
||||||
# ignore objects already known
|
|
||||||
if _object_in_grab_area.find(target) >= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Add to the list of objects in grab area
|
|
||||||
_object_in_grab_area.push_back(target)
|
|
||||||
|
|
||||||
|
|
||||||
# Called when an object enters the ranged-grab cylinder
|
|
||||||
func _on_ranged_entered(target: Node3D) -> void:
|
|
||||||
# reject objects which don't support picking up rangedly
|
|
||||||
if not 'can_ranged_grab' in target or not target.can_ranged_grab:
|
|
||||||
return
|
|
||||||
|
|
||||||
# ignore objects already known
|
|
||||||
if _object_in_ranged_area.find(target) >= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Add to the list of objects in grab area
|
|
||||||
_object_in_ranged_area.push_back(target)
|
|
||||||
|
|
||||||
|
|
||||||
# Called when an object exits the grab sphere
|
|
||||||
func _on_grab_exited(target: Node3D) -> void:
|
|
||||||
_object_in_grab_area.erase(target)
|
|
||||||
|
|
||||||
|
|
||||||
# Called when an object exits the ranged-grab cylinder
|
|
||||||
func _on_ranged_exited(target: Node3D) -> void:
|
|
||||||
_object_in_ranged_area.erase(target)
|
|
||||||
|
|
||||||
|
|
||||||
# Update the closest object field with the best choice of grab
|
|
||||||
func _update_closest_object() -> void:
|
|
||||||
# Find the closest object we can pickup
|
|
||||||
var new_closest_obj: Node3D = null
|
|
||||||
if not picked_up_object:
|
|
||||||
# Find the closest in grab area
|
|
||||||
new_closest_obj = _get_closest_grab()
|
|
||||||
if not new_closest_obj and ranged_enable:
|
|
||||||
# Find closest in ranged area
|
|
||||||
new_closest_obj = _get_closest_ranged()
|
|
||||||
|
|
||||||
# Skip if no change
|
|
||||||
if closest_object == new_closest_obj:
|
|
||||||
return
|
|
||||||
|
|
||||||
# remove highlight on old object
|
|
||||||
if is_instance_valid(closest_object):
|
|
||||||
closest_object.request_highlight(self, false)
|
|
||||||
|
|
||||||
# add highlight to new object
|
|
||||||
closest_object = new_closest_obj
|
|
||||||
if is_instance_valid(closest_object):
|
|
||||||
closest_object.request_highlight(self, true)
|
|
||||||
|
|
||||||
|
|
||||||
# Find the pickable object closest to our hand's grab location
|
|
||||||
func _get_closest_grab() -> Node3D:
|
|
||||||
var new_closest_obj: Node3D = null
|
|
||||||
var new_closest_distance := MAX_GRAB_DISTANCE2
|
|
||||||
for o in _object_in_grab_area:
|
|
||||||
# skip objects that can not be picked up
|
|
||||||
if not o.can_pick_up(self):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Save if this object is closer than the current best
|
|
||||||
var distance_squared := global_transform.origin.distance_squared_to(
|
|
||||||
o.global_transform.origin)
|
|
||||||
if distance_squared < new_closest_distance:
|
|
||||||
new_closest_obj = o
|
|
||||||
new_closest_distance = distance_squared
|
|
||||||
|
|
||||||
# Return best object
|
|
||||||
return new_closest_obj
|
|
||||||
|
|
||||||
|
|
||||||
# Find the rangedly-pickable object closest to our hand's pointing direction
|
|
||||||
func _get_closest_ranged() -> Node3D:
|
|
||||||
var new_closest_obj: Node3D = null
|
|
||||||
var new_closest_angle_dp := cos(deg_to_rad(ranged_angle))
|
|
||||||
var hand_forwards := -global_transform.basis.z
|
|
||||||
for o in _object_in_ranged_area:
|
|
||||||
# skip objects that can not be picked up
|
|
||||||
if not o.can_pick_up(self):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Save if this object is closer than the current best
|
|
||||||
var object_direction: Vector3 = o.global_transform.origin - global_transform.origin
|
|
||||||
object_direction = object_direction.normalized()
|
|
||||||
var angle_dp := hand_forwards.dot(object_direction)
|
|
||||||
if angle_dp > new_closest_angle_dp:
|
|
||||||
new_closest_obj = o
|
|
||||||
new_closest_angle_dp = angle_dp
|
|
||||||
|
|
||||||
# Return best object
|
|
||||||
return new_closest_obj
|
|
||||||
|
|
||||||
|
|
||||||
## Drop the currently held object
|
|
||||||
func drop_object() -> void:
|
|
||||||
if not is_instance_valid(picked_up_object):
|
|
||||||
return
|
|
||||||
|
|
||||||
# let go of this object
|
|
||||||
picked_up_object.let_go(
|
|
||||||
self,
|
|
||||||
_velocity_averager.linear_velocity() * impulse_factor,
|
|
||||||
_velocity_averager.angular_velocity())
|
|
||||||
picked_up_object = null
|
|
||||||
emit_signal("has_dropped")
|
|
||||||
|
|
||||||
|
|
||||||
func _pick_up_object(target: Node3D) -> void:
|
|
||||||
# check if already holding an object
|
|
||||||
if is_instance_valid(picked_up_object):
|
|
||||||
# skip if holding the target object
|
|
||||||
if picked_up_object == target:
|
|
||||||
return
|
|
||||||
# holding something else? drop it
|
|
||||||
drop_object()
|
|
||||||
|
|
||||||
# skip if target null or freed
|
|
||||||
if not is_instance_valid(target):
|
|
||||||
return
|
|
||||||
|
|
||||||
# Handle snap-zone
|
|
||||||
var snap := target as XRToolsSnapZone
|
|
||||||
if snap:
|
|
||||||
target = snap.picked_up_object
|
|
||||||
snap.drop_object()
|
|
||||||
|
|
||||||
# Pick up our target. Note, target may do instant drop_and_free
|
|
||||||
picked_up_ranged = not _object_in_grab_area.has(target)
|
|
||||||
picked_up_object = target
|
|
||||||
target.pick_up(self)
|
|
||||||
|
|
||||||
# If object picked up then emit signal
|
|
||||||
if is_instance_valid(picked_up_object):
|
|
||||||
picked_up_object.request_highlight(self, false)
|
|
||||||
emit_signal("has_picked_up", picked_up_object)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_button_pressed(p_button) -> void:
|
|
||||||
if p_button == action_button_action:
|
|
||||||
if is_instance_valid(picked_up_object) and picked_up_object.has_method("action"):
|
|
||||||
picked_up_object.action()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_button_released(_p_button) -> void:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
func _on_grip_pressed() -> void:
|
|
||||||
if is_instance_valid(picked_up_object) and !picked_up_object.press_to_hold:
|
|
||||||
drop_object()
|
|
||||||
elif is_instance_valid(closest_object):
|
|
||||||
_pick_up_object(closest_object)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_grip_release() -> void:
|
|
||||||
if is_instance_valid(picked_up_object) and picked_up_object.press_to_hold:
|
|
||||||
drop_object()
|
|
@ -1 +0,0 @@
|
|||||||
uid://bvbr1hjnyw7ji
|
|
@ -1,7 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://b4ysuy43poobf"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bvbr1hjnyw7ji" path="res://addons/godot-xr-tools/functions/function_pickup.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="FunctionPickup" type="Node3D"]
|
|
||||||
script = ExtResource("1")
|
|
||||||
grab_collision_mask = 327684
|
|
@ -1,511 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/function.svg")
|
|
||||||
class_name XRToolsFunctionPointer
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Function Pointer Script
|
|
||||||
##
|
|
||||||
## This script implements a pointer function for a players controller. Pointer
|
|
||||||
## events (entered, exited, pressed, release, and movement) are delivered by
|
|
||||||
## invoking signals on the target node.
|
|
||||||
##
|
|
||||||
## Pointer target nodes commonly extend from [XRToolsInteractableArea] or
|
|
||||||
## [XRToolsInteractableBody].
|
|
||||||
|
|
||||||
|
|
||||||
## Signal emitted when this object points at another object
|
|
||||||
signal pointing_event(event)
|
|
||||||
|
|
||||||
|
|
||||||
## Enumeration of laser show modes
|
|
||||||
enum LaserShow {
|
|
||||||
HIDE = 0, ## Hide laser
|
|
||||||
SHOW = 1, ## Show laser
|
|
||||||
COLLIDE = 2, ## Only show laser on collision
|
|
||||||
}
|
|
||||||
|
|
||||||
## Enumeration of laser length modes
|
|
||||||
enum LaserLength {
|
|
||||||
FULL = 0, ## Full length
|
|
||||||
COLLIDE = 1 ## Draw to collision
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Default pointer collision mask of 21:pointable and 23:ui-objects
|
|
||||||
const DEFAULT_MASK := 0b0000_0000_0101_0000_0000_0000_0000_0000
|
|
||||||
|
|
||||||
## Default pointer collision mask of 23:ui-objects
|
|
||||||
const SUPPRESS_MASK := 0b0000_0000_0100_0000_0000_0000_0000_0000
|
|
||||||
|
|
||||||
|
|
||||||
@export_group("General")
|
|
||||||
|
|
||||||
## Pointer enabled
|
|
||||||
@export var enabled : bool = true: set = set_enabled
|
|
||||||
|
|
||||||
## Y Offset for pointer
|
|
||||||
@export var y_offset : float = -0.013: set = set_y_offset
|
|
||||||
|
|
||||||
## Pointer distance
|
|
||||||
@export var distance : float = 10: set = set_distance
|
|
||||||
|
|
||||||
## Active button action
|
|
||||||
@export var active_button_action : String = "trigger_click"
|
|
||||||
|
|
||||||
@export_group("Laser")
|
|
||||||
|
|
||||||
## Controls when the laser is visible
|
|
||||||
@export var show_laser : LaserShow = LaserShow.SHOW: set = set_show_laser
|
|
||||||
|
|
||||||
## Controls the length of the laser
|
|
||||||
@export var laser_length : LaserLength = LaserLength.FULL: set = set_laser_length
|
|
||||||
|
|
||||||
## Laser pointer material
|
|
||||||
@export var laser_material : StandardMaterial3D = null : set = set_laser_material
|
|
||||||
|
|
||||||
## Laser pointer material when hitting target
|
|
||||||
@export var laser_hit_material : StandardMaterial3D = null : set = set_laser_hit_material
|
|
||||||
|
|
||||||
@export_group("Target")
|
|
||||||
|
|
||||||
## If true, the pointer target is shown
|
|
||||||
@export var show_target : bool = false: set = set_show_target
|
|
||||||
|
|
||||||
## Controls the target radius
|
|
||||||
@export var target_radius : float = 0.05: set = set_target_radius
|
|
||||||
|
|
||||||
## Target material
|
|
||||||
@export var target_material : StandardMaterial3D = null : set = set_target_material
|
|
||||||
|
|
||||||
@export_group("Collision")
|
|
||||||
|
|
||||||
## Pointer collision mask
|
|
||||||
@export_flags_3d_physics var collision_mask : int = DEFAULT_MASK: set = set_collision_mask
|
|
||||||
|
|
||||||
## Enable pointer collision with bodies
|
|
||||||
@export var collide_with_bodies : bool = true: set = set_collide_with_bodies
|
|
||||||
|
|
||||||
## Enable pointer collision with areas
|
|
||||||
@export var collide_with_areas : bool = false: set = set_collide_with_areas
|
|
||||||
|
|
||||||
@export_group("Suppression")
|
|
||||||
|
|
||||||
## Suppress radius
|
|
||||||
@export var suppress_radius : float = 0.2: set = set_suppress_radius
|
|
||||||
|
|
||||||
## Suppress mask
|
|
||||||
@export_flags_3d_physics var suppress_mask : int = SUPPRESS_MASK: set = set_suppress_mask
|
|
||||||
|
|
||||||
|
|
||||||
## Current target node
|
|
||||||
var target : Node3D = null
|
|
||||||
|
|
||||||
## Last target node
|
|
||||||
var last_target : Node3D = null
|
|
||||||
|
|
||||||
## Last collision point
|
|
||||||
var last_collided_at : Vector3 = Vector3.ZERO
|
|
||||||
|
|
||||||
# World scale
|
|
||||||
var _world_scale : float = 1.0
|
|
||||||
|
|
||||||
# Left controller node
|
|
||||||
var _controller_left_node : XRController3D
|
|
||||||
|
|
||||||
# Right controller node
|
|
||||||
var _controller_right_node : XRController3D
|
|
||||||
|
|
||||||
# Parent controller (if this pointer is childed to a specific controller)
|
|
||||||
var _controller : XRController3D
|
|
||||||
|
|
||||||
# The currently active controller
|
|
||||||
var _active_controller : XRController3D
|
|
||||||
|
|
||||||
|
|
||||||
## Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsFunctionPointer"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
# Do not initialise if in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Read the initial world-scale
|
|
||||||
_world_scale = XRServer.world_scale
|
|
||||||
|
|
||||||
# Check for a parent controller
|
|
||||||
_controller = XRHelpers.get_xr_controller(self)
|
|
||||||
if _controller:
|
|
||||||
# Set as active on the parent controller
|
|
||||||
_active_controller = _controller
|
|
||||||
|
|
||||||
# Get button press feedback from our parent controller
|
|
||||||
_controller.button_pressed.connect(_on_button_pressed.bind(_controller))
|
|
||||||
_controller.button_released.connect(_on_button_released.bind(_controller))
|
|
||||||
else:
|
|
||||||
# Get the left and right controllers
|
|
||||||
_controller_left_node = XRHelpers.get_left_controller(self)
|
|
||||||
_controller_right_node = XRHelpers.get_right_controller(self)
|
|
||||||
|
|
||||||
# Start out right hand controller
|
|
||||||
_active_controller = _controller_right_node
|
|
||||||
|
|
||||||
# Get button press feedback from both left and right controllers
|
|
||||||
_controller_left_node.button_pressed.connect(
|
|
||||||
_on_button_pressed.bind(_controller_left_node))
|
|
||||||
_controller_left_node.button_released.connect(
|
|
||||||
_on_button_released.bind(_controller_left_node))
|
|
||||||
_controller_right_node.button_pressed.connect(
|
|
||||||
_on_button_pressed.bind(_controller_right_node))
|
|
||||||
_controller_right_node.button_released.connect(
|
|
||||||
_on_button_released.bind(_controller_right_node))
|
|
||||||
|
|
||||||
# init our state
|
|
||||||
_update_y_offset()
|
|
||||||
_update_distance()
|
|
||||||
_update_pointer()
|
|
||||||
_update_target_radius()
|
|
||||||
_update_target_material()
|
|
||||||
_update_collision_mask()
|
|
||||||
_update_collide_with_bodies()
|
|
||||||
_update_collide_with_areas()
|
|
||||||
_update_suppress_radius()
|
|
||||||
_update_suppress_mask()
|
|
||||||
|
|
||||||
|
|
||||||
# Called on each frame to update the pickup
|
|
||||||
func _process(_delta):
|
|
||||||
# Do not process if in the editor
|
|
||||||
if Engine.is_editor_hint() or !is_inside_tree():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Track the active controller (if this pointer is not childed to a controller)
|
|
||||||
if _controller == null and _active_controller != null:
|
|
||||||
transform = _active_controller.transform
|
|
||||||
|
|
||||||
# Handle world-scale changes
|
|
||||||
var new_world_scale := XRServer.world_scale
|
|
||||||
if (_world_scale != new_world_scale):
|
|
||||||
_world_scale = new_world_scale
|
|
||||||
_update_y_offset()
|
|
||||||
|
|
||||||
# Find the new pointer target
|
|
||||||
var new_target : Node3D
|
|
||||||
var new_at : Vector3
|
|
||||||
var suppress_area := $SuppressArea
|
|
||||||
if (enabled and
|
|
||||||
not $SuppressArea.has_overlapping_bodies() and
|
|
||||||
not $SuppressArea.has_overlapping_areas() and
|
|
||||||
$RayCast.is_colliding()):
|
|
||||||
new_at = $RayCast.get_collision_point()
|
|
||||||
if target:
|
|
||||||
# Locked to 'target' even if we're colliding with something else
|
|
||||||
new_target = target
|
|
||||||
else:
|
|
||||||
# Target is whatever the raycast is colliding with
|
|
||||||
new_target = $RayCast.get_collider()
|
|
||||||
|
|
||||||
# If no current or previous collisions then skip
|
|
||||||
if not new_target and not last_target:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Handle pointer changes
|
|
||||||
if new_target and not last_target:
|
|
||||||
# Pointer entered new_target
|
|
||||||
XRToolsPointerEvent.entered(self, new_target, new_at)
|
|
||||||
|
|
||||||
# Pointer moved on new_target for the first time
|
|
||||||
XRToolsPointerEvent.moved(self, new_target, new_at, new_at)
|
|
||||||
|
|
||||||
# Update visible artifacts for hit
|
|
||||||
_visible_hit(new_at)
|
|
||||||
elif not new_target and last_target:
|
|
||||||
# Pointer exited last_target
|
|
||||||
XRToolsPointerEvent.exited(self, last_target, last_collided_at)
|
|
||||||
|
|
||||||
# Update visible artifacts for miss
|
|
||||||
_visible_miss()
|
|
||||||
elif new_target != last_target:
|
|
||||||
# Pointer exited last_target
|
|
||||||
XRToolsPointerEvent.exited(self, last_target, last_collided_at)
|
|
||||||
|
|
||||||
# Pointer entered new_target
|
|
||||||
XRToolsPointerEvent.entered(self, new_target, new_at)
|
|
||||||
|
|
||||||
# Pointer moved on new_target
|
|
||||||
XRToolsPointerEvent.moved(self, new_target, new_at, new_at)
|
|
||||||
|
|
||||||
# Move visible artifacts
|
|
||||||
_visible_move(new_at)
|
|
||||||
elif new_at != last_collided_at:
|
|
||||||
# Pointer moved on new_target
|
|
||||||
XRToolsPointerEvent.moved(self, new_target, new_at, last_collided_at)
|
|
||||||
|
|
||||||
# Move visible artifacts
|
|
||||||
_visible_move(new_at)
|
|
||||||
|
|
||||||
# Update last values
|
|
||||||
last_target = new_target
|
|
||||||
last_collided_at = new_at
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer enabled property
|
|
||||||
func set_enabled(p_enabled : bool) -> void:
|
|
||||||
enabled = p_enabled
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_pointer()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer y_offset property
|
|
||||||
func set_y_offset(p_offset : float) -> void:
|
|
||||||
y_offset = p_offset
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_y_offset()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer distance property
|
|
||||||
func set_distance(p_new_value : float) -> void:
|
|
||||||
distance = p_new_value
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_distance()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer show_laser property
|
|
||||||
func set_show_laser(p_show : LaserShow) -> void:
|
|
||||||
show_laser = p_show
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_pointer()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer laser_length property
|
|
||||||
func set_laser_length(p_laser_length : LaserLength) -> void:
|
|
||||||
laser_length = p_laser_length
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_pointer()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer laser_material property
|
|
||||||
func set_laser_material(p_laser_material : StandardMaterial3D) -> void:
|
|
||||||
laser_material = p_laser_material
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_pointer()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer laser_hit_material property
|
|
||||||
func set_laser_hit_material(p_laser_hit_material : StandardMaterial3D) -> void:
|
|
||||||
laser_hit_material = p_laser_hit_material
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_pointer()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer show_target property
|
|
||||||
func set_show_target(p_show_target : bool) -> void:
|
|
||||||
show_target = p_show_target
|
|
||||||
if is_inside_tree():
|
|
||||||
$Target.visible = enabled and show_target and last_target
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer target_radius property
|
|
||||||
func set_target_radius(p_target_radius : float) -> void:
|
|
||||||
target_radius = p_target_radius
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_target_radius()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer target_material property
|
|
||||||
func set_target_material(p_target_material : StandardMaterial3D) -> void:
|
|
||||||
target_material = p_target_material
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_target_material()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer collision_mask property
|
|
||||||
func set_collision_mask(p_new_mask : int) -> void:
|
|
||||||
collision_mask = p_new_mask
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_collision_mask()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer collide_with_bodies property
|
|
||||||
func set_collide_with_bodies(p_new_value : bool) -> void:
|
|
||||||
collide_with_bodies = p_new_value
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_collide_with_bodies()
|
|
||||||
|
|
||||||
|
|
||||||
# Set pointer collide_with_areas property
|
|
||||||
func set_collide_with_areas(p_new_value : bool) -> void:
|
|
||||||
collide_with_areas = p_new_value
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_collide_with_areas()
|
|
||||||
|
|
||||||
|
|
||||||
# Set suppress radius property
|
|
||||||
func set_suppress_radius(p_suppress_radius : float) -> void:
|
|
||||||
suppress_radius = p_suppress_radius
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_suppress_radius()
|
|
||||||
|
|
||||||
|
|
||||||
func set_suppress_mask(p_suppress_mask : int) -> void:
|
|
||||||
suppress_mask = p_suppress_mask
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_suppress_mask()
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer Y offset update handler
|
|
||||||
func _update_y_offset() -> void:
|
|
||||||
$Laser.position.y = y_offset * _world_scale
|
|
||||||
$RayCast.position.y = y_offset * _world_scale
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer distance update handler
|
|
||||||
func _update_distance() -> void:
|
|
||||||
$RayCast.target_position.z = -distance
|
|
||||||
_update_pointer()
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer target radius update handler
|
|
||||||
func _update_target_radius() -> void:
|
|
||||||
$Target.mesh.radius = target_radius
|
|
||||||
$Target.mesh.height = target_radius * 2
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer target_material update handler
|
|
||||||
func _update_target_material() -> void:
|
|
||||||
$Target.set_surface_override_material(0, target_material)
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer collision_mask update handler
|
|
||||||
func _update_collision_mask() -> void:
|
|
||||||
$RayCast.collision_mask = collision_mask
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer collide_with_bodies update handler
|
|
||||||
func _update_collide_with_bodies() -> void:
|
|
||||||
$RayCast.collide_with_bodies = collide_with_bodies
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer collide_with_areas update handler
|
|
||||||
func _update_collide_with_areas() -> void:
|
|
||||||
$RayCast.collide_with_areas = collide_with_areas
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer suppress_radius update handler
|
|
||||||
func _update_suppress_radius() -> void:
|
|
||||||
$SuppressArea/CollisionShape3D.shape.radius = suppress_radius
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer suppress_mask update handler
|
|
||||||
func _update_suppress_mask() -> void:
|
|
||||||
$SuppressArea.collision_mask = suppress_mask
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer visible artifacts update handler
|
|
||||||
func _update_pointer() -> void:
|
|
||||||
if enabled and last_target:
|
|
||||||
_visible_hit(last_collided_at)
|
|
||||||
else:
|
|
||||||
_visible_miss()
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer-activation button pressed handler
|
|
||||||
func _button_pressed() -> void:
|
|
||||||
if $RayCast.is_colliding():
|
|
||||||
# Report pressed
|
|
||||||
target = $RayCast.get_collider()
|
|
||||||
last_collided_at = $RayCast.get_collision_point()
|
|
||||||
XRToolsPointerEvent.pressed(self, target, last_collided_at)
|
|
||||||
|
|
||||||
|
|
||||||
# Pointer-activation button released handler
|
|
||||||
func _button_released() -> void:
|
|
||||||
if target:
|
|
||||||
# Report release
|
|
||||||
XRToolsPointerEvent.released(self, target, last_collided_at)
|
|
||||||
target = null
|
|
||||||
last_collided_at = Vector3(0, 0, 0)
|
|
||||||
|
|
||||||
|
|
||||||
# Button pressed handler
|
|
||||||
func _on_button_pressed(p_button : String, controller : XRController3D) -> void:
|
|
||||||
if p_button == active_button_action and enabled:
|
|
||||||
if controller == _active_controller:
|
|
||||||
_button_pressed()
|
|
||||||
else:
|
|
||||||
_active_controller = controller
|
|
||||||
|
|
||||||
|
|
||||||
# Button released handler
|
|
||||||
func _on_button_released(p_button : String, _controller : XRController3D) -> void:
|
|
||||||
if p_button == active_button_action and target:
|
|
||||||
_button_released()
|
|
||||||
|
|
||||||
|
|
||||||
# Update the laser active material
|
|
||||||
func _update_laser_active_material(hit : bool) -> void:
|
|
||||||
if hit and laser_hit_material:
|
|
||||||
$Laser.set_surface_override_material(0, laser_hit_material)
|
|
||||||
else:
|
|
||||||
$Laser.set_surface_override_material(0, laser_material)
|
|
||||||
|
|
||||||
|
|
||||||
# Update the visible artifacts to show a hit
|
|
||||||
func _visible_hit(at : Vector3) -> void:
|
|
||||||
# Show target if enabled
|
|
||||||
if show_target:
|
|
||||||
$Target.global_transform.origin = at
|
|
||||||
$Target.visible = true
|
|
||||||
|
|
||||||
# Control laser visibility
|
|
||||||
if show_laser != LaserShow.HIDE:
|
|
||||||
# Ensure the correct laser material is set
|
|
||||||
_update_laser_active_material(true)
|
|
||||||
|
|
||||||
# Adjust laser length
|
|
||||||
if laser_length == LaserLength.COLLIDE:
|
|
||||||
var collide_len : float = at.distance_to(global_transform.origin)
|
|
||||||
$Laser.mesh.size.z = collide_len
|
|
||||||
$Laser.position.z = collide_len * -0.5
|
|
||||||
else:
|
|
||||||
$Laser.mesh.size.z = distance
|
|
||||||
$Laser.position.z = distance * -0.5
|
|
||||||
|
|
||||||
# Show laser
|
|
||||||
$Laser.visible = true
|
|
||||||
else:
|
|
||||||
# Ensure laser is hidden
|
|
||||||
$Laser.visible = false
|
|
||||||
|
|
||||||
|
|
||||||
# Move the visible pointer artifacts to the target
|
|
||||||
func _visible_move(at : Vector3) -> void:
|
|
||||||
# Move target if configured
|
|
||||||
if show_target:
|
|
||||||
$Target.global_transform.origin = at
|
|
||||||
|
|
||||||
# Adjust laser length if set to collide-length
|
|
||||||
if laser_length == LaserLength.COLLIDE:
|
|
||||||
var collide_len : float = at.distance_to(global_transform.origin)
|
|
||||||
$Laser.mesh.size.z = collide_len
|
|
||||||
$Laser.position.z = collide_len * -0.5
|
|
||||||
|
|
||||||
|
|
||||||
# Update the visible artifacts to show a miss
|
|
||||||
func _visible_miss() -> void:
|
|
||||||
# Ensure target is hidden
|
|
||||||
$Target.visible = false
|
|
||||||
|
|
||||||
# Ensure the correct laser material is set
|
|
||||||
_update_laser_active_material(false)
|
|
||||||
|
|
||||||
# Hide laser if not set to show always
|
|
||||||
$Laser.visible = show_laser == LaserShow.SHOW
|
|
||||||
|
|
||||||
# Restore laser length if set to collide-length
|
|
||||||
$Laser.mesh.size.z = distance
|
|
||||||
$Laser.position.z = distance * -0.5
|
|
@ -1 +0,0 @@
|
|||||||
uid://d6fep3ggibm8
|
|
@ -1,44 +0,0 @@
|
|||||||
[gd_scene load_steps=6 format=3 uid="uid://cqhw276realc"]
|
|
||||||
|
|
||||||
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/pointer.tres" id="1"]
|
|
||||||
[ext_resource type="Script" uid="uid://d6fep3ggibm8" path="res://addons/godot-xr-tools/functions/function_pointer.gd" id="2"]
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="1"]
|
|
||||||
resource_local_to_scene = true
|
|
||||||
material = ExtResource("1")
|
|
||||||
size = Vector3(0.002, 0.002, 10)
|
|
||||||
subdivide_depth = 20
|
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="2"]
|
|
||||||
material = ExtResource("1")
|
|
||||||
radius = 0.05
|
|
||||||
height = 0.1
|
|
||||||
radial_segments = 16
|
|
||||||
rings = 8
|
|
||||||
|
|
||||||
[sub_resource type="SphereShape3D" id="SphereShape3D_k3gfm"]
|
|
||||||
radius = 0.2
|
|
||||||
|
|
||||||
[node name="FunctionPointer" type="Node3D"]
|
|
||||||
script = ExtResource("2")
|
|
||||||
|
|
||||||
[node name="RayCast" type="RayCast3D" parent="."]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.013, 0)
|
|
||||||
target_position = Vector3(0, 0, -10)
|
|
||||||
collision_mask = 5242880
|
|
||||||
|
|
||||||
[node name="Laser" type="MeshInstance3D" parent="."]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.013, -5)
|
|
||||||
cast_shadow = 0
|
|
||||||
mesh = SubResource("1")
|
|
||||||
|
|
||||||
[node name="Target" type="MeshInstance3D" parent="."]
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("2")
|
|
||||||
|
|
||||||
[node name="SuppressArea" type="Area3D" parent="."]
|
|
||||||
collision_layer = 0
|
|
||||||
collision_mask = 4194304
|
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="SuppressArea"]
|
|
||||||
shape = SubResource("SphereShape3D_k3gfm")
|
|
@ -1,106 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/hand.svg")
|
|
||||||
class_name XRToolsFunctionPoseDetector
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Function Pose Area
|
|
||||||
##
|
|
||||||
## This area works with the XRToolsHandPoseArea to control the pose
|
|
||||||
## of the VR hands.
|
|
||||||
|
|
||||||
|
|
||||||
# Default pose detector collision mask of 22:pose-area
|
|
||||||
const DEFAULT_MASK := 0b0000_0000_0010_0000_0000_0000_0000_0000
|
|
||||||
|
|
||||||
|
|
||||||
## Collision mask to detect hand pose areas
|
|
||||||
@export_flags_3d_physics var collision_mask : int = DEFAULT_MASK: set = set_collision_mask
|
|
||||||
|
|
||||||
|
|
||||||
## Hand controller
|
|
||||||
@onready var _controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
## Hand to control
|
|
||||||
@onready var _hand := XRToolsHand.find_instance(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsFunctionPoseDetector"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
# Connect signals (if controller and hand are valid)
|
|
||||||
if _controller and _hand:
|
|
||||||
if $SenseArea.area_entered.connect(_on_area_entered):
|
|
||||||
push_error("Unable to connect area_entered signal")
|
|
||||||
if $SenseArea.area_exited.connect(_on_area_exited):
|
|
||||||
push_error("Unable to connect area_exited signal")
|
|
||||||
|
|
||||||
# Update collision mask
|
|
||||||
_update_collision_mask()
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the pose area has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
if !XRHelpers.get_xr_controller(self):
|
|
||||||
warnings.append("Node must be within a branch of an XRController3D node")
|
|
||||||
|
|
||||||
# Verify hand can be found
|
|
||||||
if !XRToolsHand.find_instance(self):
|
|
||||||
warnings.append("Node must be a within a branch of an XRController node with a hand")
|
|
||||||
|
|
||||||
# Pass basic validation
|
|
||||||
return warnings
|
|
||||||
|
|
||||||
|
|
||||||
func set_collision_mask(mask : int) -> void:
|
|
||||||
collision_mask = mask
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_collision_mask()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_collision_mask() -> void:
|
|
||||||
$SenseArea.collision_mask = collision_mask
|
|
||||||
|
|
||||||
|
|
||||||
## Signal handler called when this XRToolsFunctionPoseArea enters an area
|
|
||||||
func _on_area_entered(area : Area3D) -> void:
|
|
||||||
# Igjnore if the area is not a hand-pose area
|
|
||||||
var pose_area := area as XRToolsHandPoseArea
|
|
||||||
if !pose_area:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Set the appropriate poses
|
|
||||||
if _controller.tracker == "left_hand" and pose_area.left_pose:
|
|
||||||
_hand.add_pose_override(
|
|
||||||
pose_area,
|
|
||||||
pose_area.pose_priority,
|
|
||||||
pose_area.left_pose)
|
|
||||||
# Disable grabpoints in this pose_area
|
|
||||||
pose_area.disable_grab_points()
|
|
||||||
elif _controller.tracker == "right_hand" and pose_area.right_pose:
|
|
||||||
_hand.add_pose_override(
|
|
||||||
pose_area,
|
|
||||||
pose_area.pose_priority,
|
|
||||||
pose_area.right_pose)
|
|
||||||
# Disable grabpoints in this pose_area
|
|
||||||
pose_area.disable_grab_points()
|
|
||||||
|
|
||||||
|
|
||||||
## Signal handler called when this XRToolsFunctionPoseArea leaves an area
|
|
||||||
func _on_area_exited(area : Area3D) -> void:
|
|
||||||
# Ignore if the area is not a hand-pose area
|
|
||||||
var pose_area := area as XRToolsHandPoseArea
|
|
||||||
if !pose_area:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Remove any overrides set from this hand-pose area
|
|
||||||
_hand.remove_pose_override(pose_area)
|
|
||||||
|
|
||||||
# Enable previously disabled grabpoints
|
|
||||||
pose_area.enable_grab_points()
|
|
@ -1 +0,0 @@
|
|||||||
uid://jwtxm3vcaerf
|
|
@ -1,19 +0,0 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://bft3xyxs31ci3"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://jwtxm3vcaerf" path="res://addons/godot-xr-tools/functions/function_pose_detector.gd" id="1"]
|
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="1"]
|
|
||||||
radius = 0.08
|
|
||||||
height = 0.24
|
|
||||||
|
|
||||||
[node name="FunctionPoseDetector" type="Node3D"]
|
|
||||||
script = ExtResource("1")
|
|
||||||
|
|
||||||
[node name="SenseArea" type="Area3D" parent="."]
|
|
||||||
collision_layer = 0
|
|
||||||
collision_mask = 2097152
|
|
||||||
monitorable = false
|
|
||||||
|
|
||||||
[node name="CollisionShape" type="CollisionShape3D" parent="SenseArea"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, -0.04, 0.08)
|
|
||||||
shape = SubResource("1")
|
|
@ -1,495 +0,0 @@
|
|||||||
@tool
|
|
||||||
@icon("res://addons/godot-xr-tools/editor/icons/function.svg")
|
|
||||||
class_name XRToolsFunctionTeleport
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Function Teleport Script
|
|
||||||
##
|
|
||||||
## This script provides teleport functionality.
|
|
||||||
##
|
|
||||||
## Add this scene as a sub scene of your [XRController3D] node to implement
|
|
||||||
## a teleport function on that controller.
|
|
||||||
|
|
||||||
|
|
||||||
# Default teleport collision mask of all
|
|
||||||
const DEFAULT_MASK := 0b1111_1111_1111_1111_1111_1111_1111_1111
|
|
||||||
|
|
||||||
# Default material
|
|
||||||
# gdlint:ignore = load-constant-name
|
|
||||||
const _DefaultMaterial := preload("res://addons/godot-xr-tools/materials/capsule.tres")
|
|
||||||
|
|
||||||
|
|
||||||
## If true, teleporting is enabled
|
|
||||||
@export var enabled : bool = true: set = set_enabled
|
|
||||||
|
|
||||||
## Teleport button action
|
|
||||||
@export var teleport_button_action : String = "trigger_click"
|
|
||||||
|
|
||||||
## Teleport rotation action
|
|
||||||
@export var rotation_action : String = "primary"
|
|
||||||
|
|
||||||
# Teleport Path Group
|
|
||||||
@export_group("Visuals")
|
|
||||||
|
|
||||||
## Teleport allowed color property
|
|
||||||
@export var can_teleport_color : Color = Color(0.0, 1.0, 0.0, 1.0)
|
|
||||||
|
|
||||||
## Teleport denied color property
|
|
||||||
@export var cant_teleport_color : Color = Color(1.0, 0.0, 0.0, 1.0)
|
|
||||||
|
|
||||||
## Teleport no-collision color property
|
|
||||||
@export var no_collision_color : Color = Color(45.0 / 255.0, 80.0 / 255.0, 220.0 / 255.0, 1.0)
|
|
||||||
|
|
||||||
## Teleport-arc strength
|
|
||||||
@export var strength : float = 5.0
|
|
||||||
|
|
||||||
## Teleport texture
|
|
||||||
@export var arc_texture : Texture2D \
|
|
||||||
= preload("res://addons/godot-xr-tools/images/teleport_arrow.png") \
|
|
||||||
: set = set_arc_texture
|
|
||||||
|
|
||||||
## Target texture
|
|
||||||
@export var target_texture : Texture2D \
|
|
||||||
= preload("res://addons/godot-xr-tools/images/teleport_target.png") \
|
|
||||||
: set = set_target_texture
|
|
||||||
|
|
||||||
# Player Group
|
|
||||||
@export_group("Player")
|
|
||||||
|
|
||||||
## Player height property
|
|
||||||
@export var player_height : float = 1.8: set = set_player_height
|
|
||||||
|
|
||||||
## Player radius property
|
|
||||||
@export var player_radius : float = 0.4: set = set_player_radius
|
|
||||||
|
|
||||||
## Player scene
|
|
||||||
@export var player_scene : PackedScene: set = set_player_scene
|
|
||||||
|
|
||||||
# Target Group
|
|
||||||
@export_group("Collision")
|
|
||||||
|
|
||||||
## Maximum floor slope
|
|
||||||
@export var max_slope : float = 20.0
|
|
||||||
|
|
||||||
## Collision mask
|
|
||||||
@export_flags_3d_physics var collision_mask : int = 1023
|
|
||||||
|
|
||||||
## Valid teleport layer mask
|
|
||||||
@export_flags_3d_physics var valid_teleport_mask : int = DEFAULT_MASK
|
|
||||||
|
|
||||||
|
|
||||||
## Player capsule material (ignored for custom player scenes)
|
|
||||||
var player_material : StandardMaterial3D = _DefaultMaterial : set = set_player_material
|
|
||||||
|
|
||||||
|
|
||||||
var is_on_floor : bool = true
|
|
||||||
var is_teleporting : bool = false
|
|
||||||
var can_teleport : bool = true
|
|
||||||
var teleport_rotation : float = 0.0;
|
|
||||||
var floor_normal : Vector3 = Vector3.UP
|
|
||||||
var last_target_transform : Transform3D = Transform3D()
|
|
||||||
var collision_shape : Shape3D
|
|
||||||
var step_size : float = 0.5
|
|
||||||
|
|
||||||
|
|
||||||
# Custom player scene
|
|
||||||
var player : Node3D
|
|
||||||
|
|
||||||
|
|
||||||
# World scale
|
|
||||||
@onready var ws : float = XRServer.world_scale
|
|
||||||
|
|
||||||
## Capsule shown when not using a custom player mesh
|
|
||||||
@onready var capsule : MeshInstance3D = $Target/Player_figure/Capsule
|
|
||||||
|
|
||||||
## [XRToolsPlayerBody] node.
|
|
||||||
@onready var player_body := XRToolsPlayerBody.find_instance(self)
|
|
||||||
|
|
||||||
## [XRController3D] node.
|
|
||||||
@onready var controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsFunctionTeleport"
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
# Do not initialise if in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# It's inactive when we start
|
|
||||||
$Teleport.visible = false
|
|
||||||
$Target.visible = false
|
|
||||||
|
|
||||||
# Scale to our world scale
|
|
||||||
$Teleport.mesh.size = Vector2(0.05 * ws, 1.0)
|
|
||||||
$Target.mesh.size = Vector2(ws, ws)
|
|
||||||
$Target/Player_figure.scale = Vector3(ws, ws, ws)
|
|
||||||
|
|
||||||
# get our capsule shape
|
|
||||||
collision_shape = CapsuleShape3D.new()
|
|
||||||
|
|
||||||
# Apply properties
|
|
||||||
_update_arc_texture()
|
|
||||||
_update_target_texture()
|
|
||||||
_update_player_scene()
|
|
||||||
_update_player_height()
|
|
||||||
_update_player_radius()
|
|
||||||
_update_player_material()
|
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta):
|
|
||||||
# Do not process physics if in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Skip if required nodes are missing
|
|
||||||
if !player_body or !controller:
|
|
||||||
return
|
|
||||||
|
|
||||||
# if we're not enabled no point in doing mode
|
|
||||||
if !enabled:
|
|
||||||
# reset these
|
|
||||||
is_teleporting = false;
|
|
||||||
$Teleport.visible = false
|
|
||||||
$Target.visible = false
|
|
||||||
|
|
||||||
# and stop this from running until we enable again
|
|
||||||
set_physics_process(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
# check if our world scale has changed..
|
|
||||||
var new_ws := XRServer.world_scale
|
|
||||||
if ws != new_ws:
|
|
||||||
ws = new_ws
|
|
||||||
$Teleport.mesh.size = Vector2(0.05 * ws, 1.0)
|
|
||||||
$Target.mesh.size = Vector2(ws, ws)
|
|
||||||
$Target/Player_figure.scale = Vector3(ws, ws, ws)
|
|
||||||
|
|
||||||
if controller and controller.get_is_active() and \
|
|
||||||
controller.is_button_pressed(teleport_button_action):
|
|
||||||
if !is_teleporting:
|
|
||||||
is_teleporting = true
|
|
||||||
$Teleport.visible = true
|
|
||||||
$Target.visible = true
|
|
||||||
teleport_rotation = 0.0
|
|
||||||
|
|
||||||
# get our physics engine state
|
|
||||||
var state := get_world_3d().direct_space_state
|
|
||||||
var query := PhysicsShapeQueryParameters3D.new()
|
|
||||||
|
|
||||||
# init stuff about our query that doesn't change
|
|
||||||
query.collision_mask = collision_mask
|
|
||||||
query.margin = collision_shape.margin
|
|
||||||
query.shape_rid = collision_shape.get_rid()
|
|
||||||
|
|
||||||
# make a transform for offsetting our shape, it's always
|
|
||||||
# lying on its side by default...
|
|
||||||
var shape_transform := Transform3D(
|
|
||||||
Basis(),
|
|
||||||
Vector3(0.0, player_height / 2.0, 0.0))
|
|
||||||
|
|
||||||
# update location
|
|
||||||
var teleport_global_transform : Transform3D = $Teleport.global_transform
|
|
||||||
var target_global_origin := teleport_global_transform.origin
|
|
||||||
var up := player_body.up_player
|
|
||||||
var down := -up.normalized() / ws
|
|
||||||
|
|
||||||
############################################################
|
|
||||||
# New teleport logic
|
|
||||||
# We're going to use test move in steps to find out where we hit something...
|
|
||||||
# This can be optimised loads by determining the lenght based on the angle
|
|
||||||
# between sections extending the length when we're in a flat part of the arch
|
|
||||||
# Where we do get a collission we may want to fine tune the collision
|
|
||||||
var cast_length := 0.0
|
|
||||||
var fine_tune := 1.0
|
|
||||||
var hit_something := false
|
|
||||||
var max_slope_cos := cos(deg_to_rad(max_slope))
|
|
||||||
for i in range(1,26):
|
|
||||||
var new_cast_length := cast_length + (step_size / fine_tune)
|
|
||||||
var global_target := Vector3(0.0, 0.0, -new_cast_length)
|
|
||||||
|
|
||||||
# our quadratic values
|
|
||||||
var t := global_target.z / strength
|
|
||||||
var t2 := t * t
|
|
||||||
|
|
||||||
# target to world space
|
|
||||||
global_target = teleport_global_transform * global_target
|
|
||||||
|
|
||||||
# adjust for gravity
|
|
||||||
global_target += down * t2
|
|
||||||
|
|
||||||
# test our new location for collisions
|
|
||||||
query.transform = Transform3D(
|
|
||||||
player_body.global_transform.basis,
|
|
||||||
global_target) * shape_transform
|
|
||||||
var cast_result := state.collide_shape(query, 10)
|
|
||||||
if cast_result.is_empty():
|
|
||||||
# we didn't collide with anything so check our next section...
|
|
||||||
cast_length = new_cast_length
|
|
||||||
target_global_origin = global_target
|
|
||||||
elif (fine_tune <= 16.0):
|
|
||||||
# try again with a small step size
|
|
||||||
fine_tune *= 2.0
|
|
||||||
else:
|
|
||||||
# if we don't collide make sure we keep using our current origin point
|
|
||||||
var collided_at := target_global_origin
|
|
||||||
|
|
||||||
# check for collision
|
|
||||||
var step_delta := global_target - target_global_origin
|
|
||||||
if up.dot(step_delta) > 0:
|
|
||||||
# if we're moving up, we hit the ceiling of something, we
|
|
||||||
# don't really care what
|
|
||||||
is_on_floor = false
|
|
||||||
else:
|
|
||||||
# now we cast a ray downwards to see if we're on a surface
|
|
||||||
var ray_query := PhysicsRayQueryParameters3D.new()
|
|
||||||
ray_query.from = target_global_origin + (up * 0.5 * player_height)
|
|
||||||
ray_query.to = target_global_origin - (up * 1.1 * player_height)
|
|
||||||
ray_query.collision_mask = collision_mask
|
|
||||||
|
|
||||||
var intersects := state.intersect_ray(ray_query)
|
|
||||||
if intersects.is_empty():
|
|
||||||
is_on_floor = false
|
|
||||||
else:
|
|
||||||
# did we collide with a floor or a wall?
|
|
||||||
floor_normal = intersects["normal"]
|
|
||||||
var dot := up.dot(floor_normal)
|
|
||||||
|
|
||||||
if dot > max_slope_cos:
|
|
||||||
is_on_floor = true
|
|
||||||
else:
|
|
||||||
is_on_floor = false
|
|
||||||
|
|
||||||
# Update our collision point if it's moved enough, this
|
|
||||||
# solves a little bit of jittering
|
|
||||||
var diff : Vector3 = collided_at - intersects["position"]
|
|
||||||
|
|
||||||
if diff.length() > 0.1:
|
|
||||||
collided_at = intersects["position"]
|
|
||||||
|
|
||||||
# Fail if the hit target isn't in our valid mask
|
|
||||||
var collider_mask : int = intersects["collider"].collision_layer
|
|
||||||
if not valid_teleport_mask & collider_mask:
|
|
||||||
is_on_floor = false
|
|
||||||
|
|
||||||
# we are colliding, find our if we're colliding on a wall or
|
|
||||||
# floor, one we can do, the other nope...
|
|
||||||
cast_length += (collided_at - target_global_origin).length()
|
|
||||||
target_global_origin = collided_at
|
|
||||||
hit_something = true
|
|
||||||
break
|
|
||||||
|
|
||||||
# and just update our shader
|
|
||||||
$Teleport.get_surface_override_material(0).set_shader_parameter("scale_t", 1.0 / strength)
|
|
||||||
$Teleport.get_surface_override_material(0).set_shader_parameter("down", down)
|
|
||||||
$Teleport.get_surface_override_material(0).set_shader_parameter("length", cast_length)
|
|
||||||
if hit_something:
|
|
||||||
var color := can_teleport_color
|
|
||||||
var normal := up
|
|
||||||
if is_on_floor:
|
|
||||||
# if we're on the floor we'll reorientate our target to match.
|
|
||||||
normal = floor_normal
|
|
||||||
can_teleport = true
|
|
||||||
else:
|
|
||||||
can_teleport = false
|
|
||||||
color = cant_teleport_color
|
|
||||||
|
|
||||||
# check our axis to see if we need to rotate
|
|
||||||
teleport_rotation += (delta * controller.get_vector2(rotation_action).x * -4.0)
|
|
||||||
|
|
||||||
# update target and colour
|
|
||||||
var target_basis := Basis()
|
|
||||||
target_basis.y = normal
|
|
||||||
target_basis.x = teleport_global_transform.basis.x.slide(normal).normalized()
|
|
||||||
target_basis.z = target_basis.x.cross(target_basis.y)
|
|
||||||
|
|
||||||
target_basis = target_basis.rotated(normal, teleport_rotation)
|
|
||||||
last_target_transform.basis = target_basis
|
|
||||||
last_target_transform.origin = target_global_origin + up * 0.001
|
|
||||||
$Target.global_transform = last_target_transform
|
|
||||||
|
|
||||||
$Teleport.get_surface_override_material(0).set_shader_parameter("mix_color", color)
|
|
||||||
$Target.get_surface_override_material(0).albedo_color = color
|
|
||||||
$Target.visible = can_teleport
|
|
||||||
else:
|
|
||||||
can_teleport = false
|
|
||||||
$Target.visible = false
|
|
||||||
$Teleport.get_surface_override_material(0).set_shader_parameter("mix_color", no_collision_color)
|
|
||||||
elif is_teleporting:
|
|
||||||
if can_teleport:
|
|
||||||
|
|
||||||
# Make our target using the players up vector
|
|
||||||
var new_transform := last_target_transform
|
|
||||||
new_transform.basis.y = player_body.up_player
|
|
||||||
new_transform.basis.x = new_transform.basis.y.cross(new_transform.basis.z).normalized()
|
|
||||||
new_transform.basis.z = new_transform.basis.x.cross(new_transform.basis.y).normalized()
|
|
||||||
|
|
||||||
# Teleport the player
|
|
||||||
player_body.teleport(new_transform)
|
|
||||||
|
|
||||||
# and disable
|
|
||||||
is_teleporting = false;
|
|
||||||
$Teleport.visible = false
|
|
||||||
$Target.visible = false
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the teleport has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := PackedStringArray()
|
|
||||||
|
|
||||||
# Verify we can find the XRToolsPlayerBody
|
|
||||||
if !XRToolsPlayerBody.find_instance(self):
|
|
||||||
warnings.append("This node must be within a branch of an XRToolsPlayerBody node")
|
|
||||||
|
|
||||||
# Verify we can find the XRController3D
|
|
||||||
if !XRHelpers.get_xr_controller(self):
|
|
||||||
warnings.append("This node must be within a branch of an XRController3D node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
||||||
|
|
||||||
|
|
||||||
# Provide custom property information
|
|
||||||
func _get_property_list() -> Array[Dictionary]:
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
"name" : "Player",
|
|
||||||
"type" : TYPE_NIL,
|
|
||||||
"usage" : PROPERTY_USAGE_GROUP
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name" : "player_material",
|
|
||||||
"class_name" : "StandardMaterial3D",
|
|
||||||
"type" : TYPE_OBJECT,
|
|
||||||
"usage" : PROPERTY_USAGE_NO_EDITOR if player_scene else PROPERTY_USAGE_DEFAULT,
|
|
||||||
"hint" : PROPERTY_HINT_RESOURCE_TYPE,
|
|
||||||
"hint_string" : "StandardMaterial3D"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Allow revert of custom properties
|
|
||||||
func _property_can_revert(property : StringName) -> bool:
|
|
||||||
return property == "player_material"
|
|
||||||
|
|
||||||
|
|
||||||
# Provide revert values for custom properties
|
|
||||||
func _property_get_revert(property : StringName): # Variant
|
|
||||||
if property == "player_material":
|
|
||||||
return _DefaultMaterial
|
|
||||||
|
|
||||||
|
|
||||||
# Set enabled property
|
|
||||||
func set_enabled(new_value : bool) -> void:
|
|
||||||
enabled = new_value
|
|
||||||
if enabled:
|
|
||||||
# make sure our physics process is on
|
|
||||||
set_physics_process(true)
|
|
||||||
else:
|
|
||||||
# we turn this off in physics process just in case we want to do some cleanup
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Set the arc texture
|
|
||||||
func set_arc_texture(p_arc_texture : Texture2D) -> void:
|
|
||||||
arc_texture = p_arc_texture
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_arc_texture()
|
|
||||||
|
|
||||||
|
|
||||||
# Set the target texture
|
|
||||||
func set_target_texture(p_target_texture : Texture2D) -> void:
|
|
||||||
target_texture = p_target_texture
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_target_texture()
|
|
||||||
|
|
||||||
|
|
||||||
# Set player height property
|
|
||||||
func set_player_height(p_height : float) -> void:
|
|
||||||
player_height = p_height
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_player_height()
|
|
||||||
|
|
||||||
|
|
||||||
# Set player radius property
|
|
||||||
func set_player_radius(p_radius : float) -> void:
|
|
||||||
player_radius = p_radius
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_player_radius()
|
|
||||||
|
|
||||||
|
|
||||||
# Set the player scene
|
|
||||||
func set_player_scene(p_player_scene : PackedScene) -> void:
|
|
||||||
player_scene = p_player_scene
|
|
||||||
notify_property_list_changed()
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_player_scene()
|
|
||||||
|
|
||||||
|
|
||||||
# Set the player material
|
|
||||||
func set_player_material(p_player_material : StandardMaterial3D) -> void:
|
|
||||||
player_material = p_player_material
|
|
||||||
if is_inside_tree():
|
|
||||||
_update_player_material()
|
|
||||||
|
|
||||||
|
|
||||||
# Update arc texture
|
|
||||||
func _update_arc_texture():
|
|
||||||
var material : ShaderMaterial = $Teleport.get_surface_override_material(0)
|
|
||||||
if material and arc_texture:
|
|
||||||
material.set_shader_parameter("arrow_texture", arc_texture)
|
|
||||||
|
|
||||||
|
|
||||||
# Update target texture
|
|
||||||
func _update_target_texture():
|
|
||||||
var material : StandardMaterial3D = $Target.get_surface_override_material(0)
|
|
||||||
if material and target_texture:
|
|
||||||
material.albedo_texture = target_texture
|
|
||||||
|
|
||||||
|
|
||||||
# Player height update handler
|
|
||||||
func _update_player_height() -> void:
|
|
||||||
if collision_shape:
|
|
||||||
collision_shape.height = player_height - (2.0 * player_radius)
|
|
||||||
|
|
||||||
if capsule:
|
|
||||||
capsule.mesh.height = player_height
|
|
||||||
capsule.position = Vector3(0.0, player_height/2.0, 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
# Player radius update handler
|
|
||||||
func _update_player_radius():
|
|
||||||
if collision_shape:
|
|
||||||
collision_shape.height = player_height
|
|
||||||
collision_shape.radius = player_radius
|
|
||||||
|
|
||||||
if capsule:
|
|
||||||
capsule.mesh.height = player_height
|
|
||||||
capsule.mesh.radius = player_radius
|
|
||||||
|
|
||||||
|
|
||||||
# Update the player scene
|
|
||||||
func _update_player_scene() -> void:
|
|
||||||
# Free the current player
|
|
||||||
if player:
|
|
||||||
player.queue_free()
|
|
||||||
player = null
|
|
||||||
|
|
||||||
# If specified, instantiate a new player
|
|
||||||
if player_scene:
|
|
||||||
player = player_scene.instantiate()
|
|
||||||
$Target/Player_figure.add_child(player)
|
|
||||||
|
|
||||||
# Show the capsule mesh only if we have no player
|
|
||||||
capsule.visible = player == null
|
|
||||||
|
|
||||||
|
|
||||||
# Update player material
|
|
||||||
func _update_player_material():
|
|
||||||
if player_material:
|
|
||||||
capsule.set_surface_override_material(0, player_material)
|
|
@ -1 +0,0 @@
|
|||||||
uid://dr7ofa2encot3
|
|
@ -1,37 +0,0 @@
|
|||||||
[gd_scene load_steps=8 format=3 uid="uid://fiul51tsyoop"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dr7ofa2encot3" path="res://addons/godot-xr-tools/functions/function_teleport.gd" id="1"]
|
|
||||||
[ext_resource type="Material" uid="uid://bk72wfw25ff0v" path="res://addons/godot-xr-tools/materials/teleport.tres" id="2"]
|
|
||||||
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/target.tres" id="3"]
|
|
||||||
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/capsule.tres" id="4"]
|
|
||||||
|
|
||||||
[sub_resource type="PlaneMesh" id="1"]
|
|
||||||
size = Vector2(0.05, 1)
|
|
||||||
subdivide_depth = 40
|
|
||||||
|
|
||||||
[sub_resource type="PlaneMesh" id="2"]
|
|
||||||
size = Vector2(1, 1)
|
|
||||||
|
|
||||||
[sub_resource type="CapsuleMesh" id="3"]
|
|
||||||
radius = 0.4
|
|
||||||
height = 1.8
|
|
||||||
|
|
||||||
[node name="FunctionTeleport" type="Node3D"]
|
|
||||||
script = ExtResource("1")
|
|
||||||
player_material = ExtResource("4")
|
|
||||||
|
|
||||||
[node name="Teleport" type="MeshInstance3D" parent="."]
|
|
||||||
mesh = SubResource("1")
|
|
||||||
surface_material_override/0 = ExtResource("2")
|
|
||||||
|
|
||||||
[node name="Target" type="MeshInstance3D" parent="."]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, -4.92359)
|
|
||||||
mesh = SubResource("2")
|
|
||||||
surface_material_override/0 = ExtResource("3")
|
|
||||||
|
|
||||||
[node name="Player_figure" type="Marker3D" parent="Target"]
|
|
||||||
|
|
||||||
[node name="Capsule" type="MeshInstance3D" parent="Target/Player_figure"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0)
|
|
||||||
mesh = SubResource("3")
|
|
||||||
surface_material_override/0 = ExtResource("4")
|
|
@ -1,283 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementClimb
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Climbing
|
|
||||||
##
|
|
||||||
## This script provides climbing movement for the player. To add climbing
|
|
||||||
## support, the player must also have [XRToolsFunctionPickup] nodes attached
|
|
||||||
## to the left and right controllers, and an [XRToolsPlayerBody] under the
|
|
||||||
## [XROrigin3D].
|
|
||||||
##
|
|
||||||
## Climbable objects can inherit from the climbable scene, or be [StaticBody]
|
|
||||||
## objects with the [XRToolsClimbable] script attached to them.
|
|
||||||
##
|
|
||||||
## When climbing, the global velocity of the [XRToolsPlayerBody] is averaged,
|
|
||||||
## and upon release the velocity is applied to the [XRToolsPlayerBody] with an
|
|
||||||
## optional fling multiplier, so the player can fling themselves up walls if
|
|
||||||
## desired.
|
|
||||||
|
|
||||||
|
|
||||||
## Signal invoked when the player starts climing
|
|
||||||
signal player_climb_start
|
|
||||||
|
|
||||||
## Signal invoked when the player ends climbing
|
|
||||||
signal player_climb_end
|
|
||||||
|
|
||||||
|
|
||||||
## Distance at which grabs snap
|
|
||||||
const SNAP_DISTANCE : float = 1.0
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 15
|
|
||||||
|
|
||||||
## Push forward when flinging
|
|
||||||
@export var forward_push : float = 1.0
|
|
||||||
|
|
||||||
## Velocity multiplier when flinging up walls
|
|
||||||
@export var fling_multiplier : float = 1.0
|
|
||||||
|
|
||||||
## Averages for velocity measurement
|
|
||||||
@export var velocity_averages : int = 5
|
|
||||||
|
|
||||||
|
|
||||||
# Left climbing handle
|
|
||||||
var _left_handle : Node3D
|
|
||||||
|
|
||||||
# Right climbing handle
|
|
||||||
var _right_handle : Node3D
|
|
||||||
|
|
||||||
# Dominant handle (moving the player)
|
|
||||||
var _dominant : Node3D
|
|
||||||
|
|
||||||
|
|
||||||
# Velocity averager
|
|
||||||
@onready var _averager := XRToolsVelocityAveragerLinear.new(velocity_averages)
|
|
||||||
|
|
||||||
# Left pickup node
|
|
||||||
@onready var _left_pickup_node := XRToolsFunctionPickup.find_left(self)
|
|
||||||
|
|
||||||
# Right pickup node
|
|
||||||
@onready var _right_pickup_node := XRToolsFunctionPickup.find_right(self)
|
|
||||||
|
|
||||||
# Left controller
|
|
||||||
@onready var _left_controller := XRHelpers.get_left_controller(self)
|
|
||||||
|
|
||||||
# Right controller
|
|
||||||
@onready var _right_controller := XRHelpers.get_right_controller(self)
|
|
||||||
|
|
||||||
# Left collision hand
|
|
||||||
@onready var _left_hand := XRToolsHand.find_left(self)
|
|
||||||
|
|
||||||
# Right collision hand
|
|
||||||
@onready var _right_hand := XRToolsHand.find_right(self)
|
|
||||||
|
|
||||||
# Left collision hand
|
|
||||||
@onready var _left_collision_hand := XRToolsCollisionHand.find_left(self)
|
|
||||||
|
|
||||||
# Right collision hand
|
|
||||||
@onready var _right_collision_hand := XRToolsCollisionHand.find_right(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementClimb" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
## Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready():
|
|
||||||
# In Godot 4 we must now manually call our super class ready function
|
|
||||||
super()
|
|
||||||
|
|
||||||
# Do not initialise if in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Connect pickup funcitons
|
|
||||||
if _left_pickup_node.connect("has_picked_up", _on_left_picked_up):
|
|
||||||
push_error("Unable to connect left picked up signal")
|
|
||||||
if _right_pickup_node.connect("has_picked_up", _on_right_picked_up):
|
|
||||||
push_error("Unable to connect right picked up signal")
|
|
||||||
if _left_pickup_node.connect("has_dropped", _on_left_dropped):
|
|
||||||
push_error("Unable to connect left dropped signal")
|
|
||||||
if _right_pickup_node.connect("has_dropped", _on_right_dropped):
|
|
||||||
push_error("Unable to connect right dropped signal")
|
|
||||||
|
|
||||||
|
|
||||||
## Perform player physics movement
|
|
||||||
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool):
|
|
||||||
# Disable climbing if requested
|
|
||||||
if disabled or !enabled:
|
|
||||||
_set_climbing(false, player_body)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check for climbing handles being deleted while held
|
|
||||||
if not is_instance_valid(_left_handle):
|
|
||||||
_left_handle = null
|
|
||||||
if not is_instance_valid(_right_handle):
|
|
||||||
_right_handle = null
|
|
||||||
if not is_instance_valid(_dominant):
|
|
||||||
_dominant = null
|
|
||||||
|
|
||||||
# Snap grabs if too far
|
|
||||||
if _left_handle:
|
|
||||||
var left_pickup_pos := _left_controller.global_position
|
|
||||||
var left_grab_pos = _left_handle.global_position
|
|
||||||
if left_pickup_pos.distance_to(left_grab_pos) > SNAP_DISTANCE:
|
|
||||||
_left_pickup_node.drop_object()
|
|
||||||
if _right_handle:
|
|
||||||
var right_pickup_pos := _right_controller.global_position
|
|
||||||
var right_grab_pos := _right_handle.global_position
|
|
||||||
if right_pickup_pos.distance_to(right_grab_pos) > SNAP_DISTANCE:
|
|
||||||
_right_pickup_node.drop_object()
|
|
||||||
|
|
||||||
# Update climbing
|
|
||||||
_set_climbing(_dominant != null, player_body)
|
|
||||||
|
|
||||||
# Skip if not actively climbing
|
|
||||||
if !is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Calculate how much the player has moved
|
|
||||||
var offset := Vector3.ZERO
|
|
||||||
if _dominant == _left_handle:
|
|
||||||
var left_pickup_pos := _left_controller.global_position
|
|
||||||
var left_grab_pos := _left_handle.global_position
|
|
||||||
offset = left_pickup_pos - left_grab_pos
|
|
||||||
elif _dominant == _right_handle:
|
|
||||||
var right_pickup_pos := _right_controller.global_position
|
|
||||||
var right_grab_pos := _right_handle.global_position
|
|
||||||
offset = right_pickup_pos - right_grab_pos
|
|
||||||
|
|
||||||
# Move the player by the offset
|
|
||||||
var old_position := player_body.global_position
|
|
||||||
player_body.move_and_collide(-offset)
|
|
||||||
player_body.velocity = Vector3.ZERO
|
|
||||||
|
|
||||||
# Update the players average-velocity data
|
|
||||||
var distance := player_body.global_position - old_position
|
|
||||||
_averager.add_distance(delta, distance)
|
|
||||||
|
|
||||||
# Report exclusive motion performed (to bypass gravity)
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
## Start or stop climbing
|
|
||||||
func _set_climbing(active: bool, player_body: XRToolsPlayerBody) -> void:
|
|
||||||
# Skip if no change
|
|
||||||
if active == is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Update state
|
|
||||||
is_active = active
|
|
||||||
|
|
||||||
# Handle state change
|
|
||||||
if is_active:
|
|
||||||
_averager.clear()
|
|
||||||
player_body.override_player_height(self, 0.0)
|
|
||||||
emit_signal("player_climb_start")
|
|
||||||
else:
|
|
||||||
# Calculate the forward direction (based on camera-forward)
|
|
||||||
var dir_forward = -player_body.camera_node.global_transform.basis.z \
|
|
||||||
.slide(player_body.up_player) \
|
|
||||||
.normalized()
|
|
||||||
|
|
||||||
# Set player velocity based on averaged velocity, fling multiplier,
|
|
||||||
# and a forward push
|
|
||||||
var velocity := _averager.velocity()
|
|
||||||
player_body.velocity = (velocity * fling_multiplier) + (dir_forward * forward_push)
|
|
||||||
|
|
||||||
player_body.override_player_height(self)
|
|
||||||
emit_signal("player_climb_end")
|
|
||||||
|
|
||||||
|
|
||||||
## Handler for left controller picked up
|
|
||||||
func _on_left_picked_up(what : Node3D) -> void:
|
|
||||||
# Get the climbable
|
|
||||||
var climbable = what as XRToolsClimbable
|
|
||||||
if not climbable:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the handle
|
|
||||||
_left_handle = climbable.get_grab_handle(_left_pickup_node)
|
|
||||||
if not _left_handle:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Switch dominance to the left handle
|
|
||||||
_dominant = _left_handle
|
|
||||||
|
|
||||||
# If collision hands present then target the handle
|
|
||||||
if _left_collision_hand:
|
|
||||||
_left_collision_hand.add_target_override(_left_handle, 0)
|
|
||||||
elif _left_hand:
|
|
||||||
_left_hand.add_target_override(_left_handle, 0)
|
|
||||||
|
|
||||||
|
|
||||||
## Handler for right controller picked up
|
|
||||||
func _on_right_picked_up(what : Node3D) -> void:
|
|
||||||
# Get the climbable
|
|
||||||
var climbable = what as XRToolsClimbable
|
|
||||||
if not climbable:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the handle
|
|
||||||
_right_handle = climbable.get_grab_handle(_right_pickup_node)
|
|
||||||
if not _right_handle:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Switch dominance to the right handle
|
|
||||||
_dominant = _right_handle
|
|
||||||
|
|
||||||
# If collision hands present then target the handle
|
|
||||||
if _right_collision_hand:
|
|
||||||
_right_collision_hand.add_target_override(_right_handle, 0)
|
|
||||||
elif _right_hand:
|
|
||||||
_right_hand.add_target_override(_right_handle, 0)
|
|
||||||
|
|
||||||
|
|
||||||
## Handler for left controller dropped
|
|
||||||
func _on_left_dropped() -> void:
|
|
||||||
# If collision hands present then clear handle target
|
|
||||||
if _left_collision_hand:
|
|
||||||
_left_collision_hand.remove_target_override(_left_handle)
|
|
||||||
if _left_hand:
|
|
||||||
_left_hand.remove_target_override(_left_handle)
|
|
||||||
|
|
||||||
# Release handle and transfer dominance
|
|
||||||
_left_handle = null
|
|
||||||
_dominant = _right_handle
|
|
||||||
|
|
||||||
|
|
||||||
## Handler for righ controller dropped
|
|
||||||
func _on_right_dropped() -> void:
|
|
||||||
# If collision hands present then clear handle target
|
|
||||||
if _right_collision_hand:
|
|
||||||
_right_collision_hand.remove_target_override(_right_handle)
|
|
||||||
if _right_hand:
|
|
||||||
_right_hand.remove_target_override(_right_handle)
|
|
||||||
|
|
||||||
# Release handle and transfer dominance
|
|
||||||
_right_handle = null
|
|
||||||
_dominant = _left_handle
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Verify the left controller pickup
|
|
||||||
if !XRToolsFunctionPickup.find_left(self):
|
|
||||||
warnings.append("Unable to find left XRToolsFunctionPickup node")
|
|
||||||
|
|
||||||
# Verify the right controller pickup
|
|
||||||
if !XRToolsFunctionPickup.find_right(self):
|
|
||||||
warnings.append("Unable to find right XRToolsFunctionPickup node")
|
|
||||||
|
|
||||||
# Verify velocity averages
|
|
||||||
if velocity_averages < 2:
|
|
||||||
warnings.append("Minimum of 2 velocity averages needed")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://8gnaa2i8jamp
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://bxm1ply47vaan"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://8gnaa2i8jamp" path="res://addons/godot-xr-tools/functions/movement_climb.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="MovementClimb" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,92 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementCrouch
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Crouching
|
|
||||||
##
|
|
||||||
## This script works with the [XRToolsPlayerBody] attached to the players
|
|
||||||
## [XROrigin3D].
|
|
||||||
##
|
|
||||||
## While the player presses the crounch button, the height is overridden to
|
|
||||||
## the specified crouch height.
|
|
||||||
|
|
||||||
|
|
||||||
## Enumeration of crouching modes
|
|
||||||
enum CrouchType {
|
|
||||||
HOLD_TO_CROUCH, ## Hold button to crouch
|
|
||||||
TOGGLE_CROUCH, ## Toggle crouching on button press
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 10
|
|
||||||
|
|
||||||
## Crouch height
|
|
||||||
@export var crouch_height : float = 1.0
|
|
||||||
|
|
||||||
## Crouch button
|
|
||||||
@export var crouch_button_action : String = "primary_click"
|
|
||||||
|
|
||||||
## Type of crouching
|
|
||||||
@export var crouch_type : CrouchType = CrouchType.HOLD_TO_CROUCH
|
|
||||||
|
|
||||||
|
|
||||||
## Crouching flag
|
|
||||||
var _crouching : bool = false
|
|
||||||
|
|
||||||
## Crouch button down state
|
|
||||||
var _crouch_button_down : bool = false
|
|
||||||
|
|
||||||
|
|
||||||
# Controller node
|
|
||||||
@onready var _controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementCrouch" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
# Perform jump movement
|
|
||||||
func physics_movement(_delta: float, player_body: XRToolsPlayerBody, _disabled: bool):
|
|
||||||
# Skip if the controller isn't active
|
|
||||||
if !_controller.get_is_active():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Detect crouch button down and pressed states
|
|
||||||
var crouch_button_down := _controller.is_button_pressed(crouch_button_action)
|
|
||||||
var crouch_button_pressed := crouch_button_down and !_crouch_button_down
|
|
||||||
_crouch_button_down = crouch_button_down
|
|
||||||
|
|
||||||
# Calculate new crouching state
|
|
||||||
var crouching := _crouching
|
|
||||||
match crouch_type:
|
|
||||||
CrouchType.HOLD_TO_CROUCH:
|
|
||||||
# Crouch when button down
|
|
||||||
crouching = crouch_button_down
|
|
||||||
|
|
||||||
CrouchType.TOGGLE_CROUCH:
|
|
||||||
# Toggle when button pressed
|
|
||||||
if crouch_button_pressed:
|
|
||||||
crouching = !crouching
|
|
||||||
|
|
||||||
# Update crouching state
|
|
||||||
if crouching != _crouching:
|
|
||||||
_crouching = crouching
|
|
||||||
if crouching:
|
|
||||||
player_body.override_player_height(self, crouch_height)
|
|
||||||
else:
|
|
||||||
player_body.override_player_height(self)
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Check the controller node
|
|
||||||
if !XRHelpers.get_xr_controller(self):
|
|
||||||
warnings.append("This node must be within a branch of an XRController3D node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://dhtskb0o13u7i
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://clt88d5d1dje4"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dhtskb0o13u7i" path="res://addons/godot-xr-tools/functions/movement_crouch.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="MovementCrouch" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,87 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementDirect
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Direct Movement
|
|
||||||
##
|
|
||||||
## This script provides direct movement for the player. This script works
|
|
||||||
## with the [XRToolsPlayerBody] attached to the players [XROrigin3D].
|
|
||||||
##
|
|
||||||
## The player may have multiple [XRToolsMovementDirect] nodes attached to
|
|
||||||
## different controllers to provide different types of direct movement.
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 10
|
|
||||||
|
|
||||||
## Movement speed
|
|
||||||
@export var max_speed : float = 3.0
|
|
||||||
|
|
||||||
## If true, the player can strafe
|
|
||||||
@export var strafe : bool = false
|
|
||||||
|
|
||||||
## Input action for movement direction
|
|
||||||
@export var input_action : String = "primary"
|
|
||||||
|
|
||||||
|
|
||||||
# Controller node
|
|
||||||
@onready var _controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementDirect" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
# Perform jump movement
|
|
||||||
func physics_movement(_delta: float, player_body: XRToolsPlayerBody, _disabled: bool):
|
|
||||||
# Skip if the controller isn't active
|
|
||||||
if !_controller.get_is_active():
|
|
||||||
return
|
|
||||||
|
|
||||||
## get input action with deadzone correction applied
|
|
||||||
var dz_input_action = XRToolsUserSettings.get_adjusted_vector2(_controller, input_action)
|
|
||||||
|
|
||||||
player_body.ground_control_velocity.y += dz_input_action.y * max_speed
|
|
||||||
if strafe:
|
|
||||||
player_body.ground_control_velocity.x += dz_input_action.x * max_speed
|
|
||||||
|
|
||||||
# Clamp ground control
|
|
||||||
var length := player_body.ground_control_velocity.length()
|
|
||||||
if length > max_speed:
|
|
||||||
player_body.ground_control_velocity *= max_speed / length
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Check the controller node
|
|
||||||
if !XRHelpers.get_xr_controller(self):
|
|
||||||
warnings.append("This node must be within a branch of an XRController3D node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
||||||
|
|
||||||
|
|
||||||
## Find the left [XRToolsMovementDirect] node.
|
|
||||||
##
|
|
||||||
## This function searches from the specified node for the left controller
|
|
||||||
## [XRToolsMovementDirect] assuming the node is a sibling of the [XROrigin3D].
|
|
||||||
static func find_left(node : Node) -> XRToolsMovementDirect:
|
|
||||||
return XRTools.find_xr_child(
|
|
||||||
XRHelpers.get_left_controller(node),
|
|
||||||
"*",
|
|
||||||
"XRToolsMovementDirect") as XRToolsMovementDirect
|
|
||||||
|
|
||||||
|
|
||||||
## Find the right [XRToolsMovementDirect] node.
|
|
||||||
##
|
|
||||||
## This function searches from the specified node for the right controller
|
|
||||||
## [XRToolsMovementDirect] assuming the node is a sibling of the [XROrigin3D].
|
|
||||||
static func find_right(node : Node) -> XRToolsMovementDirect:
|
|
||||||
return XRTools.find_xr_child(
|
|
||||||
XRHelpers.get_right_controller(node),
|
|
||||||
"*",
|
|
||||||
"XRToolsMovementDirect") as XRToolsMovementDirect
|
|
@ -1 +0,0 @@
|
|||||||
uid://bmb62g0bwlg2s
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://bl2nuu3qhlb5k"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bmb62g0bwlg2s" path="res://addons/godot-xr-tools/functions/movement_direct.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="MovementDirect" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,229 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementFlight
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Flying
|
|
||||||
##
|
|
||||||
## This script provides flying movement for the player. The control parameters
|
|
||||||
## are intended to support a wide variety of flight mechanics.
|
|
||||||
##
|
|
||||||
## Pitch and Bearing input devices are selected which produce a "forwards"
|
|
||||||
## reference frame. The player controls (forwards/backwards and
|
|
||||||
## left/right) are applied in relation to this reference frame.
|
|
||||||
##
|
|
||||||
## The Speed Scale and Traction parameters allow primitive flight where
|
|
||||||
## the player is in direct control of their speed (in the reference frame).
|
|
||||||
## This produces an effect described as the "Mary Poppins Flying Umbrella".
|
|
||||||
##
|
|
||||||
## The Acceleration, Drag, and Guidance parameters allow for slightly more
|
|
||||||
## realisitic flying where the player can accelerate in their reference
|
|
||||||
## frame. The drag is applied against the global reference and can be used
|
|
||||||
## to construct a terminal velocity.
|
|
||||||
##
|
|
||||||
## The Guidance property attempts to lerp the players velocity into flight
|
|
||||||
## forwards direction as if the player had guide-fins or wings.
|
|
||||||
##
|
|
||||||
## The Exclusive property specifies whether flight is exclusive (no further
|
|
||||||
## physics effects after flying) or whether additional effects such as
|
|
||||||
## the default player gravity are applied.
|
|
||||||
|
|
||||||
|
|
||||||
## Signal emitted when flight starts
|
|
||||||
signal flight_started()
|
|
||||||
|
|
||||||
## Signal emitted when flight finishes
|
|
||||||
signal flight_finished()
|
|
||||||
|
|
||||||
|
|
||||||
## Enumeration of controller to use for flight
|
|
||||||
enum FlightController {
|
|
||||||
LEFT, ## Use left controller
|
|
||||||
RIGHT, ## Use right controler
|
|
||||||
}
|
|
||||||
|
|
||||||
## Enumeration of pitch control input
|
|
||||||
enum FlightPitch {
|
|
||||||
HEAD, ## Head controls pitch
|
|
||||||
CONTROLLER, ## Controller controls pitch
|
|
||||||
}
|
|
||||||
|
|
||||||
## Enumeration of bearing control input
|
|
||||||
enum FlightBearing {
|
|
||||||
HEAD, ## Head controls bearing
|
|
||||||
CONTROLLER, ## Controller controls bearing
|
|
||||||
BODY, ## Body controls bearing
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 30
|
|
||||||
|
|
||||||
## Flight controller
|
|
||||||
@export var controller : FlightController = FlightController.LEFT
|
|
||||||
|
|
||||||
## Flight toggle button
|
|
||||||
@export var flight_button : String = "by_button"
|
|
||||||
|
|
||||||
## Flight pitch control
|
|
||||||
@export var pitch : FlightPitch = FlightPitch.CONTROLLER
|
|
||||||
|
|
||||||
## Flight bearing control
|
|
||||||
@export var bearing : FlightBearing = FlightBearing.CONTROLLER
|
|
||||||
|
|
||||||
## Flight speed from control
|
|
||||||
@export var speed_scale : float = 5.0
|
|
||||||
|
|
||||||
## Flight traction pulling flight velocity towards the controlled speed
|
|
||||||
@export var speed_traction : float = 3.0
|
|
||||||
|
|
||||||
## Flight acceleration from control
|
|
||||||
@export var acceleration_scale : float = 0.0
|
|
||||||
|
|
||||||
## Flight drag
|
|
||||||
@export var drag : float = 0.1
|
|
||||||
|
|
||||||
## Guidance effect (virtual fins/wings)
|
|
||||||
@export var guidance : float = 0.0
|
|
||||||
|
|
||||||
## If true, flight movement is exclusive preventing further movement functions
|
|
||||||
@export var exclusive : bool = true
|
|
||||||
|
|
||||||
|
|
||||||
## Flight button state
|
|
||||||
var _flight_button : bool = false
|
|
||||||
|
|
||||||
## Flight controller
|
|
||||||
var _controller : XRController3D
|
|
||||||
|
|
||||||
|
|
||||||
# Node references
|
|
||||||
@onready var _camera := XRHelpers.get_xr_camera(self)
|
|
||||||
@onready var _left_controller := XRHelpers.get_left_controller(self)
|
|
||||||
@onready var _right_controller := XRHelpers.get_right_controller(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementFlight" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
# In Godot 4 we must now manually call our super class ready function
|
|
||||||
super()
|
|
||||||
|
|
||||||
# Get the flight controller
|
|
||||||
if controller == FlightController.LEFT:
|
|
||||||
_controller = _left_controller
|
|
||||||
else:
|
|
||||||
_controller = _right_controller
|
|
||||||
|
|
||||||
|
|
||||||
# Process physics movement for flight
|
|
||||||
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool):
|
|
||||||
# Disable flying if requested, or if no controller
|
|
||||||
if disabled or !enabled or !_controller.get_is_active():
|
|
||||||
set_flying(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Detect press of flight button
|
|
||||||
var old_flight_button = _flight_button
|
|
||||||
_flight_button = _controller.is_button_pressed(flight_button)
|
|
||||||
if _flight_button and !old_flight_button:
|
|
||||||
set_flying(!is_active)
|
|
||||||
|
|
||||||
# Skip if not flying
|
|
||||||
if !is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Select the pitch vector
|
|
||||||
var pitch_vector: Vector3
|
|
||||||
if pitch == FlightPitch.HEAD:
|
|
||||||
# Use the vertical part of the 'head' forwards vector
|
|
||||||
pitch_vector = -_camera.transform.basis.z.y * player_body.up_player
|
|
||||||
else:
|
|
||||||
# Use the vertical part of the 'controller' forwards vector
|
|
||||||
pitch_vector = -_controller.transform.basis.z.y * player_body.up_player
|
|
||||||
|
|
||||||
# Select the bearing vector
|
|
||||||
var bearing_vector: Vector3
|
|
||||||
if bearing == FlightBearing.HEAD:
|
|
||||||
# Use the horizontal part of the 'head' forwards vector
|
|
||||||
bearing_vector = -_camera.global_transform.basis.z \
|
|
||||||
.slide(player_body.up_player)
|
|
||||||
elif bearing == FlightBearing.CONTROLLER:
|
|
||||||
# Use the horizontal part of the 'controller' forwards vector
|
|
||||||
bearing_vector = -_controller.global_transform.basis.z \
|
|
||||||
.slide(player_body.up_player)
|
|
||||||
else:
|
|
||||||
# Use the horizontal part of the 'body' forwards vector
|
|
||||||
var left := _left_controller.global_transform.origin
|
|
||||||
var right := _right_controller.global_transform.origin
|
|
||||||
var left_to_right := right - left
|
|
||||||
bearing_vector = left_to_right \
|
|
||||||
.rotated(player_body.up_player, PI/2) \
|
|
||||||
.slide(player_body.up_player)
|
|
||||||
|
|
||||||
# Construct the flight bearing
|
|
||||||
var forwards := (bearing_vector.normalized() + pitch_vector).normalized()
|
|
||||||
var side := forwards.cross(player_body.up_player)
|
|
||||||
|
|
||||||
# Construct the target velocity
|
|
||||||
var joy_forwards := _controller.get_vector2("primary").y
|
|
||||||
var joy_side := _controller.get_vector2("primary").x
|
|
||||||
var heading := forwards * joy_forwards + side * joy_side
|
|
||||||
|
|
||||||
# Calculate the flight velocity
|
|
||||||
var flight_velocity := player_body.velocity
|
|
||||||
flight_velocity *= 1.0 - drag * delta
|
|
||||||
flight_velocity = flight_velocity.lerp(heading * speed_scale, speed_traction * delta)
|
|
||||||
flight_velocity += heading * acceleration_scale * delta
|
|
||||||
|
|
||||||
# Apply virtual guidance effect
|
|
||||||
if guidance > 0.0:
|
|
||||||
var velocity_forwards := forwards * flight_velocity.length()
|
|
||||||
flight_velocity = flight_velocity.lerp(velocity_forwards, guidance * delta)
|
|
||||||
|
|
||||||
# If exclusive then perform the exclusive move-and-slide
|
|
||||||
if exclusive:
|
|
||||||
player_body.velocity = player_body.move_body(flight_velocity)
|
|
||||||
return true
|
|
||||||
|
|
||||||
# Update velocity and return for additional effects
|
|
||||||
player_body.velocity = flight_velocity
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
func set_flying(active: bool) -> void:
|
|
||||||
# Skip if no change
|
|
||||||
if active == is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Update state
|
|
||||||
is_active = active
|
|
||||||
|
|
||||||
# Handle state change
|
|
||||||
if is_active:
|
|
||||||
emit_signal("flight_started")
|
|
||||||
else:
|
|
||||||
emit_signal("flight_finished")
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Verify the camera
|
|
||||||
if !XRHelpers.get_xr_camera(self):
|
|
||||||
warnings.append("Unable to find XRCamera3D")
|
|
||||||
|
|
||||||
# Verify the left controller
|
|
||||||
if !XRHelpers.get_left_controller(self):
|
|
||||||
warnings.append("Unable to find left XRController3D node")
|
|
||||||
|
|
||||||
# Verify the right controller
|
|
||||||
if !XRHelpers.get_right_controller(self):
|
|
||||||
warnings.append("Unable to find left XRController3D node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://b14c3dx73nwuv
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://kyhaogt0a4q8"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://b14c3dx73nwuv" path="res://addons/godot-xr-tools/functions/movement_flight.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="MovementFlight" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,245 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementFootstep
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Footsteps
|
|
||||||
##
|
|
||||||
## This movement provider detects walking on different surfaces.
|
|
||||||
## It plays audio sounds associated with the surface the player is
|
|
||||||
## currently walking on.
|
|
||||||
|
|
||||||
|
|
||||||
## Signal emitted when a footstep is generated
|
|
||||||
signal footstep(name)
|
|
||||||
|
|
||||||
|
|
||||||
# Number of audio players to pool
|
|
||||||
const AUDIO_POOL_SIZE := 3
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 1001
|
|
||||||
|
|
||||||
## Default XRToolsSurfaceAudioType when not overridden
|
|
||||||
@export var default_surface_audio_type : XRToolsSurfaceAudioType
|
|
||||||
|
|
||||||
## Speed at which the player is considered walking
|
|
||||||
@export var walk_speed := 0.4
|
|
||||||
|
|
||||||
## Step per meter by time
|
|
||||||
@export var steps_per_meter = 1.0
|
|
||||||
|
|
||||||
|
|
||||||
# step time
|
|
||||||
var step_time = 0.0
|
|
||||||
|
|
||||||
# Last on_ground state of the player
|
|
||||||
var _old_on_ground := true
|
|
||||||
|
|
||||||
# Node representing the location of the players foot
|
|
||||||
var _foot_spatial : Node3D
|
|
||||||
|
|
||||||
# Pool of idle AudioStreamPlayer3D nodes
|
|
||||||
var _audio_pool_idle : Array[AudioStreamPlayer3D]
|
|
||||||
|
|
||||||
# Last ground node
|
|
||||||
var _ground_node : Node
|
|
||||||
|
|
||||||
# Surface audio type associated with last ground node
|
|
||||||
var _ground_node_audio_type : XRToolsSurfaceAudioType
|
|
||||||
|
|
||||||
|
|
||||||
## PlayerBody - Player Physics Body Script
|
|
||||||
@onready var player_body := XRToolsPlayerBody.find_instance(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementFootstep" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
# In Godot 4 we must now manually call our super class ready function
|
|
||||||
super()
|
|
||||||
|
|
||||||
# Construct the foot spatial - we will move it around as the player moves.
|
|
||||||
_foot_spatial = Node3D.new()
|
|
||||||
_foot_spatial.name = "FootSpatial"
|
|
||||||
add_child(_foot_spatial)
|
|
||||||
|
|
||||||
# Make the array of players in _audio_pool_idle
|
|
||||||
for i in AUDIO_POOL_SIZE:
|
|
||||||
var player = $PlayerSettings.duplicate()
|
|
||||||
player.name = "PlayerCopy%d" % (i + 1)
|
|
||||||
_foot_spatial.add_child(player)
|
|
||||||
_audio_pool_idle.append(player)
|
|
||||||
player.finished.connect(_on_player_finished.bind(player))
|
|
||||||
|
|
||||||
# Set as always active
|
|
||||||
is_active = true
|
|
||||||
|
|
||||||
# Listen for the player jumping
|
|
||||||
player_body.player_jumped.connect(_on_player_jumped)
|
|
||||||
|
|
||||||
|
|
||||||
# This method checks for configuration issues.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Verify player settings node exists
|
|
||||||
if not $PlayerSettings:
|
|
||||||
warnings.append("Missing player settings node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
||||||
|
|
||||||
|
|
||||||
func physics_movement(_delta: float, player_body: XRToolsPlayerBody, _disabled: bool):
|
|
||||||
# Update the spatial location of the foot
|
|
||||||
_update_foot_spatial()
|
|
||||||
|
|
||||||
# Update the ground audio information
|
|
||||||
_update_ground_audio()
|
|
||||||
|
|
||||||
# Skip if footsteps have been disabled
|
|
||||||
if not enabled:
|
|
||||||
step_time = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
# Detect landing on ground
|
|
||||||
if not _old_on_ground and player_body.on_ground:
|
|
||||||
# Play the ground hit sound
|
|
||||||
_play_ground_hit()
|
|
||||||
|
|
||||||
# Update the old on_ground state
|
|
||||||
_old_on_ground = player_body.on_ground
|
|
||||||
if not player_body.on_ground:
|
|
||||||
step_time = 0 # Reset when not on ground
|
|
||||||
return
|
|
||||||
|
|
||||||
# Handle slow/stopped
|
|
||||||
if player_body.ground_control_velocity.length() < walk_speed:
|
|
||||||
step_time = 0 # Reset when slow/stopped
|
|
||||||
return
|
|
||||||
|
|
||||||
# Count up the step timer, and skip if not take a step yet
|
|
||||||
step_time += _delta * player_body.ground_control_velocity.length()
|
|
||||||
if step_time > steps_per_meter:
|
|
||||||
_play_step_sound()
|
|
||||||
step_time = 0
|
|
||||||
|
|
||||||
|
|
||||||
# Update the foot spatial to be where the players foot is
|
|
||||||
func _update_foot_spatial() -> void:
|
|
||||||
# Project the players camera down to the XZ plane (real-world space)
|
|
||||||
var local_foot := player_body.camera_node.position.slide(Vector3.UP)
|
|
||||||
|
|
||||||
# Move the foot_spatial to the local foot in the global origin space
|
|
||||||
_foot_spatial.global_position = player_body.origin_node.global_transform * local_foot
|
|
||||||
|
|
||||||
|
|
||||||
# Update the ground audio information
|
|
||||||
func _update_ground_audio() -> void:
|
|
||||||
# Skip if no change
|
|
||||||
if player_body.ground_node == _ground_node:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Save the new ground node
|
|
||||||
_ground_node = player_body.ground_node
|
|
||||||
|
|
||||||
# Handle no ground
|
|
||||||
if not _ground_node:
|
|
||||||
_ground_node_audio_type = null
|
|
||||||
return
|
|
||||||
|
|
||||||
# Find the surface audio for the ground (if any)
|
|
||||||
var ground_audio : XRToolsSurfaceAudio = XRTools.find_xr_child(
|
|
||||||
_ground_node, "*", "XRToolsSurfaceAudio")
|
|
||||||
if ground_audio:
|
|
||||||
_ground_node_audio_type = ground_audio.surface_audio_type
|
|
||||||
else:
|
|
||||||
_ground_node_audio_type = default_surface_audio_type
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the player jumps
|
|
||||||
func _on_player_jumped() -> void:
|
|
||||||
# Skip if no jump sound
|
|
||||||
if not _ground_node_audio_type:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Play the jump sound
|
|
||||||
_play_sound(
|
|
||||||
_ground_node_audio_type.name,
|
|
||||||
_ground_node_audio_type.jump_sound)
|
|
||||||
|
|
||||||
|
|
||||||
# Play the hit sound made when the player lands on the ground
|
|
||||||
func _play_ground_hit() -> void:
|
|
||||||
# Skip if no hit sound
|
|
||||||
if not _ground_node_audio_type:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Play the hit sound
|
|
||||||
_play_sound(
|
|
||||||
_ground_node_audio_type.name,
|
|
||||||
_ground_node_audio_type.hit_sound)
|
|
||||||
|
|
||||||
|
|
||||||
# Play a step sound for the current ground
|
|
||||||
func _play_step_sound() -> void:
|
|
||||||
# Skip if no walk audio
|
|
||||||
if not _ground_node_audio_type or _ground_node_audio_type.walk_sounds.size() == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Pick the sound index
|
|
||||||
var idx := randi() % _ground_node_audio_type.walk_sounds.size()
|
|
||||||
|
|
||||||
# Pick the playback pitck
|
|
||||||
var pitch := randf_range(
|
|
||||||
_ground_node_audio_type.walk_pitch_minimum,
|
|
||||||
_ground_node_audio_type.walk_pitch_maximum)
|
|
||||||
|
|
||||||
# Play the walk sound
|
|
||||||
_play_sound(
|
|
||||||
_ground_node_audio_type.name,
|
|
||||||
_ground_node_audio_type.walk_sounds[idx],
|
|
||||||
pitch)
|
|
||||||
|
|
||||||
|
|
||||||
# Play the specified audio stream at the requested pitch using an
|
|
||||||
# AudioStreamPlayer3D in the idle pool of players.
|
|
||||||
func _play_sound(name : String, stream : AudioStream, pitch : float = 1.0) -> void:
|
|
||||||
# Skip if no stream provided
|
|
||||||
if not stream:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Emit the footstep signal
|
|
||||||
footstep.emit(name)
|
|
||||||
|
|
||||||
# Verify we have an audio player
|
|
||||||
if _audio_pool_idle.size() == 0:
|
|
||||||
push_warning("XRToolsMovementFootstep idle audio pool empty")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Play the sound
|
|
||||||
var player : AudioStreamPlayer3D = _audio_pool_idle.pop_front()
|
|
||||||
player.stream = stream
|
|
||||||
player.pitch_scale = pitch
|
|
||||||
player.play()
|
|
||||||
|
|
||||||
|
|
||||||
# Called when an AudioStreamPlayer3D in our pool finishes playing its sound
|
|
||||||
func _on_player_finished(player : AudioStreamPlayer3D) -> void:
|
|
||||||
_audio_pool_idle.append(player)
|
|
||||||
|
|
||||||
|
|
||||||
## Find an [XRToolsMovementFootstep] node.
|
|
||||||
##
|
|
||||||
## This function searches from the specified node for an [XRToolsMovementFootstep]
|
|
||||||
## assuming the node is a sibling of the body under an [ARVROrigin].
|
|
||||||
static func find_instance(node: Node) -> XRToolsMovementFootstep:
|
|
||||||
return XRTools.find_xr_child(
|
|
||||||
XRHelpers.get_xr_origin(node),
|
|
||||||
"*",
|
|
||||||
"XRToolsMovementFootstep") as XRToolsMovementFootstep
|
|
@ -1 +0,0 @@
|
|||||||
uid://bomxq4y2w5uu5
|
|
@ -1,8 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://0xlsitpu17r1"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bomxq4y2w5uu5" path="res://addons/godot-xr-tools/functions/movement_footstep.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="MovementFootstep" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
||||||
|
|
||||||
[node name="PlayerSettings" type="AudioStreamPlayer3D" parent="."]
|
|
@ -1,236 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementGlide
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Gliding
|
|
||||||
##
|
|
||||||
## This script provides glide mechanics for the player. This script works
|
|
||||||
## with the [XRToolsPlayerBody] attached to the players [XROrigin3D].
|
|
||||||
##
|
|
||||||
## The player enables flying by moving the controllers apart further than
|
|
||||||
## 'glide_detect_distance'.
|
|
||||||
##
|
|
||||||
## When gliding, the players fall speed will slew to 'glide_fall_speed' and
|
|
||||||
## the velocity will slew to 'glide_forward_speed' in the direction the
|
|
||||||
## player is facing.
|
|
||||||
##
|
|
||||||
## Gliding is an exclusive motion operation, and so gliding should be ordered
|
|
||||||
## after any Direct movement providers responsible for turning.
|
|
||||||
|
|
||||||
|
|
||||||
## Signal invoked when the player starts gliding
|
|
||||||
signal player_glide_start
|
|
||||||
|
|
||||||
## Signal invoked when the player ends gliding
|
|
||||||
signal player_glide_end
|
|
||||||
|
|
||||||
## Signal invoked when the player flaps
|
|
||||||
signal player_flapped
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 35
|
|
||||||
|
|
||||||
## Controller separation distance to register as glide
|
|
||||||
@export var glide_detect_distance : float = 1.0
|
|
||||||
|
|
||||||
## Minimum falling speed to be considered gliding
|
|
||||||
@export var glide_min_fall_speed : float = -1.5
|
|
||||||
|
|
||||||
## Glide falling speed
|
|
||||||
@export var glide_fall_speed : float = -2.0
|
|
||||||
|
|
||||||
## Glide forward speed
|
|
||||||
@export var glide_forward_speed : float = 12.0
|
|
||||||
|
|
||||||
## Slew rate to transition to gliding
|
|
||||||
@export var horizontal_slew_rate : float = 1.0
|
|
||||||
|
|
||||||
## Slew rate to transition to gliding
|
|
||||||
@export var vertical_slew_rate : float = 2.0
|
|
||||||
|
|
||||||
## glide rotate with roll angle
|
|
||||||
@export var turn_with_roll : bool = false
|
|
||||||
|
|
||||||
## Smooth turn speed in radians per second
|
|
||||||
@export var roll_turn_speed : float = 1
|
|
||||||
|
|
||||||
## Add vertical impulse by flapping controllers
|
|
||||||
@export var wings_impulse : bool = false
|
|
||||||
|
|
||||||
## Minimum velocity for flapping
|
|
||||||
@export var flap_min_speed : float = 0.3
|
|
||||||
|
|
||||||
## Flapping force multiplier
|
|
||||||
@export var wings_force : float = 1.0
|
|
||||||
|
|
||||||
## Minimum distance from controllers to ARVRCamera to rearm flaps.
|
|
||||||
## if set to 0, you need to reach head level with hands to rearm flaps
|
|
||||||
@export var rearm_distance_offset : float = 0.2
|
|
||||||
|
|
||||||
|
|
||||||
## Flap activated (when both controllers are near the ARVRCamera height)
|
|
||||||
var flap_armed : bool = false
|
|
||||||
|
|
||||||
## Last controllers position to calculate flapping velocity
|
|
||||||
var last_local_left_position : Vector3
|
|
||||||
var last_local_right_position : Vector3
|
|
||||||
|
|
||||||
# True if the controller positions are valid
|
|
||||||
var _has_controller_positions : bool = false
|
|
||||||
|
|
||||||
|
|
||||||
# Left controller
|
|
||||||
@onready var _left_controller := XRHelpers.get_left_controller(self)
|
|
||||||
|
|
||||||
# Right controller
|
|
||||||
@onready var _right_controller := XRHelpers.get_right_controller(self)
|
|
||||||
|
|
||||||
# ARVRCamera
|
|
||||||
@onready var _camera_node := XRHelpers.get_xr_camera(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementGlide" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool):
|
|
||||||
# Skip if disabled or either controller is off
|
|
||||||
if disabled or !enabled or \
|
|
||||||
!_left_controller.get_is_active() or \
|
|
||||||
!_right_controller.get_is_active():
|
|
||||||
_set_gliding(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
# If on the ground, then not gliding
|
|
||||||
if player_body.on_ground:
|
|
||||||
_set_gliding(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the controller left and right global horizontal positions
|
|
||||||
var left_position := _left_controller.global_transform.origin
|
|
||||||
var right_position := _right_controller.global_transform.origin
|
|
||||||
|
|
||||||
# Set default wings impulse to zero
|
|
||||||
var wings_impulse_velocity := 0.0
|
|
||||||
|
|
||||||
# If wings impulse is active, calculate flapping impulse
|
|
||||||
if wings_impulse:
|
|
||||||
# Check controllers position relative to head
|
|
||||||
var cam_local_y := _camera_node.position.y
|
|
||||||
var left_hand_over_head = cam_local_y < _left_controller.position.y + rearm_distance_offset
|
|
||||||
var right_hand_over_head = cam_local_y < _right_controller.position.y + rearm_distance_offset
|
|
||||||
if left_hand_over_head && right_hand_over_head:
|
|
||||||
flap_armed = true
|
|
||||||
|
|
||||||
if flap_armed:
|
|
||||||
# Get controller local positions
|
|
||||||
var local_left_position := _left_controller.position
|
|
||||||
var local_right_position := _right_controller.position
|
|
||||||
|
|
||||||
# Store last frame controller positions for the first step
|
|
||||||
if not _has_controller_positions:
|
|
||||||
_has_controller_positions = true
|
|
||||||
last_local_left_position = local_left_position
|
|
||||||
last_local_right_position = local_right_position
|
|
||||||
|
|
||||||
# Calculate controllers velocity only when flapping downwards
|
|
||||||
var left_wing_velocity = 0.0
|
|
||||||
var right_wing_velocity = 0.0
|
|
||||||
if local_left_position.y < last_local_left_position.y:
|
|
||||||
left_wing_velocity = local_left_position.distance_to(last_local_left_position) / delta
|
|
||||||
if local_right_position.y < last_local_right_position.y:
|
|
||||||
right_wing_velocity = local_right_position.distance_to(last_local_right_position) / delta
|
|
||||||
|
|
||||||
# Calculate wings impulse
|
|
||||||
if left_wing_velocity > flap_min_speed && right_wing_velocity > flap_min_speed:
|
|
||||||
wings_impulse_velocity = (left_wing_velocity + right_wing_velocity) / 2
|
|
||||||
wings_impulse_velocity = wings_impulse_velocity * wings_force * delta * 50
|
|
||||||
emit_signal("player_flapped")
|
|
||||||
flap_armed = false
|
|
||||||
|
|
||||||
# Store controller position for next frame
|
|
||||||
last_local_left_position = local_left_position
|
|
||||||
last_local_right_position = local_right_position
|
|
||||||
|
|
||||||
# Calculate global left to right controller vector
|
|
||||||
var left_to_right := right_position - left_position
|
|
||||||
|
|
||||||
if turn_with_roll:
|
|
||||||
var angle = -left_to_right.dot(player_body.up_player)
|
|
||||||
player_body.rotate_player(roll_turn_speed * delta * angle)
|
|
||||||
|
|
||||||
# If not falling, then not gliding
|
|
||||||
var vertical_velocity := player_body.velocity.dot(player_body.up_gravity)
|
|
||||||
vertical_velocity += wings_impulse_velocity
|
|
||||||
if vertical_velocity >= glide_min_fall_speed && wings_impulse_velocity == 0.0:
|
|
||||||
_set_gliding(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Set gliding based on hand separation
|
|
||||||
var separation := left_to_right.length() / XRServer.world_scale
|
|
||||||
_set_gliding(separation >= glide_detect_distance)
|
|
||||||
|
|
||||||
# Skip if not gliding
|
|
||||||
if !is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Lerp the vertical velocity to glide_fall_speed
|
|
||||||
vertical_velocity = lerp(vertical_velocity, glide_fall_speed, vertical_slew_rate * delta)
|
|
||||||
|
|
||||||
# Lerp the horizontal velocity towards forward_speed
|
|
||||||
var horizontal_velocity := player_body.velocity.slide(player_body.up_gravity)
|
|
||||||
var dir_forward := left_to_right \
|
|
||||||
.rotated(player_body.up_gravity, PI/2) \
|
|
||||||
.slide(player_body.up_gravity) \
|
|
||||||
.normalized()
|
|
||||||
var forward_velocity := dir_forward * glide_forward_speed
|
|
||||||
horizontal_velocity = horizontal_velocity.lerp(forward_velocity, horizontal_slew_rate * delta)
|
|
||||||
|
|
||||||
# Perform the glide
|
|
||||||
var glide_velocity := horizontal_velocity + vertical_velocity * player_body.up_gravity
|
|
||||||
player_body.velocity = player_body.move_body(glide_velocity)
|
|
||||||
|
|
||||||
# Report exclusive motion performed (to bypass gravity)
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
# Set the gliding state and fire any signals
|
|
||||||
func _set_gliding(active: bool) -> void:
|
|
||||||
# Skip if no change
|
|
||||||
if active == is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Update the is_gliding flag
|
|
||||||
is_active = active;
|
|
||||||
|
|
||||||
# Report transition
|
|
||||||
if is_active:
|
|
||||||
emit_signal("player_glide_start")
|
|
||||||
else:
|
|
||||||
emit_signal("player_glide_end")
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Verify the left controller
|
|
||||||
if !XRHelpers.get_left_controller(self):
|
|
||||||
warnings.append("Unable to find left XRController3D node")
|
|
||||||
|
|
||||||
# Verify the right controller
|
|
||||||
if !XRHelpers.get_right_controller(self):
|
|
||||||
warnings.append("Unable to find right XRController3D node")
|
|
||||||
|
|
||||||
# Check glide parameters
|
|
||||||
if glide_min_fall_speed > 0:
|
|
||||||
warnings.append("Glide minimum fall speed must be zero or less")
|
|
||||||
if glide_fall_speed > 0:
|
|
||||||
warnings.append("Glide fall speed must be zero or less")
|
|
||||||
if glide_min_fall_speed < glide_fall_speed:
|
|
||||||
warnings.append("Glide fall speed must be faster than minimum fall speed")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://dhwhy8hs4up0r
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://cvokcudrffkgc"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dhwhy8hs4up0r" path="res://addons/godot-xr-tools/functions/movement_glide.gd" id="1"]
|
|
||||||
|
|
||||||
[node name="MovementGlide" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
@ -1,241 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementGrapple
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Grapple Movement
|
|
||||||
##
|
|
||||||
## This script provide simple grapple based movement - "bat hook" style
|
|
||||||
## where the player flings a rope to the target and swings on it.
|
|
||||||
## This script works with the [XRToolsPlayerBody] attached to the players
|
|
||||||
## [XROrigin3D].
|
|
||||||
|
|
||||||
|
|
||||||
## Signal emitted when grapple starts
|
|
||||||
signal grapple_started()
|
|
||||||
|
|
||||||
## Signal emitted when grapple finishes
|
|
||||||
signal grapple_finished()
|
|
||||||
|
|
||||||
|
|
||||||
## Grapple state
|
|
||||||
enum GrappleState {
|
|
||||||
IDLE, ## Grapple is idle
|
|
||||||
FIRED, ## Grapple is fired
|
|
||||||
WINCHING, ## Grapple is winching
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Default grapple collision mask of 1-5 (world)
|
|
||||||
const DEFAULT_COLLISION_MASK := 0b0000_0000_0000_0000_0000_0000_0001_1111
|
|
||||||
|
|
||||||
# Default grapple enable mask of 5:grapple-target
|
|
||||||
const DEFAULT_ENABLE_MASK := 0b0000_0000_0000_0000_0000_0000_0001_0000
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 20
|
|
||||||
|
|
||||||
## Grapple length - use to adjust maximum distance for possible grapple hooking.
|
|
||||||
@export var grapple_length : float = 15.0
|
|
||||||
|
|
||||||
## Grapple collision mask
|
|
||||||
@export_flags_3d_physics var grapple_collision_mask : int = DEFAULT_COLLISION_MASK:
|
|
||||||
set = _set_grapple_collision_mask
|
|
||||||
|
|
||||||
## Grapple enable mask
|
|
||||||
@export_flags_3d_physics var grapple_enable_mask : int = DEFAULT_ENABLE_MASK
|
|
||||||
|
|
||||||
## Impulse speed applied to the player on first grapple
|
|
||||||
@export var impulse_speed : float = 10.0
|
|
||||||
|
|
||||||
## Winch speed applied to the player while the grapple is held
|
|
||||||
@export var winch_speed : float = 2.0
|
|
||||||
|
|
||||||
## Probably need to add export variables for line size, maybe line material at
|
|
||||||
## some point so dev does not need to make children editable to do this.
|
|
||||||
## For now, right click on grapple node and make children editable to edit these
|
|
||||||
## facets.
|
|
||||||
@export var rope_width : float = 0.02
|
|
||||||
|
|
||||||
## Air friction while grappling
|
|
||||||
@export var friction : float = 0.1
|
|
||||||
|
|
||||||
## Grapple button (triggers grappling movement). Be sure this button does not
|
|
||||||
## conflict with other functions.
|
|
||||||
@export var grapple_button_action : String = "trigger_click"
|
|
||||||
|
|
||||||
# Hook related variables
|
|
||||||
var hook_object : Node3D = null
|
|
||||||
var hook_local := Vector3(0,0,0)
|
|
||||||
var hook_point := Vector3(0,0,0)
|
|
||||||
|
|
||||||
# Grapple button state
|
|
||||||
var _grapple_button := false
|
|
||||||
|
|
||||||
# Get line creation nodes
|
|
||||||
@onready var _line_helper : Node3D = $LineHelper
|
|
||||||
@onready var _line : CSGCylinder3D = $LineHelper/Line
|
|
||||||
|
|
||||||
# Get Controller node - consider way to universalize this if user wanted to
|
|
||||||
# attach this to a gun instead of player's hand. Could consider variable to
|
|
||||||
# select controller instead.
|
|
||||||
@onready var _controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
# Get Raycast node
|
|
||||||
@onready var _grapple_raycast : RayCast3D = $Grapple_RayCast
|
|
||||||
|
|
||||||
# Get Grapple Target Node
|
|
||||||
@onready var _grapple_target : Node3D = $Grapple_Target
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementGrapple" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
# Function run when node is added to scene
|
|
||||||
func _ready():
|
|
||||||
# In Godot 4 we must now manually call our super class ready function
|
|
||||||
super()
|
|
||||||
|
|
||||||
# Skip if running in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Ensure grapple length is valid
|
|
||||||
var min_hook_length := 1.5 * XRServer.world_scale
|
|
||||||
if grapple_length < min_hook_length:
|
|
||||||
grapple_length = min_hook_length
|
|
||||||
|
|
||||||
# Set ray-cast
|
|
||||||
_grapple_raycast.target_position = Vector3(0, 0, -grapple_length) * XRServer.world_scale
|
|
||||||
_grapple_raycast.collision_mask = grapple_collision_mask
|
|
||||||
|
|
||||||
# Deal with line
|
|
||||||
_line.radius = rope_width
|
|
||||||
_line.hide()
|
|
||||||
|
|
||||||
|
|
||||||
# Update the grappling line and target
|
|
||||||
func _physics_process(_delta : float):
|
|
||||||
# Skip if running in the editor
|
|
||||||
if Engine.is_editor_hint():
|
|
||||||
return
|
|
||||||
|
|
||||||
# If pointing grappler at target then show the target
|
|
||||||
if enabled and not is_active and _is_raycast_valid():
|
|
||||||
_grapple_target.global_transform.origin = _grapple_raycast.get_collision_point()
|
|
||||||
_grapple_target.global_transform = _grapple_target.global_transform.orthonormalized()
|
|
||||||
_grapple_target.visible = true
|
|
||||||
else:
|
|
||||||
_grapple_target.visible = false
|
|
||||||
|
|
||||||
# If actively grappling then update and show the grappling line
|
|
||||||
if is_active:
|
|
||||||
var line_length := (hook_point - _controller.global_transform.origin).length()
|
|
||||||
_line_helper.look_at(hook_point, Vector3.UP)
|
|
||||||
_line.height = line_length
|
|
||||||
_line.position.z = line_length / -2
|
|
||||||
_line.visible = true
|
|
||||||
else:
|
|
||||||
_line.visible = false
|
|
||||||
|
|
||||||
|
|
||||||
# Perform grapple movement
|
|
||||||
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool):
|
|
||||||
# Disable if requested
|
|
||||||
if disabled or !enabled or !_controller.get_is_active():
|
|
||||||
_set_grappling(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Update grapple button
|
|
||||||
var old_grapple_button := _grapple_button
|
|
||||||
_grapple_button = _controller.is_button_pressed(grapple_button_action)
|
|
||||||
|
|
||||||
# Enable/disable grappling
|
|
||||||
var do_impulse := false
|
|
||||||
if is_active and !_grapple_button:
|
|
||||||
_set_grappling(false)
|
|
||||||
elif _grapple_button and !old_grapple_button and _is_raycast_valid():
|
|
||||||
hook_object = _grapple_raycast.get_collider()
|
|
||||||
hook_point = _grapple_raycast.get_collision_point()
|
|
||||||
hook_local = hook_point * hook_object.global_transform
|
|
||||||
do_impulse = true
|
|
||||||
_set_grappling(true)
|
|
||||||
|
|
||||||
# Skip if not grappling
|
|
||||||
if !is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get hook direction
|
|
||||||
hook_point = hook_object.global_transform * hook_local
|
|
||||||
var hook_vector := hook_point - _controller.global_transform.origin
|
|
||||||
var hook_length := hook_vector.length()
|
|
||||||
var hook_direction := hook_vector / hook_length
|
|
||||||
|
|
||||||
# Apply gravity
|
|
||||||
player_body.velocity += player_body.gravity * delta
|
|
||||||
|
|
||||||
# Select the grapple speed
|
|
||||||
var speed := impulse_speed if do_impulse else winch_speed
|
|
||||||
if hook_length < 1.0:
|
|
||||||
speed = 0.0
|
|
||||||
|
|
||||||
# Ensure velocity is at least winch_speed towards hook
|
|
||||||
var vdot = player_body.velocity.dot(hook_direction)
|
|
||||||
if vdot < speed:
|
|
||||||
player_body.velocity += hook_direction * (speed - vdot)
|
|
||||||
|
|
||||||
# Scale down velocity
|
|
||||||
player_body.velocity *= 1.0 - friction * delta
|
|
||||||
|
|
||||||
# Perform exclusive movement as we have dealt with gravity
|
|
||||||
player_body.velocity = player_body.move_body(player_body.velocity)
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the grapple collision mask has been modified
|
|
||||||
func _set_grapple_collision_mask(new_value: int) -> void:
|
|
||||||
grapple_collision_mask = new_value
|
|
||||||
if is_inside_tree() and _grapple_raycast:
|
|
||||||
_grapple_raycast.collision_mask = new_value
|
|
||||||
|
|
||||||
|
|
||||||
# Set the grappling state and fire any signals
|
|
||||||
func _set_grappling(active: bool) -> void:
|
|
||||||
# Skip if no change
|
|
||||||
if active == is_active:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Update the is_active flag
|
|
||||||
is_active = active;
|
|
||||||
|
|
||||||
# Report transition
|
|
||||||
if is_active:
|
|
||||||
emit_signal("grapple_started")
|
|
||||||
else:
|
|
||||||
emit_signal("grapple_finished")
|
|
||||||
|
|
||||||
|
|
||||||
# Test if the raycast is striking a valid target
|
|
||||||
func _is_raycast_valid() -> bool:
|
|
||||||
# Test if the raycast hit a collider
|
|
||||||
var target = _grapple_raycast.get_collider()
|
|
||||||
if not is_instance_valid(target):
|
|
||||||
return false
|
|
||||||
|
|
||||||
# Check collider layer
|
|
||||||
return true if target.collision_layer & grapple_enable_mask else false
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Check the controller node
|
|
||||||
if !XRHelpers.get_xr_controller(self):
|
|
||||||
warnings.append("This node must be within a branch of an XRController3D node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://cqqgtcktd1yr5
|
|
@ -1,28 +0,0 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://c78tjrtiyqna8"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cqqgtcktd1yr5" path="res://addons/godot-xr-tools/functions/movement_grapple.gd" id="1"]
|
|
||||||
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/pointer.tres" id="2_n6olo"]
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="1"]
|
|
||||||
resource_local_to_scene = true
|
|
||||||
size = Vector3(0.05, 0.05, 0.05)
|
|
||||||
subdivide_depth = 20
|
|
||||||
|
|
||||||
[node name="MovementGrapple" type="Node3D" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1")
|
|
||||||
|
|
||||||
[node name="Grapple_RayCast" type="RayCast3D" parent="."]
|
|
||||||
collision_mask = 3
|
|
||||||
debug_shape_custom_color = Color(0.862745, 0.278431, 0.278431, 1)
|
|
||||||
debug_shape_thickness = 1
|
|
||||||
|
|
||||||
[node name="Grapple_Target" type="MeshInstance3D" parent="."]
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource("1")
|
|
||||||
surface_material_override/0 = ExtResource("2_n6olo")
|
|
||||||
|
|
||||||
[node name="LineHelper" type="Node3D" parent="."]
|
|
||||||
|
|
||||||
[node name="Line" type="CSGCylinder3D" parent="LineHelper"]
|
|
||||||
transform = Transform3D(1.91069e-15, 4.37114e-08, 1, 1, -4.37114e-08, 0, 4.37114e-08, 1, -4.37114e-08, 0, 0, 0)
|
|
||||||
radius = 0.02
|
|
@ -1,156 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementJog
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Jog Movement
|
|
||||||
##
|
|
||||||
## This script provides jog-in-place movement for the player. This script
|
|
||||||
## works with the [XRToolsPlayerBody] attached to the players [XROrigin3D].
|
|
||||||
##
|
|
||||||
## The implementation uses filtering of the controller Y velocities to measure
|
|
||||||
## the approximate frequency of jog arm-swings; and uses that to
|
|
||||||
## switch between stopped, slow, and fast movement speeds.
|
|
||||||
|
|
||||||
|
|
||||||
## Speed mode enumeration
|
|
||||||
enum SpeedMode {
|
|
||||||
STOPPED, ## Not jogging
|
|
||||||
SLOW, ## Jogging slowly
|
|
||||||
FAST ## Jogging fast
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## Jog arm-swing frequency in Hz to trigger slow movement
|
|
||||||
const JOG_SLOW_FREQ := 3.5
|
|
||||||
|
|
||||||
## Jog arm-swing frequency in Hz to trigger fast movement
|
|
||||||
const JOG_FAST_FREQ := 5.5
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 10
|
|
||||||
|
|
||||||
## Slow jogging speed in meters-per-second
|
|
||||||
@export var slow_speed : float = 1.0
|
|
||||||
|
|
||||||
## Fast jogging speed in meters-per-second
|
|
||||||
@export var fast_speed : float = 3.0
|
|
||||||
|
|
||||||
|
|
||||||
# Jog arm-swing "stroke" detector "confidence-hat" signal
|
|
||||||
var _conf_hat := 0.0
|
|
||||||
|
|
||||||
# Current jog arm-swing "stroke" duration
|
|
||||||
var _current_stroke := 0.0
|
|
||||||
|
|
||||||
# Last jog arm-swing "stroke" total duration
|
|
||||||
var _last_stroke := 0.0
|
|
||||||
|
|
||||||
# Current jog-speed mode
|
|
||||||
var _speed_mode := SpeedMode.STOPPED
|
|
||||||
|
|
||||||
|
|
||||||
# Left controller
|
|
||||||
@onready var _left_controller := XRHelpers.get_left_controller(self)
|
|
||||||
|
|
||||||
# Right controller
|
|
||||||
@onready var _right_controller := XRHelpers.get_right_controller(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementJog" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
# Perform jump movement
|
|
||||||
func physics_movement(delta: float, player_body: XRToolsPlayerBody, _disabled: bool):
|
|
||||||
# Skip if the either controller is inactive
|
|
||||||
if !_left_controller.get_is_active() or !_right_controller.get_is_active():
|
|
||||||
_speed_mode = SpeedMode.STOPPED
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the arm-swing stroke frequency in Hz
|
|
||||||
var freq := _get_stroke_frequency(delta)
|
|
||||||
|
|
||||||
# Transition between stopped/slow/fast speed-modes based on thresholds.
|
|
||||||
# This thresholding has some hysteresis to make speed changes smoother.
|
|
||||||
if freq == 0:
|
|
||||||
_speed_mode = SpeedMode.STOPPED
|
|
||||||
elif freq < JOG_SLOW_FREQ:
|
|
||||||
_speed_mode = min(_speed_mode, SpeedMode.SLOW)
|
|
||||||
elif freq < JOG_FAST_FREQ:
|
|
||||||
_speed_mode = max(_speed_mode, SpeedMode.SLOW)
|
|
||||||
else:
|
|
||||||
_speed_mode = SpeedMode.FAST
|
|
||||||
|
|
||||||
# Pick the speed in meters-per-second based on the current speed-mode.
|
|
||||||
var speed := 0.0
|
|
||||||
if _speed_mode == SpeedMode.SLOW:
|
|
||||||
speed = slow_speed
|
|
||||||
elif _speed_mode == SpeedMode.FAST:
|
|
||||||
speed = fast_speed
|
|
||||||
|
|
||||||
# Contribute to the player body speed - with clamping to the maximum speed
|
|
||||||
player_body.ground_control_velocity.y += speed
|
|
||||||
var length := player_body.ground_control_velocity.length()
|
|
||||||
if length > fast_speed:
|
|
||||||
player_body.ground_control_velocity *= fast_speed / length
|
|
||||||
|
|
||||||
|
|
||||||
# Get the frequency of the last arm-swing "stroke" in Hz.
|
|
||||||
func _get_stroke_frequency(delta : float) -> float:
|
|
||||||
# Get the controller velocities
|
|
||||||
var vl := _left_controller.get_pose().linear_velocity.y
|
|
||||||
var vr := _right_controller.get_pose().linear_velocity.y
|
|
||||||
|
|
||||||
# Calculate the arm-swing "stroke" confidence. This is done by multiplying
|
|
||||||
# the left and right controller vertical velocities. As these velocities
|
|
||||||
# are highly anti-correlated while "jogging" the result is a confidence
|
|
||||||
# signal with a high "peak" on every jog "stroke".
|
|
||||||
var conf := vl * -vr
|
|
||||||
|
|
||||||
# Test for the confidence valley between strokes. This is used to signal
|
|
||||||
# when to measure the duration between strokes.
|
|
||||||
var valley := conf < _conf_hat
|
|
||||||
|
|
||||||
# Update confidence-hat. The confidence-hat signal has a fast-rise and
|
|
||||||
# slow-decay. Rising with each jog arm-swing "stroke" and then taking time
|
|
||||||
# to decay. The magnitude of the "confidence-hat" can be used as a good
|
|
||||||
# indicator of when the user is jogging; and the difference between the
|
|
||||||
# "confidence" and "confidence-hat" signals can be used to identify the
|
|
||||||
# duration of a jog arm-swing "stroke".
|
|
||||||
if valley:
|
|
||||||
# Gently decay when in the confidence valley.
|
|
||||||
_conf_hat = lerpf(_conf_hat, 0.0, delta * 2)
|
|
||||||
else:
|
|
||||||
# Quickly ramp confidence-hat to confidence
|
|
||||||
_conf_hat = lerpf(_conf_hat, conf, delta * 20)
|
|
||||||
|
|
||||||
# If the "confidence-hat" signal is too low then the user is not jogging.
|
|
||||||
# The stroke date-data is cleared and a stroke frequency of 0Hz is returned.
|
|
||||||
if _conf_hat < 0.5:
|
|
||||||
_current_stroke = 0.0
|
|
||||||
_last_stroke = 0.0
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
# Track the jog arm-swing "stroke" duration.
|
|
||||||
if valley:
|
|
||||||
# In the valley between jog arm-swing "strokes"
|
|
||||||
_current_stroke += delta
|
|
||||||
elif _current_stroke > 0.1:
|
|
||||||
# Save the measured jog arm-swing "stroke" duration.
|
|
||||||
_last_stroke = _current_stroke
|
|
||||||
_current_stroke = 0.0
|
|
||||||
|
|
||||||
# If no previous jog arm-swing "stroke" duration to report, so return 0Hz.
|
|
||||||
if _last_stroke < 0.1:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
# If the current jog arm-swing "stroke" is taking longer (slower) than 2Hz
|
|
||||||
# then truncate to 0Hz.
|
|
||||||
if _current_stroke > 0.75:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
# Return the last jog arm-swing "stroke" in Hz.
|
|
||||||
return 1.0 / _last_stroke
|
|
@ -1 +0,0 @@
|
|||||||
uid://cojto70nsrm6x
|
|
@ -1,6 +0,0 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://chcuj3jysipk8"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cojto70nsrm6x" path="res://addons/godot-xr-tools/functions/movement_jog.gd" id="1_k4cao"]
|
|
||||||
|
|
||||||
[node name="MovementJog" type="Node" groups=["movement_providers"]]
|
|
||||||
script = ExtResource("1_k4cao")
|
|
@ -1,52 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name XRToolsMovementJump
|
|
||||||
extends XRToolsMovementProvider
|
|
||||||
|
|
||||||
|
|
||||||
## XR Tools Movement Provider for Jumping
|
|
||||||
##
|
|
||||||
## This script provides jumping mechanics for the player. This script works
|
|
||||||
## with the [XRToolsPlayerBody] attached to the players [XROrigin3D].
|
|
||||||
##
|
|
||||||
## The player enables jumping by attaching an [XRToolsMovementJump] as a
|
|
||||||
## child of the appropriate [XRController3D], then configuring the jump button
|
|
||||||
## and jump velocity.
|
|
||||||
|
|
||||||
|
|
||||||
## Movement provider order
|
|
||||||
@export var order : int = 20
|
|
||||||
|
|
||||||
## Button to trigger jump
|
|
||||||
@export var jump_button_action : String = "trigger_click"
|
|
||||||
|
|
||||||
|
|
||||||
# Node references
|
|
||||||
@onready var _controller := XRHelpers.get_xr_controller(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Add support for is_xr_class on XRTools classes
|
|
||||||
func is_xr_class(name : String) -> bool:
|
|
||||||
return name == "XRToolsMovementJump" or super(name)
|
|
||||||
|
|
||||||
|
|
||||||
# Perform jump movement
|
|
||||||
func physics_movement(_delta: float, player_body: XRToolsPlayerBody, _disabled: bool):
|
|
||||||
# Skip if the jump controller isn't active
|
|
||||||
if !_controller.get_is_active():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Request jump if the button is pressed
|
|
||||||
if _controller.is_button_pressed(jump_button_action):
|
|
||||||
player_body.request_jump()
|
|
||||||
|
|
||||||
|
|
||||||
# This method verifies the movement provider has a valid configuration.
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
|
||||||
var warnings := super()
|
|
||||||
|
|
||||||
# Check the controller node
|
|
||||||
if !XRHelpers.get_xr_controller(self):
|
|
||||||
warnings.append("This node must be within a branch of an XRController3D node")
|
|
||||||
|
|
||||||
# Return warnings
|
|
||||||
return warnings
|
|
@ -1 +0,0 @@
|
|||||||
uid://xuqeipfnvp1r
|
|