| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- /*******************************************************************************
- Copyright © 2015-2022 PICO Technology Co., Ltd.All rights reserved.
- NOTICE:All information contained herein is, and remains the property of
- PICO Technology Co., Ltd. The intellectual and technical concepts
- contained herein are proprietary to PICO Technology Co., Ltd. and may be
- covered by patents, patents in process, and are protected by trade secret or
- copyright law. Dissemination of this information or reproduction of this
- material is strictly forbidden unless prior written permission is obtained from
- PICO Technology Co., Ltd.
- *******************************************************************************/
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Events;
- using System;
- using UnityEngine.XR;
- namespace Unity.XR.PXR
- {
- public class PXR_HandPose : MonoBehaviour
- {
- public TrackType trackType;
- public PXR_HandPoseConfig config;
- public UnityEvent handPoseStart;
- public UpdateEvent handPoseUpdate;
- public UnityEvent handPoseEnd;
- private List<Vector3> leftJointPos = new List<Vector3>(new Vector3[(int)HandJoint.JointMax]);
- private List<Vector3> rightJointPos = new List<Vector3>(new Vector3[(int)HandJoint.JointMax]);
- private HandJointLocations leftHandJointLocations = new HandJointLocations();
- private HandJointLocations rightHandJointLocations = new HandJointLocations();
- private bool poseStateHold;
- private bool poseStateActive;
- private float poseStateHoldTime;
- public enum TrackType
- {
- Any,
- Left,
- Right
- }
- private void HandPoseEventCheck()
- {
- switch (trackType)
- {
- case TrackType.Any:
- poseStateActive = (leftShapesActive && leftBonesActive && leftTransActive) || (rightShapesActive && rightBonesActive && rightTransActive);
- break;
- case TrackType.Left:
- poseStateActive = leftShapesActive && leftBonesActive && leftTransActive;
- break;
- case TrackType.Right:
- poseStateActive = rightShapesActive && rightBonesActive && rightTransActive;
- break;
- default:
- break;
- }
- if (poseStateHold != poseStateActive)
- {
- poseStateHold = poseStateActive;
- if (poseStateHold)
- {
- poseStateActive = true;
- if (handPoseStart != null)
- {
- handPoseStart.Invoke();
- }
- }
- else
- {
- poseStateActive = false;
- if (handPoseEnd != null)
- {
- handPoseEnd.Invoke();
- }
- }
- poseStateHoldTime = 0f;
- }
- else
- {
- if (poseStateHold)
- {
- poseStateHoldTime += Time.deltaTime;
- handPoseUpdate.Invoke(poseStateHoldTime);
- }
- }
- }
- private bool HoldCheck(bool holdState, float holdDuration, bool resultState, ref float holdTime)
- {
- if (resultState != holdState)
- {
- holdTime += Time.deltaTime;
- if (holdTime >= holdDuration)
- {
- resultState = holdState;
- }
- }
- else
- {
- holdTime = 0;
- }
- return resultState;
- }
- private void Start()
- {
- shapesHoldDuration = config.shapesRecognizer.holdDuration;
- bones = config.bonesRecognizer.Bones;
- bonesHoldDuration = config.bonesRecognizer.holdDuration;
- transTrackAxis = config.transRecognizer.trackAxis;
- transSpaceType = config.transRecognizer.spaceType;
- transTrackTarget = config.transRecognizer.trackTarget;
- transHoldDuration = config.transRecognizer.holdDuration;
- transAngleThreshold = config.transRecognizer.angleThreshold;
- transThresholdWidth = config.transRecognizer.thresholdWidth;
- }
- private void Update()
- {
- if (config == null) return;
- InputDevices.GetDeviceAtXRNode(XRNode.Head).TryGetFeatureValue(CommonUsages.devicePosition, out HMDpose);
- if (trackType == TrackType.Right || trackType == TrackType.Any)
- {
- PXR_HandTracking.GetJointLocations(HandType.HandRight, ref rightHandJointLocations);
- for (int i = 0; i < rightJointPos.Count; ++i)
- {
- if (rightHandJointLocations.jointLocations == null) break;
- rightJointPos[i] = rightHandJointLocations.jointLocations[i].pose.Position.ToVector3();
- if (i == (int)HandJoint.JointWrist)
- {
- rightWirstPos = rightHandJointLocations.jointLocations[i].pose.Position.ToVector3();
- rightWirstRot = rightHandJointLocations.jointLocations[i].pose.Orientation.ToQuat();
- }
- }
- rightShapesHold = ShapesRecognizerCheck(rightJointPos, rightWirstRot * Vector3.left, rightWirstRot * Vector3.back);
- rightShapesActive = HoldCheck(rightShapesHold, shapesHoldDuration, rightShapesActive, ref rightShapesHoldTime);
- rightBonesHold = BonesCheck(HandType.HandRight);
- rightBonesActive = HoldCheck(rightBonesHold, bonesHoldDuration, rightBonesActive, ref rightBonesHoldTime);
- rightTransHold = TransCheck(TrackType.Right, rightWirstPos, rightWirstRot, HMDpose, rightTransHold);
- rightTransActive = HoldCheck(rightTransHold, transHoldDuration, rightTransActive, ref rightTransHoldTime);
- }
- if (trackType == TrackType.Left || trackType == TrackType.Any)
- {
- PXR_HandTracking.GetJointLocations(HandType.HandLeft, ref leftHandJointLocations);
- for (int i = 0; i < leftJointPos.Count; ++i)
- {
- if (leftHandJointLocations.jointLocations == null) break;
- leftJointPos[i] = leftHandJointLocations.jointLocations[i].pose.Position.ToVector3();
- if (i == (int)HandJoint.JointWrist)
- {
- leftWirstPos = leftHandJointLocations.jointLocations[i].pose.Position.ToVector3();
- leftWirstRot = leftHandJointLocations.jointLocations[i].pose.Orientation.ToQuat();
- }
- }
- leftShapesHold = ShapesRecognizerCheck(leftJointPos, leftWirstRot * Vector3.right, leftWirstRot * Vector3.forward, -1);
- leftShapesActive = HoldCheck(leftShapesHold, shapesHoldDuration, leftShapesActive, ref leftShapesHoldTime);
- leftBonesHold = BonesCheck(HandType.HandLeft);
- leftBonesActive = HoldCheck(leftBonesHold, bonesHoldDuration, leftBonesActive, ref leftBonesHoldTime);
- leftTransHold = TransCheck(TrackType.Left, leftWirstPos, leftWirstRot, HMDpose, leftTransHold);
- leftTransActive = HoldCheck(leftTransHold, transHoldDuration, leftTransActive, ref leftTransHoldTime);
- }
- HandPoseEventCheck();
- }
- #region ShapesRecognizer
- private float shapesHoldDuration = 0.09f;
- private bool leftShapesHold;
- private bool leftShapesActive;
- private float leftShapesHoldTime;
- private bool rightShapesActive;
- private bool rightShapesHold;
- private float rightShapesHoldTime;
- private bool angleCheckValid = false;
- private bool abducCheckOpen = false;
- private Vector3 leftWirstPos;
- private Vector3 rightWirstPos;
- private Quaternion leftWirstRot;
- private Quaternion rightWirstRot;
- private Vector3 thumb0, thumb1, thumb2, thumb3;
- private Vector3 index0, index1, index2, index3;
- private Vector3 middle0, middle1, middle2, middle3;
- private Vector3 ring0, ring1, ring2, ring3;
- private Vector3 pinky0, pinky1, pinky2, pinky3;
- private bool thumbFlex, indexFlex, middleFlex, ringFlex, pinkyFlex;
- private bool thumbCurl, indexCurl, middleCurl, ringCurl, pinkyCurl;
- private bool thumbAbduc, indexAbduc, middleAbduc, ringAbduc, pinkyAbduc;
- private bool ShapesRecognizerCheck(List<Vector3> jointPos, Vector3 wirstRight, Vector3 wirstForward, int wirstDirect = 1)
- {
- thumb0 = jointPos[(int)HandJoint.JointThumbTip];
- thumb1 = jointPos[(int)HandJoint.JointThumbDistal];
- thumb2 = jointPos[(int)HandJoint.JointThumbProximal];
- thumb3 = jointPos[(int)HandJoint.JointThumbMetacarpal];
- index0 = jointPos[(int)HandJoint.JointIndexTip];
- index1 = jointPos[(int)HandJoint.JointIndexDistal];
- index2 = jointPos[(int)HandJoint.JointIndexIntermediate];
- index3 = jointPos[(int)HandJoint.JointIndexProximal];
- middle0 = jointPos[(int)HandJoint.JointMiddleTip];
- middle1 = jointPos[(int)HandJoint.JointMiddleDistal];
- middle2 = jointPos[(int)HandJoint.JointMiddleIntermediate];
- middle3 = jointPos[(int)HandJoint.JointMiddleProximal];
- ring0 = jointPos[(int)HandJoint.JointRingTip];
- ring1 = jointPos[(int)HandJoint.JointRingDistal];
- ring2 = jointPos[(int)HandJoint.JointRingIntermediate];
- ring3 = jointPos[(int)HandJoint.JointRingProximal];
- pinky0 = jointPos[(int)HandJoint.JointLittleTip];
- pinky1 = jointPos[(int)HandJoint.JointLittleDistal];
- pinky2 = jointPos[(int)HandJoint.JointLittleIntermediate];
- pinky3 = jointPos[(int)HandJoint.JointLittleProximal];
- thumbFlex = FlexionCheck(config.shapesRecognizer.thumb, wirstDirect * wirstRight, wirstDirect * wirstForward);
- indexFlex = FlexionCheck(config.shapesRecognizer.index, wirstRight, wirstForward);
- middleFlex = FlexionCheck(config.shapesRecognizer.middle, wirstRight, wirstForward);
- ringFlex = FlexionCheck(config.shapesRecognizer.ring, wirstRight, wirstForward);
- pinkyFlex = FlexionCheck(config.shapesRecognizer.pinky, wirstRight, wirstForward);
- thumbCurl = CurlCheck(config.shapesRecognizer.thumb);
- indexCurl = CurlCheck(config.shapesRecognizer.index);
- middleCurl = CurlCheck(config.shapesRecognizer.middle);
- ringCurl = CurlCheck(config.shapesRecognizer.ring);
- pinkyCurl = CurlCheck(config.shapesRecognizer.pinky);
- thumbAbduc = AbductionCheck(config.shapesRecognizer.thumb);
- indexAbduc = AbductionCheck(config.shapesRecognizer.index);
- middleAbduc = AbductionCheck(config.shapesRecognizer.middle);
- ringAbduc = AbductionCheck(config.shapesRecognizer.ring);
- pinkyAbduc = AbductionCheck(config.shapesRecognizer.pinky);
- return thumbFlex && indexFlex && middleFlex && ringFlex && pinkyFlex
- && thumbCurl && indexCurl && middleCurl && ringCurl && pinkyCurl
- && thumbAbduc && indexAbduc && middleAbduc && ringAbduc && pinkyAbduc;
- }
- private bool FlexionCheck(ShapesRecognizer.Finger finger, Vector3 wirstRight, Vector3 wirstForward)
- {
- if (finger.flexion == ShapesRecognizer.Flexion.Any) return true;
- else
- {
- float flexAngle = 0;
- switch (finger.handFinger)
- {
- case HandFinger.Thumb:
- Vector3 thumb23 = (thumb2 - thumb3);
- Vector3 thumb23_project = Vector3.ProjectOnPlane(thumb23, wirstRight);
- flexAngle = Vector3.Angle(thumb23_project, wirstForward);
- break;
- case HandFinger.Index:
- Vector3 index23 = (index2 - index3);
- Vector3 index_project = Vector3.ProjectOnPlane(index23, wirstForward);
- flexAngle = Vector3.Angle(index_project, wirstRight);
- break;
- case HandFinger.Middle:
- Vector3 middle23 = (middle2 - middle3);
- Vector3 middle_project = Vector3.ProjectOnPlane(middle23, wirstForward);
- flexAngle = Vector3.Angle(middle_project, wirstRight);
- break;
- case HandFinger.Ring:
- Vector3 ring23 = (ring2 - ring3);
- Vector3 ring_project = Vector3.ProjectOnPlane(ring23, wirstForward);
- flexAngle = Vector3.Angle(ring_project, wirstRight);
- break;
- case HandFinger.Pinky:
- Vector3 pinky23 = (pinky2 - pinky3);
- Vector3 pinky_project = Vector3.ProjectOnPlane(pinky23, wirstForward);
- flexAngle = Vector3.Angle(pinky_project, wirstRight);
- break;
- default:
- break;
- }
- return AngleCheck(flexAngle, finger.fingerConfigs.flexionConfigs.min, finger.fingerConfigs.flexionConfigs.max, finger.fingerConfigs.flexionConfigs.width,
- ShapesRecognizer.flexionMin, ShapesRecognizer.flexionMax);
- }
- }
- private bool CurlCheck(ShapesRecognizer.Finger finger)
- {
- if (finger.curl == ShapesRecognizer.Curl.Any) return true;
- else
- {
- float curlAngle = 0;
- switch (finger.handFinger)
- {
- case HandFinger.Thumb:
- Vector3 thumb01 = (thumb0 - thumb1);
- Vector3 thumb32 = (thumb3 - thumb2);
- curlAngle = Vector3.Angle(thumb01, thumb32);
- break;
- case HandFinger.Index:
- Vector3 index01 = (index0 - index1);
- Vector3 index32 = (index3 - index2);
- curlAngle = Vector3.Angle(index32, index01);
- break;
- case HandFinger.Middle:
- Vector3 middle01 = (middle0 - middle1);
- Vector3 middle32 = (middle3 - middle2);
- curlAngle = Vector3.Angle(middle32, middle01);
- break;
- case HandFinger.Ring:
- Vector3 ring01 = (ring0 - ring1);
- Vector3 ring32 = (ring3 - ring2);
- curlAngle = Vector3.Angle(ring32, ring01);
- break;
- case HandFinger.Pinky:
- Vector3 pinky01 = (pinky0 - pinky1);
- Vector3 pinky32 = (pinky3 - pinky2);
- curlAngle = Vector3.Angle(pinky32, pinky01);
- break;
- default:
- break;
- }
- return AngleCheck(curlAngle, finger.fingerConfigs.curlConfigs.min, finger.fingerConfigs.curlConfigs.max, finger.fingerConfigs.curlConfigs.width,
- ShapesRecognizer.curlMin, ShapesRecognizer.curlMax);
- }
- }
- private bool AbductionCheck(ShapesRecognizer.Finger finger)
- {
- if (finger.abduction == ShapesRecognizer.Abduction.Any) return true;
- else
- {
- float abducAngle = 0;
- Vector3 thumb12 = (thumb1 - thumb2);
- Vector3 index23 = (index2 - index3);
- Vector3 middle23 = (middle2 - middle3);
- Vector3 ring23 = (ring2 - ring3);
- Vector3 pinky23 = (pinky2 - pinky3);
- switch (finger.handFinger)
- {
- case HandFinger.Thumb:
- abducAngle = Vector3.Angle(thumb12, index23);
- break;
- case HandFinger.Index:
- abducAngle = Vector3.Angle(index23, middle23);
- break;
- case HandFinger.Middle:
- abducAngle = Vector3.Angle(middle23, ring23);
- break;
- case HandFinger.Ring:
- abducAngle = Vector3.Angle(ring23, pinky23);
- break;
- case HandFinger.Pinky:
- abducAngle = Vector3.Angle(pinky23, ring23);
- break;
- default:
- break;
- }
- bool result = false;
- if (finger.abduction == ShapesRecognizer.Abduction.Open)
- {
- result = AbducCheck(abducAngle, finger.fingerConfigs.abductionConfigs.mid, finger.fingerConfigs.abductionConfigs.width); ;
- }
- else if (finger.abduction == ShapesRecognizer.Abduction.Close)
- {
- result = !AbducCheck(abducAngle, finger.fingerConfigs.abductionConfigs.mid, finger.fingerConfigs.abductionConfigs.width); ;
- }
- return result;
- }
- }
- private bool AngleCheck(float angle, float min, float max, float width, float rangeMin, float rangeMax)
- {
- if (angle > min && angle < max)
- {
- angleCheckValid = true;
- }
- if (min - rangeMin <= 1f)
- {
- angleCheckValid = angle < max;
- }
- else if (angle < (min - width))
- {
- angleCheckValid = false;
- }
- if (rangeMax - max <= 1f)
- {
- angleCheckValid = angle > min;
- }
- else if ((angle > (max + width)))
- {
- angleCheckValid = false;
- }
- return angleCheckValid;
- }
- private bool AbducCheck(float angle, float mid, float width)
- {
- if (angle > mid + width / 2)
- {
- abducCheckOpen = true;
- }
- if (angle < mid - width / 2)
- {
- abducCheckOpen = false;
- }
- return abducCheckOpen;
- }
- #endregion
- #region BonesRecognizer
- private List<BonesRecognizer.BonesGroup> bones;
- private bool leftBonesHold;
- private bool leftBonesActive;
- private float leftBonesHoldTime;
- private bool rightBonesHold;
- private bool rightBonesActive;
- private float rightBonesHoldTime;
- private float bonesHoldDuration;
- private bool BonesCheck(HandType handType)
- {
- for (int i = 0; i < bones.Count; i++)
- {
- float distance = Vector3.Distance(GetHandJoint(handType, bones[i].bone1), GetHandJoint(handType, bones[i].bone2));
- if (distance < bones[i].distance - bones[i].thresholdWidth / 2)
- {
- bones[i].activeState = true;
- }
- else if (distance > bones[i].distance + bones[i].thresholdWidth / 2)
- {
- bones[i].activeState = false;
- }
- if (!bones[i].activeState)
- {
- return false;
- }
- }
- return true;
- }
- private Vector3 GetHandJoint(HandType hand, BonesRecognizer.HandBones bone)
- {
- if (hand == HandType.HandLeft)
- {
- return leftHandJointLocations.jointLocations[(int)bone].pose.Position.ToVector3();
- }
- else
- {
- return rightHandJointLocations.jointLocations[(int)bone].pose.Position.ToVector3();
- }
- }
- #endregion
- #region TransRecognizer
- private bool leftTransHold;
- private bool leftTransActive;
- private float leftTransHoldTime;
- private bool rightTransHold;
- private bool rightTransActive;
- private float rightTransHoldTime;
- private TransRecognizer.TrackAxis transTrackAxis;
- private TransRecognizer.SpaceType transSpaceType;
- private TransRecognizer.TrackTarget transTrackTarget;
- private float transAngleThreshold;
- private float transThresholdWidth;
- private float transHoldDuration;
- private Vector3 HMDpose;
- private Vector3 palmPos;
- private Vector3 palmAxis;
- private Vector3 targetPos;
- private bool TransCheck(TrackType trackType, Vector3 wristPos, Quaternion wristRot, Vector3 headPose, bool holdState)
- {
- GetTrackAxis(trackType, wristRot);
- GetProjectedTarget(headPose, wristRot, wristPos);
- float errorAngle = Vector3.Angle(palmAxis, targetPos);
- if (errorAngle < transAngleThreshold - transThresholdWidth / 2)
- {
- holdState = true;
- }
- if (errorAngle > transAngleThreshold + transThresholdWidth / 2)
- {
- holdState = false;
- }
- return holdState;
- }
- private Vector3 GetTrackAxis(TrackType trackType, Quaternion wristRot)
- {
- switch (transTrackAxis)
- {
- case TransRecognizer.TrackAxis.Fingers:
- palmAxis = wristRot * Vector3.forward;
- break;
- case TransRecognizer.TrackAxis.Palm:
- palmAxis = wristRot * Vector3.down;
- break;
- case TransRecognizer.TrackAxis.Thumb:
- palmAxis = trackType == TrackType.Right ? wristRot * Vector3.left : wristRot * Vector3.right;
- break;
- }
- return palmAxis;
- }
- private Vector3 GetProjectedTarget(Vector3 headPose, Quaternion wristRot, Vector3 wristPos)
- {
- palmPos = wristRot * (trackType == TrackType.Right ? new Vector3(0.08f, 0, 0) : new Vector3(-0.08f, 0, 0)) + wristPos;
- switch (transTrackTarget)
- {
- case TransRecognizer.TrackTarget.TowardsFace:
- targetPos = headPose;
- break;
- case TransRecognizer.TrackTarget.AwayFromFace:
- targetPos = palmPos * 2 - headPose;
- break;
- case TransRecognizer.TrackTarget.WorldUp:
- targetPos = palmPos + Vector3.up;
- break;
- case TransRecognizer.TrackTarget.WorldDown:
- targetPos = palmPos + Vector3.down;
- break;
- }
- targetPos -= palmPos;
- switch (transSpaceType)
- {
- case TransRecognizer.SpaceType.WorldSpace:
- break;
- case TransRecognizer.SpaceType.LocalXY:
- targetPos = Vector3.ProjectOnPlane(targetPos, wristRot * Vector3.forward);
- break;
- case TransRecognizer.SpaceType.LocalXZ:
- targetPos = Vector3.ProjectOnPlane(targetPos, wristRot * Vector3.up);
- break;
- case TransRecognizer.SpaceType.LocalYZ:
- targetPos = Vector3.ProjectOnPlane(targetPos, wristRot * Vector3.right);
- break;
- }
- return targetPos;
- }
- #endregion
- [Serializable]
- public class UpdateEvent : UnityEvent<float> { }
- }
- }
|