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.
885 lines
37 KiB
885 lines
37 KiB
|
1 month ago
|
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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|