update to latest xr tools

This commit is contained in:
Nitwel 2023-12-14 00:03:03 +01:00
parent a0433a5659
commit 2326534296
116 changed files with 1797 additions and 547 deletions

View File

@ -1,3 +1,17 @@
# 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.

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dbbxlk5acoxfg"
uid="uid://ocyj01x5mtt7"
path.s3tc="res://.godot/imported/Hold trigger to continue.png-ce0a3a4de13c262f7015326bad2cb09d.s3tc.ctex"
path.etc2="res://.godot/imported/Hold trigger to continue.png-ce0a3a4de13c262f7015326bad2cb09d.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b77u06jwo4l5y"
uid="uid://clbtsf0ahb3fm"
path.s3tc="res://.godot/imported/progress_bar.png-2ef3cbffca173889900be004fdeb1700.s3tc.ctex"
path.etc2="res://.godot/imported/progress_bar.png-2ef3cbffca173889900be004fdeb1700.etc2.ctex"
metadata={

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://cggrrnkgc7ufo"]
[gd_scene load_steps=2 format=2]
[ext_resource type="Script" path="res://addons/godot-xr-tools/audio/surface_audio.gd" id="1"]
[ext_resource path="res://addons/godot-xr-tools/audio/surface_audio.gd" type="Script" id=1]
[node name="SurfaceAudio" type="Node"]
script = ExtResource("1")
script = ExtResource( 1 )

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dd81tkixfsdrw"
uid="uid://dhnfbf4p0s74"
path="res://.godot/imported/audio.svg-20d7f0b624a1b2ef54f1b4d12970c8d0.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://8krthn43snhi"
uid="uid://cyg33jxco0rh6"
path="res://.godot/imported/body.svg-324e141d452c32f3136ca97c338025b4.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cqjrs44rcon7m"
uid="uid://bfkcd3fkyahqu"
path="res://.godot/imported/foot.svg-9e361563e010aa07be49bfb25fdb6639.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b3tktceuqduij"
uid="uid://b5vxil50s0ofi"
path="res://.godot/imported/function.svg-52c5f936037e0f38a4da2b1e16ae67fe.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://duxf7oimwxodc"
uid="uid://beko1qhyybx7e"
path="res://.godot/imported/hand.svg-a05486d804ef16320d6cf54e06292b8f.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bj0d07w1a7blg"
uid="uid://04fn15h4x333"
path="res://.godot/imported/movement_provider.svg-3c994cf0a3775c20f333be563d69fbf8.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://xtrkapd7af3l"
uid="uid://b6gwa6o27pbry"
path="res://.godot/imported/node.svg-37d53571b4a4459efefcc791c5402b4f.ctex"
metadata={
"vram_texture": false

View File

@ -6,11 +6,10 @@
[sub_resource type="ArrayMesh" id="ArrayMesh_yyajy"]
_surfaces = [{
"aabb": AABB(-2, -2, 0, 4, 4, 1e-05),
"format": 34359742465,
"format": 4097,
"index_count": 192,
"index_data": PackedByteArray(32, 0, 33, 0, 1, 0, 32, 0, 1, 0, 0, 0, 33, 0, 34, 0, 2, 0, 33, 0, 2, 0, 1, 0, 34, 0, 35, 0, 3, 0, 34, 0, 3, 0, 2, 0, 35, 0, 36, 0, 4, 0, 35, 0, 4, 0, 3, 0, 36, 0, 37, 0, 5, 0, 36, 0, 5, 0, 4, 0, 37, 0, 38, 0, 6, 0, 37, 0, 6, 0, 5, 0, 38, 0, 39, 0, 7, 0, 38, 0, 7, 0, 6, 0, 39, 0, 40, 0, 8, 0, 39, 0, 8, 0, 7, 0, 40, 0, 41, 0, 9, 0, 40, 0, 9, 0, 8, 0, 41, 0, 42, 0, 10, 0, 41, 0, 10, 0, 9, 0, 42, 0, 43, 0, 11, 0, 42, 0, 11, 0, 10, 0, 43, 0, 44, 0, 12, 0, 43, 0, 12, 0, 11, 0, 44, 0, 45, 0, 13, 0, 44, 0, 13, 0, 12, 0, 45, 0, 46, 0, 14, 0, 45, 0, 14, 0, 13, 0, 46, 0, 47, 0, 15, 0, 46, 0, 15, 0, 14, 0, 47, 0, 48, 0, 16, 0, 47, 0, 16, 0, 15, 0, 48, 0, 49, 0, 17, 0, 48, 0, 17, 0, 16, 0, 49, 0, 50, 0, 18, 0, 49, 0, 18, 0, 17, 0, 50, 0, 51, 0, 19, 0, 50, 0, 19, 0, 18, 0, 51, 0, 52, 0, 20, 0, 51, 0, 20, 0, 19, 0, 52, 0, 53, 0, 21, 0, 52, 0, 21, 0, 20, 0, 53, 0, 54, 0, 22, 0, 53, 0, 22, 0, 21, 0, 54, 0, 55, 0, 23, 0, 54, 0, 23, 0, 22, 0, 55, 0, 56, 0, 24, 0, 55, 0, 24, 0, 23, 0, 56, 0, 57, 0, 25, 0, 56, 0, 25, 0, 24, 0, 57, 0, 58, 0, 26, 0, 57, 0, 26, 0, 25, 0, 58, 0, 59, 0, 27, 0, 58, 0, 27, 0, 26, 0, 59, 0, 60, 0, 28, 0, 59, 0, 28, 0, 27, 0, 60, 0, 61, 0, 29, 0, 60, 0, 29, 0, 28, 0, 61, 0, 62, 0, 30, 0, 61, 0, 30, 0, 29, 0, 62, 0, 63, 0, 31, 0, 62, 0, 31, 0, 30, 0, 63, 0, 32, 0, 0, 0, 63, 0, 0, 0, 31, 0),
"primitive": 3,
"uv_scale": Vector4(0, 0, 0, 0),
"vertex_count": 64,
"vertex_data": PackedByteArray(0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0, 190, 20, 123, 63, 194, 197, 71, 190, 0, 0, 0, 0, 94, 131, 108, 63, 22, 239, 195, 190, 0, 0, 0, 0, 49, 219, 84, 63, 218, 57, 14, 191, 0, 0, 0, 0, 243, 4, 53, 63, 243, 4, 53, 191, 0, 0, 0, 0, 218, 57, 14, 63, 49, 219, 84, 191, 0, 0, 0, 0, 21, 239, 195, 62, 94, 131, 108, 191, 0, 0, 0, 0, 196, 197, 71, 62, 190, 20, 123, 191, 0, 0, 0, 0, 46, 189, 59, 179, 0, 0, 128, 191, 0, 0, 0, 0, 194, 197, 71, 190, 190, 20, 123, 191, 0, 0, 0, 0, 20, 239, 195, 190, 95, 131, 108, 191, 0, 0, 0, 0, 217, 57, 14, 191, 50, 219, 84, 191, 0, 0, 0, 0, 243, 4, 53, 191, 243, 4, 53, 191, 0, 0, 0, 0, 50, 219, 84, 191, 217, 57, 14, 191, 0, 0, 0, 0, 94, 131, 108, 191, 23, 239, 195, 190, 0, 0, 0, 0, 191, 20, 123, 191, 193, 197, 71, 190, 0, 0, 0, 0, 0, 0, 128, 191, 46, 189, 187, 51, 0, 0, 0, 0, 191, 20, 123, 191, 189, 197, 71, 62, 0, 0, 0, 0, 94, 131, 108, 191, 21, 239, 195, 62, 0, 0, 0, 0, 48, 219, 84, 191, 219, 57, 14, 63, 0, 0, 0, 0, 244, 4, 53, 191, 242, 4, 53, 63, 0, 0, 0, 0, 221, 57, 14, 191, 47, 219, 84, 63, 0, 0, 0, 0, 26, 239, 195, 190, 94, 131, 108, 63, 0, 0, 0, 0, 198, 197, 71, 190, 190, 20, 123, 63, 0, 0, 0, 0, 46, 222, 76, 50, 0, 0, 128, 63, 0, 0, 0, 0, 200, 197, 71, 62, 190, 20, 123, 63, 0, 0, 0, 0, 27, 239, 195, 62, 93, 131, 108, 63, 0, 0, 0, 0, 215, 57, 14, 63, 51, 219, 84, 63, 0, 0, 0, 0, 242, 4, 53, 63, 245, 4, 53, 63, 0, 0, 0, 0, 49, 219, 84, 63, 219, 57, 14, 63, 0, 0, 0, 0, 95, 131, 108, 63, 21, 239, 195, 62, 0, 0, 0, 0, 191, 20, 123, 63, 188, 197, 71, 62, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 190, 20, 251, 63, 194, 197, 199, 190, 0, 0, 0, 0, 94, 131, 236, 63, 22, 239, 67, 191, 0, 0, 0, 0, 49, 219, 212, 63, 218, 57, 142, 191, 0, 0, 0, 0, 243, 4, 181, 63, 243, 4, 181, 191, 0, 0, 0, 0, 218, 57, 142, 63, 49, 219, 212, 191, 0, 0, 0, 0, 21, 239, 67, 63, 94, 131, 236, 191, 0, 0, 0, 0, 196, 197, 199, 62, 190, 20, 251, 191, 0, 0, 0, 0, 46, 189, 187, 179, 0, 0, 0, 192, 0, 0, 0, 0, 194, 197, 199, 190, 190, 20, 251, 191, 0, 0, 0, 0, 20, 239, 67, 191, 95, 131, 236, 191, 0, 0, 0, 0, 217, 57, 142, 191, 50, 219, 212, 191, 0, 0, 0, 0, 243, 4, 181, 191, 243, 4, 181, 191, 0, 0, 0, 0, 50, 219, 212, 191, 217, 57, 142, 191, 0, 0, 0, 0, 94, 131, 236, 191, 23, 239, 67, 191, 0, 0, 0, 0, 191, 20, 251, 191, 193, 197, 199, 190, 0, 0, 0, 0, 0, 0, 0, 192, 46, 189, 59, 52, 0, 0, 0, 0, 191, 20, 251, 191, 189, 197, 199, 62, 0, 0, 0, 0, 94, 131, 236, 191, 21, 239, 67, 63, 0, 0, 0, 0, 48, 219, 212, 191, 219, 57, 142, 63, 0, 0, 0, 0, 244, 4, 181, 191, 242, 4, 181, 63, 0, 0, 0, 0, 221, 57, 142, 191, 47, 219, 212, 63, 0, 0, 0, 0, 26, 239, 67, 191, 94, 131, 236, 63, 0, 0, 0, 0, 198, 197, 199, 190, 190, 20, 251, 63, 0, 0, 0, 0, 46, 222, 204, 50, 0, 0, 0, 64, 0, 0, 0, 0, 200, 197, 199, 62, 190, 20, 251, 63, 0, 0, 0, 0, 27, 239, 67, 63, 93, 131, 236, 63, 0, 0, 0, 0, 215, 57, 142, 63, 51, 219, 212, 63, 0, 0, 0, 0, 242, 4, 181, 63, 245, 4, 181, 63, 0, 0, 0, 0, 49, 219, 212, 63, 219, 57, 142, 63, 0, 0, 0, 0, 95, 131, 236, 63, 21, 239, 67, 63, 0, 0, 0, 0, 191, 20, 251, 63, 188, 197, 199, 62, 0, 0, 0, 0)
}]

View File

@ -373,6 +373,7 @@ func drop_object() -> void:
# 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
@ -401,7 +402,7 @@ func _pick_up_object(target: Node3D) -> void:
# 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, _controller)
target.pick_up(self)
# If object picked up then emit signal
if is_instance_valid(picked_up_object):

View File

@ -4,3 +4,4 @@
[node name="FunctionPickup" type="Node3D"]
script = ExtResource("1")
grab_collision_mask = 327684

View File

@ -81,11 +81,15 @@ func _on_area_entered(area : Area3D) -> void:
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
@ -97,3 +101,6 @@ func _on_area_exited(area : Area3D) -> void:
# Remove any overrides set from this hand-pose area
_hand.remove_pose_override(pose_area)
# Enable previously disabled grabpoints
pose_area.enable_grab_points()

View File

@ -68,6 +68,12 @@ var _dominant : Node3D
# 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)
@ -205,6 +211,8 @@ func _on_left_picked_up(what : Node3D) -> void:
# 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
@ -225,6 +233,8 @@ func _on_right_picked_up(what : Node3D) -> void:
# 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
@ -232,6 +242,8 @@ 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
@ -243,6 +255,8 @@ 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

View File

@ -117,13 +117,21 @@ func _ready():
_line.hide()
# Update grapple display objects
func _process(_delta: float):
# Update the grappling line and target
func _physics_process(_delta : float):
# Skip if running in the editor
if Engine.is_editor_hint():
return
# Update grapple line
# 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)
@ -133,14 +141,6 @@ func _process(_delta: float):
else:
_line.visible = false
# Update grapple target
if enabled and !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
# Perform grapple movement
func physics_movement(delta: float, player_body: XRToolsPlayerBody, disabled: bool):
@ -220,14 +220,12 @@ func _set_grappling(active: bool) -> void:
# Test if the raycast is striking a valid target
func _is_raycast_valid() -> bool:
# Fail if raycast not colliding
if not _grapple_raycast.is_colliding():
# Test if the raycast hit a collider
var target = _grapple_raycast.get_collider()
if not is_instance_valid(target):
return false
# Get the target of the raycast
var target : CollisionObject3D = _grapple_raycast.get_collider()
# Check tartget layer
# Check collider layer
return true if target.collision_layer & grapple_enable_mask else false

View File

@ -0,0 +1,156 @@
@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

View File

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://chcuj3jysipk8"]
[ext_resource type="Script" 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")

View File

@ -0,0 +1,217 @@
@tool
class_name XRToolsMovementWorldGrab
extends XRToolsMovementProvider
## XR Tools Movement Provider for World-Grab
##
## This script provides world-grab movement for the player. To add world-grab
## support, the player must also have [XRToolsFunctionPickup] nodes attached
## to the left and right controllers, and an [XRToolsPlayerBody] under the
## [XROrigin3D].
##
## World-Grab areas inherit from the world_grab_area scene, or be [Area3D]
## nodes with the [XRToolsWorldGrabArea] script attached to them.
## Signal invoked when the player starts world-grab movement
signal player_world_grab_start
## Signal invoked when the player ends world-grab movement
signal player_world_grab_end
## Movement provider order
@export var order : int = 15
## Smallest world scale
@export var world_scale_min := 0.5
## Largest world scale
@export var world_scale_max := 2.0
# Left world-grab handle
var _left_handle : Node3D
# Right world-grab handle
var _right_handle : Node3D
# 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)
# Add support for is_xr_class on XRTools classes
func is_xr_class(name : String) -> bool:
return name == "XRToolsMovementGrabWorld" 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 world-grab movement if requested
if disabled or !enabled:
_set_world_grab_moving(false)
return
# Always set velocity to zero if enabled
player_body.velocity = Vector3.ZERO
# Check for world-grab 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
# Disable world-grab movement if not holding the world
if not _left_handle and not _right_handle:
_set_world_grab_moving(false)
return
# World grabbed
_set_world_grab_moving(true)
# Handle world-grab movement
var offset := Vector3.ZERO
if _left_handle and not _right_handle:
# Left-hand movement only
var left_pickup_pos := _left_controller.global_position
var left_grab_pos := _left_handle.global_position
offset = left_pickup_pos - left_grab_pos
elif _right_handle and not _left_handle:
# Right-hand movement only
var right_pickup_pos := _right_controller.global_position
var right_grab_pos := _right_handle.global_position
offset = right_pickup_pos - right_grab_pos
else:
# Get the world-grab handle positions
var left_grab_pos := _left_handle.global_position
var right_grab_pos := _right_handle.global_position
var grab_l2r := (right_grab_pos - left_grab_pos).slide(player_body.up_player)
var grab_mid := (left_grab_pos + right_grab_pos) * 0.5
# Get the pickup positions
var left_pickup_pos := _left_controller.global_position
var right_pickup_pos := _right_controller.global_position
var pickup_l2r := (right_pickup_pos - left_pickup_pos).slide(player_body.up_player)
var pickup_mid := (left_pickup_pos + right_pickup_pos) * 0.5
# Apply rotation
var angle := grab_l2r.signed_angle_to(pickup_l2r, player_body.up_player)
player_body.rotate_player(angle)
# Apply scale
var new_world_scale := XRServer.world_scale * grab_l2r.length() / pickup_l2r.length()
new_world_scale = clamp(new_world_scale, world_scale_min, world_scale_max)
XRServer.world_scale = new_world_scale
# Apply offset
offset = pickup_mid - grab_mid
# Move the player by the offset
var old_position := player_body.global_position
player_body.move_body(-offset / delta)
player_body.velocity = Vector3.ZERO
#player_body.move_and_collide(-offset)
# Report exclusive motion performed (to bypass gravity)
return true
## Start or stop world-grab movement
func _set_world_grab_moving(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("player_world_grab_start")
else:
emit_signal("player_world_grab_end")
## Handler for left controller picked up
func _on_left_picked_up(what : Node3D) -> void:
# Get the world-grab area
var world_grab_area = what as XRToolsWorldGrabArea
if not world_grab_area:
return
# Get the handle
_left_handle = world_grab_area.get_grab_handle(_left_pickup_node)
if not _left_handle:
return
## Handler for right controller picked up
func _on_right_picked_up(what : Node3D) -> void:
# Get the world-grab area
var world_grab_area = what as XRToolsWorldGrabArea
if not world_grab_area:
return
# Get the handle
_right_handle = world_grab_area.get_grab_handle(_right_pickup_node)
if not _right_handle:
return
## Handler for left controller dropped
func _on_left_dropped() -> void:
# Release handle and transfer dominance
_left_handle = null
## Handler for righ controller dropped
func _on_right_dropped() -> void:
# Release handle and transfer dominance
_right_handle = null
# 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")
# Return warnings
return warnings

View File

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dg3gr6ofd8yx4"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/functions/movement_world_grab.gd" id="1_0qp8q"]
[node name="MovementWorldGrab" type="Node" groups=["movement_providers"]]
script = ExtResource("1_0qp8q")

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b55738edba8fcdaab47c5e40cf4b900b10c86ea67f9d40dc041f84f4e76ec484
oid sha256:ec74b5b5948e76d01115d7ff959610515e97aca1c42870f36b4a747f2dc4cb70
size 2416

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a3e64d9a45c8ab58180b00eae7ed8e306c22686656fca4ed854b87283ed4c09
oid sha256:46cdd3222390fdf4b7e6f591958bbf30944df088a71cab919ba54a49ace2baf8
size 2424

View File

@ -0,0 +1,3 @@
[remap]
importer="keep"

View File

@ -0,0 +1,3 @@
[remap]
importer="keep"

View File

@ -0,0 +1,3 @@
[remap]
importer="keep"

View File

@ -0,0 +1,3 @@
[remap]
importer="keep"

View File

@ -79,8 +79,10 @@ func _ready():
if Engine.is_editor_hint():
return
# Disconnect from parent transform, as we move to it in the physics step
# Disconnect from parent transform as we move to it in the physics step,
# and boost the physics priority above any grab-drivers or hands.
top_level = true
process_physics_priority = -90
# Populate nodes
_controller = XRTools.find_xr_ancestor(self, "*", "XRController3D")
@ -171,6 +173,7 @@ func _move_to_target():
# Orient the hand then move
global_transform.basis = _target.global_transform.basis
move_and_slide(_target.global_position - global_position)
force_update_transform()
# This function inserts a target override into the overrides list by priority

View File

@ -41,6 +41,9 @@ var _last_world_scale : float = 1.0
var _controller : XRController3D
## Initial hand transform (from controller) - used for scaling hands
var _initial_transform : Transform3D
## Current hand transform (from controller) - after scale
var _transform : Transform3D
## Hand mesh
@ -64,6 +67,12 @@ var _force_grip := -1.0
## Force trigger value (< 0 for no force)
var _force_trigger := -1.0
# Sorted stack of TargetOverride
var _target_overrides := []
# Current target (controller or override)
var _target : Node3D
## Pose-override class
class PoseOverride:
@ -83,6 +92,20 @@ class PoseOverride:
settings = s
## Target-override class
class TargetOverride:
## Target of the override
var target : Node3D
## Target priority
var priority : int
## Target-override constructor
func _init(t : Node3D, p : int):
target = t
priority = p
# Add support for is_xr_class on XRTools classes
func is_xr_class(name : String) -> bool:
return name == "XRToolsHand"
@ -91,7 +114,15 @@ func is_xr_class(name : String) -> bool:
## Called when the node enters the scene tree for the first time.
func _ready() -> void:
# Save the initial hand transform
_transform = transform
_initial_transform = transform
_transform = _initial_transform
# Disconnect from parent transform as we move to it in the physics step,
# and boost the physics priority after any grab-drivers but before other
# processing.
if not Engine.is_editor_hint():
top_level = true
process_physics_priority = -70
# Find our controller
_controller = XRTools.find_xr_ancestor(self, "*", "XRController3D")
@ -105,12 +136,13 @@ func _ready() -> void:
_update_hand_blend_tree()
_update_hand_material_override()
_update_pose()
_update_target()
## This method is called on every frame. It checks for world-scale changes and
## scales itself causing the hand mesh and skeleton to scale appropriately.
## It then reads the grip and trigger action values to animate the hand.
func _process(_delta: float) -> void:
## This method checks for world-scale changes and scales itself causing the
## hand mesh and skeleton to scale appropriately. It then reads the grip and
## trigger action values to animate the hand.
func _physics_process(_delta: float) -> void:
# Do not run physics if in the editor
if Engine.is_editor_hint():
return
@ -118,7 +150,7 @@ func _process(_delta: float) -> void:
# Scale the hand mesh with the world scale.
if XRServer.world_scale != _last_world_scale:
_last_world_scale = XRServer.world_scale
transform = _transform.scaled(Vector3.ONE * _last_world_scale)
_transform = _initial_transform.scaled(Vector3.ONE * _last_world_scale)
emit_signal("hand_scale_changed", _last_world_scale)
# Animate the hand mesh with the controller inputs
@ -133,6 +165,9 @@ func _process(_delta: float) -> void:
$AnimationTree.set("parameters/Grip/blend_amount", grip)
$AnimationTree.set("parameters/Trigger/blend_amount", trigger)
# Move to target
global_transform = _target.global_transform * _transform
force_update_transform()
# This method verifies the hand has a valid configuration.
@ -161,7 +196,7 @@ func _get_configuration_warnings() -> PackedStringArray:
## Find an [XRToolsHand] node.
##
## This function searches from the specified node for an [XRToolsHand] assuming
## the node is a sibling of the hand under an [ARVRController].
## the node is a sibling of the hand under an [XROrigin3D].
static func find_instance(node : Node) -> XRToolsHand:
return XRTools.find_xr_child(
XRHelpers.get_xr_controller(node),
@ -169,6 +204,24 @@ static func find_instance(node : Node) -> XRToolsHand:
"XRToolsHand") as XRToolsHand
## This function searches from the specified node for the left controller
## [XRToolsHand] assuming the node is a sibling of the [XROrigin3D].
static func find_left(node : Node) -> XRToolsHand:
return XRTools.find_xr_child(
XRHelpers.get_left_controller(node),
"*",
"XRToolsHand") as XRToolsHand
## This function searches from the specified node for the right controller
## [XRToolsHand] assuming the node is a sibling of the [XROrigin3D].
static func find_right(node : Node) -> XRToolsHand:
return XRTools.find_xr_child(
XRHelpers.get_right_controller(node),
"*",
"XRToolsHand") as XRToolsHand
## Set the blend tree
func set_hand_blend_tree(blend_tree : AnimationNodeBlendTree) -> void:
hand_blend_tree = blend_tree
@ -227,6 +280,32 @@ func force_grip_trigger(grip : float = -1.0, trigger : float = -1.0) -> void:
if trigger >= 0.0: $AnimationTree.set("parameters/Trigger/blend_amount", trigger)
## This function adds a target override. The collision hand will attempt to
## move to the highest priority target, or the [XRController3D] if no override
## is specified.
func add_target_override(target : Node3D, priority : int) -> void:
# Remove any existing target override from this source
var modified := _remove_target_override(target)
# Insert the target override
_insert_target_override(target, priority)
modified = true
# Update the target
if modified:
_update_target()
## This function remove a target override.
func remove_target_override(target : Node3D) -> void:
# Remove the target override
var modified := _remove_target_override(target)
# Update the pose
if modified:
_update_target()
func _update_hand_blend_tree() -> void:
# As we're going to make modifications to our animation tree, we need to do
# a deep copy, simply setting resource local to scene does not seem to be enough
@ -329,6 +408,59 @@ func _remove_pose_override(who : Node) -> bool:
return modified
# This function inserts a target override into the overrides list by priority
# order.
func _insert_target_override(target : Node3D, priority : int) -> void:
# Construct the target override
var override := TargetOverride.new(target, priority)
# Iterate over all target overrides in the list
for pos in _target_overrides.size():
# Get the target override
var o : TargetOverride = _target_overrides[pos]
# Insert as early as possible to not invalidate sorting
if o.priority <= priority:
_target_overrides.insert(pos, override)
return
# Insert at the end
_target_overrides.push_back(override)
# This function removes a target from the overrides list
func _remove_target_override(target : Node) -> bool:
var pos := 0
var length := _target_overrides.size()
var modified := false
# Iterate over all pose overrides in the list
while pos < length:
# Get the target override
var o : TargetOverride = _target_overrides[pos]
# Check for a match
if o.target == target:
# Remove the override
_target_overrides.remove_at(pos)
modified = true
length -= 1
else:
# Advance down the list
pos += 1
# Return the modified indicator
return modified
# This function updates the target for hand movement.
func _update_target() -> void:
if _target_overrides.size():
_target = _target_overrides[0].target
else:
_target = get_parent()
static func _find_child(node : Node, type : String) -> Node:
# Iterate through all children
for child in node.get_children():

View File

@ -55,6 +55,10 @@ func is_xr_class(name : String) -> bool:
# Called when the node enters the scene tree. This constructs the physics-bone
# nodes and performs initial positioning.
func _ready():
# Skip if in the editor
if Engine.is_editor_hint():
return
# Connect the 'hand_scale_changed' signal
var physics_hand := XRToolsHand.find_instance(self) as XRToolsPhysicsHand
if physics_hand:
@ -102,6 +106,10 @@ func _ready():
# Called during the physics process and moves the physics-bone to follow the
# skeletal-bone.
func _physics_process(delta: float) -> void:
# Skip if in the editor
if Engine.is_editor_hint():
return
_move_bone(delta)

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bd7h22w1o77fp"
uid="uid://bu07alt6vkbaq"
path="res://.godot/imported/Hand_Glove_L.gltf-ac6c06fe48ca0d7ce0f9faffd177a897.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://iwqh6hhdhhdk"
uid="uid://w7th3d2hl7b5"
path="res://.godot/imported/Hand_Glove_R.gltf-0e99922c663867da8d37af9597ab1ce8.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dokq2yjsxpofc"
uid="uid://bnx4hithcij6y"
path="res://.godot/imported/Hand_Glove_low_L.gltf-7b94b12c0dff159d494da6cafd52895d.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cacrvwhji2ibw"
uid="uid://csgsp0wuw8ucy"
path="res://.godot/imported/Hand_Glove_low_R.gltf-c32907596b2287c95aff0e704780ef3d.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bh786yhm5v4fh"
uid="uid://q1l3fknstir2"
path="res://.godot/imported/Hand_Nails_L.gltf-22452b307072e9d363b1f547e2b6a365.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://duffm8pu8ask7"
uid="uid://c6s5nvekg0isa"
path="res://.godot/imported/Hand_Nails_R.gltf-35869d6a39400588d95465aa5e3b528d.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://w87o4l6qhsmd"
uid="uid://c4nba0qpi0uls"
path="res://.godot/imported/Hand_Nails_low_L.gltf-60d8096917582bfe1d760ff1ad173fc8.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cgobg2isk3rk1"
uid="uid://dunu03tc5vdbh"
path="res://.godot/imported/Hand_Nails_low_R.gltf-7d9fc9507def4b4fd9c10cdae7aa6e11.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://g510kgsy5u72"
uid="uid://16colgxrw27d"
path="res://.godot/imported/Hand_low_L.gltf-a83bdf9115e4fb62c63deabade61cbbb.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://c6rnlihkm44f0"
uid="uid://cjrac51wst3tu"
path="res://.godot/imported/Hand_low_R.gltf-8b164aff014e5c5bfa2eccc8e40e7e97.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dvkh1gxbhwri4"
uid="uid://dhaqth6q5yw4n"
path="res://.godot/imported/hand_l.gltf-6ba1595ac4c28aef3719b7f0b4f2f4f2.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -3,7 +3,7 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://ytv5cbt6dbeg"
uid="uid://ds1t8vc0kxoeo"
path="res://.godot/imported/hand_r.gltf-dd429687cadefbc3e7a21ae26addfb30.scn"
[deps]
@ -22,7 +22,6 @@ meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
@ -30,5 +29,4 @@ animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1

View File

@ -5,11 +5,10 @@
[sub_resource type="BoxShape3D" id="BoxShape3D_bv7in"]
size = Vector3(0.045, 0.075, 0.1)
[node name="CollisionHandLeft" type="StaticBody3D"]
process_priority = -90
process_physics_priority = -90
[node name="CollisionHandLeft" type="AnimatableBody3D"]
collision_layer = 131072
collision_mask = 327711
sync_to_physics = false
script = ExtResource("1_t5acd")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]

View File

@ -5,11 +5,10 @@
[sub_resource type="BoxShape3D" id="BoxShape3D_fc2ij"]
size = Vector3(0.045, 0.075, 0.1)
[node name="CollisionHandRight" type="StaticBody3D"]
process_priority = -90
process_physics_priority = -90
[node name="CollisionHandRight" type="AnimatableBody3D"]
collision_layer = 131072
collision_mask = 327711
sync_to_physics = false
script = ExtResource("1_so3hf")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=13 format=3 uid="uid://bpdj5njb8nhot"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://dvkh1gxbhwri4" path="res://addons/godot-xr-tools/hands/model/hand_l.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://dhaqth6q5yw4n" path="res://addons/godot-xr-tools/hands/model/hand_l.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_x4lr3"]
[ext_resource type="Material" uid="uid://ba02jnd2dswyn" path="res://addons/godot-xr-tools/hands/materials/cleaning_glove.material" id="4"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_rtx6p")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_y01rx")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=14 format=3 uid="uid://nqiyqnx42m61"]
[ext_resource type="PackedScene" uid="uid://dvkh1gxbhwri4" path="res://addons/godot-xr-tools/hands/model/hand_l.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://dhaqth6q5yw4n" path="res://addons/godot-xr-tools/hands/model/hand_l.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_0aa1r"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_g5ps2")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_v8isy")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,42 +1,42 @@
[gd_scene load_steps=13 format=3 uid="uid://njx823gyk04n"]
[ext_resource type="PackedScene" uid="uid://bh786yhm5v4fh" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://q1l3fknstir2" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_k56uy"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://dl8yf7ipqotd1" path="res://addons/godot-xr-tools/hands/animations/left/hand_blend_tree.tres" id="5"]
[ext_resource type="Material" uid="uid://dbvge3quu3bju" path="res://addons/godot-xr-tools/hands/materials/caucasian_hand.material" id="6"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_e6byy"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_x12eo"]
animation = &"Grip"
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_bs8g1"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_kw2gg"]
animation = &"Grip"
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_675c4"]
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_o8j0s"]
filter_enabled = true
filters = ["Armature/Skeleton3D:Little_Distal_L", "Armature/Skeleton3D:Little_Intermediate_L", "Armature/Skeleton3D:Little_Metacarpal_L", "Armature/Skeleton3D:Little_Proximal_L", "Armature/Skeleton3D:Middle_Distal_L", "Armature/Skeleton3D:Middle_Intermediate_L", "Armature/Skeleton3D:Middle_Metacarpal_L", "Armature/Skeleton3D:Middle_Proximal_L", "Armature/Skeleton3D:Ring_Distal_L", "Armature/Skeleton3D:Ring_Intermediate_L", "Armature/Skeleton3D:Ring_Metacarpal_L", "Armature/Skeleton3D:Ring_Proximal_L", "Armature/Skeleton3D:Thumb_Distal_L", "Armature/Skeleton3D:Thumb_Metacarpal_L", "Armature/Skeleton3D:Thumb_Proximal_L", "Armature/Skeleton:Little_Distal_L", "Armature/Skeleton:Little_Intermediate_L", "Armature/Skeleton:Little_Proximal_L", "Armature/Skeleton:Middle_Distal_L", "Armature/Skeleton:Middle_Intermediate_L", "Armature/Skeleton:Middle_Proximal_L", "Armature/Skeleton:Ring_Distal_L", "Armature/Skeleton:Ring_Intermediate_L", "Armature/Skeleton:Ring_Proximal_L", "Armature/Skeleton:Thumb_Distal_L", "Armature/Skeleton:Thumb_Proximal_L"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_2ssci"]
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_cxcnq"]
animation = &"Grip 5"
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_narw6"]
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_yn70n"]
filter_enabled = true
filters = ["Armature/Skeleton3D:Index_Distal_L", "Armature/Skeleton3D:Index_Intermediate_L", "Armature/Skeleton3D:Index_Metacarpal_L", "Armature/Skeleton3D:Index_Proximal_L", "Armature/Skeleton:Index_Distal_L", "Armature/Skeleton:Index_Intermediate_L", "Armature/Skeleton:Index_Proximal_L"]
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_wp4n0"]
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_v8owg"]
graph_offset = Vector2(-536, 11)
nodes/ClosedHand1/node = SubResource("AnimationNodeAnimation_e6byy")
nodes/ClosedHand1/node = SubResource("AnimationNodeAnimation_x12eo")
nodes/ClosedHand1/position = Vector2(-600, 300)
nodes/ClosedHand2/node = SubResource("AnimationNodeAnimation_bs8g1")
nodes/ClosedHand2/node = SubResource("AnimationNodeAnimation_kw2gg")
nodes/ClosedHand2/position = Vector2(-360, 300)
nodes/Grip/node = SubResource("AnimationNodeBlend2_675c4")
nodes/Grip/node = SubResource("AnimationNodeBlend2_o8j0s")
nodes/Grip/position = Vector2(0, 20)
nodes/OpenHand/node = SubResource("AnimationNodeAnimation_2ssci")
nodes/OpenHand/node = SubResource("AnimationNodeAnimation_cxcnq")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_narw6")
nodes/Trigger/node = SubResource("AnimationNodeBlend2_yn70n")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftHand" type="Node3D"]
script = ExtResource("3")
@ -73,7 +73,7 @@ surface_material_override/0 = ExtResource("6")
[node name="AnimationPlayer" parent="Hand_Nails_L" instance=ExtResource("2")]
[node name="AnimationTree" type="AnimationTree" parent="."]
tree_root = SubResource("AnimationNodeBlendTree_wp4n0")
tree_root = SubResource("AnimationNodeBlendTree_v8owg")
anim_player = NodePath("../Hand_Nails_L/AnimationPlayer")
active = true
parameters/Grip/blend_amount = 0.0

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://cy03d57iyrci"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://bh786yhm5v4fh" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_L.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://q1l3fknstir2" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_L.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_fye1l"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand_physics_bone.gd" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_40ncn")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_fjpy6")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=14 format=3 uid="uid://ddsj74hpt1kgb"]
[ext_resource type="PackedScene" uid="uid://bd7h22w1o77fp" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bu07alt6vkbaq" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://dl8yf7ipqotd1" path="res://addons/godot-xr-tools/hands/animations/left/hand_blend_tree.tres" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_rnhp7"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_2ih7n")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_02eq5")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftPhysicsHand" type="Node3D"]
script = ExtResource("6")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=13 format=3 uid="uid://hpr0v012ghsb"]
[ext_resource type="PackedScene" uid="uid://bd7h22w1o77fp" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bu07alt6vkbaq" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_s33di"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_kjpaa")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_kisbg")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=13 format=3 uid="uid://d1tl07geqm5xq"]
[ext_resource type="PackedScene" uid="uid://ytv5cbt6dbeg" path="res://addons/godot-xr-tools/hands/model/hand_r.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://ds1t8vc0kxoeo" path="res://addons/godot-xr-tools/hands/model/hand_r.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_xmpie"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_gm5ny")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_3o6s4")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=14 format=3 uid="uid://cigx2gnf4tg4d"]
[ext_resource type="PackedScene" uid="uid://ytv5cbt6dbeg" path="res://addons/godot-xr-tools/hands/model/hand_r.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://ds1t8vc0kxoeo" path="res://addons/godot-xr-tools/hands/model/hand_r.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_23oai"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_4mwbe")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_x7ees")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=13 format=3 uid="uid://raeeicvvindd"]
[ext_resource type="PackedScene" uid="uid://duffm8pu8ask7" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_R.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://c6s5nvekg0isa" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_R.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_lydk3"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_0hd6d")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_okkj6")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://kcqomcjq52np"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://duffm8pu8ask7" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://c6s5nvekg0isa" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_R.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_gqplw"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand_physics_bone.gd" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_4bb4h")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_b8ks0")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://b6xesntm4qeo5"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://iwqh6hhdhhdk" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://w7th3d2hl7b5" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_R.gltf" id="2"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://m85b1gogdums" path="res://addons/godot-xr-tools/hands/animations/right/hand_blend_tree.tres" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_h0fv3"]
[ext_resource type="Material" uid="uid://dy4ya7w0k18ds" path="res://addons/godot-xr-tools/hands/materials/glove_caucasian_dark_camo.material" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_dc0m1")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_guoth")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightPhysicsHand" type="Node3D"]
script = ExtResource("6")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=13 format=3 uid="uid://dup2vp8v5rpi5"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://iwqh6hhdhhdk" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://w7th3d2hl7b5" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_R.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_3ih8a"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://m85b1gogdums" path="res://addons/godot-xr-tools/hands/animations/right/hand_blend_tree.tres" id="4"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_4vdom")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_hef27")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=13 format=3 uid="uid://bq86r4yll8po"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://g510kgsy5u72" path="res://addons/godot-xr-tools/hands/model/Hand_low_L.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://16colgxrw27d" path="res://addons/godot-xr-tools/hands/model/Hand_low_L.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_wyae6"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://dl8yf7ipqotd1" path="res://addons/godot-xr-tools/hands/animations/left/hand_blend_tree.tres" id="5"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_470u2")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_fhgyt")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=13 format=3 uid="uid://b4kad2kuba1yn"]
[ext_resource type="PackedScene" uid="uid://w87o4l6qhsmd" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://c4nba0qpi0uls" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_ry7qg"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_c3h37")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_ihxeh")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=14 format=3 uid="uid://b8hc2ex21u8xj"]
[ext_resource type="PackedScene" uid="uid://g510kgsy5u72" path="res://addons/godot-xr-tools/hands/model/Hand_low_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://16colgxrw27d" path="res://addons/godot-xr-tools/hands/model/Hand_low_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_4017m"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_fvmpw")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_a1c3t")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://bdwmserhqai5h"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://w87o4l6qhsmd" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_L.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://c4nba0qpi0uls" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_L.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_t17lq"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand_physics_bone.gd" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_3l13i")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_ms3kw")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://bj1aas6kty3qx"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://dokq2yjsxpofc" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_L.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://bnx4hithcij6y" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_L.gltf" id="2"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://dl8yf7ipqotd1" path="res://addons/godot-xr-tools/hands/animations/left/hand_blend_tree.tres" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_oehre"]
[ext_resource type="Material" uid="uid://dy4ya7w0k18ds" path="res://addons/godot-xr-tools/hands/materials/glove_caucasian_dark_camo.material" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_vkt4w")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_1yhw8")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftPhysicsHand" type="Node3D"]
script = ExtResource("6")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=13 format=3 uid="uid://bx1xdisoqo1f6"]
[ext_resource type="PackedScene" uid="uid://dokq2yjsxpofc" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bnx4hithcij6y" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_L.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://the6y7swe6j0" path="res://addons/godot-xr-tools/hands/animations/left/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dertgu7k8alls" path="res://addons/godot-xr-tools/hands/poses/pose_default_left.tres" id="3_iq5xt"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_csu2l")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_akg52")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="LeftHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=13 format=3 uid="uid://xqimcf20s2jp"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://c6rnlihkm44f0" path="res://addons/godot-xr-tools/hands/model/Hand_low_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://cjrac51wst3tu" path="res://addons/godot-xr-tools/hands/model/Hand_low_R.gltf" id="2"]
[ext_resource type="Material" uid="uid://ba02jnd2dswyn" path="res://addons/godot-xr-tools/hands/materials/cleaning_glove.material" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_r4xyu"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="4"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_l50hj")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_vrc3g")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightHand" type="Node3D"]
script = ExtResource("4")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=13 format=3 uid="uid://l2n30mpbkdyw"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://cgobg2isk3rk1" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://dunu03tc5vdbh" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_R.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_f67ka"]
[ext_resource type="Material" uid="uid://dbvge3quu3bju" path="res://addons/godot-xr-tools/hands/materials/caucasian_hand.material" id="4"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_covtt")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_h5uio")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://bkdeegb8w3oah"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://c6rnlihkm44f0" path="res://addons/godot-xr-tools/hands/model/Hand_low_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://cjrac51wst3tu" path="res://addons/godot-xr-tools/hands/model/Hand_low_R.gltf" id="2"]
[ext_resource type="Material" uid="uid://bhiiya7ow6h8v" path="res://addons/godot-xr-tools/hands/materials/labglove.material" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_e8slj"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_1fjc2")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_ai2qv")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightPhysicsHand" type="Node3D"]
script = ExtResource("4")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://btf05hjpw6k05"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://cgobg2isk3rk1" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://dunu03tc5vdbh" path="res://addons/godot-xr-tools/hands/model/Hand_Nails_low_R.gltf" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/physics_hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_b86a5"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand_physics_bone.gd" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_l0mk4")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_it0su")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightPhysicsHand" type="Node3D"]
script = ExtResource("3")

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=14 format=3 uid="uid://c2uvwvdlsfuep"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="1"]
[ext_resource type="PackedScene" uid="uid://cacrvwhji2ibw" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_R.gltf" id="2"]
[ext_resource type="PackedScene" uid="uid://csgsp0wuw8ucy" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_R.gltf" id="2"]
[ext_resource type="AnimationNodeBlendTree" uid="uid://m85b1gogdums" path="res://addons/godot-xr-tools/hands/animations/right/hand_blend_tree.tres" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_xodao"]
[ext_resource type="Material" uid="uid://dy4ya7w0k18ds" path="res://addons/godot-xr-tools/hands/materials/glove_caucasian_dark_camo.material" id="4"]
@ -37,7 +37,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_8kdoe")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_5qnsq")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightPhysicsHand" type="Node3D"]
script = ExtResource("6")

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=13 format=3 uid="uid://chumpejwiub7f"]
[ext_resource type="PackedScene" uid="uid://cacrvwhji2ibw" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_R.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://csgsp0wuw8ucy" path="res://addons/godot-xr-tools/hands/model/Hand_Glove_low_R.gltf" id="1"]
[ext_resource type="PackedScene" uid="uid://bxipj7hyjwy6f" path="res://addons/godot-xr-tools/hands/animations/right/AnimationPlayer.tscn" id="2"]
[ext_resource type="Script" path="res://addons/godot-xr-tools/hands/hand.gd" id="3"]
[ext_resource type="Resource" uid="uid://dmpjtk0l8vh6v" path="res://addons/godot-xr-tools/hands/poses/pose_default_right.tres" id="3_2wryo"]
@ -36,7 +36,7 @@ nodes/OpenHand/node = SubResource("AnimationNodeAnimation_4o3a2")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("AnimationNodeBlend2_5v4fm")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
[node name="RightHand" type="Node3D"]
script = ExtResource("3")

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bcgvvpllho2e1"
uid="uid://cs7m7m0k2506g"
path="res://.godot/imported/african_baseColor.png-c1a63b2c85973a5f7673482d994697e9.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bbegc1t6sypfe"
uid="uid://dk8o82rjww802"
path="res://.godot/imported/african_realistic_baseColor.png-67cf0f3230115c16edbeaf7b76430f7c.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ura2vjsqegjt"
uid="uid://c506svon268ms"
path.s3tc="res://.godot/imported/caucasian_baseColor.png-2ffa4b91e9a1b3bcc11c22eccd941be7.s3tc.ctex"
path.etc2="res://.godot/imported/caucasian_baseColor.png-2ffa4b91e9a1b3bcc11c22eccd941be7.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dphwanfi4jxrk"
uid="uid://n1xq4cm67o07"
path="res://.godot/imported/caucasian_realistic_baseColor.png-6d28d56c1e76917b749358861074492c.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://deh22migllf4t"
uid="uid://dv711ytktesj2"
path.s3tc="res://.godot/imported/cleaning_glove_baseColor.png-775896445d04c5f9ecad1dcd0609e336.s3tc.ctex"
path.etc2="res://.godot/imported/cleaning_glove_baseColor.png-775896445d04c5f9ecad1dcd0609e336.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c2hanl27i7oc1"
uid="uid://cocorrka3dtq3"
path="res://.godot/imported/glove_african_dark_camo.png-3a526d8d251e73995f6fd3ba5cdd0b4e.ctex"
metadata={
"vram_texture": false

View File

@ -2,26 +2,28 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://vfkxkijr37rm"
path="res://.godot/imported/glove_african_green_camo.png-b4a9e045aac0313108230034e03317fd.ctex"
uid="uid://bidphehpipwhg"
path.s3tc="res://.godot/imported/glove_african_green_camo.png-b4a9e045aac0313108230034e03317fd.s3tc.ctex"
path.etc2="res://.godot/imported/glove_african_green_camo.png-b4a9e045aac0313108230034e03317fd.etc2.ctex"
metadata={
"vram_texture": false
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://addons/godot-xr-tools/hands/textures/glove_african_green_camo.png"
dest_files=["res://.godot/imported/glove_african_green_camo.png-b4a9e045aac0313108230034e03317fd.ctex"]
dest_files=["res://.godot/imported/glove_african_green_camo.png-b4a9e045aac0313108230034e03317fd.s3tc.ctex", "res://.godot/imported/glove_african_green_camo.png-b4a9e045aac0313108230034e03317fd.etc2.ctex"]
[params]
compress/mode=0
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=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
@ -31,4 +33,4 @@ 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
detect_3d/compress_to=0

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://daphmv2yp22x"
uid="uid://c7jutkax2fd1k"
path.s3tc="res://.godot/imported/glove_caucasian_dark_camo.png-338e35680a2de0f7c6ad596eba6a3d5f.s3tc.ctex"
path.etc2="res://.godot/imported/glove_caucasian_dark_camo.png-338e35680a2de0f7c6ad596eba6a3d5f.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bvbbghjfsau1b"
uid="uid://dgkfppran5j1f"
path.s3tc="res://.godot/imported/glove_caucasian_green_camo.png-009f6b98bc0f9d73ac78a6b6900a0d00.s3tc.ctex"
path.etc2="res://.godot/imported/glove_caucasian_green_camo.png-009f6b98bc0f9d73ac78a6b6900a0d00.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://duqdr6qqrim0s"
uid="uid://bw8rmigwt5pr4"
path.s3tc="res://.godot/imported/glove_fingerless_normal.png-46736b8f0a977e37b17257f7bbbb718f.s3tc.ctex"
path.etc2="res://.godot/imported/glove_fingerless_normal.png-46736b8f0a977e37b17257f7bbbb718f.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://3fax6yglkrd3"
uid="uid://d2iocwfr47kqp"
path.s3tc="res://.godot/imported/glove_fingerless_occlusionRoughnessMetallic.png-441bf1f84442c3cd6ae3834c9c982565.s3tc.ctex"
path.etc2="res://.godot/imported/glove_fingerless_occlusionRoughnessMetallic.png-441bf1f84442c3cd6ae3834c9c982565.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dasjkfath7rqu"
uid="uid://dt6k4nuvpo38o"
path.s3tc="res://.godot/imported/glove_normal.png-da82759e655cb7106cb0172b7076b948.s3tc.ctex"
path.etc2="res://.godot/imported/glove_normal.png-da82759e655cb7106cb0172b7076b948.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b5v1xfnkyjaf6"
uid="uid://yslwgnpbpeen"
path.s3tc="res://.godot/imported/glove_occlusionRoughnessMetallic.png-1a61c4638e30c81daa59d92c8681eb2c.s3tc.ctex"
path.etc2="res://.godot/imported/glove_occlusionRoughnessMetallic.png-1a61c4638e30c81daa59d92c8681eb2c.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dlkn0p2k7gfqc"
uid="uid://b6tgv6ucbgv3r"
path="res://.godot/imported/hands_normal.png-291c7217751274bfe1199e3de899450f.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dy5hyp5n7o6gl"
uid="uid://h3sjbnu6uplb"
path.s3tc="res://.godot/imported/hands_occlusionRoughnessMetallic.png-73229fc54301aa8833117ebde8abfd4e.s3tc.ctex"
path.etc2="res://.godot/imported/hands_occlusionRoughnessMetallic.png-73229fc54301aa8833117ebde8abfd4e.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cxwjhfe1yvm65"
uid="uid://d4gbpgjwjuovb"
path.s3tc="res://.godot/imported/labglove_baseColor.png-2eb9fa4d9955a5755a96f0663ee3ceb2.s3tc.ctex"
path.etc2="res://.godot/imported/labglove_baseColor.png-2eb9fa4d9955a5755a96f0663ee3ceb2.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://b8a7ol64q60tb"
uid="uid://bcll0cv3rie0i"
path="res://.godot/imported/icon.png-1d8efcc4c33e64800e22ea3150ef148d.ctex"
metadata={
"vram_texture": false

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ciimvkgy0mee0"
uid="uid://8s03ra8tp0hr"
path.s3tc="res://.godot/imported/ring.png-34d9c9e9780f2732148a5b14fcac48dd.s3tc.ctex"
path.etc2="res://.godot/imported/ring.png-34d9c9e9780f2732148a5b14fcac48dd.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bbfwq7mhqgwjm"
uid="uid://ddoj6c345cb0c"
path.s3tc="res://.godot/imported/teleport_arrow.png-f1bd44b6f478277692b3fa29171b62d3.s3tc.ctex"
path.etc2="res://.godot/imported/teleport_arrow.png-f1bd44b6f478277692b3fa29171b62d3.etc2.ctex"
metadata={

View File

@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://cygfooytyfhyd"
uid="uid://cu4j1s8qr1rjq"
path.s3tc="res://.godot/imported/teleport_target.png-cd812f7d5692711ac91f6c8a4753ad73.s3tc.ctex"
path.etc2="res://.godot/imported/teleport_target.png-cd812f7d5692711ac91f6c8a4753ad73.etc2.ctex"
metadata={

View File

@ -47,29 +47,29 @@ func _ready() -> void:
# Called on every frame when the handle is held to check for snapping
func _process(_delta: float) -> void:
# Skip if not picked up
if !picked_up_by:
if not is_picked_up():
return
# If too far from the origin then drop the handle
var origin_pos = handle_origin.global_transform.origin
var handle_pos = global_transform.origin
if handle_pos.distance_to(origin_pos) > snap_distance:
picked_up_by.drop_object()
drop()
# Called when the handle is picked up
func pick_up(by, with_controller) -> void:
func pick_up(by) -> void:
# Call the base-class to perform the pickup
super(by, with_controller)
super(by)
# Enable the process function while held
set_process(true)
# Called when the handle is dropped
func let_go(_p_linear_velocity: Vector3, _p_angular_velocity: Vector3) -> void:
func let_go(by: Node3D, _p_linear_velocity: Vector3, _p_angular_velocity: Vector3) -> void:
# Call the base-class to perform the drop, but with no velocity
super(Vector3.ZERO, Vector3.ZERO)
super(by, Vector3.ZERO, Vector3.ZERO)
# Disable the process function as no-longer held
set_process(false)

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://bwqiturebyt3h"]
[gd_scene load_steps=2 format=2]
[ext_resource type="Script" path="res://addons/godot-xr-tools/interactables/interactable_hinge.gd" id="1"]
[ext_resource path="res://addons/godot-xr-tools/interactables/interactable_hinge.gd" type="Script" id=1]
[node name="InteractableHinge" type="Spatial"]
script = ExtResource("1")
script = ExtResource( 1 )

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://cnfk38alyp72q"]
[gd_scene load_steps=2 format=2]
[ext_resource type="Script" path="res://addons/godot-xr-tools/interactables/interactable_joystick.gd" id="1"]
[ext_resource path="res://addons/godot-xr-tools/interactables/interactable_joystick.gd" type="Script" id=1]
[node name="InteractableJoystick" type="Spatial"]
script = ExtResource("1")
script = ExtResource( 1 )

View File

@ -10,9 +10,9 @@ resource_local_to_scene = true
render_priority = 0
shader = ExtResource("2")
shader_parameter/albedo = Color(1, 1, 1, 1)
shader_parameter/value = 0.2
shader_parameter/fade = 0.05
shader_parameter/radius = 0.8
shader_parameter/value = 0.2
shader_parameter/width = 0.2
[node name="HoldButton" type="Node3D"]

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=8 format=3 uid="uid://cjccb67fjl4aw"]
[gd_scene load_steps=8 format=3]
[ext_resource type="Script" path="res://addons/godot-xr-tools/misc/vr_common_shader_cache.gd" id="1"]
[ext_resource type="Material" uid="uid://bk72wfw25ff0v" path="res://addons/godot-xr-tools/materials/teleport.tres" id="2_6822k"]
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/teleport.tres" id="2_6822k"]
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/target.tres" id="3_agvdv"]
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/capsule.tres" id="4_gxjsg"]
[ext_resource type="Material" path="res://addons/godot-xr-tools/materials/pointer.tres" id="5_12251"]

View File

@ -15,8 +15,15 @@ extends Node3D
## If true, the grip control must be held to keep holding the climbable
var press_to_hold : bool = true
## Dictionary of temporary grab-handles indexed by the pickup node.
var grab_locations := {}
## Array of permanent grab points.
var _grab_points : Array[XRToolsGrabPoint] = []
## Dictionary of temporary grabs keyed by the pickup node
var _grab_temps : Dictionary = {}
## Dictionary of active grabs keyed by the pickup node
var _grabs : Dictionary = {}
# Add support for is_xr_class on XRTools classes
@ -24,41 +31,79 @@ func is_xr_class(name : String) -> bool:
return name == "XRToolsClimbable"
# Called when the node becomes "ready"
func _ready() -> void:
# Get all grab points
for child in get_children():
var grab_point := child as XRToolsGrabPoint
if grab_point:
_grab_points.push_back(grab_point)
# Called by XRToolsFunctionPickup
func is_picked_up() -> bool:
return false
func can_pick_up(_by: Node3D) -> bool:
return true
# Called by XRToolsFunctionPickup when user presses the action button while holding this object
func action():
pass
# Ignore highlighting requests from XRToolsFunctionPickup
func request_highlight(_from, _on) -> void:
pass
# Called by XRToolsFunctionPickup when this is picked up by a controller
func pick_up(by: Node3D, _with_controller: XRController3D) -> void:
# Get the ID to save the grab handle under
var id = by.get_instance_id()
func pick_up(by: Node3D) -> void:
# Get the best permanent grab-point
var point := _get_grab_point(by)
if not point:
# Get a temporary grab-point for the pickup
point = _grab_temps.get(by)
if not point:
# Create a new temporary grab-point childed to the climbable
point = Node3D.new()
add_child(point)
_grab_temps[by] = point
# Get or construct the grab handle
var handle = grab_locations.get(id)
if not handle:
handle = Node3D.new()
add_child(handle)
grab_locations[id] = handle
# Set the temporary to the current positon
point.global_transform = by.global_transform
# Save the grab
_grabs[by] = point
# Set the handles global transform. As it's a child of this
# climbable it will move as the climbable moves
handle.global_transform = by.global_transform
# Called by XRToolsFunctionPickup when this is let go by a controller
func let_go(_p_linear_velocity: Vector3, _p_angular_velocity: Vector3) -> void:
pass
func let_go(by: Node3D, _p_linear_velocity: Vector3, _p_angular_velocity: Vector3) -> void:
_grabs.erase(by);
# Get the grab handle
func get_grab_handle(p: Node3D) -> Node3D:
return grab_locations.get(p.get_instance_id())
func get_grab_handle(by: Node3D) -> Node3D:
return _grabs.get(by)
## Find the most suitable grab-point for the grabber
func _get_grab_point(by : Node3D) -> Node3D:
# Find the best grab-point
var fitness := 0.0
var point : XRToolsGrabPoint = null
for p in _grab_points:
var f := p.can_grab(by, null)
if f > fitness:
fitness = f
point = p
# Resolve redirection
while point is XRToolsGrabPointRedirect:
point = point.target
# Return the best grab point
print_verbose("%s> picked grab-point %s" % [name, point])
return point

View File

@ -1,11 +1,11 @@
@tool
class_name XRToolsForceBody
extends StaticBody3D
extends AnimatableBody3D
## XRTools Force Body script
##
## This script enhances StaticBody3D with move_and_slide and the ability
## This script enhances AnimatableBody3D with move_and_slide and the ability
## to push bodies by emparting forces on them.
@ -42,6 +42,9 @@ func is_xr_class(name : String) -> bool:
## This function moves and slides along the [param move] vector. It returns
## information about the last collision, or null if no collision
func move_and_slide(move : Vector3) -> ForceBodyCollision:
# Make sure this is off or weird shit happens...
sync_to_physics = false
# Loop performing the movement steps
var step_move := move
var ret : ForceBodyCollision = null

View File

@ -0,0 +1,171 @@
class_name Grab
extends Grabber
## Grab Class
##
## This class encodes information about an active grab. Additionally it applies
## hand poses and collision exceptions as appropriate.
## Priority for grip poses
const GRIP_POSE_PRIORITY := 100
## Priority for grip targeting
const GRIP_TARGET_PRIORITY := 100
## Grab target
var what : XRToolsPickable
## Grab point information
var point : XRToolsGrabPoint
## Hand grab point information
var hand_point : XRToolsGrabPointHand
## Grab transform
var transform : Transform3D
## Position drive strength
var drive_position : float = 1.0
## Angle drive strength
var drive_angle : float = 1.0
## Aim drive strength
var drive_aim : float = 0.0
## Has target arrived at grab point
var _arrived : bool = false
## Initialize the grab
func _init(
p_grabber : Grabber,
p_what : XRToolsPickable,
p_point : XRToolsGrabPoint,
p_precise : bool) -> void:
# Copy the grabber information
by = p_grabber.by
pickup = p_grabber.pickup
controller = p_grabber.controller
hand = p_grabber.hand
collision_hand = p_grabber.collision_hand
# Set the point
what = p_what
point = p_point
hand_point = p_point as XRToolsGrabPointHand
# Calculate the grab transform
if p_point:
transform = p_point.transform
elif p_precise:
transform = p_what.global_transform.inverse() * by.global_transform
else:
transform = Transform3D.IDENTITY
# Set the drive parameters
if hand_point:
drive_position = hand_point.drive_position
drive_angle = hand_point.drive_angle
drive_aim = hand_point.drive_aim
# Apply collision exceptions
if collision_hand:
what.add_collision_exception_with(collision_hand)
collision_hand.add_collision_exception_with(what)
## Set the target as arrived at the grab-point
func set_arrived() -> void:
# Ignore if already arrived
if _arrived:
return
# Set arrived and apply any hand pose
print_verbose("%s> arrived at %s" % [what.name, point])
_arrived = true
_set_hand_pose()
# Report the grab
print_verbose("%s> grabbed by %s", [what.name, by.name])
what.grabbed.emit(what, by)
## Set the grab point
func set_grab_point(p_point : XRToolsGrabPoint) -> void:
# Skip if no change
if p_point == point:
return
# Remove any current pose override
_clear_hand_pose()
# Update the grab point
point = p_point
hand_point = point as XRToolsGrabPointHand
# Update the transform
if point:
transform = point.transform
# Apply the new hand grab-point settings
if hand_point:
drive_position = hand_point.drive_position
drive_angle = hand_point.drive_angle
drive_aim = hand_point.drive_aim
# Apply any pose overrides
if _arrived:
_set_hand_pose()
# Report switch
print_verbose("%s> switched grab point to %s", [what.name, point.name])
what.released.emit(what, by)
what.grabbed.emit(what, by)
## Release the grip
func release() -> void:
# Clear any hand pose
_clear_hand_pose()
# Remove collision exceptions
if is_instance_valid(collision_hand):
what.remove_collision_exception_with(collision_hand)
collision_hand.remove_collision_exception_with(what)
# Report the release
print_verbose("%s> released by %s", [what.name, by.name])
what.released.emit(what, by)
# Set hand-pose overrides
func _set_hand_pose() -> void:
# Skip if not hand
if not is_instance_valid(hand) or not is_instance_valid(hand_point):
return
# Apply the hand-pose
if hand_point.hand_pose:
hand.add_pose_override(hand_point, GRIP_POSE_PRIORITY, hand_point.hand_pose)
# Apply hand snapping
if hand_point.snap_hand:
hand.add_target_override(hand_point, GRIP_TARGET_PRIORITY)
# Clear any hand-pose overrides
func _clear_hand_pose() -> void:
# Skip if not hand
if not is_instance_valid(hand) or not is_instance_valid(hand_point):
return
# Remove hand-pose
hand.remove_pose_override(hand_point)
# Remove hand snapping
hand.remove_target_override(hand_point)

View File

@ -0,0 +1,216 @@
class_name XRToolsGrabDriver
extends RemoteTransform3D
## Grab state
enum State {
LERP,
SNAP,
}
## Drive state
var state : State = State.SNAP
## Target pickable
var target : XRToolsPickable
## Primary grab information
var primary : Grab = null
## Secondary grab information
var secondary : Grab = null
## Lerp start position
var lerp_start : Transform3D
## Lerp total duration
var lerp_duration : float = 1.0
## Lerp time
var lerp_time : float = 0.0
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta : float) -> void:
# Skip if no primary node
if not is_instance_valid(primary):
return
# Set destination from primary grab
var destination := primary.by.global_transform * primary.transform.inverse()
# If present, apply secondary-node contributions
if is_instance_valid(secondary):
# Calculate lerp coefficients based on drive strengths
var position_lerp := _vote(primary.drive_position, secondary.drive_position)
var angle_lerp := _vote(primary.drive_angle, secondary.drive_angle)
# Calculate the transform from secondary grab
var x1 := destination
var x2 := secondary.by.global_transform * secondary.transform.inverse()
# Independently lerp the angle and position
destination = Transform3D(
x1.basis.slerp(x2.basis, angle_lerp),
x1.origin.lerp(x2.origin, position_lerp))
# Test if we need to apply aiming
if secondary.drive_aim > 0.0:
# Convert destination from global to primary-local
destination = primary.by.global_transform.inverse() * destination
# Calculate the from and to vectors in primary-local space
var secondary_from := destination * secondary.transform.origin
var secondary_to := primary.by.to_local(secondary.by.global_position)
# Build shortest arc
secondary_from = secondary_from.normalized()
secondary_to = secondary_to.normalized()
var spherical := Quaternion(secondary_from, secondary_to)
# Build aim-rotation
var rotate := Basis.IDENTITY.slerp(Basis(spherical), secondary.drive_aim)
destination = Transform3D(rotate, Vector3.ZERO) * destination
# Convert destination from primary-local to global
destination = primary.by.global_transform * destination
# Handle update
match state:
State.LERP:
# Progress the lerp
lerp_time += delta
if lerp_time < lerp_duration:
# Interpolate from lerp_start to destination
destination = lerp_start.interpolate_with(
destination,
lerp_time / lerp_duration)
else:
# Lerp completed
state = State.SNAP
if primary: primary.set_arrived()
if secondary: secondary.set_arrived()
# Apply the destination transform
global_transform = destination
force_update_transform()
if is_instance_valid(target):
target.force_update_transform()
## Set the secondary grab point
func add_grab(p_grab : Grab) -> void:
# Set the secondary grab
if p_grab.hand_point and p_grab.hand_point.mode == XRToolsGrabPointHand.Mode.PRIMARY:
print_verbose("%s> new primary grab %s" % [target.name, p_grab.by.name])
secondary = primary
primary = p_grab
else:
print_verbose("%s> new secondary grab %s" % [target.name, p_grab.by.name])
secondary = p_grab
# If snapped then report arrived at the new grab
if state == State.SNAP:
p_grab.set_arrived()
## Get the grab information for the grab node
func get_grab(by : Node3D) -> Grab:
if primary and primary.by == by:
return primary
if secondary and secondary.by == by:
return secondary
return null
func remove_grab(p_grab : Grab) -> void:
# Remove the appropriate grab
if p_grab == primary:
# Remove primary (secondary promoted)
print_verbose("%s> %s (primary) released" % [target.name, p_grab.by.name])
primary = secondary
secondary = null
elif p_grab == secondary:
# Remove secondary
print_verbose("%s> %s (secondary) released" % [target.name, p_grab.by.name])
secondary = null
# Discard the driver
func discard():
remote_path = NodePath()
queue_free()
# Create the driver to lerp the target from its current location to the
# primary grab-point.
static func create_lerp(
p_target : Node3D,
p_grab : Grab,
p_lerp_speed : float) -> XRToolsGrabDriver:
print_verbose("%s> lerping %s" % [p_target.name, p_grab.by.name])
# Construct the driver lerping from the current position
var driver := XRToolsGrabDriver.new()
driver.name = p_target.name + "_driver"
driver.top_level = true
driver.process_physics_priority = -80
driver.state = State.LERP
driver.target = p_target
driver.primary = p_grab
driver.global_transform = p_target.global_transform
# Calculate the start and duration
var end := p_grab.by.global_transform * p_grab.transform
var delta := end.origin - p_target.global_position
driver.lerp_start = p_target.global_transform
driver.lerp_duration = delta.length() / p_lerp_speed
# Add the driver as a neighbor of the target as RemoteTransform3D nodes
# cannot be descendands of the targets they drive.
p_target.get_parent().add_child(driver)
driver.remote_path = driver.get_path_to(p_target)
# Return the driver
return driver
# Create the driver to instantly snap to the primary grab-point.
static func create_snap(
p_target : Node3D,
p_grab : Grab) -> XRToolsGrabDriver:
print_verbose("%s> snapping to %s" % [p_target.name, p_grab.by.name])
# Construct the driver snapped to the held position
var driver := XRToolsGrabDriver.new()
driver.name = p_target.name + "_driver"
driver.top_level = true
driver.process_physics_priority = -80
driver.state = State.SNAP
driver.target = p_target
driver.primary = p_grab
driver.global_transform = p_grab.by.global_transform * p_grab.transform.inverse()
# Snapped to grab-point so report arrived
p_grab.set_arrived()
# Add the driver as a neighbor of the target as RemoteTransform3D nodes
# cannot be descendands of the targets they drive.
p_target.get_parent().add_child(driver)
driver.remote_path = driver.get_path_to(p_target)
# Return the driver
return driver
# Calculate the lerp voting from a to b
static func _vote(a : float, b : float) -> float:
if a == 0.0 and b == 0.0:
return 0.0
return b / (a + b)

View File

@ -13,6 +13,16 @@ extends Marker3D
@export var enabled : bool = true
## Test if a grabber can grab by this grab-point
func can_grab(_grabber : Node) -> bool:
return enabled
## Evaluate fitness of the proposed grab, with 0.0 for not allowed.
func can_grab(grabber : Node3D, _current : XRToolsGrabPoint) -> float:
if not enabled:
return 0.0
# Return the distance-weighted fitness
return _weight(grabber)
# Return a distance-weighted fitness weight in the range (0.0 - max]
func _weight(grabber : Node3D, max : float = 1.0) -> float:
var distance := global_position.distance_to(grabber.global_position)
return max / (1.0 + distance)

View File

@ -16,6 +16,13 @@ enum Hand {
RIGHT, ## Right hand
}
## Grab mode for this grab point
enum Mode {
GENERAL, ## General grab point
PRIMARY, ## Primary-hand grab point
SECONDARY ## Secondary-hand grab point
}
## Hand preview option
enum PreviewMode {
CLOSED, ## Preview hand closed
@ -30,15 +37,33 @@ const LEFT_HAND_PATH := "res://addons/godot-xr-tools/hands/scenes/lowpoly/left_h
const RIGHT_HAND_PATH := "res://addons/godot-xr-tools/hands/scenes/lowpoly/right_hand_low.tscn"
## Grab-point handle
@export var handle : String
## Which hand this grab point is for
@export var hand : Hand: set = _set_hand
## Hand grab mode
@export var mode : Mode = Mode.GENERAL
## Snap the hand mesh to the grab-point
@export var snap_hand : bool = true
## Hand pose
@export var hand_pose : XRToolsHandPoseSettings: set = _set_hand_pose
## If true, the hand is shown in the editor
@export var editor_preview_mode : PreviewMode = PreviewMode.CLOSED: set = _set_editor_preview_mode
## How much this grab-point drives the position
@export var drive_position : float = 1.0
## How much this grab-point drives the angle
@export var drive_angle : float = 1.0
## How much this grab-point drives the aim
@export var drive_aim : float = 0.0
## Hand to use for editor preview
var _editor_preview_hand : XRToolsHand
@ -52,26 +77,27 @@ func _ready():
## Test if a grabber can grab by this grab-point
func can_grab(_grabber : Node) -> bool:
func can_grab(grabber : Node3D, current : XRToolsGrabPoint) -> float:
# Skip if not enabled
if not enabled:
return false
return 0.0
# Get the grabber controller
var controller := _get_grabber_controller(_grabber)
if not controller:
return false
# Verify the hand matches
if not _is_correct_hand(grabber):
return 0.0
# Only allow left controller to grab left-hand grab points
if hand == Hand.LEFT and controller.tracker != "left_hand":
return false
# Fail if the hand grab is not permitted
if not _is_valid_hand_grab(current):
return 0.0
# Only allow right controller to grab right-hand grab points
if hand == Hand.RIGHT and controller.tracker != "right_hand":
return false
# Get the distance-weighted fitness in the range (0.0 - 0.5], but boost
# to [0.5 - 1.0] for valid "specific" grabs.
var fitness := _weight(grabber, 0.5)
if mode != Mode.GENERAL:
fitness += 0.5
# Allow grab
return true
# Return the grab fitness
return fitness
func _set_hand(new_value : Hand) -> void:
@ -122,17 +148,58 @@ func _update_editor_preview() -> void:
add_child(_editor_preview_hand)
# Is the grabber for the correct hand
func _is_correct_hand(grabber : Node3D) -> bool:
# Find the controller
var controller := _get_grabber_controller(grabber)
if not controller:
return false
# If left hand then verify left controller
if hand == Hand.LEFT and controller.tracker != "left_hand":
return false
# If right hand then verify right controller
if hand == Hand.RIGHT and controller.tracker != "right_hand":
return false
# Controller matches hand
return true
# Test if hand grab is permitted
func _is_valid_hand_grab(current : XRToolsGrabPoint) -> bool:
# Not a valid hand grab if currently held by something other than a hand
var current_hand := current as XRToolsGrabPointHand
if current and not current_hand:
return false
# Not valid if grabbing the same named handle
if handle and current_hand and handle == current_hand.handle:
return false
# Not valid if attempting PRIMARY grab while current is PRIMARY
if mode == Mode.PRIMARY and current_hand and current_hand.mode == Mode.PRIMARY:
return false
# Not valid if attempting SECONDARY grab while no current
if mode == Mode.SECONDARY and not current_hand:
return false
# Hand is allowed to grab
return true
# Get the controller associated with a grabber
static func _get_grabber_controller(_grabber : Node) -> XRController3D:
static func _get_grabber_controller(grabber : Node3D) -> XRController3D:
# Ensure the grabber is valid
if not is_instance_valid(_grabber):
if not is_instance_valid(grabber):
return null
# Ensure the pickup is a function pickup for a controller
var pickup := _grabber as XRToolsFunctionPickup
var pickup := grabber as XRToolsFunctionPickup
if not pickup:
return null
# Get the controller associated with the pickup
return pickup.get_controller()

View File

@ -0,0 +1,17 @@
@tool
class_name XRToolsGrabPointRedirect
extends XRToolsGrabPoint
## Grab point to redirect grabbing to
@export var target : XRToolsGrabPoint
## Evaluate fitness of the proposed grab, with 0.0 for not allowed.
func can_grab(grabber : Node3D, current : XRToolsGrabPoint) -> float:
# Fail if no target
if not is_instance_valid(target):
return 0.0
# Consult the target
return target.can_grab(grabber, current)

Some files were not shown because too many files have changed in this diff Show More