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)