You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
884 lines
37 KiB
884 lines
37 KiB
using System; |
|
using System.Linq; |
|
using System.Collections.Generic; |
|
using UnityEngine; |
|
using UnityEditor; |
|
using UnityEngine.Events; |
|
using DG.Tweening; |
|
using UnityEngine.Windows; |
|
using static UDE_HAND_INTERACTION.HandInteractionSetting; |
|
using static UDE_HAND_INTERACTION.InteractableObject; |
|
|
|
namespace UDE_HAND_INTERACTION |
|
{ |
|
public class HandInteractor : MonoBehaviour |
|
{ |
|
[HideInInspector] |
|
public FingerInteractData FingerInteractData; |
|
[HideInInspector] |
|
public HandDriver HandDriver { get; private set; } |
|
private List<float> JointList; |
|
|
|
[SerializeField, Space] |
|
private UnityEvent WhenHandInteract; |
|
[SerializeField] |
|
private UnityEvent WhenHandDisable; |
|
|
|
private bool SeparateFinger; |
|
[HideInInspector] |
|
public float ActiveRange; |
|
[HideInInspector] |
|
public float ActiveIntense; |
|
[HideInInspector] |
|
public InteractType interactType; |
|
[HideInInspector] |
|
public KeyCode ShortCutKeyCode; |
|
|
|
private Vector3 RegularTransferPosition = Vector3.zero; |
|
|
|
private List<Finger> Fingers = new(); |
|
private List<Vector3> FixedPoseAngle = new(); |
|
[HideInInspector] |
|
public List<Quaternion> OriginalAngle; |
|
[HideInInspector] |
|
public List<Transform> Joints; |
|
public enum Handness |
|
{ |
|
Left, |
|
Right |
|
} |
|
[HideInInspector] |
|
public Handness handness; |
|
[HideInInspector] |
|
public Transform TipTrans; |
|
[HideInInspector] |
|
public InteractableObject InteractObject{ get; private set; } |
|
[HideInInspector] |
|
public InteractableObject ContactObject; |
|
[HideInInspector] |
|
public HandInteraction ChosenPose { get; private set; } |
|
private Dictionary<InteractableObject, Collider> TriggerObjects = new(); |
|
private bool[] ActiveCnt; |
|
[HideInInspector] |
|
public bool InActive { private set; get; } = false; |
|
private bool[] OnceAvtive = new bool[5] { true, true, true, true, true }; |
|
private float[] ActiveClock = new float[5] { 0, 0, 0, 0, 0 }; |
|
private bool OnceDisable = true; |
|
private bool UpdateCheckResult = false; |
|
private bool TriggerEnterCheck = false; |
|
|
|
private InteractableObject RemoveOperation; |
|
private bool FollowJointInClone = false; |
|
private HandInteractor CloneTarget; |
|
|
|
private SkinnedMeshRenderer HandSkinnedMeshRenderer; |
|
|
|
public Transform DetectArea { get; private set; } |
|
private LineRenderer lineRender; |
|
private List<Vector3> UsedLine = new(); |
|
private float CurveHighest = 0; |
|
public Vector3 CurveHighestVec { get; private set; } = Vector3.zero; |
|
|
|
private Ray[] DetectRays; |
|
private RaycastHit[] raycastHits = new RaycastHit[6]; |
|
private Dictionary<InteractableObject, float> RayDetectedTarget = new Dictionary<InteractableObject, float>(); |
|
private InteractableObject RayTargetObj; |
|
private InteractableObject AreaObject; |
|
|
|
private StaticPoseDetector staticPoseDetector => transform.parent?.GetComponentInChildren<StaticPoseDetector>(); |
|
private List<HandStaticPose> staticPoses; |
|
private List<Vector3> FullAngleList; |
|
|
|
void Awake() |
|
{ |
|
HandDriver = GetComponentInChildren<HandDriver>(); |
|
if (HandDriver.Hand == HandDriver.HandType.Right) |
|
{ |
|
handness = Handness.Right; |
|
} |
|
else |
|
{ |
|
handness = Handness.Left; |
|
} |
|
} |
|
|
|
void Start() |
|
{ |
|
OriginalAngle = new() |
|
{ |
|
HandDriver.Thumb1.localRotation, |
|
HandDriver.Thumb2.localRotation, |
|
HandDriver.Thumb3.localRotation, |
|
HandDriver.Index1.localRotation, |
|
HandDriver.Index2.localRotation, |
|
HandDriver.Index3.localRotation, |
|
HandDriver.Middle1.localRotation, |
|
HandDriver.Middle2.localRotation, |
|
HandDriver.Middle3.localRotation, |
|
HandDriver.Ring1.localRotation, |
|
HandDriver.Ring2.localRotation, |
|
HandDriver.Ring3.localRotation, |
|
HandDriver.Pinky1.localRotation, |
|
HandDriver.Pinky2.localRotation, |
|
HandDriver.Pinky3.localRotation, |
|
}; |
|
ActiveCnt = new bool[5] { false, false, false, false, false }; |
|
//ActiveRange = FingerInteractData.ActiveRange; |
|
ShortCutKeyCode = FingerInteractData.ShortcutKeyCode; |
|
|
|
DetectArea = transform.Find("DetectArea"); |
|
HandSkinnedMeshRenderer = transform.GetComponentInChildren<SkinnedMeshRenderer>(); |
|
lineRender = DetectArea.TryGetComponent<LineRenderer>(out var line) ? line : null; |
|
|
|
staticPoses = (handness == Handness.Left) ? staticPoseDetector?.LeftPoses : staticPoseDetector?.RightPoses; |
|
} |
|
|
|
private void OnTriggerEnter(Collider other) |
|
{ |
|
Transform obj = other.transform; |
|
if (obj.TryGetComponent<InteractableObject>(out var _)) |
|
{ |
|
TriggerEnterCheck = !UpdateCheckResult; |
|
return; |
|
} |
|
while (obj.parent != null) |
|
{ |
|
obj = obj.parent; |
|
if (obj.TryGetComponent<InteractableObject>(out var _)) |
|
{ |
|
TriggerEnterCheck = !UpdateCheckResult; |
|
return; |
|
} |
|
} |
|
} |
|
|
|
private void OnTriggerStay(Collider other) |
|
{ |
|
Transform trans = other.transform; |
|
InteractableObject obj = null; |
|
if (trans.TryGetComponent<InteractableObject>(out var _obj)) |
|
{ |
|
obj = _obj; |
|
} |
|
while (trans.parent != null) |
|
{ |
|
trans = trans.parent; |
|
if (trans.TryGetComponent<InteractableObject>(out var __obj)) |
|
{ |
|
obj = __obj; |
|
} |
|
} |
|
if (obj == null) return; |
|
|
|
ContactObject = obj; |
|
if (!TriggerEnterCheck) |
|
{ |
|
TriggerEnterCheck = !UpdateCheckResult; |
|
} |
|
InteractableObject selected = InteractObject; |
|
bool ActivePermission = false; |
|
if (!InActive) |
|
{ |
|
if (!TriggerObjects.ContainsKey(obj) && !FollowJointInClone) |
|
{ |
|
TriggerObjects.Add(obj, other); |
|
} |
|
int Priority = int.MaxValue; |
|
List<InteractableObject> DeleteList = new(); |
|
foreach(var (interact_object, collider) in TriggerObjects) |
|
{ |
|
if (collider != null && Vector3.Distance(collider.ClosestPoint(transform.Find("DetectArea").position), transform.Find("DetectArea").position) > ActiveRange) |
|
{ |
|
DeleteList.Add(interact_object); |
|
continue; |
|
} |
|
if (interact_object != null && interact_object.InteractPriority < Priority) |
|
{ |
|
Priority = interact_object.InteractPriority; |
|
selected = interact_object; |
|
} |
|
} |
|
foreach(var del in DeleteList) |
|
{ |
|
TriggerObjects.Remove(del); |
|
} |
|
if (selected == null) return; |
|
if (selected.objectFollowType == ObjectFollowType.NoPose) |
|
{ |
|
ChosenPose = null; |
|
var SettingData = selected.GetComponentInChildren<HandInteractionSetting>(); |
|
Fingers = new() |
|
{ |
|
new Finger("Thumb", SettingData.Thumb, SettingData.ThumbIntense), |
|
new Finger("Index", SettingData.Index, SettingData.IndexIntense), |
|
new Finger("Middle", SettingData.Middle, SettingData.MiddleIntense), |
|
new Finger("Ring", SettingData.Ring, SettingData.RingIntense), |
|
new Finger("Pinky", SettingData.Pinky, SettingData.PinkyIntense), |
|
}; |
|
SeparateFinger = SettingData.SeparateFinger; |
|
ActiveIntense = SettingData.ActiveIntense; |
|
interactType = SettingData.interactType; |
|
ActivePermission = ActiveChecker(); |
|
} |
|
else |
|
{ |
|
var SortedDict = GetFairInteractableDict(selected); |
|
ChosenPose = SortedDict.Values.FirstOrDefault(); |
|
foreach (HandInteraction interact in SortedDict.Values) |
|
{ |
|
var SettingData = interact.GetComponent<HandInteractionSetting>(); |
|
Fingers = new() |
|
{ |
|
new Finger("Thumb", SettingData.Thumb, SettingData.ThumbIntense), |
|
new Finger("Index", SettingData.Index, SettingData.IndexIntense), |
|
new Finger("Middle", SettingData.Middle, SettingData.MiddleIntense), |
|
new Finger("Ring", SettingData.Ring, SettingData.RingIntense), |
|
new Finger("Pinky", SettingData.Pinky, SettingData.PinkyIntense), |
|
}; |
|
SeparateFinger = SettingData.SeparateFinger; |
|
ActiveIntense = SettingData.ActiveIntense; |
|
interactType = SettingData.interactType; |
|
|
|
if (ActiveChecker()) |
|
{ |
|
ChosenPose = interact; |
|
ActivePermission = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (ChosenPose != null && ChosenPose.TryGetComponent<RegularShapeFit>(out var _regular)) |
|
{ |
|
CalCloestPoint(TipTrans.position, _regular.RealStart, _regular.RealEnd, _regular.TipDist); |
|
} |
|
else |
|
{ |
|
RegularTransferPosition = Vector3.zero; |
|
} |
|
|
|
var FingerSet = ChosenPose?.HandPose.FingersFreedom; |
|
FixedPoseAngle = new(); |
|
for (int i = 0; i < 15; ++i) |
|
{ |
|
if (ChosenPose != null && selected.objectFollowType != ObjectFollowType.NoPose && (FingerSet == null || FingerSet[i / 3] != JointFreedom.Free)) |
|
{ |
|
FixedPoseAngle.Add((OriginalAngle[i] * ChosenPose.HandPose.JointRotations[i]).eulerAngles); |
|
} |
|
else |
|
{ |
|
FixedPoseAngle.Add(Vector3.zero); |
|
} |
|
} |
|
} |
|
//Debug.Log(selected.name + "/" + ActivePermission + " " + TriggerEnterCheck + !InActive + !selected.IsFollow); |
|
selected.interactableObjectOutline?.EnableWithHand((int)handness, !selected.IsFollow); |
|
AreaObject = selected; |
|
|
|
if (ActivePermission && TriggerEnterCheck && !InActive && (!selected.IsFollow || ChosenPose.GetComponent<HandInteractionSetting>().OperationInsideObject)) |
|
{ |
|
InteractObject = selected; |
|
selected.HandActive(gameObject, ChosenPose, RegularTransferPosition); |
|
if (ChosenPose != null && ChosenPose.TryGetComponent<FingerUseInteraction>(out var script)) |
|
{ |
|
script.ResetInteraction(); |
|
} |
|
InActive = true; |
|
Debug.Log($"Active Hand : {handness}, Active Object: {selected.name}, Interaction Pose: {ChosenPose?.name}"); |
|
WhenHandInteract.Invoke(); |
|
} |
|
|
|
} |
|
|
|
private void OnTriggerExit(Collider other) |
|
{ |
|
Transform trans = other.transform; |
|
InteractableObject obj = null; |
|
AreaObject = null; |
|
if (trans.TryGetComponent<InteractableObject>(out var _obj)) |
|
{ |
|
obj = _obj; |
|
} |
|
while (trans.parent != null && obj == null) |
|
{ |
|
trans = trans.parent; |
|
if (trans.TryGetComponent<InteractableObject>(out var __obj)) |
|
{ |
|
obj = __obj; |
|
} |
|
} |
|
if (obj == null) return; |
|
|
|
obj.interactableObjectOutline?.EnableWithHand((int)handness, false); |
|
TriggerObjects.Remove(obj); |
|
if (ChosenPose != null && ChosenPose.GetComponent<HandInteractionSetting>().OperationInsideObject && InActive) |
|
{ |
|
RemoveOperation = obj; |
|
return; |
|
} |
|
|
|
if (InteractObject == obj && !InteractObject.IsFollow) |
|
{ |
|
WhenHandDisable.Invoke(); |
|
TriggerEnterCheck = false; |
|
InteractObject = null; |
|
ChosenPose = null; |
|
RegularTransferPosition = Vector3.zero; |
|
obj.HandDisable((Handedness)handness); |
|
Fingers.Clear(); |
|
} |
|
} |
|
|
|
void Update() |
|
{ |
|
if (gameObject.name.Contains("(")) |
|
{ |
|
return; |
|
} |
|
|
|
if (UnityEngine.Input.GetKeyDown(ShortCutKeyCode)) |
|
{ |
|
RecordInteractionButton(); |
|
} |
|
|
|
JointList = new() |
|
{ |
|
Math.Abs(HandDriver.thumb3.z), |
|
Math.Abs(HandDriver.index2.z), |
|
Math.Abs(HandDriver.middle2.z), |
|
Math.Abs(HandDriver.ring2.z), |
|
Math.Abs(HandDriver.pinky2.z), |
|
}; |
|
|
|
RayDetect(); |
|
|
|
if (Fingers.Count != 5) |
|
{ |
|
Fingers = new() { new(""), new(""), new(""), new(""), new("") }; |
|
interactType = InteractType.AllFingers; |
|
} |
|
int Index = 0; |
|
foreach (var finger in Fingers) |
|
{ |
|
ActiveCnt[Index] = false; |
|
if (SeparateFinger && finger.IsActive && JointList[Index] >= finger.MaxAngle * finger.Threshold) |
|
{ |
|
ActiveCnt[Index] = true; |
|
} |
|
else if (!SeparateFinger && JointList[Index] >= finger.MaxAngle * ActiveIntense) |
|
{ |
|
ActiveCnt[Index] = true; |
|
} |
|
Index++; |
|
} |
|
|
|
Joints = new() |
|
{ |
|
HandDriver.Thumb1, |
|
HandDriver.Thumb2, |
|
HandDriver.Thumb3, |
|
HandDriver.Index1, |
|
HandDriver.Index2, |
|
HandDriver.Index3, |
|
HandDriver.Middle1, |
|
HandDriver.Middle2, |
|
HandDriver.Middle3, |
|
HandDriver.Ring1, |
|
HandDriver.Ring2, |
|
HandDriver.Ring3, |
|
HandDriver.Pinky1, |
|
HandDriver.Pinky2, |
|
HandDriver.Pinky3, |
|
}; |
|
|
|
FullAngleList = new() |
|
{ |
|
HandDriver.thumb1, |
|
HandDriver.thumb2, |
|
HandDriver.thumb3, |
|
HandDriver.index1, |
|
HandDriver.index2, |
|
HandDriver.index3, |
|
HandDriver.middle1, |
|
HandDriver.middle2, |
|
HandDriver.middle3, |
|
HandDriver.ring1, |
|
HandDriver.ring2, |
|
HandDriver.ring3, |
|
HandDriver.pinky1, |
|
HandDriver.pinky2, |
|
HandDriver.pinky3, |
|
}; |
|
|
|
PoseDetect(); |
|
|
|
//Debug.Log(ActiveCnt + "//" + InActive); |
|
if (!ActiveChecker(true)) InActive = false; |
|
|
|
if (InteractObject == null && RayTargetObj == null) return; |
|
|
|
if(RayTargetObj != null && InteractObject == null) |
|
{ |
|
InteractObject = RayTargetObj; |
|
InActive = ActiveChecker(); |
|
if(AreaObject == null || AreaObject == RayTargetObj) |
|
{ |
|
|
|
RayTargetObj.interactableObjectOutline?.EnableWithHand((int)handness, !InActive); |
|
} |
|
} |
|
|
|
if(AreaObject != null && AreaObject != RayTargetObj) |
|
{ |
|
InteractObject = AreaObject; |
|
} |
|
|
|
if (InActive) |
|
{ |
|
InteractObject.HandActive(gameObject, ChosenPose, RegularTransferPosition); |
|
for (int i = 0; i < 5; ++i) |
|
{ |
|
if (ChosenPose == null) break; |
|
ActiveClock[i] += Time.deltaTime; |
|
bool TempActive = (SeparateFinger && JointList[i] >= Fingers[i].MaxAngle * Fingers[i].Threshold) || |
|
(!SeparateFinger && JointList[i] >= Fingers[i].MaxAngle * ActiveIntense); |
|
switch ((int)ChosenPose.HandPose.FingersFreedom[i]) |
|
{ |
|
case 1: |
|
if (OnceAvtive[i]) |
|
{ |
|
for (int j = 3 * i; j < 3 * (i + 1); ++j) |
|
{ |
|
Joints[j].DOLocalRotate(FixedPoseAngle[j], 0.4f, RotateMode.Fast); |
|
} |
|
OnceAvtive[i] = false; |
|
ActiveClock[i] = 0; |
|
} |
|
else if (ActiveClock[i] >= 0.39f) |
|
{ |
|
for (int j = 3 * i; j < 3 * (i + 1); ++j) |
|
{ |
|
Joints[j].DOLocalRotate(FixedPoseAngle[j], 0f, RotateMode.Fast); |
|
} |
|
} |
|
break; |
|
case 2: |
|
if (OnceAvtive[i] && TempActive) |
|
{ |
|
for (int j = 3 * i; j < 3 * (i + 1); ++j) |
|
{ |
|
Joints[j].DOLocalRotate(FixedPoseAngle[j], 0.4f, RotateMode.Fast); |
|
} |
|
OnceAvtive[i] = false; |
|
ActiveClock[i] = 0; |
|
} |
|
else if (ActiveClock[i] >= 0.39f && TempActive) |
|
{ |
|
for (int j = 3 * i; j < 3 * (i + 1); ++j) |
|
{ |
|
Joints[j].DOLocalRotate(FixedPoseAngle[j], 0f, RotateMode.Fast); |
|
} |
|
} |
|
break; |
|
case 3: |
|
var UseInteraction = ChosenPose.GetComponent<FingerUseInteraction>(); |
|
float rate = JointList[i] / Fingers[i].MaxAngle; |
|
if (TempActive) |
|
{ |
|
var UsePoseAngle = UseInteraction.FingerConfig((rate - Fingers[i].Threshold) / (1 - Fingers[i].Threshold), i); |
|
for (int j = 3 * i, k = 0; j < 3 * (i + 1); ++j) |
|
{ |
|
Joints[j].DOLocalRotate((OriginalAngle[j] * UsePoseAngle[k++]).eulerAngles, 0.05f, RotateMode.Fast); |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (!InActive) |
|
{ |
|
InteractObject.HandDisable((Handedness)handness); |
|
if (RemoveOperation != null) |
|
{ |
|
TriggerObjects.Clear(); |
|
InteractObject = null; |
|
WhenHandDisable.Invoke(); |
|
ChosenPose = null; |
|
RemoveOperation = null; |
|
} |
|
RegularTransferPosition = Vector3.zero; |
|
//OnceAvtive = true; |
|
for (int i = 0; i < 5; ++i) |
|
{ |
|
OnceAvtive[i] = true; |
|
} |
|
if (OnceDisable) |
|
{ |
|
for (int i = 0; i < 15; ++i) |
|
{ |
|
if (FixedPoseAngle[i] != Vector3.zero) |
|
{ |
|
//Joints[i].DOLocalRotate(Joints[i].localEulerAngles, 0.3f, RotateMode.Fast); |
|
} |
|
} |
|
//OnceDisable = false; |
|
} |
|
} |
|
|
|
//move from late update |
|
if (FollowJointInClone && GameObject.Find("Network Manager") != null) |
|
{ |
|
for (int i = 0; i < CloneTarget.Joints.Count; ++i) |
|
{ |
|
CloneTarget.Joints[i].DOLocalRotate(Joints[i].localEulerAngles, 0f); |
|
} |
|
} |
|
} |
|
|
|
private bool ActiveChecker(bool IsUpdate = false) |
|
{ |
|
bool[] ActiveCheck = new bool[2] { false, true }; |
|
if(ActiveCnt == null) |
|
{ |
|
ActiveCnt = new bool[5]; |
|
} |
|
bool res = false; |
|
for (int i = 0; i < 5; ++i) |
|
{ |
|
if (Fingers[i].IsActive != SeparateFinger) continue; |
|
if (interactType == InteractType.AnyFinger) |
|
{ |
|
ActiveCheck[0] |= ActiveCnt[i]; |
|
res = ActiveCheck[0]; |
|
} |
|
else if (interactType == InteractType.AllFingers) |
|
{ |
|
ActiveCheck[1] &= ActiveCnt[i]; |
|
res = ActiveCheck[1]; |
|
} |
|
} |
|
if (IsUpdate) UpdateCheckResult = res; |
|
return res; |
|
} |
|
|
|
private Dictionary<float, HandInteraction> GetFairInteractableDict(InteractableObject obj) |
|
{ |
|
Dictionary<float, HandInteraction> InteractableList = new(); |
|
foreach (var interact in obj.HandInteractions) |
|
{ |
|
if ((int)interact.handness == (int)handness) |
|
{ |
|
float distance = Vector3.Distance(interact.transform.GetChild(0).position, TipTrans.position); |
|
float RotationDistance = Quaternion.Angle(transform.rotation, interact.transform.rotation); |
|
if (interact.TryGetComponent<RegularShapeFit>(out var regular)) |
|
{ |
|
distance = CalCloestPoint(TipTrans.position, regular.RealStart, regular.RealEnd, regular.TipDist); |
|
} |
|
InteractableList[distance * RotationDistance] = interact; |
|
} |
|
} |
|
return InteractableList.OrderBy(item => item.Key).ToDictionary(item => item.Key, item => item.Value); |
|
} |
|
|
|
private float CalCloestPoint(Vector3 handPoint, Vector3 lineStart, Vector3 lineEnd, float radius) |
|
{ |
|
Vector3 lineDirection = lineEnd - lineStart; |
|
float lineLength = lineDirection.magnitude; |
|
lineDirection.Normalize(); |
|
Vector3 CenterPoint = lineStart + Vector3.Project(handPoint - lineStart, lineDirection); |
|
|
|
if ((CenterPoint - lineStart).magnitude > lineLength || (CenterPoint - lineEnd).magnitude > lineLength) |
|
{ |
|
CenterPoint = (CenterPoint - lineStart).magnitude > lineLength ? lineEnd : lineStart; |
|
RegularTransferPosition = CenterPoint; |
|
return CloestDirectionOnCircle(CenterPoint, handPoint, handPoint + lineDirection, radius); |
|
} |
|
else |
|
{ |
|
RegularTransferPosition = CenterPoint; |
|
return (handPoint - CenterPoint).magnitude - radius; |
|
} |
|
} |
|
|
|
private float CloestDirectionOnCircle(Vector3 point, Vector3 lineStart, Vector3 lineEnd, float radius) |
|
{ |
|
Vector3 lineDir = lineEnd - lineStart; |
|
Vector3 pontDir = point - lineStart; |
|
float Dot = Vector3.Dot(pontDir, lineDir); |
|
Vector3 centerToEdge = (pontDir - Dot * lineDir.normalized).normalized * radius; |
|
return (pontDir + centerToEdge).magnitude; |
|
} |
|
|
|
public void HandVisionControl(bool IsShow, HandInteractor clone) |
|
{ |
|
HandSkinnedMeshRenderer.gameObject.SetActive(IsShow); |
|
FollowJointInClone = !IsShow; |
|
CloneTarget = clone; |
|
} |
|
|
|
public void ForceActive(InteractableObject newObj, HandInteraction newInteraction) |
|
{ |
|
TriggerObjects = new(); |
|
InteractObject?.HandDisable((Handedness)handness); |
|
if (newObj != null) |
|
{ |
|
TriggerObjects.Add(newObj, null); |
|
} |
|
InteractObject = newObj; |
|
ChosenPose = newInteraction; |
|
InteractObject?.HandActive(gameObject, ChosenPose, Vector3.zero); |
|
} |
|
|
|
void PoseDetect() |
|
{ |
|
if (staticPoses == null || staticPoses.Count == 0) return; |
|
foreach(var pose in staticPoses) |
|
{ |
|
List<Quaternion> PoseRot = pose._handPose.JointRotations.ToList(); |
|
bool Judge = true; |
|
Judge &= (!pose.ConsiderWrist || Quaternion.Angle(transform.rotation, pose.transform.rotation) <= 30); |
|
if (Judge) |
|
{ |
|
for (int i = 0; i < FullAngleList.Count; ++i) |
|
{ |
|
var TotalCheck = Quaternion.Angle(Quaternion.Euler(FullAngleList[i]), PoseRot[i]) <= 25; |
|
if (!TotalCheck) |
|
{ |
|
var regularPose = RegularAngle(PoseRot[i].eulerAngles); |
|
TotalCheck = Math.Abs(FullAngleList[i].x - regularPose.x) <= 10 && |
|
Math.Abs(FullAngleList[i].y - regularPose.y) <= 10 && |
|
Math.Abs(FullAngleList[i].z - regularPose.z) <= 10; |
|
} |
|
Judge &= TotalCheck; |
|
} |
|
} |
|
pose.IsPoseDetected(Judge); |
|
} |
|
} |
|
|
|
void RayDetect() |
|
{ |
|
if (lineRender == null) return; |
|
|
|
int sign = handness == Handness.Left ? 1 : -1; |
|
DetectRays = new Ray[6] |
|
{ |
|
new Ray(DetectArea.position, DetectArea.forward), |
|
new Ray(DetectArea.position, DetectArea.forward + sign * DetectArea.right), |
|
new Ray(DetectArea.position, sign * DetectArea.right), |
|
new Ray(DetectArea.position, sign * DetectArea.right - DetectArea.forward), |
|
new Ray(DetectArea.position, sign * DetectArea.right + (float)Math.Sqrt(3) * DetectArea.up), |
|
new Ray(DetectArea.position, sign * DetectArea.right - (float)Math.Sqrt(3) * DetectArea.up) |
|
}; |
|
|
|
RayDetectedTarget.Clear(); |
|
for (int i = 0; i < DetectRays.Length; ++i) |
|
{ |
|
if (Physics.Raycast(DetectRays[i], out raycastHits[i]) && raycastHits[i].collider.gameObject.TryGetComponent<InteractableObject>(out var interactableObject)) |
|
{ |
|
float Dist = Vector3.Distance(DetectArea.position, interactableObject.transform.position); |
|
if (interactableObject.objectFollowType != ObjectFollowType.Distance || Dist > interactableObject.TrackingDistance || interactableObject.IsFollow) continue; |
|
if (RayDetectedTarget.ContainsKey(interactableObject)) |
|
{ |
|
RayDetectedTarget[interactableObject] = Math.Min(RayDetectedTarget[interactableObject], Dist); |
|
} |
|
else |
|
{ |
|
RayDetectedTarget[interactableObject] = Dist; |
|
} |
|
} |
|
} |
|
int priority = 99; |
|
if(RayTargetObj != null && RayTargetObj != AreaObject) |
|
{ |
|
RayTargetObj.interactableObjectOutline?.EnableWithHand((int)handness, false); |
|
} |
|
if (RayTargetObj == InteractObject && !InActive) InteractObject = null; |
|
RayTargetObj = null; |
|
foreach (InteractableObject obj in RayDetectedTarget.Keys) |
|
{ |
|
if (priority > obj.InteractPriority) |
|
{ |
|
priority = obj.InteractPriority; |
|
RayTargetObj = obj; |
|
} |
|
else if (priority == obj.InteractPriority && RayDetectedTarget[obj] < RayDetectedTarget[RayTargetObj]) |
|
{ |
|
RayTargetObj = obj; |
|
} |
|
} |
|
|
|
UsedLine.Clear(); |
|
if (RayTargetObj != null && !InActive && AreaObject == null) |
|
{ |
|
var SortedDict = GetFairInteractableDict(RayTargetObj); |
|
ChosenPose = SortedDict.Values.FirstOrDefault(); |
|
|
|
var SettingData = ChosenPose.GetComponent<HandInteractionSetting>(); |
|
Fingers = new() |
|
{ |
|
new Finger("Thumb", SettingData.Thumb, SettingData.ThumbIntense), |
|
new Finger("Index", SettingData.Index, SettingData.IndexIntense), |
|
new Finger("Middle", SettingData.Middle, SettingData.MiddleIntense), |
|
new Finger("Ring", SettingData.Ring, SettingData.RingIntense), |
|
new Finger("Pinky", SettingData.Pinky, SettingData.PinkyIntense), |
|
}; |
|
SeparateFinger = SettingData.SeparateFinger; |
|
ActiveIntense = SettingData.ActiveIntense; |
|
interactType = SettingData.interactType; |
|
|
|
if (ChosenPose.TryGetComponent<RegularShapeFit>(out var _regular)) |
|
{ |
|
CalCloestPoint(TipTrans.position, _regular.RealStart, _regular.RealEnd, _regular.TipDist); |
|
} |
|
else |
|
{ |
|
RegularTransferPosition = Vector3.zero; |
|
} |
|
|
|
var FingerSet = ChosenPose.HandPose.FingersFreedom; |
|
FixedPoseAngle = new(); |
|
for (int i = 0; i < 15; ++i) |
|
{ |
|
if (ChosenPose != null && RayTargetObj.objectFollowType != ObjectFollowType.NoPose && (FingerSet == null || FingerSet[i / 3] != JointFreedom.Free)) |
|
{ |
|
FixedPoseAngle.Add((OriginalAngle[i] * ChosenPose.HandPose.JointRotations[i]).eulerAngles); |
|
} |
|
else |
|
{ |
|
FixedPoseAngle.Add(Vector3.zero); |
|
} |
|
} |
|
|
|
Vector3 HandToObj = RayTargetObj.transform.position - DetectArea.position; |
|
float Distance = HandToObj.magnitude; |
|
Vector3 project = Vector3.Project(DetectArea.forward, HandToObj); |
|
Vector3 normal = (DetectArea.forward - project).normalized; |
|
|
|
float MaxAngle = (float)Math.Atan(0.6 / Distance); |
|
float CurAngle = Vector3.Angle(DetectArea.forward, HandToObj); |
|
for (int i = 0; i <= 100; ++i) |
|
{ |
|
float x = (Distance / 100) * i; |
|
float y = 0; |
|
if (CurAngle <= MaxAngle) |
|
{ |
|
y = -(float)Math.Tan(CurAngle) / Distance * (float)Math.Pow(x, 2) + (float)Math.Tan(CurAngle) * x; |
|
} |
|
else |
|
{ |
|
y = (-0.6f) / (float)Math.Pow(Distance, 2) * (float)Math.Pow(x, 2) + 0.6f / Distance * x; |
|
} |
|
Vector3 newPos = DetectArea.position + HandToObj.normalized * x + normal * y; |
|
CurveHighest = Math.Max(y, CurveHighest); |
|
if (i >= 15 && i <=96) |
|
{ |
|
UsedLine.Add(newPos); |
|
} |
|
} |
|
CurveHighestVec = normal * CurveHighest; |
|
} |
|
lineRender.positionCount = UsedLine.Count; |
|
lineRender.SetPositions(UsedLine.ToArray()); |
|
} |
|
|
|
Vector3 RegularAngle(Vector3 euler) |
|
{ |
|
var X = Math.Abs(euler.x) > 180 ? Math.Sign(euler.x) == 1 ? euler.x - 360 : euler.x + 360 : euler.x; |
|
var Y = Math.Abs(euler.y) > 180 ? Math.Sign(euler.y) == 1 ? euler.y - 360 : euler.y + 360 : euler.y; |
|
var Z = Math.Abs(euler.z) > 180 ? Math.Sign(euler.z) == 1 ? euler.z - 360 : euler.z + 360 : euler.z; |
|
return new Vector3(X, Y, Z); |
|
} |
|
|
|
public void RecordInteractionButton() |
|
{ |
|
if (ContactObject == null) |
|
{ |
|
Debug.LogError("Not In Runtime or Interaction Area!"); |
|
return; |
|
} |
|
#if UNITY_EDITOR |
|
GameObject virtualHand = ContactObject.DefaultVirtualHand; |
|
var RecordedTarget = Instantiate(virtualHand, ContactObject.transform); |
|
var interaction = RecordedTarget.GetComponent<HandInteraction>(); |
|
interaction._relativeTo = ContactObject.transform; |
|
interaction._handPose = new() |
|
{ |
|
Handedness = (Handedness)handness, |
|
JointRotations = new Quaternion[15] |
|
{ |
|
Quaternion.Inverse(OriginalAngle[0]) * Joints[0].localRotation, |
|
Quaternion.Inverse(OriginalAngle[1]) * Joints[1].localRotation, |
|
Quaternion.Inverse(OriginalAngle[2]) * Joints[2].localRotation, |
|
Quaternion.Inverse(OriginalAngle[3]) * Joints[3].localRotation, |
|
Quaternion.Inverse(OriginalAngle[4]) * Joints[4].localRotation, |
|
Quaternion.Inverse(OriginalAngle[5]) * Joints[5].localRotation, |
|
Quaternion.Inverse(OriginalAngle[6]) * Joints[6].localRotation, |
|
Quaternion.Inverse(OriginalAngle[7]) * Joints[7].localRotation, |
|
Quaternion.Inverse(OriginalAngle[8]) * Joints[8].localRotation, |
|
Quaternion.Inverse(OriginalAngle[9]) * Joints[9].localRotation, |
|
Quaternion.Inverse(OriginalAngle[10]) * Joints[10].localRotation, |
|
Quaternion.Inverse(OriginalAngle[11]) * Joints[11].localRotation, |
|
Quaternion.Inverse(OriginalAngle[12]) * Joints[12].localRotation, |
|
Quaternion.Inverse(OriginalAngle[13]) * Joints[13].localRotation, |
|
Quaternion.Inverse(OriginalAngle[14]) * Joints[14].localRotation, |
|
} |
|
}; |
|
RecordedTarget.transform.position = transform.position; |
|
if (!Directory.Exists(Application.dataPath + "/SDK/Interaction SDK/TempVirtual")) |
|
{ |
|
Directory.CreateDirectory(Application.dataPath + "/SDK/Interaction SDK/TempVirtual"); |
|
} |
|
string PrefabName = "RecordedInteraction"; |
|
int index = 1; |
|
while (Directory.Exists(Application.dataPath + $"/SDK/Interaction SDK/TempVirtual/{PrefabName}.prefab")) |
|
{ |
|
PrefabName = "RecordedInteraction" + index++; |
|
} |
|
PrefabUtility.SaveAsPrefabAsset(RecordedTarget, Application.dataPath + $"/SDK/Interaction SDK/TempVirtual/{PrefabName}.prefab"); |
|
#endif |
|
} |
|
|
|
public void RecordStaticPoseButton() |
|
{ |
|
#if UNITY_EDITOR |
|
GameObject virtualHand = new GameObject(name + "NewStaticPose"); |
|
virtualHand.transform.SetParent(staticPoseDetector.transform); |
|
var staticPose = virtualHand.AddComponent<HandStaticPose>(); |
|
Quaternion[] Target = new Quaternion[15]; |
|
if(OriginalAngle.Count == 0) |
|
{ |
|
for(int i = 0; i < 15; ++i) |
|
{ |
|
Target[i] = Quaternion.identity; |
|
} |
|
} |
|
else |
|
{ |
|
for (int i = 0; i < 15; ++i) |
|
{ |
|
Target[i] = Quaternion.Inverse(OriginalAngle[i]) * Joints[i].localRotation; |
|
} |
|
} |
|
staticPose._handPose = new() |
|
{ |
|
Handedness = (Handedness)handness, |
|
JointRotations = Target |
|
}; |
|
virtualHand.transform.position = transform.position; |
|
if (!Directory.Exists(Application.dataPath + "/SDK/Interaction SDK/TempVirtual")) |
|
{ |
|
Directory.CreateDirectory(Application.dataPath + "/SDK/Interaction SDK/TempVirtual"); |
|
} |
|
string PrefabName = virtualHand.name; |
|
int index = 1; |
|
while (File.Exists(Application.dataPath + $"/SDK/Interaction SDK/TempVirtual/{PrefabName}.prefab")) |
|
{ |
|
PrefabName = virtualHand.name + index++; |
|
} |
|
PrefabUtility.SaveAsPrefabAsset(virtualHand, Application.dataPath + $"/SDK/Interaction SDK/TempVirtual/{PrefabName}.prefab"); |
|
#endif |
|
} |
|
} |
|
}
|
|
|