206 lines
5.3 KiB
GDScript
206 lines
5.3 KiB
GDScript
@tool
|
|
class_name XRToolsGrabPointHand
|
|
extends XRToolsGrabPoint
|
|
|
|
|
|
## XR Tools Grab Point Hand Script
|
|
##
|
|
## This script allows specifying a grab point for a specific hand. Additionally
|
|
## the grab point can be used to control the pose of the hand, and to allow the
|
|
## grab point position to be fine-tuned in the editor.
|
|
|
|
|
|
## Hand for this grab point
|
|
enum Hand {
|
|
LEFT, ## Left 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
|
|
OPEN, ## Preview hand open
|
|
}
|
|
|
|
|
|
## Left hand scene path (for editor preview)
|
|
const LEFT_HAND_PATH := "res://addons/godot-xr-tools/hands/scenes/lowpoly/left_hand_low.tscn"
|
|
|
|
## Right hand scene path (for editor preview)
|
|
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
|
|
|
|
|
|
## Called when the node enters the scene tree for the first time.
|
|
func _ready():
|
|
# If in the editor then update the preview
|
|
if Engine.is_editor_hint():
|
|
_update_editor_preview()
|
|
|
|
|
|
## Test if a grabber can grab by this grab-point
|
|
func can_grab(grabber : Node3D, current : XRToolsGrabPoint) -> float:
|
|
# Skip if not enabled
|
|
if not enabled:
|
|
return 0.0
|
|
|
|
# Verify the hand matches
|
|
if not _is_correct_hand(grabber):
|
|
return 0.0
|
|
|
|
# Fail if the hand grab is not permitted
|
|
if not _is_valid_hand_grab(current):
|
|
return 0.0
|
|
|
|
# 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
|
|
|
|
# Return the grab fitness
|
|
return fitness
|
|
|
|
|
|
func _set_hand(new_value : Hand) -> void:
|
|
hand = new_value
|
|
if Engine.is_editor_hint():
|
|
_update_editor_preview()
|
|
|
|
|
|
func _set_hand_pose(new_value : XRToolsHandPoseSettings) -> void:
|
|
hand_pose = new_value
|
|
if Engine.is_editor_hint():
|
|
_update_editor_preview()
|
|
|
|
|
|
func _set_editor_preview_mode(new_value : PreviewMode) -> void:
|
|
editor_preview_mode = new_value
|
|
if Engine.is_editor_hint():
|
|
_update_editor_preview()
|
|
|
|
|
|
func _update_editor_preview() -> void:
|
|
# Discard any existing hand model
|
|
if _editor_preview_hand:
|
|
remove_child(_editor_preview_hand)
|
|
_editor_preview_hand.queue_free()
|
|
_editor_preview_hand = null
|
|
|
|
# Pick the hand scene
|
|
var hand_path := LEFT_HAND_PATH if hand == Hand.LEFT else RIGHT_HAND_PATH
|
|
var hand_scene : PackedScene = load(hand_path)
|
|
if !hand_scene:
|
|
return
|
|
|
|
# Construct the model
|
|
_editor_preview_hand = hand_scene.instantiate()
|
|
|
|
# Set the pose
|
|
if hand_pose:
|
|
_editor_preview_hand.add_pose_override(self, 0.0, hand_pose)
|
|
|
|
# Set the grip override
|
|
if editor_preview_mode == PreviewMode.CLOSED:
|
|
_editor_preview_hand.force_grip_trigger(1.0, 1.0)
|
|
else:
|
|
_editor_preview_hand.force_grip_trigger(0.0, 0.0)
|
|
|
|
# Add the editor-preview hand as a child
|
|
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 : Node3D) -> XRController3D:
|
|
# Ensure the grabber is valid
|
|
if not is_instance_valid(grabber):
|
|
return null
|
|
|
|
# Ensure the pickup is a function pickup for a controller
|
|
var pickup := grabber as XRToolsFunctionPickup
|
|
if not pickup:
|
|
return null
|
|
|
|
# Get the controller associated with the pickup
|
|
return pickup.get_controller()
|