immersive-home/app/addons/xr-autohandtracker/auto_tracker.gd
2024-05-06 18:22:12 +02:00

216 lines
8.9 KiB
GDScript

extends Node3D
# The autotracker is swapped onto the xr_controller_node when hand-tracking is active
# so that we can insert in our own button and float signals from the hand gestures,
# as well as setting the pose from the xr_aimpose (which is filtered by the system during hand tracking)
# Calling set_pose emits a pose_changed signal that copies its values into the xr_controller_node
var xr_autotracker : XRPositionalTracker = null
var xr_autopose : XRPose = null
var autotrackeractive = false
var graspsqueezer = SqueezeButton.new()
var pinchsqueezer = SqueezeButton.new()
# Called when the node enters the scene tree for the first time.
func _ready():
$ThumbstickBoundaries/InnerRing.mesh.outer_radius = innerringrad
$ThumbstickBoundaries/InnerRing.mesh.inner_radius = 0.95*innerringrad
$ThumbstickBoundaries/OuterRing.mesh.outer_radius = outerringrad
$ThumbstickBoundaries/OuterRing.mesh.inner_radius = 0.95*outerringrad
$ThumbstickBoundaries/UpDisc.transform.origin.y = updowndistbutton
$ThumbstickBoundaries/DownDisc.transform.origin.y = -updowndistbutton
func setupautotracker(tracker_nhand, islefthand, xr_controller_node):
xr_autotracker = XRPositionalTracker.new()
xr_autotracker.hand = tracker_nhand
xr_autotracker.name = "left_autohand" if islefthand else "right_autohand"
xr_autotracker.profile = "/interaction_profiles/autohand" # "/interaction_profiles/none"
xr_autotracker.type = 2
xr_autotracker.set_pose(xr_controller_node.pose, Transform3D(), Vector3(), Vector3(), XRPose.TrackingConfidence.XR_TRACKING_CONFIDENCE_NONE)
xr_autopose = xr_autotracker.get_pose(xr_controller_node.pose)
graspsqueezer.setinputstrings(xr_autotracker, "grip", "", "grip_click")
pinchsqueezer.setinputstrings(xr_autotracker, "trigger", "trigger_touch", "trigger_click")
XRServer.add_tracker(xr_autotracker)
func activateautotracker(xr_controller_node):
xr_controller_node.set_tracker(xr_autotracker.name)
autotrackeractive = true
func deactivateautotracker(xr_controller_node, xr_tracker):
setaxbybuttonstatus(0)
graspsqueezer.applysqueeze(graspsqueezer.touchbuttondistance + 1)
pinchsqueezer.applysqueeze(pinchsqueezer.touchbuttondistance + 1)
xr_controller_node.set_tracker(xr_tracker.name)
autotrackeractive = false
func autotrackgestures(oxrjps, xrt, xr_camera_node):
thumbsticksimulation(oxrjps, xrt, xr_camera_node)
# detect forming a fist
var middleknuckletip = (oxrjps[OpenXRInterface.HAND_JOINT_MIDDLE_TIP] - oxrjps[OpenXRInterface.HAND_JOINT_MIDDLE_PROXIMAL]).length()
var ringknuckletip = (oxrjps[OpenXRInterface.HAND_JOINT_RING_TIP] - oxrjps[OpenXRInterface.HAND_JOINT_RING_PROXIMAL]).length()
var littleknuckletip = (oxrjps[OpenXRInterface.HAND_JOINT_LITTLE_TIP] - oxrjps[OpenXRInterface.HAND_JOINT_LITTLE_PROXIMAL]).length()
var avgknuckletip = (middleknuckletip + ringknuckletip + littleknuckletip)/3
graspsqueezer.applysqueeze(avgknuckletip)
# detect the finger pinch
var pinchdist = (oxrjps[OpenXRInterface.HAND_JOINT_INDEX_TIP] - oxrjps[OpenXRInterface.HAND_JOINT_THUMB_TIP]).length()
pinchsqueezer.applysqueeze(pinchdist*2)
#$GraspMarker.global_transform.origin = xrt*oxrjps[OpenXRInterface.HAND_JOINT_MIDDLE_TIP]
#$GraspMarker.visible = buttoncurrentlyclicked
var thumbstickstartpt = null
const thumbdistancecontact = 0.025
const thumbdistancerelease = 0.045
const innerringrad = 0.05
const outerringrad = 0.22
const updowndisttouch = 0.08
const updowndistbutton = 0.12
var thumbsticktouched = false
var axbybuttonstatus = 0 # -2:by_button, -1:by_touch, 1:ax_touch, 1:ax_button
var by_is_up = true
func setaxbybuttonstatus(newaxbybuttonstatus):
if axbybuttonstatus == newaxbybuttonstatus:
return
if abs(axbybuttonstatus) == 2:
xr_autotracker.set_input("ax_button" if axbybuttonstatus > 0 else "by_button", false)
axbybuttonstatus = 1 if axbybuttonstatus > 0 else -1
if axbybuttonstatus == newaxbybuttonstatus:
return
xr_autotracker.set_input("ax_touch" if axbybuttonstatus > 0 else "by_touch", false)
axbybuttonstatus = 0
if axbybuttonstatus == newaxbybuttonstatus:
return
xr_autotracker.set_input("ax_touch" if newaxbybuttonstatus > 0 else "by_touch", true)
axbybuttonstatus = 1 if newaxbybuttonstatus > 0 else -1
if axbybuttonstatus == newaxbybuttonstatus:
return
xr_autotracker.set_input("ax_button" if newaxbybuttonstatus > 0 else "by_button", true)
axbybuttonstatus = newaxbybuttonstatus
func thumbsticksimulation(oxrjps, xrt, xr_camera_node):
var middletip = oxrjps[OpenXRInterface.HAND_JOINT_MIDDLE_TIP]
var thumbtip = oxrjps[OpenXRInterface.HAND_JOINT_THUMB_TIP]
var ringtip = oxrjps[OpenXRInterface.HAND_JOINT_RING_TIP]
var tipcen = (middletip + thumbtip + ringtip)/3.0
var middleknuckle = oxrjps[OpenXRInterface.HAND_JOINT_MIDDLE_PROXIMAL]
var thumbdistance = max((middletip - tipcen).length(), (thumbtip - tipcen).length(), (ringtip - tipcen).length())
if thumbstickstartpt == null:
if thumbdistance < thumbdistancecontact and middleknuckle.y < tipcen.y - 0.029:
thumbstickstartpt = tipcen
visible = true
global_transform.origin = xrt*tipcen
$ThumbstickBoundaries.global_transform.origin = xrt*thumbstickstartpt
else:
if thumbdistance > thumbdistancerelease:
thumbstickstartpt = null
if thumbsticktouched:
xr_autotracker.set_input("primary", Vector2(0.0, 0.0))
xr_autotracker.set_input("primary_touch", true)
thumbsticktouched = false
setaxbybuttonstatus(0)
visible = (thumbstickstartpt != null)
if thumbstickstartpt != null:
$DragRod.global_transform = sticktransformB(xrt*thumbstickstartpt, xrt*tipcen)
var facingangle = Vector2(xr_camera_node.transform.basis.z.x, xr_camera_node.transform.basis.z.z).angle() if xr_camera_node != null else 0.0
var hvec = Vector2(tipcen.x - thumbstickstartpt.x, tipcen.z - thumbstickstartpt.z)
var hv = hvec.rotated(deg_to_rad(90) - facingangle)
var hvlen = hv.length()
if not thumbsticktouched:
var frat = hvlen/max(hvlen, innerringrad)
frat = frat*frat*frat
$ThumbstickBoundaries/InnerRing.get_surface_override_material(0).albedo_color.a = frat
$ThumbstickBoundaries/OuterRing.get_surface_override_material(0).albedo_color.a = frat
if hvlen > innerringrad:
xr_autotracker.set_input("primary_touch", true)
thumbsticktouched = true
if thumbsticktouched:
var hvN = hv/max(hvlen, outerringrad)
xr_autotracker.set_input("primary", Vector2(hvN.x, -hvN.y))
var ydist = (tipcen.y - thumbstickstartpt.y)
var rawnewaxbybuttonstatus = 0
if ydist > updowndisttouch:
$ThumbstickBoundaries/UpDisc.visible = true
$ThumbstickBoundaries/UpDisc.get_surface_override_material(0).albedo_color.a = (ydist - updowndisttouch)/(updowndistbutton - updowndisttouch)*0.5 if ydist < updowndistbutton else 1.0
rawnewaxbybuttonstatus = 2 if ydist > updowndistbutton else 1
else:
$ThumbstickBoundaries/UpDisc.visible = false
if ydist < -updowndisttouch:
$ThumbstickBoundaries/DownDisc.visible = true
$ThumbstickBoundaries/DownDisc.get_surface_override_material(0).albedo_color.a = (-ydist - updowndisttouch)/(updowndistbutton - updowndisttouch)*0.5 if -ydist < updowndistbutton else 1.0
rawnewaxbybuttonstatus = -2 if -ydist > updowndistbutton else -1
else:
$ThumbstickBoundaries/DownDisc.visible = false
setaxbybuttonstatus(rawnewaxbybuttonstatus*(1 if by_is_up else -1))
class SqueezeButton:
const touchbuttondistance = 0.07
const depressbuttondistance = 0.04
const clickbuttononratio = 0.6
const clickbuttonoffratio = 0.4
var xr_autotracker = null
var squeezestring = ""
var touchstring = ""
var clickstring = ""
var buttoncurrentlyclicked = false
var buttoncurrentlytouched = false
func setinputstrings(lxr_autotracker, lsqueezestring, ltouchstring, lclickstring):
xr_autotracker = lxr_autotracker
squeezestring = lsqueezestring
touchstring = ltouchstring
clickstring = lclickstring
func xrsetinput(name, value):
if xr_autotracker and name:
xr_autotracker.set_input(name, value)
func applysqueeze(squeezedistance):
var buttonratio = min(inverse_lerp(touchbuttondistance, depressbuttondistance, squeezedistance), 1.0)
if buttonratio < 0.0:
if buttoncurrentlytouched:
xrsetinput(squeezestring, 0.0)
xrsetinput(touchstring, false)
buttoncurrentlytouched = false
else:
xrsetinput(squeezestring, buttonratio)
if not buttoncurrentlytouched:
xrsetinput(touchstring, true)
buttoncurrentlytouched = true
var buttonclicked = (buttonratio > (clickbuttonoffratio if buttoncurrentlyclicked else clickbuttononratio))
if buttonclicked != buttoncurrentlyclicked:
xrsetinput(clickstring, buttonclicked)
buttoncurrentlyclicked = buttonclicked
const stickradius = 0.01
static func sticktransformB(j1, j2):
var v = j2 - j1
var vlen = v.length()
var b
if vlen != 0:
var vy = v/vlen
var vyunaligned = Vector3(0,1,0) if abs(vy.y) < abs(vy.x) + abs(vy.z) else Vector3(1,0,0)
var vz = vy.cross(vyunaligned)
var vx = vy.cross(vz)
b = Basis(vx*stickradius, v, vz*stickradius)
else:
b = Basis().scaled(Vector3(0.01, 0.0, 0.01))
return Transform3D(b, (j1 + j2)*0.5)