Replace UltimateXR with HurricaneVR
This commit is contained in:
3
Assets/HurricaneVR/Framework/Scripts/Core/Bags.meta
Normal file
3
Assets/HurricaneVR/Framework/Scripts/Core/Bags.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f99ff4d40314101aa47047f5dfaec5d
|
||||
timeCreated: 1600273619
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Bags
|
||||
{
|
||||
|
||||
public class HVRForceGrabberBag : HVRTriggerGrabbableBag
|
||||
{
|
||||
/// <summary>
|
||||
/// what was once here is now in the force grabber component itself for line of sight checking
|
||||
/// </summary>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee407dd0e26344b884367d0f6d4a806f
|
||||
timeCreated: 1598308977
|
||||
@@ -0,0 +1,321 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Bags
|
||||
{
|
||||
|
||||
|
||||
public class HVRGrabbableBag : MonoBehaviour
|
||||
{
|
||||
public VRGrabbableEvent GrabbableRemoved = new VRGrabbableEvent();
|
||||
|
||||
[Header("Settings")]
|
||||
[Tooltip("If true, grabbed objects will be penalized with the sorting.")]
|
||||
public bool PenalizeGrabbed = true;
|
||||
|
||||
public HVRSortMode hvrSortMode = HVRSortMode.SquareMagnitude;
|
||||
public float MaxDistanceAllowed;
|
||||
|
||||
[Header("Transforms")]
|
||||
[Tooltip("If assigned, the position of this transform will be used to calculate the distance.")]
|
||||
public Transform DistanceSource;
|
||||
|
||||
public HVRGrabberBase Grabber;
|
||||
|
||||
public List<HVRGrabbable> ValidGrabbables = new List<HVRGrabbable>(1000);
|
||||
public HVRGrabbable ClosestGrabbable;
|
||||
|
||||
public List<HVRGrabbable> IgnoredGrabbables = new List<HVRGrabbable>();
|
||||
|
||||
[SerializeField]
|
||||
private List<HVRGrabbable> _allGrabbables = new List<HVRGrabbable>();
|
||||
private readonly HashSet<HVRGrabbable> _distinctGrabbables = new HashSet<HVRGrabbable>();
|
||||
private readonly List<HVRGrabbable> _grabbablesToRemove = new List<HVRGrabbable>(1000);
|
||||
|
||||
private readonly HashSet<HVRGrabbable> _ignoredGrabbables = new HashSet<HVRGrabbable>();
|
||||
private Sorter sorter = new Sorter();
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
IgnoredGrabbables.ForEach(g => _ignoredGrabbables.Add(g));
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
Calculate();
|
||||
}
|
||||
|
||||
protected void AddGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
if (_distinctGrabbables.Contains(grabbable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_distinctGrabbables.Add(grabbable);
|
||||
_allGrabbables.Add(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void RemoveGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
if (_distinctGrabbables.Contains(grabbable))
|
||||
{
|
||||
_allGrabbables.Remove(grabbable);
|
||||
}
|
||||
_distinctGrabbables.Remove(grabbable);
|
||||
GrabbableRemoved.Invoke(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void Calculate()
|
||||
{
|
||||
ValidGrabbables.Clear();
|
||||
_grabbablesToRemove.Clear();
|
||||
sorter.DistanceMap.Clear();
|
||||
|
||||
var anyDestroyed = false;
|
||||
|
||||
for (var i = 0; i < _allGrabbables.Count; i++)
|
||||
{
|
||||
var grabbable = _allGrabbables[i];
|
||||
if (!grabbable)// || !grabbable.gameObject.activeInHierarchy || !grabbable.enabled)
|
||||
{
|
||||
anyDestroyed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
sorter.DistanceMap[grabbable] = DistanceToGrabbable(grabbable);
|
||||
|
||||
if (!grabbable.HasConcaveColliders && sorter.DistanceMap[grabbable] > MaxDistanceAllowed)
|
||||
{
|
||||
_grabbablesToRemove.Add(grabbable);
|
||||
}
|
||||
else if (IsValid(grabbable))
|
||||
{
|
||||
if (PenalizeGrabbed && grabbable.IsBeingHeld)
|
||||
{
|
||||
sorter.DistanceMap[grabbable] += 1000f;
|
||||
}
|
||||
|
||||
ValidGrabbables.Add(grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
if (anyDestroyed)
|
||||
{
|
||||
_distinctGrabbables.RemoveWhere(e => !e);
|
||||
for (int i = _allGrabbables.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var grabbable = _allGrabbables[i];
|
||||
if (!grabbable)
|
||||
{
|
||||
_allGrabbables.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var index = 0; index < _grabbablesToRemove.Count; index++)
|
||||
{
|
||||
var invalid = _grabbablesToRemove[index];
|
||||
RemoveGrabbable(invalid);
|
||||
}
|
||||
|
||||
SortHelper.Sort(ValidGrabbables, 0, ValidGrabbables.Count, sorter);
|
||||
|
||||
ClosestGrabbable = ValidGrabbables.Count > 0 ? ValidGrabbables[0] : null;
|
||||
}
|
||||
|
||||
public virtual float DistanceToGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
var point = DistanceSource ? DistanceSource.position : Grabber.transform.position;
|
||||
|
||||
if (hvrSortMode == HVRSortMode.Distance)
|
||||
return grabbable.GetDistanceToGrabber(point);
|
||||
return grabbable.GetSquareDistanceToGrabber(point);
|
||||
}
|
||||
|
||||
protected virtual bool IsValid(HVRGrabbable grabbable)
|
||||
{
|
||||
if(!grabbable.gameObject.activeInHierarchy || !grabbable.enabled)
|
||||
return false;
|
||||
|
||||
if (grabbable.RequiresGrabbable)
|
||||
{
|
||||
if (!grabbable.RequiredGrabbable || !grabbable.RequiredGrabbable.IsBeingHeld)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return grabbable.CanBeGrabbed && !_ignoredGrabbables.Contains(grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SortHelper
|
||||
{
|
||||
|
||||
public static void Sort<T>(List<T> keys, int index, int length, IComparer<T> comparer)
|
||||
{
|
||||
SortHelper.IntrospectiveSort(keys, index, length, comparer);
|
||||
}
|
||||
|
||||
private static void SwapIfGreater<T>(List<T> keys, IComparer<T> comparer, int a, int b)
|
||||
{
|
||||
if (a == b || comparer.Compare(keys[a], keys[b]) <= 0)
|
||||
return;
|
||||
T key = keys[a];
|
||||
keys[a] = keys[b];
|
||||
keys[b] = key;
|
||||
}
|
||||
|
||||
private static void Swap<T>(List<T> a, int i, int j)
|
||||
{
|
||||
if (i == j)
|
||||
return;
|
||||
T obj = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = obj;
|
||||
}
|
||||
|
||||
internal static void IntrospectiveSort<T>(List<T> keys, int left, int length, IComparer<T> comparer)
|
||||
{
|
||||
if (length < 2)
|
||||
return;
|
||||
IntroSort<T>(keys, left, length + left - 1, 2 * FloorLog2(keys.Count), comparer);
|
||||
}
|
||||
|
||||
internal static int FloorLog2(int n)
|
||||
{
|
||||
int num = 0;
|
||||
for (; n >= 1; n /= 2)
|
||||
++num;
|
||||
return num;
|
||||
}
|
||||
|
||||
private static void IntroSort<T>(
|
||||
List<T> keys,
|
||||
int lo,
|
||||
int hi,
|
||||
int depthLimit,
|
||||
IComparer<T> comparer)
|
||||
{
|
||||
int num1;
|
||||
for (; hi > lo; hi = num1 - 1)
|
||||
{
|
||||
int num2 = hi - lo + 1;
|
||||
if (num2 <= 16)
|
||||
{
|
||||
if (num2 == 1)
|
||||
break;
|
||||
if (num2 == 2)
|
||||
{
|
||||
SwapIfGreater(keys, comparer, lo, hi);
|
||||
break;
|
||||
}
|
||||
if (num2 == 3)
|
||||
{
|
||||
SwapIfGreater(keys, comparer, lo, hi - 1);
|
||||
SwapIfGreater(keys, comparer, lo, hi);
|
||||
SwapIfGreater(keys, comparer, hi - 1, hi);
|
||||
break;
|
||||
}
|
||||
InsertionSort(keys, lo, hi, comparer);
|
||||
break;
|
||||
}
|
||||
if (depthLimit == 0)
|
||||
{
|
||||
Heapsort(keys, lo, hi, comparer);
|
||||
break;
|
||||
}
|
||||
--depthLimit;
|
||||
num1 = PickPivotAndPartition(keys, lo, hi, comparer);
|
||||
IntroSort(keys, num1 + 1, hi, depthLimit, comparer);
|
||||
}
|
||||
}
|
||||
|
||||
private static int PickPivotAndPartition<T>(List<T> keys, int lo, int hi, IComparer<T> comparer)
|
||||
{
|
||||
int index = lo + (hi - lo) / 2;
|
||||
SwapIfGreater(keys, comparer, lo, index);
|
||||
SwapIfGreater(keys, comparer, lo, hi);
|
||||
SwapIfGreater(keys, comparer, index, hi);
|
||||
T key = keys[index];
|
||||
Swap(keys, index, hi - 1);
|
||||
int i = lo;
|
||||
int j = hi - 1;
|
||||
while (i < j)
|
||||
{
|
||||
do
|
||||
{
|
||||
} while (comparer.Compare(keys[++i], key) < 0);
|
||||
do
|
||||
{
|
||||
} while (comparer.Compare(key, keys[--j]) < 0);
|
||||
if (i < j)
|
||||
Swap(keys, i, j);
|
||||
else
|
||||
break;
|
||||
}
|
||||
Swap(keys, i, hi - 1);
|
||||
return i;
|
||||
}
|
||||
|
||||
private static void Heapsort<T>(List<T> keys, int lo, int hi, IComparer<T> comparer)
|
||||
{
|
||||
int n = hi - lo + 1;
|
||||
for (int i = n / 2; i >= 1; --i)
|
||||
DownHeap(keys, i, n, lo, comparer);
|
||||
for (int index = n; index > 1; --index)
|
||||
{
|
||||
Swap(keys, lo, lo + index - 1);
|
||||
DownHeap(keys, 1, index - 1, lo, comparer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DownHeap<T>(List<T> keys, int i, int n, int lo, IComparer<T> comparer)
|
||||
{
|
||||
T key = keys[lo + i - 1];
|
||||
int num;
|
||||
for (; i <= n / 2; i = num)
|
||||
{
|
||||
num = 2 * i;
|
||||
if (num < n && comparer.Compare(keys[lo + num - 1], keys[lo + num]) < 0)
|
||||
++num;
|
||||
if (comparer.Compare(key, keys[lo + num - 1]) < 0)
|
||||
keys[lo + i - 1] = keys[lo + num - 1];
|
||||
else
|
||||
break;
|
||||
}
|
||||
keys[lo + i - 1] = key;
|
||||
}
|
||||
|
||||
private static void InsertionSort<T>(List<T> keys, int lo, int hi, IComparer<T> comparer)
|
||||
{
|
||||
for (int index1 = lo; index1 < hi; ++index1)
|
||||
{
|
||||
int index2 = index1;
|
||||
T key;
|
||||
for (key = keys[index1 + 1]; index2 >= lo && comparer.Compare(key, keys[index2]) < 0; --index2)
|
||||
keys[index2 + 1] = keys[index2];
|
||||
keys[index2 + 1] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class Sorter : IComparer<HVRGrabbable>
|
||||
{
|
||||
internal readonly Dictionary<HVRGrabbable, float> DistanceMap = new Dictionary<HVRGrabbable, float>();
|
||||
|
||||
public int Compare(HVRGrabbable x, HVRGrabbable y)
|
||||
{
|
||||
return DistanceMap[x].CompareTo(DistanceMap[y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fb655091c7347beb9e915d00e497747
|
||||
timeCreated: 1596346935
|
||||
218
Assets/HurricaneVR/Framework/Scripts/Core/Bags/HVRSocketBag.cs
Normal file
218
Assets/HurricaneVR/Framework/Scripts/Core/Bags/HVRSocketBag.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Bags
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by the hand to detect sockets for removing objects from them
|
||||
/// </summary>
|
||||
public class HVRSocketBag : MonoBehaviour, IComparer<HVRSocket>
|
||||
{
|
||||
private readonly Dictionary<HVRSocket, HashSet<Collider>> _map = new Dictionary<HVRSocket, HashSet<Collider>>();
|
||||
|
||||
|
||||
public HVRHandGrabber Grabber;
|
||||
public float MaxDistanceAllowed;
|
||||
public HVRSortMode hvrSortMode = HVRSortMode.SquareMagnitude;
|
||||
public HVRSocket[] IgnoredSockets;
|
||||
|
||||
[Header("Transforms")]
|
||||
[Tooltip("If assigned, the position of this transform will be used to calculate the distance.")]
|
||||
public Transform DistanceSource;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
if (!Grabber)
|
||||
{
|
||||
Grabber = GetComponentInParent<HVRHandGrabber>();
|
||||
}
|
||||
|
||||
if (Math.Abs(MaxDistanceAllowed) < .001)
|
||||
{
|
||||
MaxDistanceAllowed = 1.5f;
|
||||
}
|
||||
|
||||
if (IgnoredSockets != null && IgnoredSockets.Length > 0)
|
||||
{
|
||||
_ignoredSockets = new HashSet<HVRSocket>(IgnoredSockets);
|
||||
}
|
||||
}
|
||||
|
||||
[Header("Debugging")]
|
||||
public HVRSocket ClosestSocket;
|
||||
public readonly HashSet<HVRSocket> AllSockets = new HashSet<HVRSocket>();
|
||||
private readonly List<HVRSocket> _allSockets = new List<HVRSocket>(20);
|
||||
public List<HVRSocket> ValidSockets = new List<HVRSocket>(1000);
|
||||
private readonly List<HVRSocket> SocketsToRemove = new List<HVRSocket>(1000);
|
||||
private Dictionary<HVRSocket, float> DistanceMap = new Dictionary<HVRSocket, float>();
|
||||
private HashSet<HVRSocket> _ignoredSockets;
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
Calculate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the bag to ignore the provided socket when it's trigger collider overlaps ours.
|
||||
/// </summary>
|
||||
public void IgnoreSocket(HVRSocket socket)
|
||||
{
|
||||
if (_ignoredSockets == null)
|
||||
{
|
||||
_ignoredSockets = new HashSet<HVRSocket>();
|
||||
}
|
||||
|
||||
_ignoredSockets.Add(socket);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops ignoring the provided socket
|
||||
/// </summary>
|
||||
public void UnIgnoreSocket(HVRSocket socket)
|
||||
{
|
||||
_ignoredSockets.Remove(socket);
|
||||
}
|
||||
|
||||
protected void AddSocket(HVRSocket socket)
|
||||
{
|
||||
if (AllSockets.Contains(socket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_allSockets.Add(socket);
|
||||
AllSockets.Add(socket);
|
||||
}
|
||||
|
||||
|
||||
protected void RemoveSocket(HVRSocket socket)
|
||||
{
|
||||
if (AllSockets.Contains(socket))
|
||||
{
|
||||
AllSockets.Remove(socket);
|
||||
_allSockets.Remove(socket);
|
||||
}
|
||||
|
||||
if (_map.ContainsKey(socket))
|
||||
{
|
||||
_map.Remove(socket);
|
||||
}
|
||||
}
|
||||
|
||||
protected void Calculate()
|
||||
{
|
||||
ValidSockets.Clear();
|
||||
SocketsToRemove.Clear();
|
||||
|
||||
var anyDestroyedOrDisabled = false;
|
||||
|
||||
for (var i = 0; i < _allSockets.Count; i++)
|
||||
{
|
||||
var socket = _allSockets[i];
|
||||
if (!socket || !socket.gameObject.activeInHierarchy || !socket.enabled)
|
||||
{
|
||||
anyDestroyedOrDisabled = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
var distance = DistanceToSocket(socket);
|
||||
|
||||
DistanceMap[socket] = distance;
|
||||
|
||||
if (distance > MaxDistanceAllowed)
|
||||
{
|
||||
SocketsToRemove.Add(socket);
|
||||
}
|
||||
else if (IsValid(socket))
|
||||
{
|
||||
ValidSockets.Add(socket);
|
||||
}
|
||||
}
|
||||
|
||||
if (anyDestroyedOrDisabled)
|
||||
{
|
||||
AllSockets.RemoveWhere(socket => !socket || !socket.gameObject.activeInHierarchy || !socket.enabled);
|
||||
_allSockets.RemoveAll(socket => !socket || !socket.gameObject.activeInHierarchy || !socket.enabled);
|
||||
}
|
||||
|
||||
for (var index = 0; index < SocketsToRemove.Count; index++)
|
||||
{
|
||||
var invalid = SocketsToRemove[index];
|
||||
RemoveSocket(invalid);
|
||||
}
|
||||
|
||||
// x->y ascending sort
|
||||
//ValidSockets.Sort(this);
|
||||
SortHelper.Sort(ValidSockets, 0, ValidSockets.Count, this);
|
||||
|
||||
ClosestSocket = ValidSockets.FirstOrDefault();
|
||||
}
|
||||
|
||||
public virtual float DistanceToSocket(HVRSocket socket)
|
||||
{
|
||||
var point = DistanceSource ? DistanceSource.position : Grabber.transform.position;
|
||||
|
||||
if (hvrSortMode == HVRSortMode.Distance)
|
||||
return socket.GetDistanceToGrabber(point);
|
||||
return socket.GetSquareDistanceToGrabber(point);
|
||||
}
|
||||
|
||||
protected bool IsValid(HVRSocket Socket)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
var socket = other.GetComponent<HVRSocket>();
|
||||
|
||||
if (socket)
|
||||
{
|
||||
if (_ignoredSockets != null && _ignoredSockets.Contains(socket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_map.TryGetValue(socket, out var colliders))
|
||||
{
|
||||
colliders = new HashSet<Collider>();
|
||||
_map[socket] = colliders;
|
||||
}
|
||||
|
||||
if (colliders.Count == 0)
|
||||
{
|
||||
AddSocket(socket);
|
||||
}
|
||||
|
||||
colliders.Add(other);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
var socket = other.GetComponent<HVRSocket>();
|
||||
|
||||
if (socket)
|
||||
{
|
||||
if (_map.TryGetValue(socket, out var colliders))
|
||||
{
|
||||
colliders.Remove(other);
|
||||
}
|
||||
|
||||
if (colliders == null || colliders.Count == 0)
|
||||
{
|
||||
RemoveSocket(socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Compare(HVRSocket x, HVRSocket y)
|
||||
{
|
||||
return DistanceMap[x].CompareTo(DistanceMap[y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9592152fa64433fa0a6766a74007789
|
||||
timeCreated: 1599789992
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Bags
|
||||
{
|
||||
public class HVRTriggerGrabbableBag : HVRGrabbableBag
|
||||
{
|
||||
private readonly Dictionary<HVRGrabbable, HashSet<Collider>> _map = new Dictionary<HVRGrabbable, HashSet<Collider>>();
|
||||
|
||||
[Tooltip("If true it will use Collider.ClosestPoint method to determine the closest grabbable.")]
|
||||
public bool UseColliderDistance = true;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (Math.Abs(MaxDistanceAllowed) < .001)
|
||||
{
|
||||
//Debug.Log($"{gameObject.name}: MaxDistanceAllowed too low, setting to 1.5f");
|
||||
MaxDistanceAllowed = 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
other.TryGetComponent<HVRGrabbable>(out var grabbable);
|
||||
other.TryGetComponent<HVRGrabbableChild>(out var childGrabbable);
|
||||
|
||||
if (!grabbable)
|
||||
{
|
||||
if (childGrabbable && childGrabbable.ParentGrabbable)
|
||||
{
|
||||
grabbable = childGrabbable.ParentGrabbable;
|
||||
}
|
||||
}
|
||||
|
||||
if (HVRSettings.Instance.UseAttachedRigidBody && !grabbable && other.attachedRigidbody)
|
||||
{
|
||||
other.attachedRigidbody.TryGetComponent<HVRGrabbable>(out grabbable);
|
||||
}
|
||||
|
||||
if (HVRSettings.Instance.ComponentInParentFallback && !grabbable)
|
||||
{
|
||||
grabbable = other.GetComponentInParent<HVRGrabbable>();
|
||||
}
|
||||
|
||||
if (grabbable)
|
||||
{
|
||||
if (grabbable.FilterGrabColliders && !grabbable.GrabCollidersSet.Contains(other))
|
||||
return;
|
||||
|
||||
if (!_map.TryGetValue(grabbable, out var colliders))
|
||||
{
|
||||
colliders = new HashSet<Collider>();
|
||||
_map[grabbable] = colliders;
|
||||
}
|
||||
|
||||
if (colliders.Count == 0)
|
||||
{
|
||||
AddGrabbable(grabbable);
|
||||
}
|
||||
|
||||
colliders.Add(other);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
|
||||
other.TryGetComponent<HVRGrabbable>(out var grabbable);
|
||||
other.TryGetComponent<HVRGrabbableChild>(out var childGrabbable);
|
||||
if (!grabbable && childGrabbable && childGrabbable.ParentGrabbable)
|
||||
{
|
||||
grabbable = childGrabbable.ParentGrabbable;
|
||||
}
|
||||
|
||||
if (HVRSettings.Instance.UseAttachedRigidBody && !grabbable && other.attachedRigidbody)
|
||||
{
|
||||
other.attachedRigidbody.TryGetComponent<HVRGrabbable>(out grabbable);
|
||||
}
|
||||
|
||||
if (HVRSettings.Instance.ComponentInParentFallback && !grabbable)
|
||||
{
|
||||
grabbable = other.GetComponentInParent<HVRGrabbable>();
|
||||
}
|
||||
|
||||
if (grabbable)
|
||||
{
|
||||
if (_map.TryGetValue(grabbable, out var colliders))
|
||||
{
|
||||
colliders.Remove(other);
|
||||
}
|
||||
|
||||
if (colliders == null || colliders.Count == 0)
|
||||
{
|
||||
RemoveGrabbable(grabbable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override float DistanceToGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
if (UseColliderDistance && _map.TryGetValue(grabbable, out var colliders) && grabbable.UseColliderClosestPoint)
|
||||
{
|
||||
var distance = float.MaxValue;
|
||||
var anchor = DistanceSource ? DistanceSource.position : Grabber.transform.position;
|
||||
|
||||
foreach (var grabbableCollider in colliders)
|
||||
{
|
||||
if (!grabbableCollider)
|
||||
continue;
|
||||
|
||||
Vector3 point;
|
||||
if (grabbable.HasConcaveColliders && grabbableCollider is MeshCollider meshCollider && !meshCollider.convex)
|
||||
{
|
||||
if (!grabbableCollider.Raycast(new Ray(anchor, grabbableCollider.bounds.center - anchor), out var hit, Vector3.Distance(grabbableCollider.bounds.center, anchor)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
point = hit.point;
|
||||
}
|
||||
else
|
||||
{
|
||||
point = grabbableCollider.ClosestPoint(anchor);
|
||||
}
|
||||
|
||||
var current = Vector3.Distance(point, anchor);
|
||||
if (current < distance)
|
||||
{
|
||||
distance = current;
|
||||
}
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
return base.DistanceToGrabbable(grabbable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e55483a495124baab45ceb8df9a68da9
|
||||
timeCreated: 1596346950
|
||||
@@ -0,0 +1 @@
|
||||
//left in to prevent compile errors
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5969b23a56534b28b24c515e2b1826d9
|
||||
timeCreated: 1601444721
|
||||
3
Assets/HurricaneVR/Framework/Scripts/Core/Grabbers.meta
Normal file
3
Assets/HurricaneVR/Framework/Scripts/Core/Grabbers.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18fc546c9cc5464cba366ed43c3e10b7
|
||||
timeCreated: 1600273615
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Grabbers
|
||||
{
|
||||
public class HVRCloneDelete : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53ae4d82f1d97b44686e84aac4f5077b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,834 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using HurricaneVR.Framework.Components;
|
||||
using HurricaneVR.Framework.ControllerInput;
|
||||
using HurricaneVR.Framework.Core.HandPoser;
|
||||
using HurricaneVR.Framework.Core.ScriptableObjects;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using HurricaneVR.Framework.Shared.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Grabbers
|
||||
{
|
||||
public class HVRForceGrabber : HVRGrabberBase
|
||||
{
|
||||
[Header("Components")]
|
||||
public HVRForceGrabberLaser Laser;
|
||||
|
||||
public HVRHandGrabber HandGrabber;
|
||||
public HVRGrabbableHoverBase GrabIndicator;
|
||||
public HVRHandPoser GrabPoser;
|
||||
public HVRHandPoser HoverPoser;
|
||||
|
||||
[Header("Line of Sight")]
|
||||
[Tooltip("If true, ray cast from the RaycastOrigin need to hit objects in the trigger collider, otherwise they can't be grabbed.")]
|
||||
public bool RequireLineOfSight = true;
|
||||
|
||||
[Tooltip("Used to shoot ray casts at the grabbable to check if there is line of sight before grabbing.")]
|
||||
public Transform RaycastOrigin;
|
||||
|
||||
[Tooltip("If true uses collider closest point as ray cast target, if not uses collider bounds center")]
|
||||
public bool UseClosestPoint = true;
|
||||
|
||||
[Tooltip("If true, uses RaycastLayermask field of the hand grabber for line of sight checks.")]
|
||||
public bool UseHandLayerMask = true;
|
||||
|
||||
[DrawIf("UseHandLayerMask", false)]
|
||||
[Tooltip("Layer mask to determine line of sight to the grabbable.")]
|
||||
public LayerMask RaycastLayermask;
|
||||
|
||||
[Tooltip("Max distance of the line of sight ray cast.")]
|
||||
public float MaxRayCastDistance = 10f;
|
||||
|
||||
|
||||
[Header("Settings")]
|
||||
public HVRForceGrabMode GrabStyle = HVRForceGrabMode.ForcePull;
|
||||
|
||||
public AudioClip SFXGrab;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.ForcePull)]
|
||||
public HVRForcePullSettings ForcePullSettings;
|
||||
|
||||
//[Header("Gravity Gloves Style Settings")]
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public bool RequiresFlick;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float ForceTime = 1f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float YOffset = .3f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float FlickStartThreshold = 1.25f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float FlickEndThreshold = .25f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float QuickMoveThreshold = 1.25f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float QuickMoveResetThreshold = .25f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float MaximumVelocityPostCollision = 5f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float MaximumVelocityAutoGrab = 5f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public bool AutoGrab = true;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float AdditionalAutoGrabTime = 1f;
|
||||
|
||||
[DrawIf("GrabStyle", HVRForceGrabMode.GravityGloves)]
|
||||
public float AutoGrabDistance = .2f;
|
||||
|
||||
[Header("Debug")]
|
||||
public bool SlowMo;
|
||||
|
||||
public float TimeScale = .25f;
|
||||
|
||||
public HVRPlayerInputs Inputs => HandGrabber.Inputs;
|
||||
|
||||
private bool _grabbableCollided;
|
||||
|
||||
public override Vector3 JointAnchorWorldPosition => HandGrabber.JointAnchorWorldPosition;
|
||||
|
||||
|
||||
private bool _canFlick;
|
||||
private bool _canQuickStart;
|
||||
private Coroutine _additionalGrabRoutine;
|
||||
private HVRGrabbableHoverBase _grabIndicator;
|
||||
private Transform _anchor;
|
||||
private Rigidbody _forceRB;
|
||||
|
||||
public float VelocityMagnitude => HandGrabber.HVRTrackedController.VelocityMagnitude;
|
||||
public float AngularVelocityMagnitude => HandGrabber.HVRTrackedController.AngularVelocityMagnitude;
|
||||
|
||||
public HVRHandSide HandSide => HandGrabber.HandSide;
|
||||
|
||||
public bool IsForceGrabbing { get; private set; }
|
||||
|
||||
public bool IsAiming { get; private set; }
|
||||
|
||||
public override bool IsDistanceGrabber => true;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (!HandGrabber)
|
||||
{
|
||||
HandGrabber = GetComponentInChildren<HVRHandGrabber>();
|
||||
}
|
||||
|
||||
if (!HandGrabber)
|
||||
{
|
||||
Debug.LogWarning("Cannot find HandGrabber. Make sure to assign or have it on this level or below.");
|
||||
}
|
||||
|
||||
CheckForceAnchor();
|
||||
|
||||
if (!ForcePullSettings)
|
||||
{
|
||||
ForcePullSettings = ScriptableObject.CreateInstance<HVRForcePullSettings>();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForceAnchor()
|
||||
{
|
||||
if (!_anchor)
|
||||
{
|
||||
var go = new GameObject("ForceAnchor");
|
||||
_anchor = go.transform;
|
||||
_forceRB = _anchor.gameObject.AddComponent<Rigidbody>();
|
||||
_forceRB.isKinematic = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (RequiresFlick && GrabStyle == HVRForceGrabMode.GravityGloves)
|
||||
{
|
||||
CheckFlick();
|
||||
CheckDrawRay();
|
||||
}
|
||||
|
||||
CheckGripButtonGrab();
|
||||
UpdateGrabIndicator();
|
||||
}
|
||||
|
||||
private void CheckFlick()
|
||||
{
|
||||
if (IsGrabbing || !IsHovering || !Inputs.GetForceGrabActive(HandSide))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_canFlick && AngularVelocityMagnitude > FlickStartThreshold)
|
||||
{
|
||||
TryGrab(HoverTarget);
|
||||
_canFlick = false;
|
||||
}
|
||||
|
||||
if (AngularVelocityMagnitude < FlickEndThreshold)
|
||||
{
|
||||
_canFlick = true;
|
||||
}
|
||||
|
||||
if (VelocityMagnitude < QuickMoveResetThreshold)
|
||||
{
|
||||
_canQuickStart = true;
|
||||
}
|
||||
|
||||
if (_canQuickStart && VelocityMagnitude > QuickMoveThreshold)
|
||||
{
|
||||
TryGrab(HoverTarget);
|
||||
_canQuickStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckGripButtonGrab()
|
||||
{
|
||||
if ((!RequiresFlick || GrabStyle == HVRForceGrabMode.ForcePull) && !IsGrabbing && IsHovering && Inputs.GetForceGrabActivated(HandSide))
|
||||
{
|
||||
TryGrab(HoverTarget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CheckDrawRay()
|
||||
{
|
||||
if (!IsGrabbing && HoverTarget && Inputs.GetForceGrabActive(HandSide))
|
||||
{
|
||||
Laser.Enable(HoverTarget.transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
Laser.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void CheckUnHover()
|
||||
{
|
||||
if (RequiresFlick && GrabStyle == HVRForceGrabMode.GravityGloves && !HandGrabber.IsGrabbing && Inputs.GetForceGrabActive(HandSide) && HoverTarget && !HoverTarget.IsBeingForcedGrabbed && !HoverTarget.IsBeingHeld)
|
||||
{
|
||||
IsAiming = true;
|
||||
return;
|
||||
}
|
||||
|
||||
IsAiming = false;
|
||||
base.CheckUnHover();
|
||||
}
|
||||
|
||||
public override bool CanGrab(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!grabbable.CanHandGrab(HandGrabber))
|
||||
return false;
|
||||
|
||||
if (grabbable.IsSocketed)
|
||||
return false;
|
||||
|
||||
if (!grabbable.ForceGrabbable || grabbable.IsBeingForcedGrabbed || grabbable.IsBeingHeld)
|
||||
return false;
|
||||
|
||||
if (HandGrabber.IsGrabbing || HandGrabber.IsHovering || HandGrabber.IsHoveringSocket)
|
||||
return false;
|
||||
|
||||
if (!grabbable.Rigidbody)
|
||||
return false;
|
||||
|
||||
if (RequireLineOfSight && !CheckForLineOfSight(
|
||||
RaycastOrigin ? RaycastOrigin.position : transform.position,
|
||||
grabbable,
|
||||
UseHandLayerMask ? HandGrabber.RaycastLayermask : RaycastLayermask, MaxRayCastDistance, UseClosestPoint))
|
||||
return false;
|
||||
|
||||
return base.CanGrab(grabbable);
|
||||
}
|
||||
|
||||
public override bool CanHover(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!CanGrab(grabbable))
|
||||
return false;
|
||||
return base.CanHover(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void OnGrabbedHaptics()
|
||||
{
|
||||
if (IsMine && HVRInputManager.Instance.GrabHaptics)
|
||||
{
|
||||
HandGrabber.Controller.Vibrate(HVRInputManager.Instance.GrabHaptics.ForceGrab);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnGrabbed(HVRGrabArgs args)
|
||||
{
|
||||
//Debug.Log($"force grabbed!");
|
||||
base.OnGrabbed(args);
|
||||
|
||||
if (_additionalGrabRoutine != null)
|
||||
{
|
||||
StopCoroutine(_additionalGrabRoutine);
|
||||
}
|
||||
|
||||
if (HandGrabber.HandAnimator)
|
||||
{
|
||||
if (GrabPoser)
|
||||
{
|
||||
HandGrabber.SetAnimatorPose(GrabPoser);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetAnimator();
|
||||
}
|
||||
}
|
||||
|
||||
args.Grabbable.IsBeingForcedGrabbed = true;
|
||||
IsForceGrabbing = true;
|
||||
if (GrabStyle == HVRForceGrabMode.GravityGloves)
|
||||
{
|
||||
StartCoroutine(GravityGloves(args.Grabbable));
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckForceAnchor();
|
||||
StartCoroutine(ForcePull(args.Grabbable));
|
||||
}
|
||||
|
||||
Grabbed.Invoke(this, args.Grabbable);
|
||||
args.Grabbable.Collided.AddListener(OnGrabbableCollided);
|
||||
args.Grabbable.Grabbed.AddListener(OnGrabbableGrabbed);
|
||||
|
||||
if (SFXGrab)
|
||||
if (SFXPlayer.Instance)
|
||||
SFXPlayer.Instance.PlaySFX(SFXGrab, transform.position);
|
||||
|
||||
OnGrabbedHaptics();
|
||||
}
|
||||
|
||||
protected override void OnReleased(HVRGrabbable grabbable)
|
||||
{
|
||||
base.OnReleased(grabbable);
|
||||
|
||||
grabbable.IsBeingForcedGrabbed = false;
|
||||
}
|
||||
|
||||
protected override void OnHoverEnter(HVRGrabbable grabbable)
|
||||
{
|
||||
base.OnHoverEnter(grabbable);
|
||||
|
||||
OnHoverHaptics();
|
||||
|
||||
if (grabbable.ShowForceGrabIndicator)
|
||||
{
|
||||
if (grabbable.ForceGrabIndicator)
|
||||
{
|
||||
_grabIndicator = grabbable.ForceGrabIndicator;
|
||||
}
|
||||
else
|
||||
{
|
||||
_grabIndicator = GrabIndicator;
|
||||
}
|
||||
|
||||
if (_grabIndicator)
|
||||
{
|
||||
_grabIndicator.Enable();
|
||||
_grabIndicator.Hover();
|
||||
}
|
||||
}
|
||||
|
||||
if (HoverPoser)
|
||||
{
|
||||
HandGrabber.SetAnimatorPose(HoverPoser);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnHoverHaptics()
|
||||
{
|
||||
if (IsMine && HVRInputManager.Instance.GrabHaptics)
|
||||
{
|
||||
HandGrabber.Controller.Vibrate(HVRInputManager.Instance.GrabHaptics.ForceHover);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHoverExit(HVRGrabbable grabbable)
|
||||
{
|
||||
base.OnHoverExit(grabbable);
|
||||
|
||||
if (_grabIndicator)
|
||||
{
|
||||
_grabIndicator.Unhover();
|
||||
_grabIndicator.Disable();
|
||||
}
|
||||
|
||||
if (!IsGrabbing)
|
||||
{
|
||||
ResetAnimator();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetAnimator()
|
||||
{
|
||||
if (HandGrabber.HandAnimator)
|
||||
{
|
||||
if (GrabPoser && HandGrabber.HandAnimator.CurrentPoser == GrabPoser || HoverPoser && HandGrabber.HandAnimator.CurrentPoser == HoverPoser)
|
||||
{
|
||||
HandGrabber.HandAnimator.ResetToDefault();
|
||||
HandGrabber.ResetCloneAnimator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator ForcePull(HVRGrabbable grabbable)
|
||||
{
|
||||
var rb = grabbable.Rigidbody;
|
||||
var angularDrag = rb.angularDrag;
|
||||
var com = rb.centerOfMass;
|
||||
var drag = rb.drag;
|
||||
|
||||
HandGrabber.DisableHandCollision(grabbable);
|
||||
|
||||
rb.angularDrag = 0f;
|
||||
rb.drag = 0f;
|
||||
|
||||
IsHoldActive = true;
|
||||
|
||||
var grabPoint = grabbable.GetGrabPointTransform(HandGrabber, GrabpointFilter.ForceGrab);
|
||||
if (!grabPoint)
|
||||
grabPoint = grabbable.transform;
|
||||
|
||||
var posableGrabPoint = grabPoint.GetComponent<HVRPosableGrabPoint>();
|
||||
|
||||
var isPhysicsGrab = grabbable.PoseType == PoseType.PhysicPoser;
|
||||
if (!isPhysicsGrab && grabbable.PoseType != PoseType.Offset)
|
||||
{
|
||||
isPhysicsGrab = !posableGrabPoint && grabbable.PhysicsPoserFallback;
|
||||
}
|
||||
|
||||
var settings = grabbable.ForcePullOverride;
|
||||
if (!settings)
|
||||
settings = ForcePullSettings;
|
||||
|
||||
var SlerpDamper = settings.SlerpDamper;
|
||||
var SlerpMaxForce = settings.SlerpMaxForce;
|
||||
var SlerpSpring = settings.SlerpSpring;
|
||||
|
||||
var DynamicGrabThreshold = settings.DynamicGrabThreshold;
|
||||
var DistanceThreshold = settings.DistanceThreshold;
|
||||
var Speed = settings.MaxSpeed;
|
||||
var DistanceToRotate = settings.RotateTriggerDistance;
|
||||
var RotateOverDistance = settings.RotateOverDistance;
|
||||
|
||||
var relativeAnchor = grabbable.transform.InverseTransformPoint(grabPoint.transform.position);
|
||||
if (posableGrabPoint)
|
||||
{
|
||||
relativeAnchor = HandGrabber.GetAnchorInGrabbableSpace(grabbable, posableGrabPoint);
|
||||
rb.centerOfMass = relativeAnchor;
|
||||
}
|
||||
|
||||
var grabbableAnchor = grabbable.transform.TransformPoint(relativeAnchor);
|
||||
|
||||
_anchor.SetPositionAndRotation(grabbableAnchor, posableGrabPoint ? posableGrabPoint.GetPoseWorldRotation(HandSide) : grabbable.transform.rotation);
|
||||
|
||||
var joint = _anchor.gameObject.AddComponent<ConfigurableJoint>();
|
||||
joint.autoConfigureConnectedAnchor = false;
|
||||
joint.rotationDriveMode = RotationDriveMode.Slerp;
|
||||
joint.SetSlerpDrive(SlerpSpring, SlerpDamper, SlerpMaxForce);
|
||||
joint.connectedBody = rb;
|
||||
joint.connectedAnchor = rb.transform.InverseTransformPoint(grabbableAnchor);
|
||||
joint.anchor = Vector3.zero;
|
||||
|
||||
var limit = isPhysicsGrab ? DynamicGrabThreshold : DistanceThreshold;
|
||||
|
||||
|
||||
var rotating = false;
|
||||
var rotateSpeed = 0f;
|
||||
var elapsed = 0f;
|
||||
var needsRotating = posableGrabPoint;
|
||||
|
||||
if (SlowMo) Time.timeScale = TimeScale;
|
||||
|
||||
var startDistance = Vector3.Distance(HandGrabber.JointAnchorWorldPosition, grabbableAnchor);
|
||||
var distance = startDistance;
|
||||
|
||||
while (GrabbedTarget && Inputs.GetForceGrabActive(HandSide) && distance > limit)
|
||||
{
|
||||
elapsed += Time.fixedDeltaTime;
|
||||
|
||||
grabbableAnchor = grabbable.transform.TransformPoint(relativeAnchor);
|
||||
var delta = JointAnchorWorldPosition - grabbableAnchor;
|
||||
distance = delta.magnitude;
|
||||
|
||||
if (isPhysicsGrab && distance < DynamicGrabThreshold && HandGrabber.TryTransferDistanceGrab(grabbable, posableGrabPoint))
|
||||
{
|
||||
rb.angularVelocity = Vector3.zero;
|
||||
rb.velocity = Vector3.zero;
|
||||
break;
|
||||
}
|
||||
|
||||
var invDt = 1f / Time.fixedDeltaTime;
|
||||
var targetVel = HandGrabber.Rigidbody.velocity;
|
||||
var velocity = rb.velocity;
|
||||
var mass = rb.mass;
|
||||
|
||||
var desiredVel = Vector3.ClampMagnitude(delta * invDt, settings.MaxSpeed);
|
||||
|
||||
var desiredForce = (desiredVel - (velocity - targetVel)) * (invDt * mass);
|
||||
var force = Vector3.ClampMagnitude(desiredForce, settings.MaxAccelerationForce);
|
||||
|
||||
var dampMulti = (settings.DampDistance - Mathf.Clamp(distance, 0f, settings.DampDistance)) / settings.DampDistance;
|
||||
var gravityForce = rb.useGravity ? -Physics.gravity * (mass * settings.CounterGravityFactor) : Vector3.zero;
|
||||
|
||||
rb.AddForce(force + gravityForce);
|
||||
rb.velocity = Vector3.Lerp(velocity, targetVel, dampMulti * settings.DampSpeed * Time.fixedDeltaTime);
|
||||
|
||||
if (needsRotating && !rotating)
|
||||
{
|
||||
if (settings.RotationTrigger == ForcePullRotationTrigger.DistanceToHand)
|
||||
{
|
||||
rotating = distance < DistanceToRotate && posableGrabPoint;
|
||||
}
|
||||
else if (settings.RotationTrigger == ForcePullRotationTrigger.PercentTraveled)
|
||||
{
|
||||
rotating = (startDistance - distance) / startDistance > settings.RotateTriggerPercent / 100f;
|
||||
}
|
||||
else if (settings.RotationTrigger == ForcePullRotationTrigger.TimeSinceStart)
|
||||
{
|
||||
rotating = elapsed > settings.RotateTriggerTime;
|
||||
}
|
||||
|
||||
if (rotating)
|
||||
{
|
||||
if (settings.RotationStyle == ForceRotationStyle.RotateOverDistance)
|
||||
{
|
||||
var rotatateDistance = Mathf.Min(RotateOverDistance, distance);
|
||||
var time = rotatateDistance / Speed;
|
||||
rotateSpeed = Quaternion.Angle(joint.transform.rotation, HandGrabber.CachedWorldRotation) / time;
|
||||
}
|
||||
else if (settings.RotationStyle == ForceRotationStyle.RotateOverRemaining)
|
||||
{
|
||||
var time = distance / Speed;
|
||||
rotateSpeed = Quaternion.Angle(joint.transform.rotation, HandGrabber.CachedWorldRotation) / time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rotating)
|
||||
{
|
||||
_anchor.rotation = Quaternion.RotateTowards(_anchor.rotation, HandGrabber.CachedWorldRotation, rotateSpeed * Time.fixedDeltaTime);
|
||||
}
|
||||
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
|
||||
if (SlowMo) Time.timeScale = 1f;
|
||||
ResetAnimator();
|
||||
|
||||
|
||||
joint.connectedBody = null;
|
||||
Destroy(joint);
|
||||
|
||||
IsForceGrabbing = false;
|
||||
IsHoldActive = false;
|
||||
|
||||
if (grabbable)
|
||||
{
|
||||
rb.angularDrag = angularDrag;
|
||||
rb.drag = drag;
|
||||
rb.velocity = Vector3.ClampMagnitude(rb.velocity, settings.MaxMissSpeed);
|
||||
rb.angularVelocity = Vector3.ClampMagnitude(rb.angularVelocity, settings.MaxMissAngularSpeed);
|
||||
rb.centerOfMass = com;
|
||||
|
||||
|
||||
if (IsGrabbing)
|
||||
{
|
||||
if (distance < limit)
|
||||
{
|
||||
if (HandGrabber.TryTransferDistanceGrab(grabbable, posableGrabPoint))
|
||||
{
|
||||
rb.angularVelocity = Vector3.zero;
|
||||
rb.velocity = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
HandGrabber.EnableHandCollision(grabbable);
|
||||
ForceRelease();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HandGrabber.EnableHandCollision(grabbable);
|
||||
ForceRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IEnumerator GravityGloves(HVRGrabbable grabbable)
|
||||
{
|
||||
var grabbed = false;
|
||||
var grabPoint = grabbable.GetGrabPointTransform(HandGrabber, GrabpointFilter.ForceGrab);
|
||||
if (!grabPoint)
|
||||
grabPoint = grabbable.transform;
|
||||
|
||||
var posableGrabPoint = grabPoint.GetComponent<HVRPosableGrabPoint>();
|
||||
|
||||
var rb = grabbable.Rigidbody;
|
||||
var drag = rb.drag;
|
||||
var angularDrag = rb.angularDrag;
|
||||
var useGrav = rb.useGravity;
|
||||
|
||||
try
|
||||
{
|
||||
HandGrabber.DisableHandCollision(grabbable);
|
||||
|
||||
_grabbableCollided = false;
|
||||
IsHoldActive = true;
|
||||
|
||||
|
||||
grabbable.Rigidbody.useGravity = false;
|
||||
grabbable.Rigidbody.drag = 0f;
|
||||
grabbable.Rigidbody.angularDrag = 0f;
|
||||
|
||||
fts.solve_ballistic_arc_lateral(false,
|
||||
grabPoint.position,
|
||||
ForceTime,
|
||||
JointAnchorWorldPosition,
|
||||
JointAnchorWorldPosition.y + YOffset,
|
||||
out var velocity,
|
||||
out var gravity);
|
||||
|
||||
grabbable.Rigidbody.velocity = velocity;
|
||||
|
||||
var elapsed = 0f;
|
||||
|
||||
_anchor.position = grabbable.transform.position;
|
||||
_anchor.rotation = grabbable.transform.rotation;
|
||||
|
||||
if (posableGrabPoint)
|
||||
{
|
||||
_anchor.rotation = posableGrabPoint.GetPoseWorldRotation(HandSide);
|
||||
}
|
||||
|
||||
var joint = _anchor.gameObject.AddComponent<ConfigurableJoint>();
|
||||
joint.autoConfigureConnectedAnchor = false;
|
||||
joint.rotationDriveMode = RotationDriveMode.Slerp;
|
||||
joint.SetLinearDrive(0f, 0f, 0f);
|
||||
joint.SetSlerpDrive(10000f, 100f, 10000f);
|
||||
joint.connectedBody = rb;
|
||||
joint.connectedAnchor = rb.transform.InverseTransformPoint(grabPoint.position);
|
||||
|
||||
var rotating = false;
|
||||
var rotateSpeed = 0f;
|
||||
var needsRotating = posableGrabPoint;
|
||||
|
||||
while (GrabbedTarget)
|
||||
{
|
||||
if (elapsed > ForceTime)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var currentVector = JointAnchorWorldPosition - grabPoint.position;
|
||||
|
||||
currentVector.y = 0;
|
||||
var distance = currentVector.magnitude;
|
||||
|
||||
var percentTime = elapsed / ForceTime;
|
||||
var yExtra = YOffset * (1 - percentTime);
|
||||
|
||||
if (percentTime < .3) _grabbableCollided = false;
|
||||
else if (_grabbableCollided)
|
||||
{
|
||||
if (grabbable.Rigidbody.velocity.magnitude > MaximumVelocityPostCollision)
|
||||
grabbable.Rigidbody.velocity = grabbable.Rigidbody.velocity.normalized * MaximumVelocityPostCollision;
|
||||
ForceRelease();
|
||||
//Debug.Log($"Collided while force grabbing.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (AutoGrab && HandGrabber.IsValidGrabbable(GrabbedTarget) && HandGrabber.TryTransferDistanceGrab(GrabbedTarget, posableGrabPoint))
|
||||
{
|
||||
grabbed = true;
|
||||
IsForceGrabbing = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (AutoGrab && (JointAnchorWorldPosition - grabPoint.position).magnitude < AutoGrabDistance)
|
||||
{
|
||||
if (HandGrabber.TryTransferDistanceGrab(GrabbedTarget, posableGrabPoint))
|
||||
{
|
||||
grabbed = true;
|
||||
IsForceGrabbing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (distance < .1f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fts.solve_ballistic_arc_lateral(
|
||||
false,
|
||||
grabPoint.position,
|
||||
ForceTime - elapsed,
|
||||
JointAnchorWorldPosition,
|
||||
JointAnchorWorldPosition.y + yExtra,
|
||||
out velocity, out gravity);
|
||||
|
||||
grabbable.Rigidbody.velocity = velocity;
|
||||
grabbable.Rigidbody.AddForce(-Vector3.up * gravity, ForceMode.Acceleration);
|
||||
|
||||
if (needsRotating)
|
||||
{
|
||||
if (!rotating && percentTime > .3f)
|
||||
{
|
||||
var time = distance / velocity.magnitude;
|
||||
rotateSpeed = Quaternion.Angle(joint.transform.rotation, HandGrabber.CachedWorldRotation) / time;
|
||||
rotating = true;
|
||||
}
|
||||
|
||||
if (rotating)
|
||||
{
|
||||
joint.transform.rotation = Quaternion.RotateTowards(joint.transform.rotation, HandGrabber.CachedWorldRotation, rotateSpeed * Time.fixedDeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.fixedDeltaTime;
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
|
||||
joint.connectedBody = null;
|
||||
Destroy(joint);
|
||||
|
||||
ResetAnimator();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (grabbed)
|
||||
{
|
||||
rb.angularVelocity = Vector3.zero;
|
||||
rb.velocity = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
//hand grabber disables collision
|
||||
HandGrabber.EnableHandCollision(grabbable);
|
||||
}
|
||||
|
||||
IsHoldActive = false;
|
||||
if (grabbable)
|
||||
{
|
||||
rb.useGravity = useGrav;
|
||||
rb.drag = drag;
|
||||
rb.angularDrag = angularDrag;
|
||||
grabbable.Collided.RemoveListener(OnGrabbableCollided);
|
||||
grabbable.Grabbed.RemoveListener(OnGrabbableGrabbed);
|
||||
}
|
||||
|
||||
if (IsGrabbing)
|
||||
{
|
||||
if (AutoGrab && AdditionalAutoGrabTime > 0)
|
||||
{
|
||||
_additionalGrabRoutine = StartCoroutine(ContinueAutoGrab(grabbable, posableGrabPoint));
|
||||
}
|
||||
else
|
||||
{
|
||||
ForceRelease();
|
||||
}
|
||||
}
|
||||
|
||||
IsForceGrabbing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ContinueAutoGrab(HVRGrabbable grabbable, HVRPosableGrabPoint grabPoint)
|
||||
{
|
||||
HandGrabber.DisableHandCollision(grabbable);
|
||||
|
||||
var t = grabPoint ? grabPoint.transform : grabbable.transform;
|
||||
var grabbed = false;
|
||||
var elapsed = 0f;
|
||||
while (grabbable && elapsed < AdditionalAutoGrabTime && !grabbable.IsBeingHeld)
|
||||
{
|
||||
if (grabbable.Rigidbody.velocity.magnitude > MaximumVelocityAutoGrab)
|
||||
grabbable.Rigidbody.velocity *= .9f;
|
||||
|
||||
|
||||
if ((JointAnchorWorldPosition - t.position).magnitude < AutoGrabDistance)
|
||||
{
|
||||
if (HandGrabber.TryTransferDistanceGrab(grabbable, grabPoint))
|
||||
{
|
||||
grabbable.Rigidbody.angularVelocity = Vector3.zero;
|
||||
grabbable.Rigidbody.velocity = Vector3.zero;
|
||||
grabbed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
elapsed += Time.fixedDeltaTime;
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
|
||||
if (!grabbed)
|
||||
{
|
||||
HandGrabber.EnableHandCollision(grabbable);
|
||||
ForceRelease();
|
||||
}
|
||||
|
||||
_additionalGrabRoutine = null;
|
||||
}
|
||||
|
||||
private void OnGrabbableGrabbed(HVRGrabberBase arg0, HVRGrabbable grabbable)
|
||||
{
|
||||
//Debug.Log($"Grabbed while force grabbing.");
|
||||
}
|
||||
|
||||
private void OnGrabbableCollided(HVRGrabbable g)
|
||||
{
|
||||
_grabbableCollided = true;
|
||||
}
|
||||
|
||||
private void UpdateGrabIndicator()
|
||||
{
|
||||
if (!IsHovering || !_grabIndicator || !HoverTarget.ShowForceGrabIndicator)
|
||||
return;
|
||||
|
||||
if (_grabIndicator.LookAtCamera && HVRManager.Instance.Camera)
|
||||
{
|
||||
_grabIndicator.transform.LookAt(HVRManager.Instance.Camera);
|
||||
}
|
||||
|
||||
if (_grabIndicator.HoverPosition == HVRHoverPosition.Self)
|
||||
return;
|
||||
|
||||
var grabPoint = HoverTarget.GetGrabPointTransform(HandGrabber, GrabpointFilter.ForceGrab);
|
||||
|
||||
var position = HoverTarget.transform.position;
|
||||
if (grabPoint && _grabIndicator.HoverPosition == HVRHoverPosition.GrabPoint)
|
||||
{
|
||||
position = HandGrabber.GetGrabIndicatorPosition(HoverTarget, grabPoint, true);
|
||||
}
|
||||
|
||||
_grabIndicator.transform.position = position;
|
||||
}
|
||||
}
|
||||
|
||||
public enum HVRForceGrabMode
|
||||
{
|
||||
GravityGloves,
|
||||
ForcePull
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82a1b90bd6e7417ca9833e08bd4a1ec3
|
||||
timeCreated: 1598243548
|
||||
@@ -0,0 +1,643 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Core.Bags;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Grabbers
|
||||
{
|
||||
public abstract class HVRGrabberBase : MonoBehaviour
|
||||
{
|
||||
public VRGrabberEvent BeforeGrabbed = new VRGrabberEvent();
|
||||
public VRGrabberEvent Grabbed = new VRGrabberEvent();
|
||||
public VRGrabberEvent Released = new VRGrabberEvent();
|
||||
public VRGrabberEvent BeforeHoverEnter = new VRGrabberEvent();
|
||||
public VRGrabberEvent HoverEnter = new VRGrabberEvent();
|
||||
public VRGrabberEvent HoverExit = new VRGrabberEvent();
|
||||
|
||||
|
||||
[Header("Grabbable Bags")]
|
||||
#pragma warning disable CS0649
|
||||
[SerializeField]
|
||||
private HVRGrabbableBag _grabBag;
|
||||
#pragma warning restore CS0649
|
||||
public List<HVRGrabbableBag> GrabBags = new List<HVRGrabbableBag>();
|
||||
|
||||
public virtual Quaternion ControllerRotation { get; set; } = Quaternion.identity;
|
||||
|
||||
public bool AllowHovering { get; set; }
|
||||
public virtual bool AllowGrabbing { get; set; }
|
||||
public bool IsGrabbing { get; private set; }
|
||||
public bool IsHovering { get; private set; }
|
||||
|
||||
public HVRGrabbable HoverTarget
|
||||
{
|
||||
get => _hoveredTarget;
|
||||
internal set
|
||||
{
|
||||
_hoveredTarget = value;
|
||||
IsHovering = value;
|
||||
}
|
||||
}
|
||||
|
||||
public HVRGrabbable HeldObject => GrabbedTarget;
|
||||
|
||||
public HVRGrabbable GrabbedTarget
|
||||
{
|
||||
get => _grabbedTarget;
|
||||
protected set
|
||||
{
|
||||
_grabbedTarget = value;
|
||||
IsGrabbing = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsGrabActivated { get; protected set; }
|
||||
|
||||
public virtual bool IsHoldActive { get; protected set; }
|
||||
|
||||
public virtual bool IsHandGrabber => false;
|
||||
public virtual bool IsSocket => false;
|
||||
|
||||
public virtual bool IsDistanceGrabber => false;
|
||||
|
||||
public virtual bool AllowSwap => false;
|
||||
|
||||
public Rigidbody Rigidbody { get; protected set; }
|
||||
|
||||
protected Transform _grabPoint;
|
||||
|
||||
public virtual Transform GrabPoint
|
||||
{
|
||||
get => _grabPoint;
|
||||
set { _grabPoint = value; }
|
||||
}
|
||||
|
||||
|
||||
public virtual Vector3 JointAnchorWorldPosition { get; }
|
||||
|
||||
public virtual bool IsMine { get; set; } = true;
|
||||
public virtual bool PerformUpdate { get; set; } = true;
|
||||
|
||||
public bool PullingGrabbable { get; set; }
|
||||
|
||||
private HVRGrabbable _grabbedTarget;
|
||||
private HVRGrabbable _hoveredTarget;
|
||||
private Ray _lineOfSightRay;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
HVRManager.Instance?.UnregisterGrabber(this);
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
HVRManager.Instance?.UnregisterGrabber(this);
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Rigidbody = GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
HVRManager.Instance?.RegisterGrabber(this);
|
||||
|
||||
AllowGrabbing = true;
|
||||
AllowHovering = true;
|
||||
|
||||
if (_grabBag)
|
||||
{
|
||||
if (!GrabBags.Contains(_grabBag))
|
||||
{
|
||||
GrabBags.Add(_grabBag);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bag in GrabBags)
|
||||
{
|
||||
bag.Grabber = this;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (PerformUpdate)
|
||||
{
|
||||
CheckUnHover();
|
||||
CheckRelease();
|
||||
CheckHover();
|
||||
CheckGrab();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void FixedUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected virtual void CheckRelease()
|
||||
{
|
||||
if (IsGrabbing)
|
||||
{
|
||||
if (!IsHoldActive)
|
||||
ReleaseGrabbable(this, GrabbedTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ForceRelease()
|
||||
{
|
||||
//Debug.Log("Force Releasing.");
|
||||
if (IsGrabbing)
|
||||
{
|
||||
ReleaseGrabbable(this, GrabbedTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log("Nothing to force release.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this object should be released from it's existing grabber prior to be grabbed by another.
|
||||
/// </summary>
|
||||
protected virtual bool CheckSwapReleaseRequired(HVRGrabbable grabbable)
|
||||
{
|
||||
return grabbable.IsBeingForcedGrabbed || grabbable.PrimaryGrabber && grabbable.PrimaryGrabber.AllowSwap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will check if this object should be released from it's primary grabber and release it if so.
|
||||
/// </summary>
|
||||
protected virtual void CheckSwapRelease(HVRGrabbable grabbable)
|
||||
{
|
||||
if (CheckSwapReleaseRequired(grabbable))
|
||||
SwapRelease(grabbable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the grabbable from it's current grabber, this is prior to being grabbed by another grabber.
|
||||
/// </summary>
|
||||
protected virtual void SwapRelease(HVRGrabbable grabbable)
|
||||
{
|
||||
grabbable.PrimaryGrabber.ForceRelease();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the release sequence on the provided grabber and grabbable.
|
||||
/// </summary>
|
||||
/// <param name="raiseEvents">If true the Released Unity events on the grabber and grabbable will execute.</param>
|
||||
public static void ReleaseGrabbable(HVRGrabberBase grabber, HVRGrabbable grabbable, bool raiseEvents = true, bool isHandSwap = false)
|
||||
{
|
||||
grabber.OnReleased(grabbable);
|
||||
grabbable.InternalOnReleased(grabber);
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
grabbable.Released.Invoke(grabber, grabbable);
|
||||
if (grabber.IsHandGrabber)
|
||||
{
|
||||
grabbable.HandReleased.Invoke(grabber as HVRHandGrabber, grabbable);
|
||||
if (grabbable.Grabbers.Count == 0 && !isHandSwap)
|
||||
{
|
||||
grabbable.HandFullReleased.Invoke(grabber as HVRHandGrabber, grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
if (grabber.IsSocket)
|
||||
{
|
||||
grabbable.UnSocketed.Invoke(grabber as HVRSocket, grabbable);
|
||||
}
|
||||
else if (grabber.IsDistanceGrabber)
|
||||
{
|
||||
grabbable.DistanceReleased.Invoke(new HVRDistanceReleaseArgs(grabber as HVRForceGrabber, grabbable, grabbable.SwappingDistanceToHand));
|
||||
}
|
||||
}
|
||||
|
||||
grabbable.SwappingDistanceToHand = false;
|
||||
}
|
||||
|
||||
public virtual bool IsValidGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
for (var i = 0; i < GrabBags.Count; i++)
|
||||
{
|
||||
var bag = GrabBags[i];
|
||||
if (bag.ValidGrabbables.Contains(grabbable))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest grabbable in the grabbable bag.
|
||||
/// </summary>
|
||||
public virtual HVRGrabbable GetClosestGrabbable()
|
||||
{
|
||||
for (var i = 0; i < GrabBags.Count; i++)
|
||||
{
|
||||
var bag = GrabBags[i];
|
||||
if (bag.ClosestGrabbable)
|
||||
return bag.ClosestGrabbable;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest grabbable in the grabbable bag that satisfies canGrab delegate.
|
||||
/// </summary>
|
||||
public virtual HVRGrabbable GetClosestGrabbable(Predicate<HVRGrabbable> canGrab)
|
||||
{
|
||||
for (var i = 0; i < GrabBags.Count; i++)
|
||||
{
|
||||
var bag = GrabBags[i];
|
||||
if (bag.ClosestGrabbable && canGrab(bag.ClosestGrabbable))
|
||||
return bag.ClosestGrabbable;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual void CheckGrab()
|
||||
{
|
||||
if (!IsGrabActivated || !AllowGrabbing || IsGrabbing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var g = 0; g < GrabBags.Count; g++)
|
||||
{
|
||||
var grabBag = GrabBags[g];
|
||||
for (var i = 0; i < grabBag.ValidGrabbables.Count; i++)
|
||||
{
|
||||
var grabbable = grabBag.ValidGrabbables[i];
|
||||
if (TryGrab(grabbable))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual bool TryGrab(HVRGrabbable grabbable, bool force = false)
|
||||
{
|
||||
if (force || CanGrab(grabbable))
|
||||
{
|
||||
GrabGrabbable(this, grabbable);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TryGrabNoEvents(HVRGrabbable grabbable, bool force = false)
|
||||
{
|
||||
if (force || CanGrab(grabbable))
|
||||
{
|
||||
GrabGrabbable(this, grabbable, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void GrabGrabbable(HVRGrabberBase grabber, HVRGrabbable grabbable, bool raiseEvents = true)
|
||||
{
|
||||
CheckSwapRelease(grabbable);
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
grabber.BeforeGrabbed.Invoke(grabber, grabbable);
|
||||
}
|
||||
|
||||
grabbable.InternalOnBeforeGrabbed(grabber);
|
||||
var args = new HVRGrabArgs(grabbable);
|
||||
args.RaiseEvents = raiseEvents;
|
||||
|
||||
|
||||
grabber.OnBeforeGrabbed(args);
|
||||
|
||||
if (args.Cancel)
|
||||
{
|
||||
grabbable.InternalOnGrabCanceled(grabber);
|
||||
return;
|
||||
}
|
||||
|
||||
grabber.GrabbedTarget = grabbable;
|
||||
grabber.OnGrabbed(args);
|
||||
|
||||
if (args.Cancel)
|
||||
{
|
||||
grabber.GrabbedTarget = null;
|
||||
grabbable.InternalOnGrabCanceled(grabber);
|
||||
}
|
||||
else
|
||||
{
|
||||
grabbable.InternalOnGrabbed(grabber);
|
||||
grabber.InternalOnAfterGrabbed(grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void InternalOnGrabbed(HVRGrabArgs args)
|
||||
{
|
||||
OnGrabbed(args);
|
||||
}
|
||||
|
||||
protected virtual void OnBeforeGrabbed(HVRGrabArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnGrabbed(HVRGrabArgs args)
|
||||
{
|
||||
args.Grabbable.Destroyed.AddListener(OnGrabbableDestroyed);
|
||||
}
|
||||
|
||||
internal virtual void InternalOnAfterGrabbed(HVRGrabbable grabbable)
|
||||
{
|
||||
OnAfterGrabbed(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void OnAfterGrabbed(HVRGrabbable grabbable)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void CheckUnHover()
|
||||
{
|
||||
if (!HoverTarget)
|
||||
return;
|
||||
|
||||
var closestValid = ClosestValidHover();
|
||||
|
||||
if (!CanHover(HoverTarget) || closestValid != HoverTarget)
|
||||
{
|
||||
UnhoverGrabbable(this, HoverTarget);
|
||||
}
|
||||
}
|
||||
|
||||
protected HVRGrabbable ClosestValidHover(bool triggerOnly)
|
||||
{
|
||||
for (var g = 0; g < GrabBags.Count; g++)
|
||||
{
|
||||
var grabBag = GrabBags[g];
|
||||
for (var i = 0; i < grabBag.ValidGrabbables.Count; i++)
|
||||
{
|
||||
var grabbable = grabBag.ValidGrabbables[i];
|
||||
|
||||
if (triggerOnly)
|
||||
{
|
||||
if (grabbable.IsSocketed && grabbable.Socket.GrabDetectionType == HVRGrabDetection.Grabbable)
|
||||
{
|
||||
if (!grabbable.Socket.CanRemoveGrabbable || grabbable.Socket.GrabControl != HVRGrabControls.TriggerOnly)
|
||||
continue;
|
||||
}
|
||||
else if (grabbable.GrabControl != HVRGrabControls.TriggerOnly)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grabbable.IsSocketed && grabbable.Socket.GrabDetectionType == HVRGrabDetection.Grabbable)
|
||||
{
|
||||
if (!grabbable.Socket.CanRemoveGrabbable || grabbable.Socket.GrabControl == HVRGrabControls.TriggerOnly)
|
||||
continue;
|
||||
}
|
||||
else if (grabbable.GrabControl == HVRGrabControls.TriggerOnly)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CanHover(grabbable))
|
||||
{
|
||||
return grabbable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected HVRGrabbable ClosestValidHover()
|
||||
{
|
||||
for (var g = 0; g < GrabBags.Count; g++)
|
||||
{
|
||||
var grabBag = GrabBags[g];
|
||||
for (var i = 0; i < grabBag.ValidGrabbables.Count; i++)
|
||||
{
|
||||
var grabbable = grabBag.ValidGrabbables[i];
|
||||
|
||||
if (CanHover(grabbable))
|
||||
{
|
||||
return grabbable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected virtual bool CheckHover()
|
||||
{
|
||||
if (IsHovering || !AllowHovering)
|
||||
{
|
||||
if (IsHovering && !HoverTarget)
|
||||
{
|
||||
HoverTarget = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var closestValid = ClosestValidHover();
|
||||
if (!closestValid)
|
||||
return false;
|
||||
|
||||
HoverGrabbable(this, closestValid);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected internal virtual void OnBeforeHover(HVRGrabbable grabbable)
|
||||
{
|
||||
}
|
||||
|
||||
protected internal virtual void OnAfterHover(HVRGrabbable grabbable)
|
||||
{
|
||||
}
|
||||
|
||||
protected void HoverGrabbable(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
if (grabber.IsHovering)
|
||||
return;
|
||||
|
||||
OnBeforeHover(grabbable);
|
||||
grabber.BeforeHoverEnter.Invoke(grabber, grabbable);
|
||||
|
||||
grabber.HoverTarget = grabbable;
|
||||
|
||||
grabbable.InternalOnHoverEnter(grabber);
|
||||
grabber.OnHoverEnter(grabbable);
|
||||
grabbable.HoverEnter.Invoke(grabber, grabbable);
|
||||
grabber.HoverEnter.Invoke(grabber, grabbable);
|
||||
}
|
||||
|
||||
protected void UnhoverGrabbable(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (grabbable && grabber)
|
||||
{
|
||||
grabbable.InternalOnHoverExit(grabber);
|
||||
grabbable.HoverExit.Invoke(grabber, grabbable);
|
||||
}
|
||||
|
||||
if (grabber.HoverTarget)
|
||||
{
|
||||
grabber.OnHoverExit(grabbable);
|
||||
grabber.HoverExit.Invoke(grabber, grabbable);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (grabber)
|
||||
grabber.HoverTarget = null;
|
||||
|
||||
OnAfterHover(grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanGrab(HVRGrabbable grabbable)
|
||||
{
|
||||
if (grabbable.MasterGrabbable && grabbable.MasterGrabbable.IsSocketed)
|
||||
return false;
|
||||
return AllowGrabbing && !IsGrabbing;
|
||||
}
|
||||
|
||||
public virtual bool CanHover(HVRGrabbable grabbable)
|
||||
{
|
||||
return AllowHovering;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void OnReleased(HVRGrabbable grabbable)
|
||||
{
|
||||
//Debug.Log($"released {gameObject.name}");
|
||||
GrabbedTarget = null;
|
||||
grabbable.Destroyed.RemoveListener(OnGrabbableDestroyed);
|
||||
}
|
||||
|
||||
protected virtual void OnGrabbableDestroyed(HVRGrabbable grabbable)
|
||||
{
|
||||
grabbable.BeingDestroyed = true;
|
||||
|
||||
if (IsGrabbing)
|
||||
{
|
||||
//Debug.Log($"destroyed grab {gameObject.name}");
|
||||
GrabbedTarget = null;
|
||||
OnReleased(grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHoverGrabbableDestroyed(HVRGrabbable grabbable)
|
||||
{
|
||||
if (IsHovering)
|
||||
{
|
||||
//Debug.Log($"destroyed hover {gameObject.name}");
|
||||
HoverTarget = null;
|
||||
OnHoverExit(grabbable);
|
||||
HoverExit.Invoke(this, grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
protected virtual void OnHoverEnter(HVRGrabbable grabbable)
|
||||
{
|
||||
if (HVRSettings.Instance.VerboseGrabbableEvents)
|
||||
Debug.Log($"OnHoverEnter {gameObject.name} -> {grabbable.name}");
|
||||
grabbable.Destroyed.AddListener(OnHoverGrabbableDestroyed);
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
protected virtual void OnHoverExit(HVRGrabbable grabbable)
|
||||
{
|
||||
if (HVRSettings.Instance.VerboseGrabbableEvents)
|
||||
Debug.Log($"OnHoverExit {gameObject.name}-> {grabbable.name}");
|
||||
grabbable.Destroyed.RemoveListener(OnHoverGrabbableDestroyed);
|
||||
}
|
||||
|
||||
|
||||
public bool CheckForLineOfSight(Vector3 rayOrigin, HVRGrabbable grabbable, LayerMask RaycastLayermask, float rayMaxDistance = .75f, bool useClosestPoint = true)
|
||||
{
|
||||
if (CheckLineOfSight(rayOrigin, grabbable, RaycastLayermask, rayMaxDistance, grabbable.Colliders, QueryTriggerInteraction.Ignore, useClosestPoint))
|
||||
return true;
|
||||
|
||||
if (CheckLineOfSight(rayOrigin, grabbable, RaycastLayermask, rayMaxDistance, grabbable.Triggers, QueryTriggerInteraction.Collide, useClosestPoint))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
private bool CheckLineOfSight(Vector3 rayOrigin, HVRGrabbable grabbable, LayerMask RaycastLayermask, float rayMaxDistance, List<Collider> colliders, QueryTriggerInteraction queryTrigger, bool useClosestPoint = true)
|
||||
{
|
||||
_lineOfSightRay.origin = rayOrigin;
|
||||
|
||||
for (var i = 0; i < colliders.Count; i++)
|
||||
{
|
||||
var grabbableCollider = colliders[i];
|
||||
|
||||
if (!grabbableCollider)
|
||||
continue;
|
||||
|
||||
if (!useClosestPoint || grabbable.HasConcaveColliders && grabbableCollider is MeshCollider meshCollider && !meshCollider.convex ||
|
||||
grabbable.HasWheelCollider && grabbableCollider is WheelCollider)
|
||||
{
|
||||
_lineOfSightRay.direction = grabbableCollider.bounds.center - _lineOfSightRay.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
var closestPoint = grabbableCollider.ClosestPoint(rayOrigin);
|
||||
if (closestPoint == rayOrigin && grabbableCollider.bounds.Contains(rayOrigin))
|
||||
{
|
||||
if (HVRSettings.Instance.VerboseGrabbableEvents)
|
||||
{
|
||||
Debug.Log($"Line of sight origin inside collider.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_lineOfSightRay.direction = closestPoint - _lineOfSightRay.origin;
|
||||
}
|
||||
|
||||
if (Physics.Raycast(_lineOfSightRay, out var hit, rayMaxDistance, RaycastLayermask, queryTrigger))
|
||||
{
|
||||
if (Equals(grabbableCollider, hit.collider))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class HVRGrabArgs
|
||||
{
|
||||
public HVRGrabArgs(HVRGrabbable grabbable)
|
||||
{
|
||||
Grabbable = grabbable;
|
||||
}
|
||||
|
||||
public bool Cancel;
|
||||
public HVRGrabbable Grabbable;
|
||||
public bool RaiseEvents = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59d0d668c7d045f0914e86e73f332a4f
|
||||
timeCreated: 1596310217
|
||||
@@ -0,0 +1,31 @@
|
||||
using Assets.HurricaneVR.Framework.Shared.Utilities;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Grabbers
|
||||
{
|
||||
/// <summary>
|
||||
/// Example code to grab an object in code on start
|
||||
/// </summary>
|
||||
public class HVRHandGrabOnStart : MonoBehaviour
|
||||
{
|
||||
public HVRHandGrabber Grabber;
|
||||
public HVRGrabbable Grabbable;
|
||||
public void Start()
|
||||
{
|
||||
this.ExecuteNextUpdate(() =>
|
||||
{
|
||||
if (Grabbable && Grabber)
|
||||
{
|
||||
Grabber.Grab(Grabbable, HVRGrabTrigger.Toggle);
|
||||
if (!Grabber.GrabbedTarget)
|
||||
{
|
||||
Grabbable.MainTransform.position = Grabber.transform.position;
|
||||
Grabber.TryGrab(Grabbable, true);
|
||||
Grabber.GrabToggleActive = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a9c9943e7c254d4cb39623d0a3d5c36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3278
Assets/HurricaneVR/Framework/Scripts/Core/Grabbers/HVRHandGrabber.cs
Normal file
3278
Assets/HurricaneVR/Framework/Scripts/Core/Grabbers/HVRHandGrabber.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce026d01d1f538044a048be125f5a85e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
834
Assets/HurricaneVR/Framework/Scripts/Core/Grabbers/HVRSocket.cs
Normal file
834
Assets/HurricaneVR/Framework/Scripts/Core/Grabbers/HVRSocket.cs
Normal file
@@ -0,0 +1,834 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Assets.HurricaneVR.Framework.Shared.Utilities;
|
||||
using HurricaneVR.Framework.Core.Bags;
|
||||
using HurricaneVR.Framework.Core.Sockets;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Grabbers
|
||||
{
|
||||
public class HVRSocket : HVRGrabberBase
|
||||
{
|
||||
[Header("Grab Settings")]
|
||||
public HVRGrabControls GrabControl = HVRGrabControls.GripOrTrigger;
|
||||
|
||||
public HVRGrabDetection GrabDetectionType = HVRGrabDetection.Socket;
|
||||
|
||||
[Tooltip("If true the hand socket detector must have detected this socket to be placed as well.")]
|
||||
public bool CheckHandOverlap;
|
||||
|
||||
[Tooltip("Releases the current grabbable if another valid one is in range")]
|
||||
public bool ReleasesOnHover;
|
||||
|
||||
[Tooltip("If true, the hand grabbing an object out of this will instantly bring the object to pose orientation.")]
|
||||
public bool InstantHandPose;
|
||||
|
||||
public SocketHoldType HoldType = SocketHoldType.Kinematic;
|
||||
|
||||
[Tooltip("If supplied, this object will be cloned when one is removed.")]
|
||||
public GameObject AutoSpawnPrefab;
|
||||
|
||||
[Tooltip("If > 0 the last object released cannot be grabbed again until the timeout is reached")]
|
||||
public float GrabTimeout;
|
||||
|
||||
[Tooltip("If true item's must be placed with a hand grabber.")]
|
||||
public bool GrabbableMustBeHeld = true;
|
||||
|
||||
[Tooltip("If true will snatch from a hand on hover.")]
|
||||
public bool GrabsFromHand;
|
||||
|
||||
[Tooltip("Actions to apply when the socket is being hovered by a grabbable. Auto populates if empty")]
|
||||
public HVRSocketHoverAction[] HoverActions;
|
||||
|
||||
[Tooltip("Actions to apply when the socket is being hovered by a hand.")]
|
||||
public HVRSocketHoverAction[] HandGrabActions;
|
||||
|
||||
[Tooltip("If parent grabbable is socketed, disable grabbing.")]
|
||||
public bool ParentDisablesGrab;
|
||||
|
||||
[Tooltip("Parent grabbable used with ParentDisablesGrab.")]
|
||||
public HVRGrabbable ParentGrabbable;
|
||||
|
||||
[Tooltip("If false then you can't remove the grabbable via hand grab.")]
|
||||
public bool CanRemoveGrabbable = true;
|
||||
|
||||
[Tooltip("Scales the grabbable down to fit based on Size and the model bounds.")]
|
||||
public bool ScaleGrabbable;
|
||||
|
||||
[Tooltip("Grabbable scales down to this size along its longest extent.")]
|
||||
public float Size;
|
||||
|
||||
[Tooltip("If the grabbable stabber is stabbing something, can this socket grab it ?")]
|
||||
public bool CanGrabStabbingGrabbable;
|
||||
|
||||
[Header("SFX")]
|
||||
[Tooltip("Prioritized SFX to play for anything socketed")]
|
||||
public AudioClip AudioGrabbedOverride;
|
||||
|
||||
[Tooltip("Prioritized SFX to play for anything released")]
|
||||
public AudioClip AudioReleasedOverride;
|
||||
|
||||
[Tooltip("Fallback grabbed sfx to play if the socketable doesn't have one.")]
|
||||
public AudioClip AudioGrabbedFallback;
|
||||
|
||||
[Tooltip("Fallback released sfx to play if the socketable doesn't have one.")]
|
||||
public AudioClip AudioReleasedFallback;
|
||||
|
||||
|
||||
[Header("Socketable Filtering")]
|
||||
[Tooltip("Filters to filter out socketables.")]
|
||||
public HVRSocketFilter[] SocketFilters;
|
||||
|
||||
[Tooltip("If multiple filters are in use, must all be valid or just one?")]
|
||||
public SocketCondition FilterCondition = SocketCondition.AND;
|
||||
|
||||
[Header("Misc")]
|
||||
[Tooltip("If supplied the hand will use this point when sorting distance to the closest socket instead of the socket position")]
|
||||
public Transform DistanceSource;
|
||||
|
||||
[Tooltip("Used by the socketable to decide which saved pose to use.")]
|
||||
public string PoseTag;
|
||||
|
||||
[Tooltip("If false, the socketed object colliders remain active, only works for static or kinematic rb sockets.")]
|
||||
public bool DisableCollision = true;
|
||||
|
||||
[Tooltip("Fires when an AutoSpawnedPrefab is instantiated.")]
|
||||
public SocketSpawnEvent SpawnedPrefab = new SocketSpawnEvent();
|
||||
|
||||
|
||||
[Header("Debugging")]
|
||||
public bool DebugScale;
|
||||
|
||||
protected Transform _previousParent;
|
||||
protected Vector3 _previousScale;
|
||||
protected bool _appQuitting;
|
||||
protected HVRGrabbable _timeoutGrabbable;
|
||||
private RigidbodyInterpolation _rbInterpolation;
|
||||
private float _rbDrag;
|
||||
private float _rbAngularDrag;
|
||||
private float _rbMass;
|
||||
private bool _rbGravity;
|
||||
private bool _rbKinematic;
|
||||
private float _socketMass;
|
||||
protected bool _hadRigidBody;
|
||||
protected bool _ignoreGrabSFX;
|
||||
protected Coroutine _fixPositionRoutine;
|
||||
|
||||
/// <summary>
|
||||
/// If assigned only this grabbable can enter this socket.
|
||||
/// </summary>
|
||||
public HVRGrabbable LinkedGrabbable { get; set; }
|
||||
|
||||
public override bool IsGrabActivated => !IsGrabbing;
|
||||
public override bool IsHoldActive => IsGrabbing;
|
||||
|
||||
public override bool AllowSwap => true;
|
||||
|
||||
public virtual bool CanInteract { get; set; } = true;
|
||||
|
||||
public override bool IsSocket => true;
|
||||
|
||||
public int? PoseHash { get; set; }
|
||||
|
||||
public bool CanAddGrabbable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanInteract)
|
||||
return false;
|
||||
if (ParentDisablesGrab && ParentGrabbable && ParentGrabbable.IsSocketed)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (!Rigidbody)
|
||||
{
|
||||
Rigidbody = GetComponentInParent<Rigidbody>();
|
||||
}
|
||||
|
||||
if (Rigidbody && !Rigidbody.isKinematic && !DisableCollision)
|
||||
{
|
||||
Debug.LogWarning($"Sockets with a non kinematic rigidbody should not disable DisableCollision");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(PoseTag))
|
||||
PoseHash = Animator.StringToHash(PoseTag);
|
||||
|
||||
//if (!Rigidbody && HoldType == SocketHoldType.RemoveRigidbody)
|
||||
//{
|
||||
// HoldType = SocketHoldType.Kinematic;
|
||||
// Debug.LogWarning($"Socket set to Kinematic, no rigidbody was found or assigned.");
|
||||
//}
|
||||
|
||||
if (GrabBags.Count == 0)
|
||||
{
|
||||
var bag = gameObject.AddComponent<HVRTriggerGrabbableBag>();
|
||||
GrabBags.Add(bag);
|
||||
bag.Grabber = this;
|
||||
}
|
||||
|
||||
SetupParentDisablesGrab();
|
||||
|
||||
if (SocketFilters == null || SocketFilters.Length == 0)
|
||||
SocketFilters = GetComponents<HVRSocketFilter>();
|
||||
if (HoverActions == null || HoverActions.Length == 0)
|
||||
HoverActions = GetComponents<HVRSocketHoverAction>();
|
||||
|
||||
StartCoroutine(WaitForUpdate(CheckAutoSpawn));
|
||||
}
|
||||
|
||||
private void SetupParentDisablesGrab()
|
||||
{
|
||||
if (ParentDisablesGrab && !ParentGrabbable)
|
||||
{
|
||||
ParentGrabbable = GetComponentInParent<HVRGrabbable>();
|
||||
}
|
||||
|
||||
if (ParentDisablesGrab && !ParentGrabbable)
|
||||
{
|
||||
Debug.LogWarning($"{gameObject.name}'s socket has ParentDisablesGrab without a ParentGrabbable");
|
||||
ParentDisablesGrab = false;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator WaitForUpdate(Action action)
|
||||
{
|
||||
yield return null;
|
||||
action();
|
||||
}
|
||||
|
||||
protected virtual void CheckAutoSpawn()
|
||||
{
|
||||
if (AutoSpawnPrefab)
|
||||
{
|
||||
var clone = Instantiate(AutoSpawnPrefab);
|
||||
var cloneGrabbable = clone.GetComponent<HVRGrabbable>();
|
||||
if (cloneGrabbable)
|
||||
{
|
||||
TryGrab(cloneGrabbable, true, true);
|
||||
SpawnedPrefab.Invoke(this, clone);
|
||||
}
|
||||
else
|
||||
Debug.Log($"Socket {name} has a AutoSpawnPrefab without an HVRGrabbable component");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
_appQuitting = true;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (DebugScale)
|
||||
{
|
||||
DebugScale = false;
|
||||
if (GrabbedTarget)
|
||||
UpdateScale(GrabbedTarget);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CheckHover()
|
||||
{
|
||||
if (base.CheckHover()) return true;
|
||||
|
||||
//take over another invalid socket if we are valid
|
||||
for (var g = 0; g < GrabBags.Count; g++)
|
||||
{
|
||||
var grabBag = GrabBags[g];
|
||||
for (var i = 0; i < grabBag.ValidGrabbables.Count; i++)
|
||||
{
|
||||
var grabbable = grabBag.ValidGrabbables[i];
|
||||
|
||||
if (!grabbable.SocketHoverer)
|
||||
continue;
|
||||
|
||||
if (!grabbable.SocketHoverer.IsValid(grabbable) && IsValid(grabbable))
|
||||
{
|
||||
UnhoverGrabbable(grabbable.SocketHoverer, grabbable);
|
||||
HoverGrabbable(this, grabbable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CanHover(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!grabbable.Socketable)
|
||||
return false;
|
||||
|
||||
if (grabbable.Socketable.AnyLinkedGrabbablesHeld)
|
||||
return false;
|
||||
|
||||
if (!CanInteract)
|
||||
return false;
|
||||
if (IsGrabbing && !ReleasesOnHover)
|
||||
return false;
|
||||
if (grabbable.SocketHoverer && grabbable.SocketHoverer != this)
|
||||
return false;
|
||||
if (GrabbableMustBeHeld && grabbable.GrabberCount != 1)
|
||||
return false;
|
||||
var handGrabber = grabbable.PrimaryGrabber as HVRHandGrabber;
|
||||
if (!handGrabber)
|
||||
return false;
|
||||
if (_timeoutGrabbable && _timeoutGrabbable == grabbable)
|
||||
return false;
|
||||
|
||||
if (CheckHandOverlap)
|
||||
{
|
||||
if (!handGrabber.SocketBag.AllSockets.Contains(this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return base.CanHover(grabbable);
|
||||
}
|
||||
|
||||
protected override void OnHoverEnter(HVRGrabbable grabbable)
|
||||
{
|
||||
if (ReleasesOnHover && IsGrabbing)
|
||||
{
|
||||
ForceRelease();
|
||||
}
|
||||
|
||||
|
||||
if (GrabsFromHand)
|
||||
{
|
||||
if (CanAddGrabbable && grabbable.IsBeingHeld && grabbable.GrabberCount == 1 &&
|
||||
grabbable.PrimaryGrabber.IsHandGrabber && CanGrabEx(grabbable))
|
||||
{
|
||||
grabbable.PrimaryGrabber.ForceRelease();
|
||||
//need to let the joint get destroyed so it doesn't bring the hand into the socket area forcefully
|
||||
this.ExecuteNextUpdate(() =>
|
||||
{
|
||||
if (!IsGrabbing)
|
||||
TryGrab(grabbable, true);
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
grabbable.Released.AddListener(OnHoverGrabbableReleased);
|
||||
|
||||
|
||||
base.OnHoverEnter(grabbable);
|
||||
if (HoverActions != null)
|
||||
{
|
||||
foreach (var action in HoverActions)
|
||||
{
|
||||
if (action)
|
||||
action.OnHoverEnter(this, grabbable, IsValid(grabbable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnHandGrabberEntered()
|
||||
{
|
||||
if (HandGrabActions != null)
|
||||
{
|
||||
foreach (var action in HandGrabActions)
|
||||
{
|
||||
if (action)
|
||||
action.OnHoverEnter(this, GrabbedTarget, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnHandGrabberExited()
|
||||
{
|
||||
if (HandGrabActions != null)
|
||||
{
|
||||
foreach (var action in HandGrabActions)
|
||||
{
|
||||
if (action)
|
||||
action.OnHoverExit(this, GrabbedTarget, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnHoverGrabbableReleased(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
UnhoverGrabbable(grabber, grabbable);
|
||||
//drop could have been a hand swap or some other swapping action
|
||||
//so we wait til next frame and see if it's not grabbed by something else this frame
|
||||
StartCoroutine(TryGrabGrabbable(grabbable));
|
||||
}
|
||||
|
||||
private IEnumerator TryGrabGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
yield return new WaitForFixedUpdate();
|
||||
|
||||
if (CanAddGrabbable && TryGrab(grabbable))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHoverExit(HVRGrabbable grabbable)
|
||||
{
|
||||
grabbable.Released.RemoveListener(OnHoverGrabbableReleased);
|
||||
base.OnHoverExit(grabbable);
|
||||
if (HoverActions != null)
|
||||
{
|
||||
foreach (var action in HoverActions)
|
||||
{
|
||||
if (action)
|
||||
action.OnHoverExit(this, grabbable, IsValid(grabbable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckGrab()
|
||||
{
|
||||
if (GrabbableMustBeHeld) return;
|
||||
base.CheckGrab();
|
||||
}
|
||||
|
||||
//public bool DebugCanGrab;
|
||||
|
||||
public override bool CanGrab(HVRGrabbable grabbable)
|
||||
{
|
||||
//if (DebugCanGrab && !GrabbedTarget)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
if (grabbable.IsBeingHeld && grabbable != GrabbedTarget)
|
||||
return false;
|
||||
|
||||
return CanGrabEx(grabbable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bypass the held check for GrabsFromHand
|
||||
/// </summary>
|
||||
protected virtual bool CanGrabEx(HVRGrabbable grabbable)
|
||||
{
|
||||
if (grabbable.IsStabbing && !CanGrabStabbingGrabbable || grabbable.IsStabbed)
|
||||
return false;
|
||||
|
||||
if (!grabbable.Socketable)
|
||||
return false;
|
||||
|
||||
if (grabbable.Socketable.AnyLinkedGrabbablesHeld)
|
||||
return false;
|
||||
|
||||
if (LinkedGrabbable && LinkedGrabbable != grabbable)
|
||||
return false;
|
||||
|
||||
if (grabbable.IsBeingForcedGrabbed)
|
||||
return false;
|
||||
|
||||
if (!IsValid(grabbable))
|
||||
return false;
|
||||
|
||||
if (_timeoutGrabbable && _timeoutGrabbable == grabbable)
|
||||
return false;
|
||||
|
||||
return base.CanGrab(grabbable) && (!GrabbedTarget || GrabbedTarget == grabbable);
|
||||
}
|
||||
|
||||
public virtual bool IsValid(HVRGrabbable grabbable)
|
||||
{
|
||||
if (LinkedGrabbable)
|
||||
{
|
||||
return LinkedGrabbable == grabbable;
|
||||
}
|
||||
|
||||
if (grabbable.IsStabbing && !CanGrabStabbingGrabbable || grabbable.IsStabbed || !grabbable.Socketable)
|
||||
return false;
|
||||
|
||||
if (grabbable.Socketable.AnyLinkedGrabbablesHeld)
|
||||
return false;
|
||||
|
||||
if (SocketFilters != null)
|
||||
{
|
||||
var anyValid = false;
|
||||
foreach (var filter in SocketFilters)
|
||||
{
|
||||
if (filter.IsValid(grabbable.Socketable) && FilterCondition == SocketCondition.OR)
|
||||
{
|
||||
anyValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filter.IsValid(grabbable.Socketable) && FilterCondition == SocketCondition.AND)
|
||||
return false;
|
||||
}
|
||||
|
||||
return anyValid || FilterCondition == SocketCondition.AND;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected internal override void OnBeforeHover(HVRGrabbable grabbable)
|
||||
{
|
||||
base.OnBeforeHover(grabbable);
|
||||
grabbable.SocketHoverer = this;
|
||||
}
|
||||
|
||||
protected internal override void OnAfterHover(HVRGrabbable grabbable)
|
||||
{
|
||||
base.OnAfterHover(grabbable);
|
||||
grabbable.SocketHoverer = null;
|
||||
}
|
||||
|
||||
protected override void OnGrabbed(HVRGrabArgs args)
|
||||
{
|
||||
base.OnGrabbed(args);
|
||||
|
||||
var grabbable = args.Grabbable;
|
||||
_previousParent = grabbable.transform.parent;
|
||||
_previousScale = grabbable.transform.localScale;
|
||||
|
||||
|
||||
AttachGrabbable(grabbable);
|
||||
OnGrabbableParented(grabbable);
|
||||
HandleRigidBodyGrab(grabbable);
|
||||
PlaySocketedSFX(grabbable.Socketable);
|
||||
|
||||
if (args.RaiseEvents)
|
||||
{
|
||||
Grabbed.Invoke(this, grabbable);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void AttachGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
grabbable.transform.parent = transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the socketed local position
|
||||
/// </summary>
|
||||
public virtual Vector3 GetTargetPosition(HVRGrabbable grabbable)
|
||||
{
|
||||
var socketable = grabbable.Socketable;
|
||||
if (!grabbable || !socketable)
|
||||
return Vector3.zero;
|
||||
|
||||
if (socketable.SocketOrientation)
|
||||
{
|
||||
var offSet = -socketable.SocketOrientation.localPosition;
|
||||
var delta = Quaternion.Inverse(socketable.SocketOrientation.localRotation);
|
||||
offSet = delta * offSet;
|
||||
|
||||
offSet.x *= grabbable.transform.localScale.x;
|
||||
offSet.y *= grabbable.transform.localScale.y;
|
||||
offSet.z *= grabbable.transform.localScale.z;
|
||||
|
||||
return offSet;
|
||||
}
|
||||
|
||||
return socketable.GetPositionOffset(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the socketed local rotation;
|
||||
/// </summary>
|
||||
public virtual Quaternion GetTargetRotation(HVRGrabbable grabbable)
|
||||
{
|
||||
var socketable = grabbable.Socketable;
|
||||
if (!socketable)
|
||||
return Quaternion.identity;
|
||||
|
||||
if (socketable.SocketOrientation)
|
||||
{
|
||||
return Quaternion.Inverse(socketable.SocketOrientation.localRotation);
|
||||
}
|
||||
|
||||
return socketable.GetRotationOffset(this);
|
||||
}
|
||||
|
||||
protected virtual void OnGrabbableParented(HVRGrabbable grabbable)
|
||||
{
|
||||
UpdateScale(grabbable);
|
||||
PositionGrabbable(grabbable);
|
||||
RotateGrabbable(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void PositionGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
grabbable.transform.localPosition = GetTargetPosition(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void RotateGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
grabbable.transform.localRotation = GetTargetRotation(grabbable);
|
||||
}
|
||||
|
||||
protected virtual void HandleRigidBodyGrab(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!grabbable.Rigidbody)
|
||||
return;
|
||||
|
||||
_rbKinematic = grabbable.Rigidbody.isKinematic;
|
||||
_rbInterpolation = grabbable.Rigidbody.interpolation;
|
||||
|
||||
|
||||
switch (HoldType)
|
||||
{
|
||||
case SocketHoldType.Kinematic:
|
||||
{
|
||||
grabbable.Rigidbody.velocity = Vector3.zero;
|
||||
grabbable.Rigidbody.angularVelocity = Vector3.zero;
|
||||
grabbable.Rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
|
||||
grabbable.Rigidbody.isKinematic = true;
|
||||
grabbable.Rigidbody.interpolation = RigidbodyInterpolation.None;
|
||||
if (DisableCollision) grabbable.SetAllToTrigger();
|
||||
}
|
||||
break;
|
||||
case SocketHoldType.RemoveRigidbody:
|
||||
{
|
||||
_hadRigidBody = true;
|
||||
_rbMass = grabbable.Rigidbody.mass;
|
||||
_rbGravity = grabbable.Rigidbody.useGravity;
|
||||
_rbDrag = grabbable.Rigidbody.drag;
|
||||
_rbAngularDrag = grabbable.Rigidbody.angularDrag;
|
||||
|
||||
if (Rigidbody)
|
||||
{
|
||||
_socketMass = Rigidbody.mass;
|
||||
Rigidbody.mass += _rbMass;
|
||||
}
|
||||
|
||||
Destroy(grabbable.Rigidbody);
|
||||
if (_fixPositionRoutine != null)
|
||||
{
|
||||
StopCoroutine(_fixPositionRoutine);
|
||||
}
|
||||
|
||||
_fixPositionRoutine = StartCoroutine(SetPositionNextFrame(grabbable));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CleanupRigidBody(HVRGrabbable grabbable)
|
||||
{
|
||||
var rb = grabbable.Rigidbody;
|
||||
if (HoldType == SocketHoldType.RemoveRigidbody && _hadRigidBody)
|
||||
{
|
||||
rb = grabbable.Rigidbody = grabbable.gameObject.AddComponent<Rigidbody>();
|
||||
if (Rigidbody)
|
||||
{
|
||||
Rigidbody.mass = _socketMass;
|
||||
}
|
||||
|
||||
rb.useGravity = _rbGravity;
|
||||
rb.mass = _rbMass;
|
||||
rb.drag = _rbDrag;
|
||||
rb.angularDrag = _rbAngularDrag;
|
||||
}
|
||||
|
||||
if (rb)
|
||||
{
|
||||
rb.collisionDetectionMode = grabbable.OriginalCollisionMode;
|
||||
rb.isKinematic = _rbKinematic;
|
||||
rb.interpolation = _rbInterpolation;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator SetPositionNextFrame(HVRGrabbable grabbable)
|
||||
{
|
||||
var position = grabbable.transform.localPosition;
|
||||
var rotation = grabbable.transform.localRotation;
|
||||
yield return null;
|
||||
if (grabbable.PrimaryGrabber == this)
|
||||
{
|
||||
grabbable.transform.localPosition = position;
|
||||
grabbable.transform.localRotation = rotation;
|
||||
}
|
||||
|
||||
_fixPositionRoutine = null;
|
||||
}
|
||||
|
||||
protected virtual void PlaySocketedSFX(HVRSocketable socketable)
|
||||
{
|
||||
if (_ignoreGrabSFX)
|
||||
return;
|
||||
|
||||
if (AudioGrabbedOverride)
|
||||
{
|
||||
PlaySFX(AudioGrabbedOverride);
|
||||
}
|
||||
else if (socketable.SocketedClip)
|
||||
{
|
||||
PlaySFX(socketable.SocketedClip);
|
||||
}
|
||||
else if (AudioGrabbedFallback)
|
||||
{
|
||||
PlaySFX(AudioGrabbedFallback);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void PlayUnsocketedSFX(HVRGrabbable grabbable)
|
||||
{
|
||||
if (AudioReleasedOverride)
|
||||
{
|
||||
PlaySFX(AudioReleasedOverride);
|
||||
}
|
||||
else if (grabbable.Socketable.UnsocketedClip)
|
||||
{
|
||||
PlaySFX(grabbable.Socketable.UnsocketedClip);
|
||||
}
|
||||
else if (AudioReleasedFallback)
|
||||
{
|
||||
PlaySFX(AudioReleasedFallback);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void PlaySFX(AudioClip clip)
|
||||
{
|
||||
if (SFXPlayer.Instance) SFXPlayer.Instance.PlaySFX(clip, transform.position);
|
||||
}
|
||||
|
||||
protected virtual float GetSocketableScaleSize(HVRSocketable socketable)
|
||||
{
|
||||
return socketable.GetSocketScaleSize(this);
|
||||
}
|
||||
|
||||
protected virtual void UpdateScale(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!grabbable || !ScaleGrabbable)
|
||||
return;
|
||||
|
||||
var finalScale = ComputeScale(grabbable.Socketable);
|
||||
grabbable.transform.localScale = finalScale;
|
||||
}
|
||||
|
||||
public virtual Vector3 ComputeScale(HVRSocketable socketable)
|
||||
{
|
||||
var axis = GetSocketableScaleSize(socketable);
|
||||
var ratio = Size / axis;
|
||||
ratio *= socketable.SocketScale;
|
||||
var counterScale = socketable.CounterScale;
|
||||
return new Vector3(ratio * counterScale.x, ratio * counterScale.y, ratio * counterScale.z);
|
||||
}
|
||||
|
||||
protected override void OnReleased(HVRGrabbable grabbable)
|
||||
{
|
||||
if (_appQuitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnReleased(grabbable);
|
||||
|
||||
Released.Invoke(this, grabbable);
|
||||
|
||||
if (grabbable.BeingDestroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CleanupRigidBody(grabbable);
|
||||
|
||||
grabbable.ResetToNonTrigger();
|
||||
grabbable.transform.parent = _previousParent;
|
||||
|
||||
if (ScaleGrabbable)
|
||||
{
|
||||
grabbable.transform.localScale = _previousScale;
|
||||
}
|
||||
|
||||
_previousParent = null;
|
||||
|
||||
PlayUnsocketedSFX(grabbable);
|
||||
|
||||
CheckAutoSpawn();
|
||||
|
||||
if (GrabTimeout > .00001f)
|
||||
{
|
||||
StartCoroutine(GrabTimeoutRoutine(grabbable));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanGrabbableBeRemoved(HVRHandGrabber hand)
|
||||
{
|
||||
if (!CanRemoveGrabbable)
|
||||
return false;
|
||||
if (!CanInteract)
|
||||
return false;
|
||||
if (ParentDisablesGrab && ParentGrabbable && ParentGrabbable.IsSocketed)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected virtual IEnumerator GrabTimeoutRoutine(HVRGrabbable grabbable)
|
||||
{
|
||||
_timeoutGrabbable = grabbable;
|
||||
yield return new WaitForSeconds(GrabTimeout);
|
||||
_timeoutGrabbable = null;
|
||||
}
|
||||
|
||||
public virtual bool TryGrab(HVRGrabbable grabbable, bool force = false, bool ignoreGrabSound = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ignoreGrabSFX = ignoreGrabSound;
|
||||
return base.TryGrab(grabbable, force);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ignoreGrabSFX = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance between this grabbable and the provided grabber
|
||||
/// </summary>
|
||||
public virtual float GetDistanceToGrabber(Vector3 point)
|
||||
{
|
||||
var ourPoint = DistanceSource ? DistanceSource.position : transform.position;
|
||||
|
||||
return Vector3.Distance(point, ourPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Squared Distance between this grabbable and the provided grabber
|
||||
/// </summary>
|
||||
public virtual float GetSquareDistanceToGrabber(Vector3 point)
|
||||
{
|
||||
var ourPoint = DistanceSource ? DistanceSource.position : transform.position;
|
||||
|
||||
return (point - ourPoint).sqrMagnitude;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SocketSpawnEvent : UnityEvent<HVRSocket, GameObject>
|
||||
{
|
||||
}
|
||||
|
||||
public enum SocketCondition
|
||||
{
|
||||
AND,
|
||||
OR
|
||||
}
|
||||
|
||||
public enum SocketHoldType
|
||||
{
|
||||
Kinematic,
|
||||
RemoveRigidbody
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a27bb777100ada4459dda04ca3779632
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using HurricaneVR.Framework.Core.Bags;
|
||||
using HurricaneVR.Framework.Core.Sockets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Grabbers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Grabber that uses a HVRSocketContainer as a target of the grab. Main uses are over the shoulder or chest collection type inventories
|
||||
/// </summary>
|
||||
public class HVRSocketContainerGrabber : HVRGrabberBase
|
||||
{
|
||||
public HVRSocketContainer SocketContainer;
|
||||
|
||||
[Tooltip("If true item's must be placed with a hand grabber.")]
|
||||
public bool GrabbleMustBeHeld = true;
|
||||
|
||||
public override bool IsGrabActivated => !GrabbleMustBeHeld && SocketContainer.HasAvailableSocket();
|
||||
public override bool AllowSwap => true;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (GrabBags.Count == 0)
|
||||
{
|
||||
var bag = gameObject.AddComponent<HVRTriggerGrabbableBag>();
|
||||
GrabBags.Add(bag);
|
||||
bag.Grabber = this;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanHover(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!SocketContainer)
|
||||
return false;
|
||||
if (grabbable.IsBeingForcedGrabbed)
|
||||
return false;
|
||||
if (grabbable.GrabberCount != 1)
|
||||
return false;
|
||||
var handGrabber = grabbable.PrimaryGrabber as HVRHandGrabber;
|
||||
if (handGrabber == null)
|
||||
return false;
|
||||
|
||||
if (!SocketContainer.HasAvailableSocket(grabbable))
|
||||
return false;
|
||||
|
||||
if (grabbable.IsStabbing || grabbable.IsStabbed)
|
||||
return false;
|
||||
|
||||
return base.CanHover(grabbable);
|
||||
}
|
||||
|
||||
public override bool CanGrab(HVRGrabbable grabbable)
|
||||
{
|
||||
if (!SocketContainer)
|
||||
return false;
|
||||
if (grabbable.IsBeingForcedGrabbed)
|
||||
return false;
|
||||
if (grabbable.IsBeingHeld)
|
||||
return false;
|
||||
if (!SocketContainer.HasAvailableSocket(grabbable))
|
||||
return false;
|
||||
if (grabbable.IsStabbing || grabbable.IsStabbed)
|
||||
return false;
|
||||
return base.CanGrab(grabbable);
|
||||
}
|
||||
|
||||
protected override void OnHoverEnter(HVRGrabbable grabbable)
|
||||
{
|
||||
if (GrabbleMustBeHeld)
|
||||
grabbable.Released.AddListener(OnHoverGrabbableReleased);
|
||||
base.OnHoverEnter(grabbable);
|
||||
}
|
||||
|
||||
protected override void OnHoverExit(HVRGrabbable grabbable)
|
||||
{
|
||||
if (GrabbleMustBeHeld)
|
||||
grabbable.Released.RemoveListener(OnHoverGrabbableReleased);
|
||||
base.OnHoverExit(grabbable);
|
||||
}
|
||||
|
||||
private void OnHoverGrabbableReleased(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
UnhoverGrabbable(grabber, grabbable);
|
||||
//something else might have grabbed this, like a another one of these grabbers
|
||||
StartCoroutine(TryGrabGrabbable(grabbable));
|
||||
}
|
||||
|
||||
private IEnumerator TryGrabGrabbable(HVRGrabbable grabbable)
|
||||
{
|
||||
yield return null;
|
||||
|
||||
if (!grabbable.IsBeingHeld)
|
||||
SocketContainer.TryAddGrabbable(grabbable);
|
||||
}
|
||||
|
||||
protected override void OnGrabbed(HVRGrabArgs args)
|
||||
{
|
||||
//socket cannot grab something if it's being held, so we force release it first so it's CanGrab will not return false
|
||||
ForceRelease();
|
||||
SocketContainer.TryAddGrabbable(args.Grabbable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3480dc27077fb6a47a99066438ce883f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("HurricaneVR.PUN")]
|
||||
[assembly: InternalsVisibleTo("HurricaneVR.SteamVR")]
|
||||
[assembly: InternalsVisibleTo("HurricaneVR.Oculus")]
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d7e99ffe74c473aab93a12e849053f0
|
||||
timeCreated: 1603423829
|
||||
@@ -0,0 +1,129 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
[RequireComponent(typeof(LineRenderer))]
|
||||
public class HVRForceGrabberLaser : MonoBehaviour
|
||||
{
|
||||
public LineRenderer LineRenderer;
|
||||
public int Points = 50;
|
||||
public Transform Target;
|
||||
public float Percent = .6f;
|
||||
|
||||
private Vector3[] _points;
|
||||
private int _previousPoints;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (!LineRenderer) LineRenderer = GetComponent<LineRenderer>();
|
||||
|
||||
_points = new Vector3[Points];
|
||||
_previousPoints = Points;
|
||||
//LineRenderer.enabled = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
CheckResize();
|
||||
UpdateLaser();
|
||||
}
|
||||
|
||||
private void UpdateLaser()
|
||||
{
|
||||
if (!Target) return;
|
||||
|
||||
A = transform.position;
|
||||
D = Target.position;
|
||||
|
||||
var distance = Vector3.Distance(A, D) * Percent;
|
||||
|
||||
B = transform.position + transform.forward * distance;
|
||||
C = B;
|
||||
|
||||
Gizmos.color = Color.white;
|
||||
var resolution = 1f / Points;
|
||||
|
||||
for (var i = 1; i <= Mathf.FloorToInt(1f / resolution); i++)
|
||||
{
|
||||
var t = i * resolution;
|
||||
var point = GetPointOnBezierCurve(A, B, C, D, t);
|
||||
_points[i - 1] = point;
|
||||
}
|
||||
|
||||
LineRenderer.positionCount = _points.Length;
|
||||
LineRenderer.SetPositions(_points);
|
||||
}
|
||||
|
||||
private void CheckResize()
|
||||
{
|
||||
if (_previousPoints != Points)
|
||||
{
|
||||
_points = new Vector3[Points];
|
||||
}
|
||||
|
||||
_previousPoints = Points;
|
||||
}
|
||||
|
||||
public void Enable(Transform target)
|
||||
{
|
||||
Target = target;
|
||||
LineRenderer.enabled = true;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
Target = null;
|
||||
LineRenderer.enabled = false;
|
||||
}
|
||||
|
||||
//Easier to use ABCD for the positions of the points so they are the same as in the tutorial image
|
||||
Vector3 A, B, C, D;
|
||||
|
||||
//Display without having to press play
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (!Target) return;
|
||||
A = transform.position;
|
||||
D = Target.position;
|
||||
|
||||
var distance = Vector3.Distance(A, D) * Percent;
|
||||
|
||||
B = transform.position + transform.forward * distance;
|
||||
C = B;
|
||||
|
||||
Gizmos.color = Color.white;
|
||||
|
||||
Vector3 lastPos = A;
|
||||
|
||||
for (var i = 1; i <= Mathf.FloorToInt(1f / .02f); i++)
|
||||
{
|
||||
float t = i * .02f;
|
||||
Vector3 newPos = GetPointOnBezierCurve(A, B, C, D, t);
|
||||
Gizmos.DrawLine(lastPos, newPos);
|
||||
lastPos = newPos;
|
||||
}
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
|
||||
Gizmos.DrawLine(A, B);
|
||||
Gizmos.DrawLine(C, D);
|
||||
}
|
||||
|
||||
Vector3 GetPointOnBezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
||||
{
|
||||
float u = 1f - t;
|
||||
float t2 = t * t;
|
||||
float u2 = u * u;
|
||||
float u3 = u2 * u;
|
||||
float t3 = t2 * t;
|
||||
|
||||
Vector3 result =
|
||||
(u3) * p0 +
|
||||
(3f * u2 * t) * p1 +
|
||||
(3f * u * t2) * p2 +
|
||||
(t3) * p3;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18f590c92db08f642b7b64c13ff01bff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
|
||||
/// <summary>
|
||||
/// Filters the incoming grabbing hand by left or right hand only
|
||||
/// </summary>
|
||||
public class HVRGrabHandFilter : HVRHandGrabFilter
|
||||
{
|
||||
|
||||
public HandOptions AllowedHands;
|
||||
|
||||
public override bool CanBeGrabbed(HVRHandGrabber hand)
|
||||
{
|
||||
if (AllowedHands == HandOptions.Both)
|
||||
return true;
|
||||
|
||||
if (AllowedHands == HandOptions.Left && hand.IsLeftHand)
|
||||
return true;
|
||||
|
||||
if (AllowedHands == HandOptions.Right && hand.IsRightHand)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//[Flags]
|
||||
public enum HandOptions
|
||||
{
|
||||
Left = 0, Right = 1, Both = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cf9028b057d4b7db0423f5d0a26074b
|
||||
timeCreated: 1674090737
|
||||
10
Assets/HurricaneVR/Framework/Scripts/Core/HVRGrabPoints.cs
Normal file
10
Assets/HurricaneVR/Framework/Scripts/Core/HVRGrabPoints.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
public class HVRGrabPoints : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7737b083a4a391b49adfccaf05aeb91e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1591
Assets/HurricaneVR/Framework/Scripts/Core/HVRGrabbable.cs
Normal file
1591
Assets/HurricaneVR/Framework/Scripts/Core/HVRGrabbable.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ebc937e678155e4d86feeb9248cca97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
public class HVRGrabbableChild : MonoBehaviour
|
||||
{
|
||||
public HVRGrabbable ParentGrabbable;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (!ParentGrabbable) ParentGrabbable = GetComponentInParent<HVRGrabbable>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c75c5c18c01eee4ab0ab9c8cd3c3000
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
public class HVRGrabberManager : MonoBehaviour
|
||||
{
|
||||
protected virtual void Awake()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void RegisterGrabber(HVRGrabberBase grabber)
|
||||
{
|
||||
grabber.BeforeGrabbed.AddListener(OnBeforeGrabberGrabbed);
|
||||
grabber.Grabbed.AddListener(OnGrabberGrabbed);
|
||||
grabber.Released.AddListener(OnGrabberReleased);
|
||||
}
|
||||
|
||||
protected virtual void OnBeforeGrabberGrabbed(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void UnregisterGrabber(HVRGrabberBase grabber)
|
||||
{
|
||||
grabber.Grabbed.RemoveListener(OnGrabberGrabbed);
|
||||
grabber.Released.RemoveListener(OnGrabberReleased);
|
||||
}
|
||||
|
||||
protected virtual void OnGrabberReleased(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnGrabberGrabbed(HVRGrabberBase grabber, HVRGrabbable grabbable)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 511b6afc52cd40c889e42589b4d724c0
|
||||
timeCreated: 1603249821
|
||||
@@ -0,0 +1,39 @@
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
[RequireComponent(typeof(HVRGrabbable))]
|
||||
public class HVRHandGrabEvent : MonoBehaviour
|
||||
{
|
||||
public HVRGrabbable Grabbable { get; private set; }
|
||||
|
||||
public UnityEvent Grabbed = new UnityEvent();
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Grabbable = GetComponent<HVRGrabbable>();
|
||||
Grabbable.HandGrabbed.AddListener(OnHandGrabbed);
|
||||
Grabbable.TrackingType = HVRGrabTracking.None;
|
||||
Grabbable.ForceGrabbable = false;
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
Grabbable.CanBeGrabbed = CheckEnableGrab();
|
||||
}
|
||||
|
||||
protected virtual bool CheckEnableGrab()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnHandGrabbed(HVRHandGrabber hand, HVRGrabbable arg1)
|
||||
{
|
||||
arg1.ForceRelease();
|
||||
Grabbed.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3258b231e1a6c448ba0ad62a5d6bb50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using HurricaneVR.Framework.Core;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Optional filter applied to HVRGrabbable object to decide if the potential hand can grab this object
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(HVRGrabbable))]
|
||||
public abstract class HVRHandGrabFilter : MonoBehaviour
|
||||
{
|
||||
public HVRGrabbable Grabbable { get; set; }
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (TryGetComponent(out HVRGrabbable g))
|
||||
Grabbable = g;
|
||||
}
|
||||
|
||||
public abstract bool CanBeGrabbed(HVRHandGrabber hand);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bef30b910ae46c4acd561544cc1e389
|
||||
timeCreated: 1674090329
|
||||
69
Assets/HurricaneVR/Framework/Scripts/Core/HVRHandPhysics.cs
Normal file
69
Assets/HurricaneVR/Framework/Scripts/Core/HVRHandPhysics.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class HVRHandPhysics : MonoBehaviour
|
||||
{
|
||||
public Collider[] HandColliders;// { get; private set; }
|
||||
public Rigidbody Rigidbody { get; private set; }
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Rigidbody = gameObject.GetRigidbody();
|
||||
}
|
||||
|
||||
public void SetupColliders()
|
||||
{
|
||||
HandColliders = GetComponentsInChildren<Collider>().Where(e => !e.isTrigger && e.enabled).ToArray();
|
||||
}
|
||||
|
||||
public void DisableCollision()
|
||||
{
|
||||
Rigidbody.detectCollisions = false;
|
||||
}
|
||||
|
||||
public void SetAllToTrigger()
|
||||
{
|
||||
foreach (var handCollider in HandColliders)
|
||||
{
|
||||
handCollider.isTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetToNonTrigger()
|
||||
{
|
||||
foreach (var handCollider in HandColliders)
|
||||
{
|
||||
handCollider.isTrigger = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableCollision()
|
||||
{
|
||||
Rigidbody.detectCollisions = true;
|
||||
}
|
||||
|
||||
public void IgnoreCollision(List<Collider> colliders, bool ignore)
|
||||
{
|
||||
if (HandColliders == null || colliders == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var handCollider in HandColliders)
|
||||
{
|
||||
foreach (var col in colliders)
|
||||
{
|
||||
if (col)
|
||||
{
|
||||
Physics.IgnoreCollision(handCollider, col, ignore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6477d7ab11e624344bd8d26783ef171f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using HurricaneVR.Framework.Core.Sockets;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Dummy socketable added while linking in case one isn't provided so the socket code will not break.
|
||||
/// </summary>
|
||||
public class HVRLinkedSocketable : HVRSocketable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e342e34c74884f399b6b418e8b6b9273
|
||||
timeCreated: 1666497596
|
||||
74
Assets/HurricaneVR/Framework/Scripts/Core/HVRManager.cs
Normal file
74
Assets/HurricaneVR/Framework/Scripts/Core/HVRManager.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using HurricaneVR.Framework.Core.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
public class HVRManager : MonoBehaviour
|
||||
{
|
||||
public static HVRManager Instance { get; private set; }
|
||||
|
||||
public HVRGrabberManager GrabberManager;
|
||||
public HVRPlayerController PlayerController;
|
||||
public Transform Camera;
|
||||
public HVRScreenFade ScreenFader { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (!Instance)
|
||||
{
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(this.gameObject);
|
||||
if (!GrabberManager)
|
||||
{
|
||||
GrabberManager = gameObject.AddComponent<HVRGrabberManager>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this);
|
||||
}
|
||||
|
||||
var finder = FindObjectOfType<HVRGlobalFadeFinder>();
|
||||
if (finder)
|
||||
{
|
||||
ScreenFader = finder.gameObject.GetComponent<HVRScreenFade>();
|
||||
}
|
||||
|
||||
if (!PlayerController)
|
||||
{
|
||||
PlayerController = FindObjectOfType<HVRPlayerController>();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterGrabber(HVRGrabberBase grabber)
|
||||
{
|
||||
if (GrabberManager)
|
||||
{
|
||||
GrabberManager.RegisterGrabber(grabber);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterGrabber(HVRGrabberBase grabber)
|
||||
{
|
||||
if (GrabberManager)
|
||||
{
|
||||
GrabberManager.UnregisterGrabber(grabber);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void IgnorePlayerCollision(IEnumerable<Collider> colliders)
|
||||
{
|
||||
if (PlayerController)
|
||||
PlayerController.IgnoreCollision(colliders);
|
||||
}
|
||||
|
||||
public void ScreenFade(float alpha, float speed)
|
||||
{
|
||||
if(ScreenFader)
|
||||
ScreenFader.Fade(alpha, speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 923ec526580e484ca0d4716c43ccccc8
|
||||
timeCreated: 1600302382
|
||||
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class HVRPhysicsGrabbable : HVRGrabbable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ed6e70729ed4913846a8b06675e1581
|
||||
timeCreated: 1602191380
|
||||
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
public class HVRRequireOtherGrabbable : MonoBehaviour
|
||||
{
|
||||
[Tooltip("This grabbable must be held otherwise this grabbable cannot be grabbed.")]
|
||||
public HVRGrabbable Grabbable;
|
||||
|
||||
[Tooltip("If the required grabbable is dropped, should we release?")]
|
||||
public bool DropIfReleased;
|
||||
|
||||
[Tooltip("If the required grabbable is dropped, let's try and grab it.")]
|
||||
public bool GrabRequiredIfReleased;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbf858a0f4504aa4adf9207e33864cfe
|
||||
timeCreated: 1604714079
|
||||
478
Assets/HurricaneVR/Framework/Scripts/Core/HVRSettings.cs
Normal file
478
Assets/HurricaneVR/Framework/Scripts/Core/HVRSettings.cs
Normal file
@@ -0,0 +1,478 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using HurricaneVR.Framework.Core.HandPoser;
|
||||
using HurricaneVR.Framework.Core.ScriptableObjects;
|
||||
using HurricaneVR.Framework.Core.Sockets;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
public class HVRSettings : ScriptableObject
|
||||
{
|
||||
private const string HurricaneVRUploader = "HurricaneVRUploader";
|
||||
|
||||
private static HVRSettings _instance;
|
||||
|
||||
public static HVRSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = Resources.Load<HVRSettings>(HandPoserSettings);
|
||||
|
||||
//Asset doesn't exist, create it
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = HVRSettings.CreateInstance<HVRSettings>();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
_instance.Setup(_instance);
|
||||
if (Application.productName != HurricaneVRUploader)
|
||||
{
|
||||
_instance.AddAssetToResource(_instance, HandPoserSettingsFileName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
if (!_instance.LineGrabSettings)
|
||||
{
|
||||
_instance.LineGrabSettings = FindJointSettings("HVR_LineGrabSettings");
|
||||
}
|
||||
#endif
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
[Header("Directories")]
|
||||
|
||||
public string LocalEditorRootDirectory;
|
||||
public string LocalRootDirectory;
|
||||
public string LocalResourcesDirectory;
|
||||
public string LocalRuntimePosesDirectory;
|
||||
public string LocalPosesDirectory;
|
||||
|
||||
|
||||
|
||||
public const string HandPoserSettings = "HVRSettings";
|
||||
public const string HandPoserSettingsFileName = HandPoserSettings + ".asset";
|
||||
|
||||
public const string DefaultOpenHand = "OculusCustomHandOpen";
|
||||
|
||||
public const string DefaultLeftHand = "HVR_left_hand";
|
||||
public const string DefaultRightHand = "HVR_right_hand";
|
||||
|
||||
public const string RuntimePoses = "RuntimePoses";
|
||||
|
||||
[Header("Misc Settings")]
|
||||
public bool AutoApplyGrabbableLayer = true;
|
||||
|
||||
[Tooltip("Tag Filter / Socketables will populate with this scriptable object if assigned.")]
|
||||
public HVRSocketableTags DefaultSocketableTags;
|
||||
|
||||
[Header("Pose Settings")]
|
||||
|
||||
public GameObject LeftHand;
|
||||
public GameObject RightHand;
|
||||
public bool InverseKinematics;
|
||||
public bool IKHandMirroring = true;
|
||||
public GameObject FullBody;
|
||||
public Vector3 HandPoseHandleOffset = new Vector3(0f, .08f, .0f);
|
||||
public HVRHandPose OpenHandPose;
|
||||
public bool PoserShowsOneFinger;
|
||||
|
||||
[Header("Hand Poser Finger Curl Defaults")]
|
||||
public HVRFingerType ThumbCurlType = HVRFingerType.Static;
|
||||
public HVRFingerType IndexCurlType = HVRFingerType.Static;
|
||||
public HVRFingerType MiddleCurlType = HVRFingerType.Static;
|
||||
public HVRFingerType RingCurlType = HVRFingerType.Static;
|
||||
public HVRFingerType PinkyCurlType = HVRFingerType.Static;
|
||||
|
||||
[Range(0f, 1f)] public float ThumbStart = 1f;
|
||||
[Range(0f, 1f)] public float IndexStart = 1f;
|
||||
[Range(0f, 1f)] public float MiddleStart = 1f;
|
||||
[Range(0f, 1f)] public float RingStart = 1f;
|
||||
[Range(0f, 1f)] public float PinkyStart = 1f;
|
||||
|
||||
[Header("Grab Detection")]
|
||||
public bool UseAttachedRigidBody;
|
||||
public bool ComponentInParentFallback;
|
||||
|
||||
[Header("Grab Settings")]
|
||||
[Tooltip("If true then holding trigger loosens the line grab without releasing grip")]
|
||||
public bool LineGrabTriggerLoose;
|
||||
|
||||
[Header("Joint Setting Defaults")]
|
||||
[Tooltip("Default joint settings when grabbing an object.")]
|
||||
public HVRJointSettings DefaultJointSettings;
|
||||
|
||||
public HVRJointSettings LineGrabSettings;
|
||||
|
||||
|
||||
[Header("Debugging")]
|
||||
public bool VerboseGrabbableEvents;
|
||||
public bool VerboseHandGrabberEvents;
|
||||
public bool DisableHaptics;
|
||||
|
||||
public GameObject GetPoserHand(HVRHandSide side)
|
||||
{
|
||||
if (side == HVRHandSide.Left) return LeftHand;
|
||||
return RightHand;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
|
||||
public string PosesDirectory => GetPosesDirectory();
|
||||
|
||||
private void Setup(HVRSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
TryFindRoot();
|
||||
TryFindResources();
|
||||
|
||||
if (!DefaultJointSettings)
|
||||
{
|
||||
DefaultJointSettings = FindJointSettings("HVR_GrabbableSettings");
|
||||
}
|
||||
|
||||
LineGrabSettings = FindJointSettings("HVR_LineGrabSettings");
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
TryCreateRuntimePoseFolder();
|
||||
|
||||
SetupDefaultHands(settings);
|
||||
}
|
||||
|
||||
private static void SetupDefaultHands(HVRSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (settings.LeftHand == null)
|
||||
{
|
||||
settings.LeftHand = FindPrefab(DefaultLeftHand);
|
||||
}
|
||||
|
||||
if (settings.RightHand == null)
|
||||
{
|
||||
settings.RightHand = FindPrefab(DefaultRightHand);
|
||||
}
|
||||
|
||||
//if (settings.OpenHandPose == null)
|
||||
//{
|
||||
// settings.OpenHandPose = FindAsset<HVRHandPose>(DefaultOpenHand);
|
||||
//}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void TryFindRoot()
|
||||
{
|
||||
try
|
||||
{
|
||||
var frameworkDir = GetRootFrameworkDirectory();
|
||||
|
||||
System.IO.DirectoryInfo assetsDirectoryInfo = new DirectoryInfo(Application.dataPath);
|
||||
LocalRootDirectory = frameworkDir.Substring(assetsDirectoryInfo.Parent.FullName.Length + 1);
|
||||
LocalEditorRootDirectory = LocalRootDirectory + "Editor" + Path.DirectorySeparatorChar;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetRootFrameworkDirectory()
|
||||
{
|
||||
var root = ScriptableObject.CreateInstance<HVRRootFinder>();
|
||||
var rootScript = UnityEditor.MonoScript.FromScriptableObject(root);
|
||||
var rootPath = UnityEditor.AssetDatabase.GetAssetPath(rootScript);
|
||||
var rootFileInfo = new FileInfo(rootPath);
|
||||
|
||||
return rootFileInfo.Directory.FullName.Replace($"HurricaneVR{Path.DirectorySeparatorChar}Framework{Path.DirectorySeparatorChar}Scripts",
|
||||
$"HurricaneVR{Path.DirectorySeparatorChar}Framework{Path.DirectorySeparatorChar}");
|
||||
}
|
||||
|
||||
private string GetResourcesDirectory()
|
||||
{
|
||||
var root = GetRootFrameworkDirectory();
|
||||
var path = Path.Combine(root, "Resources");
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
private string GetDefaultRuntimePosesDirectory()
|
||||
{
|
||||
return Path.Combine(GetResourcesDirectory(), RuntimePoses);
|
||||
}
|
||||
|
||||
public string GetRuntimePosesDirectory()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(LocalRuntimePosesDirectory))
|
||||
{
|
||||
Debug.LogWarning($"LocalRunTimePosesDirectory is blank.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (LocalRuntimePosesDirectory.StartsWith("Assets\\") || LocalRuntimePosesDirectory.StartsWith("Assets/"))
|
||||
{
|
||||
return Application.dataPath + LocalRuntimePosesDirectory.Substring(6);
|
||||
}
|
||||
|
||||
Debug.LogWarning($"LocalRunTimePosesDirectory is blank.");
|
||||
|
||||
return GetDefaultRuntimePosesDirectory();
|
||||
}
|
||||
|
||||
public string GetPosesDirectory()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(LocalPosesDirectory))
|
||||
{
|
||||
Debug.LogWarning($"LocalPosesDirectory is blank.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (LocalPosesDirectory.StartsWith("Assets\\") || LocalPosesDirectory.StartsWith("Assets/"))
|
||||
{
|
||||
return Application.dataPath + LocalPosesDirectory.Substring(6);
|
||||
}
|
||||
|
||||
Debug.LogWarning($"LocalPosesDirectory is blank.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void TryFindResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = GetResourcesDirectory();
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
LocalResourcesDirectory = LocalRootDirectory + "Resources" + Path.DirectorySeparatorChar;
|
||||
//Debug.Log($"HVRSettings.ResourcesPath={ResourcesDirectory}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void TryCreateRuntimePoseFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(LocalResourcesDirectory)) return;
|
||||
|
||||
if (string.IsNullOrEmpty(LocalRuntimePosesDirectory))
|
||||
{
|
||||
LocalRuntimePosesDirectory = LocalResourcesDirectory + "RuntimePoses" + Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(GetDefaultRuntimePosesDirectory());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
[InspectorButton("ShowPosesFolderChooser")]
|
||||
public string ChosePosesDirectory = "Choose Pose Directory";
|
||||
|
||||
[InspectorButton("ShowRuntimePosesFolderChooser", 300)]
|
||||
public string ChoseRunTimePosesDirectory = "Choose RunTime Poses Directory";
|
||||
|
||||
public void ShowPosesFolderChooser()
|
||||
{
|
||||
var PosesDirectory = EditorUtility.OpenFolderPanel("Choose Pose Directory", null, null);
|
||||
LocalPosesDirectory = PosesDirectory.Substring(Application.dataPath.IndexOf("Assets"));
|
||||
EditorUtility.SetDirty(this);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
public void ShowRuntimePosesFolderChooser()
|
||||
{
|
||||
var RuntimePosesDirectory = EditorUtility.OpenFolderPanel("Choose Pose Directory", null, null);
|
||||
LocalRuntimePosesDirectory = RuntimePosesDirectory.Substring(Application.dataPath.IndexOf("Assets"));
|
||||
EditorUtility.SetDirty(this);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
|
||||
[InspectorButton("ReloadGlobalsMethod")]
|
||||
public string ReloadGlobals = "Reload Globals";
|
||||
public void ReloadGlobalsMethod()
|
||||
{
|
||||
if (Instance)
|
||||
{
|
||||
Setup(Instance);
|
||||
EditorUtility.SetDirty(this);
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public HVRHandPose SaveRunTimePose(HVRHandPose pose, string fileName, string directory = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var RuntimePosesDirectory = GetRuntimePosesDirectory();
|
||||
if (RuntimePosesDirectory == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
var folder = Path.Combine(RuntimePosesDirectory, directory);
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
if (!fileName.EndsWith(".asset"))
|
||||
{
|
||||
fileName += ".asset";
|
||||
}
|
||||
|
||||
string path;
|
||||
if (!string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
path = Path.Combine(Path.Combine(LocalRuntimePosesDirectory, directory), fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Path.Combine(LocalRuntimePosesDirectory, fileName);
|
||||
}
|
||||
|
||||
pose = AssetUtils.CreateOrReplaceAsset(pose, path);
|
||||
Debug.Log($"Saved {fileName} to {LocalRuntimePosesDirectory}");
|
||||
return pose;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public HVRHandPose SavePoseToDefault(HVRHandPose pose, string fileName, string directory = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var PosesDirectory = GetPosesDirectory();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(PosesDirectory))
|
||||
{
|
||||
//Debug.Log($"Setup PosesDirectory and LocalPosesDirectory.");
|
||||
return SaveRunTimePose(pose, fileName, null);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
var folder = Path.Combine(PosesDirectory, directory);
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
if (!fileName.EndsWith(".asset"))
|
||||
{
|
||||
fileName += ".asset";
|
||||
}
|
||||
|
||||
string path;
|
||||
if (!string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
path = Path.Combine(Path.Combine(LocalPosesDirectory, directory), fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Path.Combine(LocalPosesDirectory, fileName);
|
||||
}
|
||||
|
||||
return AssetUtils.CreateOrReplaceAsset(pose, path);
|
||||
//Debug.Log($"Saved {fileName} to {LocalPosesDirectory}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void AddAssetToResource<T>(T asset, string name) where T : Object
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!name.EndsWith(".asset"))
|
||||
{
|
||||
name += ".asset";
|
||||
}
|
||||
|
||||
var path = Path.Combine(LocalResourcesDirectory, name);
|
||||
AssetUtils.CreateOrReplaceAsset<T>(asset, path);
|
||||
|
||||
//Debug.Log($"Saved {name} to {LocalResourcesDirectory}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static TAssetType FindAsset<TAssetType>(string name) where TAssetType : UnityEngine.Object
|
||||
{
|
||||
|
||||
string[] defaultPaths = UnityEditor.AssetDatabase.FindAssets(name);
|
||||
if (defaultPaths != null && defaultPaths.Length > 0)
|
||||
{
|
||||
string defaultGUID = defaultPaths[0];
|
||||
string defaultPath = UnityEditor.AssetDatabase.GUIDToAssetPath(defaultGUID);
|
||||
var defaultAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<TAssetType>(defaultPath);
|
||||
|
||||
if (defaultAsset == null)
|
||||
Debug.LogError($"Unable to find asset of {name}. Found path: " + defaultPath);
|
||||
|
||||
return defaultAsset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HVRJointSettings FindJointSettings(string name)
|
||||
{
|
||||
return FindAsset<HVRJointSettings>($"t:hvrjointsettings {name}");
|
||||
}
|
||||
|
||||
public static GameObject FindPrefab(string name)
|
||||
{
|
||||
return FindAsset<GameObject>(string.Format("t:Prefab {0}", name));
|
||||
}
|
||||
#endif
|
||||
public void OnValidate()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b3a39c3d29bcbf45b97a504fc1dedbe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
87
Assets/HurricaneVR/Framework/Scripts/Core/HVRSocketLink.cs
Normal file
87
Assets/HurricaneVR/Framework/Scripts/Core/HVRSocketLink.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using Assets.HurricaneVR.Framework.Shared.Utilities;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core
|
||||
{
|
||||
[RequireComponent(typeof(HVRGrabbable))]
|
||||
public class HVRSocketLink : MonoBehaviour
|
||||
{
|
||||
public HVRSocket Socket;
|
||||
public float ReturnTime = 0f;
|
||||
public bool PlaySocketedSFX = true;
|
||||
|
||||
public HVRGrabbable Grabbable { get; private set; }
|
||||
|
||||
private bool _returning;
|
||||
private float _time;
|
||||
private float _elapsed;
|
||||
private Vector3 _startPos;
|
||||
private Quaternion _startRot;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Setup();
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
Grabbable = GetComponent<HVRGrabbable>();
|
||||
Grabbable.HandFullReleased.AddListener(OnGrabbableReleased);
|
||||
if (!Grabbable.Socketable)
|
||||
{
|
||||
//Socket code expects this, add dummy if not found.
|
||||
Grabbable.Socketable = Grabbable.gameObject.AddComponent<HVRLinkedSocketable>();
|
||||
}
|
||||
if (Socket)
|
||||
{
|
||||
Socket.LinkedGrabbable = Grabbable;
|
||||
this.ExecuteNextUpdate(() => Socket.TryGrab(Grabbable, true, true));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (_returning)
|
||||
{
|
||||
var pos = Socket.transform.TransformPoint(Socket.GetTargetPosition(Grabbable));
|
||||
var rot = Socket.transform.rotation * Socket.GetTargetRotation(Grabbable);
|
||||
var lerp = _elapsed / _time;
|
||||
_elapsed += Time.deltaTime;
|
||||
Grabbable.transform.position = Vector3.Lerp(_startPos, pos, lerp);
|
||||
Grabbable.transform.rotation = Quaternion.Lerp(_startRot, rot, lerp);
|
||||
|
||||
if (lerp >= 1f)
|
||||
{
|
||||
Grabbable.CanBeGrabbed = true;
|
||||
_returning = false;
|
||||
Grabbable.ResetToNonTrigger();
|
||||
Socket.TryGrab(Grabbable, true, PlaySocketedSFX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGrabbableReleased(HVRGrabberBase arg0, HVRGrabbable arg1)
|
||||
{
|
||||
if (!Socket)
|
||||
return;
|
||||
|
||||
if (ReturnTime > 0f)
|
||||
{
|
||||
_returning = true;
|
||||
var pos = Socket.transform.TransformPoint(Socket.GetTargetPosition(Grabbable));
|
||||
_time = ReturnTime;
|
||||
_elapsed = 0f;
|
||||
Grabbable.SetAllToTrigger();
|
||||
_startPos = Grabbable.transform.position;
|
||||
_startRot = Grabbable.transform.rotation;
|
||||
Grabbable.CanBeGrabbed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Socket.TryGrab(Grabbable, true, PlaySocketedSFX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96785e435f4a7a04d84021e497eeb783
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/HurricaneVR/Framework/Scripts/Core/HandPoser.meta
Normal file
8
Assets/HurricaneVR/Framework/Scripts/Core/HandPoser.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb861c724e29b4449b327730f729e990
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 516c9b05d2377ab4fba54bd370ec2157
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class HVRHandPoseData
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
public HVRPosableFingerData Thumb;
|
||||
public HVRPosableFingerData Index;
|
||||
public HVRPosableFingerData Middle;
|
||||
public HVRPosableFingerData Ring;
|
||||
public HVRPosableFingerData Pinky;
|
||||
|
||||
private HVRPosableFingerData[] _fingers;
|
||||
|
||||
public HVRPosableFingerData[] Fingers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_fingers == null || _fingers.Length == 0)
|
||||
{
|
||||
var fingers = new List<HVRPosableFingerData>();
|
||||
|
||||
if (Thumb != null)
|
||||
{
|
||||
fingers.Add(Thumb);
|
||||
}
|
||||
|
||||
if (Index != null)
|
||||
{
|
||||
fingers.Add(Index);
|
||||
}
|
||||
|
||||
if (Middle != null)
|
||||
{
|
||||
fingers.Add(Middle);
|
||||
}
|
||||
|
||||
if (Ring != null)
|
||||
{
|
||||
fingers.Add(Ring);
|
||||
}
|
||||
|
||||
if (Pinky != null)
|
||||
{
|
||||
fingers.Add(Pinky);
|
||||
}
|
||||
|
||||
_fingers = fingers.ToArray();
|
||||
}
|
||||
|
||||
return _fingers;
|
||||
}
|
||||
}
|
||||
|
||||
public HVRHandPoseData()
|
||||
{
|
||||
}
|
||||
|
||||
public HVRHandPoseData DeepCopy()
|
||||
{
|
||||
var copy = new HVRHandPoseData();
|
||||
|
||||
copy.Position = Position;
|
||||
copy.Rotation = Rotation;
|
||||
copy.Thumb = Thumb?.DeepCopy();
|
||||
copy.Index = Index?.DeepCopy();
|
||||
copy.Middle = Middle?.DeepCopy();
|
||||
copy.Ring = Ring?.DeepCopy();
|
||||
copy.Pinky = Pinky?.DeepCopy();
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void CopyTo(HVRHandPoseData data)
|
||||
{
|
||||
data.Position = Position;
|
||||
data.Rotation = Rotation;
|
||||
|
||||
for (var i = 0; i < Fingers.Length; i++)
|
||||
{
|
||||
var finger = Fingers[i];
|
||||
|
||||
for (var j = 0; j < finger.Bones.Count; j++)
|
||||
{
|
||||
var bone = finger.Bones[j];
|
||||
|
||||
data.Fingers[i].Bones[j].Position = bone.Position;
|
||||
data.Fingers[i].Bones[j].Rotation = bone.Rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var writer = new BinaryWriter(ms))
|
||||
{
|
||||
writer.Write(Position.x);
|
||||
writer.Write(Position.y);
|
||||
writer.Write(Position.z);
|
||||
|
||||
writer.Write(Rotation.x);
|
||||
writer.Write(Rotation.y);
|
||||
writer.Write(Rotation.z);
|
||||
writer.Write(Rotation.w);
|
||||
|
||||
writer.Write(Fingers.Length);
|
||||
|
||||
foreach (var finger in Fingers)
|
||||
{
|
||||
writer.Write(finger.Bones.Count);
|
||||
|
||||
foreach (var bone in finger.Bones)
|
||||
{
|
||||
writer.Write(bone.Position.x);
|
||||
writer.Write(bone.Position.y);
|
||||
writer.Write(bone.Position.z);
|
||||
|
||||
writer.Write(bone.Rotation.x);
|
||||
writer.Write(bone.Rotation.y);
|
||||
writer.Write(bone.Rotation.z);
|
||||
writer.Write(bone.Rotation.w);
|
||||
}
|
||||
}
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static HVRHandPoseData FromByteArray(byte[] bytes, HVRHandSide side)
|
||||
{
|
||||
var pose = new HVRHandPoseData();
|
||||
|
||||
using (var ms = new MemoryStream(bytes))
|
||||
{
|
||||
using (var reader = new BinaryReader(ms))
|
||||
{
|
||||
pose.Position = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
pose.Rotation = new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
|
||||
var fingers = reader.ReadInt32();
|
||||
|
||||
for (var i = 0; i < fingers; i++)
|
||||
{
|
||||
var finger = new HVRPosableFingerData();
|
||||
finger.Bones = new List<HVRPosableBoneData>();
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
pose.Thumb = finger;
|
||||
break;
|
||||
case 1:
|
||||
pose.Index = finger;
|
||||
break;
|
||||
case 2:
|
||||
pose.Middle = finger;
|
||||
break;
|
||||
case 3:
|
||||
pose.Ring = finger;
|
||||
break;
|
||||
case 4:
|
||||
pose.Pinky = finger;
|
||||
break;
|
||||
}
|
||||
|
||||
var bones = reader.ReadInt32();
|
||||
for (var j = 0; j < bones; j++)
|
||||
{
|
||||
var bone = new HVRPosableBoneData();
|
||||
finger.Bones.Add(bone);
|
||||
bone.Position = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
bone.Rotation = new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pose;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa807b4a09a146cd984c5554578977a7
|
||||
timeCreated: 1597000245
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class HVRPosableBoneData
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
public HVRPosableBoneData DeepCopy()
|
||||
{
|
||||
return new HVRPosableBoneData()
|
||||
{
|
||||
Position = Position,
|
||||
Rotation = Rotation
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cdb877408734211b200721827ec211f
|
||||
timeCreated: 1597000264
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class HVRPosableFingerData
|
||||
{
|
||||
public List<HVRPosableBoneData> Bones = new List<HVRPosableBoneData>();
|
||||
|
||||
public HVRPosableFingerData DeepCopy()
|
||||
{
|
||||
var finger = new HVRPosableFingerData();
|
||||
foreach (var bone in Bones)
|
||||
{
|
||||
finger.Bones.Add(bone.DeepCopy());
|
||||
}
|
||||
|
||||
return finger;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01072954f48f4b5ebb2286e938a452f8
|
||||
timeCreated: 1597000256
|
||||
@@ -0,0 +1,37 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using UnityEngine;
|
||||
|
||||
//namespace HurricaneVR.Framework.Shared.HandPoser.Data
|
||||
//{
|
||||
// [CreateAssetMenu(menuName = "HurricaneVR/RuntimePoses", fileName = "RuntimePoses")]
|
||||
// public class HVRRuntimePosesData : ScriptableObject
|
||||
// {
|
||||
// public List<VRRunTimePose> PosableGrabPoints = new List<VRRunTimePose>();
|
||||
|
||||
// public void AddPose(VRRunTimePose pose)
|
||||
// {
|
||||
// PosableGrabPoints.Add(pose);
|
||||
// }
|
||||
|
||||
//#if UNITY_EDITOR
|
||||
// public void Save()
|
||||
// {
|
||||
// UnityEditor.AssetDatabase.Refresh();
|
||||
// UnityEditor.EditorUtility.SetDirty(this);
|
||||
// UnityEditor.AssetDatabase.SaveAssets();
|
||||
// }
|
||||
|
||||
//#endif
|
||||
|
||||
// }
|
||||
|
||||
// [Serializable]
|
||||
// public class VRRunTimePose
|
||||
// {
|
||||
// public HVRHandPoseData Left;
|
||||
// public HVRHandPoseData Right;
|
||||
// public Vector3 Position;
|
||||
// public Quaternion Rotation;
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c83ae622a0254145b072f111f9df16b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public static class HVRAnimationParameters
|
||||
{
|
||||
private static readonly Dictionary<string, float> _leftFloatParams = new Dictionary<string, float>();
|
||||
private static readonly Dictionary<string, float> _rightFloatParams = new Dictionary<string, float>();
|
||||
private static readonly Dictionary<string, bool> _leftBooleanParams = new Dictionary<string, bool>();
|
||||
private static readonly Dictionary<string, bool> _rightBooleanParams = new Dictionary<string, bool>();
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void Init()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_leftFloatParams?.Clear();
|
||||
_rightFloatParams?.Clear();
|
||||
_leftBooleanParams?.Clear();
|
||||
_rightBooleanParams?.Clear();
|
||||
}
|
||||
|
||||
public static void ClearFloatParameter(HVRHandSide side, string parameter)
|
||||
{
|
||||
var map = side == HVRHandSide.Left ? _leftFloatParams : _rightFloatParams;
|
||||
if(map.ContainsKey(parameter))
|
||||
{
|
||||
map.Remove(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearBoolParameter(HVRHandSide side, string parameter)
|
||||
{
|
||||
var map = side == HVRHandSide.Left ? _leftBooleanParams : _rightBooleanParams;
|
||||
if (map.ContainsKey(parameter))
|
||||
{
|
||||
map.Remove(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void SetFloatParameter(HVRHandSide side, string parameter, float value)
|
||||
{
|
||||
var map = side == HVRHandSide.Left ? _leftFloatParams : _rightFloatParams;
|
||||
map[parameter] = value;
|
||||
}
|
||||
|
||||
public static float GetFloatParameter(HVRHandSide side, string parameter)
|
||||
{
|
||||
var map = side == HVRHandSide.Left ? _leftFloatParams : _rightFloatParams;
|
||||
map.TryGetValue(parameter, out float value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void SetBoolParameter(HVRHandSide side, string parameter, bool value)
|
||||
{
|
||||
var map = side == HVRHandSide.Left ? _leftBooleanParams : _rightBooleanParams;
|
||||
map[parameter] = value;
|
||||
}
|
||||
|
||||
public static bool GetBoolParameter(HVRHandSide side, string parameter)
|
||||
{
|
||||
var map = side == HVRHandSide.Left ? _leftBooleanParams : _rightBooleanParams;
|
||||
map.TryGetValue(parameter, out bool value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee092aa77d534bf489a538216bb710b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using HurricaneVR.Framework.ControllerInput;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRGrabPointSwapper : HVRInputAction
|
||||
{
|
||||
public HVRPosableGrabPoint[] GrabPoints;
|
||||
|
||||
[Tooltip("Rotation direction when moving to this grab point index.")]
|
||||
public HVRAxis[] RotateAxis;
|
||||
|
||||
[Tooltip("Time it takes to get to the next grab point.")]
|
||||
public float SwapTime = .2f;
|
||||
|
||||
[Tooltip("Used when other hands with poser index > 0.")]
|
||||
public PoserGrabPoints[] OtherHands;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
if (GrabPoints != null)
|
||||
{
|
||||
GrabPoints = GrabPoints.Where(e => e).ToArray();
|
||||
}
|
||||
|
||||
//Grabbable.ParentHandModel = false;
|
||||
}
|
||||
|
||||
protected override void CheckInput(HVRController controller)
|
||||
{
|
||||
var activated = GetActivated(controller);
|
||||
|
||||
if (activated)
|
||||
{
|
||||
Swap();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool GetActivated(HVRController controller)
|
||||
{
|
||||
var activated = false;
|
||||
|
||||
if (controller.ControllerType == HVRControllerType.WMR)
|
||||
{
|
||||
activated = controller.Side == HVRHandSide.Right ? controller.TrackPadLeft.JustActivated : controller.TrackPadRight.JustActivated;
|
||||
}
|
||||
else if (controller.ControllerType == HVRControllerType.Vive)
|
||||
{
|
||||
activated = HVRInputManager.Instance.RightController.TrackPadDown.JustActivated;
|
||||
}
|
||||
else
|
||||
{
|
||||
activated = controller.PrimaryButtonState.JustActivated;
|
||||
}
|
||||
|
||||
return activated;
|
||||
}
|
||||
|
||||
public virtual void Swap()
|
||||
{
|
||||
var hand = Grabbable.HandGrabbers[0];
|
||||
var points = GrabPoints;
|
||||
|
||||
if (hand.PoserIndex > 0)
|
||||
{
|
||||
if (OtherHands == null || hand.PoserIndex - 1 >= OtherHands.Length)
|
||||
return;
|
||||
|
||||
points = OtherHands[hand.PoserIndex - 1].GrabPoints;
|
||||
}
|
||||
|
||||
if (points == null || points.Length == 0)
|
||||
return;
|
||||
|
||||
var index = Array.IndexOf(points, hand.PosableGrabPoint);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
var nextIndex = index + 1;
|
||||
if (index == points.Length - 1)
|
||||
nextIndex = 0;
|
||||
|
||||
var current = hand.PosableGrabPoint;
|
||||
var next = points[nextIndex];
|
||||
var axis = HVRAxis.X;
|
||||
if (RotateAxis != null && RotateAxis.Length > 0 && nextIndex < RotateAxis.Length)
|
||||
axis = RotateAxis[nextIndex];
|
||||
hand.ChangeGrabPoint(next, SwapTime, axis);
|
||||
OnGrabPointSwapped(current, next);
|
||||
}
|
||||
|
||||
protected virtual void OnGrabPointSwapped(HVRPosableGrabPoint previous, HVRPosableGrabPoint next)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PoserGrabPoints
|
||||
{
|
||||
public HVRPosableGrabPoint[] GrabPoints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb138fd2e7814b38be218571e904049c
|
||||
timeCreated: 1635052804
|
||||
@@ -0,0 +1,566 @@
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Core.HandPoser.Data;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using Time = UnityEngine.Time;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRHandAnimator : MonoBehaviour
|
||||
{
|
||||
[FormerlySerializedAs("PosePosAndRot")]
|
||||
[Header("Settings")]
|
||||
[Tooltip("If true the default poser will pose the hand's local position and rotation")]
|
||||
public bool DefaultPoseHand = true; //fyi - false is best, only left true for older users and their projects
|
||||
|
||||
[Tooltip("Finger bend speed when dynamic pose is active")]
|
||||
public float DynamicPoseSpeed = 16f;
|
||||
|
||||
[Header("Components")]
|
||||
public HVRPhysicsPoser PhysicsPoser;
|
||||
public HVRPosableHand Hand;
|
||||
public HVRHandPoser DefaultPoser;
|
||||
|
||||
[Tooltip("Used if the HVRPosableHand component is not on the transform that needs to be posed. IKTarget for VRIK is one example.")]
|
||||
public Transform HandOverride;
|
||||
|
||||
|
||||
[Header("Debug View")]
|
||||
public HVRHandPoser CurrentPoser;
|
||||
|
||||
public HVRHandPoser OverridePoser;
|
||||
|
||||
/// <summary>
|
||||
/// Current hand pose, moves towards BlendedPose based on the speed defined on the Primary Pose
|
||||
/// </summary>
|
||||
private HVRHandPoseData CurrentPose;
|
||||
|
||||
/// <summary>
|
||||
/// Resting hand pose when not holding anything
|
||||
/// </summary>
|
||||
private HVRHandPoseData DefaultPose;
|
||||
|
||||
/// <summary>
|
||||
/// Maintains the state the hand should be moving towards
|
||||
/// </summary>
|
||||
private HVRHandPoseData BlendedPose;
|
||||
|
||||
/// <summary>
|
||||
/// Current Primary Pose of the active Hand Poser
|
||||
/// </summary>
|
||||
private HVRHandPoseData PrimaryPose;
|
||||
|
||||
/// <summary>
|
||||
/// Blend poses are copied to this to apply finger curls to.
|
||||
/// </summary>
|
||||
private HVRHandPoseData BlendTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps tracking of each blend pose target current value
|
||||
/// </summary>
|
||||
private readonly List<HVRHandPoseData> Blends = new List<HVRHandPoseData>(10);
|
||||
|
||||
|
||||
public bool IsMine { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to the finger curl arrays managed by the framework in Start(). Can be overriden after start with a float[5] array if you want to supply your own curl data.
|
||||
/// </summary>
|
||||
public float[] FingerCurlSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable to disable finger curl influence on the hand pose
|
||||
/// </summary>
|
||||
public bool IgnoreCurls { get; set; } = false;
|
||||
|
||||
public bool DynamicPose { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the CurrentPoser is a pose from a held object
|
||||
/// </summary>
|
||||
public bool HandHeldPose { get; internal set; }
|
||||
|
||||
private bool _poseHand = true;
|
||||
private bool _poseHandOverride;
|
||||
private float[] _fingerCurls;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
_fingerCurls = new float[5];
|
||||
|
||||
if (!PhysicsPoser)
|
||||
{
|
||||
PhysicsPoser = GetComponent<HVRPhysicsPoser>();
|
||||
}
|
||||
|
||||
if (!DefaultPoser)
|
||||
{
|
||||
DefaultPoser = GetComponent<HVRHandPoser>();
|
||||
}
|
||||
|
||||
if (!Hand)
|
||||
{
|
||||
Hand = GetComponent<HVRPosableHand>();
|
||||
}
|
||||
|
||||
|
||||
DefaultPose = DefaultPoser.PrimaryPose.Pose.GetPose(Hand.IsLeft).DeepCopy();
|
||||
CurrentPose = DefaultPose.DeepCopy();
|
||||
BlendedPose = DefaultPose.DeepCopy();
|
||||
BlendTarget = DefaultPose.DeepCopy();
|
||||
|
||||
if (IsMine)
|
||||
{
|
||||
FingerCurlSource = Hand.IsLeft ? HVRController.LeftFingerCurls : HVRController.RightFingerCurls;
|
||||
}
|
||||
|
||||
ValidateDefaultFingerType("Thumb", ref DefaultPoser.PrimaryPose.ThumbType);
|
||||
ValidateDefaultFingerType("Index", ref DefaultPoser.PrimaryPose.IndexType);
|
||||
ValidateDefaultFingerType("Middle", ref DefaultPoser.PrimaryPose.MiddleType);
|
||||
ValidateDefaultFingerType("Ring", ref DefaultPoser.PrimaryPose.RingType);
|
||||
ValidateDefaultFingerType("Pinky", ref DefaultPoser.PrimaryPose.PinkyType);
|
||||
|
||||
ResetToDefault();
|
||||
}
|
||||
|
||||
private void ValidateDefaultFingerType(string fingerName, ref HVRFingerType finger)
|
||||
{
|
||||
if (finger == HVRFingerType.Close)
|
||||
{
|
||||
//Debug.LogWarning($"{name} Default HVRHandPoser Primary Pose has Finger Curls [{fingerName}] set to Close. Setting to Static.");
|
||||
finger = HVRFingerType.Static;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
UpdateFingerCurls();
|
||||
UpdatePoser();
|
||||
}
|
||||
|
||||
protected virtual void UpdateFingerCurls()
|
||||
{
|
||||
if (FingerCurlSource == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_fingerCurls[i] = FingerCurlSource[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void ZeroFingerCurls()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_fingerCurls[i] = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
private void UpdatePoser()
|
||||
{
|
||||
if (DynamicPose)
|
||||
{
|
||||
PrimaryPose.CopyTo(BlendedPose);
|
||||
ApplyBlend(CurrentPose, BlendedPose, DefaultPoser.PrimaryPose, DynamicPoseSpeed);
|
||||
Hand.Pose(CurrentPose, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var poseHand = _poseHand;
|
||||
var poser = CurrentPoser;
|
||||
if (OverridePoser && !HandHeldPose)
|
||||
{
|
||||
poser = OverridePoser;
|
||||
poseHand = _poseHandOverride;
|
||||
}
|
||||
|
||||
if (!poser)
|
||||
return;
|
||||
|
||||
UpdateBlends(poser);
|
||||
ApplyBlending(poser);
|
||||
|
||||
if (poseHand)
|
||||
{
|
||||
if (HandOverride)
|
||||
{
|
||||
HandOverride.localPosition = CurrentPose.Position;
|
||||
HandOverride.localRotation = CurrentPose.Rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
Hand.Pose(CurrentPose, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Hand.Pose(CurrentPose, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBlends(HVRHandPoser poser)
|
||||
{
|
||||
if (!IsMine)
|
||||
return;
|
||||
|
||||
UpdateBlendValue(poser.PrimaryPose);
|
||||
|
||||
if (poser.Blends == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < poser.Blends.Count; i++)
|
||||
{
|
||||
UpdateBlendValue(poser.Blends[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the blend value depending on it's settings, immediate, button parameter, string parameter etc.
|
||||
/// </summary>
|
||||
/// <param name="blend"></param>
|
||||
private void UpdateBlendValue(HVRHandPoseBlend blend)
|
||||
{
|
||||
if (blend.Disabled)
|
||||
{
|
||||
blend.Value = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (blend.Type == BlendType.Manual) return;
|
||||
|
||||
if (blend.Type == BlendType.Immediate)
|
||||
{
|
||||
blend.Value = 1f;
|
||||
}
|
||||
else if (blend.ButtonParameter)
|
||||
{
|
||||
var button = HVRController.GetButtonState(Hand.Side, blend.Button);
|
||||
if (blend.Type == BlendType.BooleanParameter)
|
||||
{
|
||||
blend.Value = button.Active ? 1f : 0f;
|
||||
}
|
||||
else if (blend.Type == BlendType.FloatParameter)
|
||||
{
|
||||
blend.Value = button.Value;
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(blend.AnimationParameter) && blend.AnimationParameter != "None")
|
||||
{
|
||||
if (blend.Type == BlendType.BooleanParameter)
|
||||
{
|
||||
blend.Value = HVRAnimationParameters.GetBoolParameter(Hand.Side, blend.AnimationParameter) ? 1f : 0f;
|
||||
}
|
||||
else if (blend.Type == BlendType.FloatParameter)
|
||||
{
|
||||
blend.Value = HVRAnimationParameters.GetFloatParameter(Hand.Side, blend.AnimationParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ApplyBlending(HVRHandPoser poser)
|
||||
{
|
||||
PrimaryPose.CopyTo(BlendedPose);
|
||||
ApplyFingerCurls(DefaultPose, PrimaryPose, BlendedPose, poser.PrimaryPose);
|
||||
|
||||
if (poser.Blends != null)
|
||||
{
|
||||
for (int i = 0; i < poser.Blends.Count; i++)
|
||||
{
|
||||
var blend = poser.Blends[i];
|
||||
|
||||
if (blend.Disabled || !blend.Pose)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var blendPose = blend.Pose.GetPose(Hand.Side);
|
||||
|
||||
if (blendPose == null) continue;
|
||||
|
||||
blendPose.CopyTo(BlendTarget); //copied to apply the finger curls to prevent mucking up the original pose
|
||||
|
||||
//apply finger curl to target pose
|
||||
ApplyFingerCurls(PrimaryPose, blendPose, BlendTarget, blend);
|
||||
|
||||
//updated target pose by weighted value and speed
|
||||
UpdateBlendTarget(PrimaryPose, BlendTarget, Blends[i], blend);
|
||||
|
||||
//update blended pose by weight to target
|
||||
UpdateBlendedPose(Blends[i], BlendedPose, blend);
|
||||
}
|
||||
}
|
||||
|
||||
ApplyBlend(CurrentPose, BlendedPose, poser.PrimaryPose, poser.PrimaryPose.Speed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the pose target by lerping between the hand relaxed pose and the target pose using finger curl tracking
|
||||
/// </summary>
|
||||
private void ApplyFingerCurls(HVRHandPoseData startPose, HVRHandPoseData endPose, HVRHandPoseData targetPose, HVRHandPoseBlend blend)
|
||||
{
|
||||
for (int i = 0; i < targetPose.Fingers.Length; i++)
|
||||
{
|
||||
var targetFinger = targetPose.Fingers[i];
|
||||
var startFinger = startPose.Fingers[i];
|
||||
var endFinger = endPose.Fingers[i];
|
||||
|
||||
var fingerType = blend.GetFingerType(i);
|
||||
|
||||
if (fingerType != HVRFingerType.Close)
|
||||
continue;
|
||||
|
||||
var fingerStart = blend.GetFingerStart(i);
|
||||
var curl = _fingerCurls[i];
|
||||
|
||||
if (IgnoreCurls)
|
||||
{
|
||||
curl = 0f;
|
||||
}
|
||||
|
||||
var remainder = 1 - fingerStart;
|
||||
curl = fingerStart + curl * remainder;
|
||||
curl = Mathf.Clamp(curl, 0f, 1f);
|
||||
|
||||
|
||||
for (int j = 0; j < targetFinger.Bones.Count; j++)
|
||||
{
|
||||
targetFinger.Bones[j].Position = Vector3.Lerp(startFinger.Bones[j].Position, endFinger.Bones[j].Position, curl);
|
||||
targetFinger.Bones[j].Rotation = Quaternion.Lerp(startFinger.Bones[j].Rotation, endFinger.Bones[j].Rotation, curl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the blended pose towards the weight target lerp(start, end, value * weight) using the blend speed
|
||||
/// </summary>
|
||||
private void UpdateBlendTarget(HVRHandPoseData startPose, HVRHandPoseData endPose, HVRHandPoseData blendedPose, HVRHandPoseBlend blend)
|
||||
{
|
||||
var lerp = blend.Value * blend.Weight;
|
||||
var blendLerp = blend.Speed < .01f ? 1f : blend.Speed * Time.deltaTime;
|
||||
|
||||
if (blend.Mask == HVRHandPoseMask.None || (blend.Mask & HVRHandPoseMask.Hand) == HVRHandPoseMask.Hand)
|
||||
{
|
||||
var targetPos = Vector3.Lerp(startPose.Position, endPose.Position, lerp);
|
||||
var targetRot = Quaternion.Lerp(startPose.Rotation, endPose.Rotation, lerp);
|
||||
|
||||
blendedPose.Position = Vector3.Lerp(blendedPose.Position, targetPos, blendLerp);
|
||||
blendedPose.Rotation = Quaternion.Lerp(blendedPose.Rotation, targetRot, blendLerp);
|
||||
}
|
||||
|
||||
for (var i = 0; i < blendedPose.Fingers.Length; i++)
|
||||
{
|
||||
var blendedFinger = blendedPose.Fingers[i];
|
||||
var endFinger = endPose.Fingers[i];
|
||||
var startFinger = startPose.Fingers[i];
|
||||
|
||||
if (!TryGetMask(i, out var mask)) continue;
|
||||
|
||||
if (blend.Mask == HVRHandPoseMask.None || (blend.Mask & mask) == mask)
|
||||
{
|
||||
for (var j = 0; j < blendedFinger.Bones.Count; j++)
|
||||
{
|
||||
var blendedBone = blendedFinger.Bones[j];
|
||||
var endBone = endFinger.Bones[j];
|
||||
var startBone = startFinger.Bones[j];
|
||||
|
||||
var targetPos = Vector3.Lerp(startBone.Position, endBone.Position, lerp);
|
||||
var targetRot = Quaternion.Lerp(startBone.Rotation, endBone.Rotation, lerp);
|
||||
|
||||
blendedBone.Position = Vector3.Lerp(blendedBone.Position, targetPos, blendLerp);
|
||||
blendedBone.Rotation = Quaternion.Lerp(blendedBone.Rotation, targetRot, blendLerp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the blended pose to the blend target based on weighted value.
|
||||
/// </summary>
|
||||
private void UpdateBlendedPose(HVRHandPoseData targetPose, HVRHandPoseData blendedPose, HVRHandPoseBlend blend)
|
||||
{
|
||||
var lerp = blend.Value * blend.Weight;
|
||||
|
||||
if (blend.Mask == HVRHandPoseMask.None || (blend.Mask & HVRHandPoseMask.Hand) == HVRHandPoseMask.Hand)
|
||||
{
|
||||
blendedPose.Position = Vector3.Lerp(blendedPose.Position, targetPose.Position, lerp);
|
||||
blendedPose.Rotation = Quaternion.Lerp(blendedPose.Rotation, targetPose.Rotation, lerp);
|
||||
}
|
||||
|
||||
for (var i = 0; i < blendedPose.Fingers.Length; i++)
|
||||
{
|
||||
if (!TryGetMask(i, out var mask)) continue;
|
||||
|
||||
if (blend.Mask == HVRHandPoseMask.None || (blend.Mask & mask) == mask)
|
||||
{
|
||||
for (var j = 0; j < blendedPose.Fingers[i].Bones.Count; j++)
|
||||
{
|
||||
var blendedBone = blendedPose.Fingers[i].Bones[j];
|
||||
|
||||
blendedBone.Position = Vector3.Lerp(blendedBone.Position, targetPose.Fingers[i].Bones[j].Position, lerp);
|
||||
blendedBone.Rotation = Quaternion.Lerp(blendedBone.Rotation, targetPose.Fingers[i].Bones[j].Rotation, lerp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool TryGetMask(int i, out HVRHandPoseMask mask)
|
||||
{
|
||||
mask = HVRHandPoseMask.None;
|
||||
if (i == 0) mask = HVRHandPoseMask.Thumb;
|
||||
else if (i == 1) mask = HVRHandPoseMask.Index;
|
||||
else if (i == 2) mask = HVRHandPoseMask.Middle;
|
||||
else if (i == 3) mask = HVRHandPoseMask.Ring;
|
||||
else if (i == 4) mask = HVRHandPoseMask.Pinky;
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplyBlend(HVRHandPoseData currentHand, HVRHandPoseData targetHandPose, HVRHandPoseBlend blend, float speed)
|
||||
{
|
||||
var blendLerp = speed < .01f ? 1f : speed * Time.deltaTime;
|
||||
|
||||
if (blend.Mask == HVRHandPoseMask.None || (blend.Mask & HVRHandPoseMask.Hand) == HVRHandPoseMask.Hand)
|
||||
{
|
||||
currentHand.Position = Vector3.Lerp(currentHand.Position, targetHandPose.Position, blendLerp);
|
||||
currentHand.Rotation = Quaternion.Lerp(currentHand.Rotation, targetHandPose.Rotation, blendLerp);
|
||||
}
|
||||
|
||||
for (var i = 0; i < currentHand.Fingers.Length; i++)
|
||||
{
|
||||
var currentFinger = currentHand.Fingers[i];
|
||||
var targetFinger = targetHandPose.Fingers[i];
|
||||
|
||||
if (!TryGetMask(i, out var mask)) continue;
|
||||
|
||||
if (blend.Mask == HVRHandPoseMask.None || (blend.Mask & mask) == mask)
|
||||
{
|
||||
for (var j = 0; j < currentFinger.Bones.Count; j++)
|
||||
{
|
||||
var currentBone = currentFinger.Bones[j];
|
||||
var targetBone = targetFinger.Bones[j];
|
||||
|
||||
currentBone.Position = Vector3.Lerp(currentBone.Position, targetBone.Position, blendLerp);
|
||||
currentBone.Rotation = Quaternion.Lerp(currentBone.Rotation, targetBone.Rotation, blendLerp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetToDefault()
|
||||
{
|
||||
DynamicPose = false;
|
||||
_poseHand = DefaultPoseHand;
|
||||
CurrentPoser = DefaultPoser;
|
||||
if (HandHeldPose || !OverridePoser)
|
||||
{
|
||||
SetupPoser(CurrentPoser);
|
||||
}
|
||||
else if (OverridePoser)
|
||||
{
|
||||
SetupPoser(OverridePoser);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartDynamicPose(HVRHandPoseData pose)
|
||||
{
|
||||
CurrentPoser = null;
|
||||
DynamicPose = true;
|
||||
PrimaryPose = pose;
|
||||
_poseHand = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or reset (pass null) a poser that will take precedence over framework hover posers, but won't take precedence over held object poses.
|
||||
/// </summary>
|
||||
public virtual void SetOverridePoser(HVRHandPoser poser, bool poseHand = false)
|
||||
{
|
||||
_poseHandOverride = poseHand;
|
||||
OverridePoser = poser;
|
||||
|
||||
if (!poser)
|
||||
{
|
||||
SetupPoser(CurrentPoser);
|
||||
}
|
||||
else if (!HandHeldPose)
|
||||
{
|
||||
SetupPoser(poser);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHeldPoser(HVRHandPoser poser, bool poseHand = false)
|
||||
{
|
||||
HandHeldPose = true;
|
||||
_poseHand = poseHand;
|
||||
CurrentPoser = poser;
|
||||
SetupPoser(poser);
|
||||
}
|
||||
|
||||
public void OnHeldObjectReleased()
|
||||
{
|
||||
HandHeldPose = false;
|
||||
DynamicPose = false;
|
||||
CurrentPoser = DefaultPoser;
|
||||
_poseHand = DefaultPoseHand;
|
||||
if (OverridePoser)
|
||||
{
|
||||
SetupPoser(OverridePoser);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupPoser(CurrentPoser);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetCurrentPoser(HVRHandPoser poser, bool poseHand = false)
|
||||
{
|
||||
_poseHand = poseHand;
|
||||
CurrentPoser = poser;
|
||||
if (!OverridePoser || HandHeldPose)
|
||||
SetupPoser(poser);
|
||||
}
|
||||
|
||||
protected virtual void SetupPoser(HVRHandPoser poser)
|
||||
{
|
||||
if (!poser || poser.PrimaryPose == null) return;
|
||||
|
||||
PrimaryPose = poser.PrimaryPose.Pose.GetPose(Hand.IsLeft);
|
||||
|
||||
//set the currentpose to the current hand state
|
||||
Hand.CopyHandData(CurrentPose);
|
||||
|
||||
//add blends to cache if necessary
|
||||
if (poser.Blends.Count > Blends.Count)
|
||||
{
|
||||
var count = Blends.Count;
|
||||
for (int i = 0; i < poser.Blends.Count - count; i++)
|
||||
{
|
||||
Blends.Add(DefaultPose.DeepCopy());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0; i < poser.Blends.Count; i++)
|
||||
{
|
||||
poser.Blends[i].Value = 0f; //reset blend weight
|
||||
PrimaryPose.CopyTo(Blends[i]); //copy primary pose to the blend pose as a base
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ebbffaca5ed8a340ae381fb8a5695d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
[CreateAssetMenu(menuName = "HurricaneVR/Mirror Settings", fileName = "HandMirrorSettings")]
|
||||
public class HVRHandMirrorSettings : ScriptableObject
|
||||
{
|
||||
public bool UseThumbSetting;
|
||||
public bool UseIndexSetting;
|
||||
public bool UseMiddleSetting;
|
||||
public bool UseRingSetting;
|
||||
public bool UsePinkySetting;
|
||||
|
||||
public HVRJointMirrorSetting AllSetting;
|
||||
public HVRJointMirrorSetting ThumbSetting;
|
||||
public HVRJointMirrorSetting IndexSetting;
|
||||
public HVRJointMirrorSetting MiddleSetting;
|
||||
public HVRJointMirrorSetting RingSetting;
|
||||
public HVRJointMirrorSetting PinkySetting;
|
||||
|
||||
public List<HVRJointMirrorSetting> ThumbSettings;
|
||||
public List<HVRJointMirrorSetting> IndexSettings;
|
||||
public List<HVRJointMirrorSetting> MiddleSettings;
|
||||
public List<HVRJointMirrorSetting> RingSettings;
|
||||
public List<HVRJointMirrorSetting> PinkySettings;
|
||||
}
|
||||
|
||||
public enum FingerMirrorRotation
|
||||
{
|
||||
Same, Opposite, Minus180, Plus180, Neg180Minus, P180Minus
|
||||
}
|
||||
|
||||
public enum FingerMirrorPosition
|
||||
{
|
||||
Opposite, Same
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HVRJointMirrorSetting
|
||||
{
|
||||
public FingerMirrorRotation XRotation;
|
||||
public FingerMirrorRotation YRotation;
|
||||
public FingerMirrorRotation ZRotation;
|
||||
|
||||
public FingerMirrorPosition XPosition;
|
||||
public FingerMirrorPosition YPosition;
|
||||
public FingerMirrorPosition ZPosition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: becb07c7d7e24b889e62802a34e26d48
|
||||
timeCreated: 1606578207
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using HurricaneVR.Framework.Core.HandPoser.Data;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRHandMirrorer : MonoBehaviour
|
||||
{
|
||||
|
||||
public virtual void MirrorFingers(HVRPosableHand hand, HVRHandPoseData clone)
|
||||
{
|
||||
if (hand.Thumb != null)
|
||||
{
|
||||
clone.Thumb = MirrorFingerData(hand.Thumb);
|
||||
}
|
||||
|
||||
if (hand.Index != null)
|
||||
{
|
||||
clone.Index = MirrorFingerData(hand.Index);
|
||||
}
|
||||
|
||||
if (hand.Middle != null)
|
||||
{
|
||||
clone.Middle = MirrorFingerData(hand.Middle);
|
||||
}
|
||||
|
||||
if (hand.Ring != null)
|
||||
{
|
||||
clone.Ring = MirrorFingerData(hand.Ring);
|
||||
}
|
||||
|
||||
if (hand.Pinky != null)
|
||||
{
|
||||
clone.Pinky = MirrorFingerData(hand.Pinky);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public virtual void MirrorFingers(HVRPosableHand source, HVRPosableHand target)
|
||||
{
|
||||
MirrorFinger(source.Thumb, target.Thumb);
|
||||
MirrorFinger(source.Index, target.Index);
|
||||
MirrorFinger(source.Middle, target.Middle);
|
||||
MirrorFinger(source.Ring, target.Ring);
|
||||
MirrorFinger(source.Pinky, target.Pinky);
|
||||
}
|
||||
|
||||
public virtual HVRPosableFingerData MirrorFingerData(HVRPosableFinger finger)
|
||||
{
|
||||
var fingerData = new HVRPosableFingerData();
|
||||
|
||||
for (var i = 0; i < finger.Bones.Count; i++)
|
||||
{
|
||||
var boneData = new HVRPosableBoneData();
|
||||
|
||||
var bone = finger.Bones[i];
|
||||
|
||||
var rot = MirrorBone(bone, out var pos);
|
||||
boneData.Position = pos;
|
||||
boneData.Rotation = rot;
|
||||
|
||||
fingerData.Bones.Add(boneData);
|
||||
}
|
||||
|
||||
return fingerData;
|
||||
}
|
||||
|
||||
public virtual void MirrorFinger(HVRPosableFinger source, HVRPosableFinger target)
|
||||
{
|
||||
if (source == null || target == null || source.Bones.Count != target.Bones.Count)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < source.Bones.Count; i++)
|
||||
{
|
||||
var sourceBone = source.Bones[i];
|
||||
target.Bones[i].Transform.localRotation = MirrorBone(sourceBone, out var pos);
|
||||
target.Bones[i].Transform.localPosition = pos;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Quaternion MirrorBone(HVRPosableBone bone, out Vector3 position)
|
||||
{
|
||||
var cross = Vector3.Cross(bone.Forward, bone.Up);
|
||||
var otherCross = Vector3.Cross(bone.OtherForward, bone.OtherUp);
|
||||
|
||||
|
||||
var forwardAngle = Vector3.Scale(bone.Transform.localEulerAngles, bone.Forward).magnitude;
|
||||
var upAngle = Vector3.Scale(bone.Transform.localEulerAngles, bone.Up).magnitude;
|
||||
var rightAngle = Vector3.Scale(bone.Transform.localEulerAngles, cross).magnitude;
|
||||
|
||||
position = bone.Transform.localPosition;
|
||||
|
||||
var fp = Vector3.Scale(position, ABS(bone.Forward));
|
||||
var up = Vector3.Scale(position, ABS(bone.Up));
|
||||
var rp = Vector3.Scale(position, ABS(cross));
|
||||
|
||||
if (Vector3.Dot(bone.Forward, bone.OtherForward) > 0)
|
||||
{
|
||||
forwardAngle *= -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
fp *= -1f;
|
||||
}
|
||||
|
||||
if (Vector3.Dot(bone.Up, bone.OtherUp) > 0)
|
||||
{
|
||||
upAngle *= -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
up *= -1f;
|
||||
}
|
||||
|
||||
if (Vector3.Dot(cross, otherCross) < 0)
|
||||
{
|
||||
rightAngle *= -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
rp *= -1f;
|
||||
}
|
||||
|
||||
|
||||
var final = forwardAngle * ABS(bone.OtherForward) + upAngle * ABS(bone.OtherUp) + rightAngle * ABS(otherCross);
|
||||
position = Vector3.Scale(fp, ABS(bone.OtherForward)) +
|
||||
Vector3.Scale(up, ABS(bone.OtherUp)) +
|
||||
Vector3.Scale(rp, ABS(otherCross));
|
||||
|
||||
var rot = Quaternion.Euler(final);
|
||||
return rot;
|
||||
}
|
||||
|
||||
public Vector3 ABS(Vector3 v)
|
||||
{
|
||||
return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79648a050a4a4121bb4329449344e118
|
||||
timeCreated: 1637797213
|
||||
@@ -0,0 +1,32 @@
|
||||
using HurricaneVR.Framework.Core.HandPoser.Data;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRHandPose : ScriptableObject
|
||||
{
|
||||
public bool SnappedLeft;
|
||||
|
||||
public HVRHandPoseData LeftHand;
|
||||
public HVRHandPoseData RightHand;
|
||||
|
||||
public HVRHandPoseData GetPose(bool isLeft)
|
||||
{
|
||||
return isLeft ? LeftHand : RightHand;
|
||||
}
|
||||
|
||||
public HVRHandPoseData GetPose(HVRHandSide side)
|
||||
{
|
||||
return side == HVRHandSide.Left ? LeftHand : RightHand;
|
||||
}
|
||||
|
||||
public HVRHandPose DeepCopy()
|
||||
{
|
||||
var copy = ScriptableObject.CreateInstance<HVRHandPose>();
|
||||
copy.LeftHand = LeftHand.DeepCopy();
|
||||
copy.RightHand = RightHand.DeepCopy();
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc30735a6f974fda88a33f70e2b17a3b
|
||||
timeCreated: 1596957987
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
[Serializable]
|
||||
public class HVRHandPoseBlend
|
||||
{
|
||||
public const string DefaultParameter = "";
|
||||
|
||||
public HVRHandPose Pose;
|
||||
[Range(0, 1)] public float Weight = 1f;
|
||||
public HVRHandPoseMask Mask = HVRHandPoseMask.None;
|
||||
|
||||
public BlendType Type = BlendType.Immediate;
|
||||
|
||||
/// <summary>
|
||||
/// Primary Pose : The speed the hand will move toward the blended target pose.
|
||||
/// Secondary Pose : The speed Bool and Immediate type blends will move toward the defined secondary pose.
|
||||
/// </summary>
|
||||
public float Speed = 16;
|
||||
public string AnimationParameter;
|
||||
public bool ButtonParameter;
|
||||
public HVRButtons Button;
|
||||
public bool Disabled;
|
||||
|
||||
public HVRFingerType ThumbType = HVRFingerType.Static;
|
||||
public HVRFingerType IndexType = HVRFingerType.Static;
|
||||
public HVRFingerType MiddleType = HVRFingerType.Static;
|
||||
public HVRFingerType RingType = HVRFingerType.Static;
|
||||
public HVRFingerType PinkyType = HVRFingerType.Static;
|
||||
|
||||
public float ThumbStart = .75f;
|
||||
public float IndexStart = 1f;
|
||||
public float MiddleStart;
|
||||
public float RingStart;
|
||||
public float PinkyStart;
|
||||
|
||||
[NonSerialized]
|
||||
public float Value;
|
||||
|
||||
//[NonSerialized]
|
||||
//public float ElapsedTime;
|
||||
|
||||
public HVRHandPoseBlend()
|
||||
{
|
||||
if (AnimationParameter == null || string.IsNullOrWhiteSpace(AnimationParameter))
|
||||
{
|
||||
AnimationParameter = DefaultParameter;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDefaults()
|
||||
{
|
||||
Speed = 16f;
|
||||
AnimationParameter = DefaultParameter;
|
||||
Weight = 1f;
|
||||
Mask = HVRHandPoseMask.None;
|
||||
Type = BlendType.Immediate;
|
||||
ButtonParameter = false;
|
||||
|
||||
ThumbType = HVRSettings.Instance.ThumbCurlType;
|
||||
IndexType = HVRSettings.Instance.IndexCurlType;
|
||||
MiddleType = HVRSettings.Instance.MiddleCurlType;
|
||||
RingType = HVRSettings.Instance.RingCurlType;
|
||||
PinkyType = HVRSettings.Instance.PinkyCurlType;
|
||||
|
||||
ThumbStart = HVRSettings.Instance.ThumbStart;
|
||||
IndexStart = HVRSettings.Instance.IndexStart;
|
||||
MiddleStart = HVRSettings.Instance.MiddleStart;
|
||||
RingStart = HVRSettings.Instance.RingStart;
|
||||
PinkyStart = HVRSettings.Instance.PinkyStart;
|
||||
}
|
||||
|
||||
public HVRFingerType GetFingerType(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return ThumbType;
|
||||
case 1:
|
||||
return IndexType;
|
||||
case 2:
|
||||
return MiddleType;
|
||||
case 3:
|
||||
return RingType;
|
||||
case 4:
|
||||
return PinkyType;
|
||||
default:
|
||||
return HVRFingerType.Static;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetFingerStart(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return ThumbStart;
|
||||
case 1:
|
||||
return IndexStart;
|
||||
case 2:
|
||||
return MiddleStart;
|
||||
case 3:
|
||||
return RingStart;
|
||||
case 4:
|
||||
return PinkyStart;
|
||||
default:
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum BlendType
|
||||
{
|
||||
Immediate, Manual, FloatParameter, BooleanParameter
|
||||
}
|
||||
|
||||
public enum HVRFingerType
|
||||
{
|
||||
Static,
|
||||
Close
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c0ffcacdaf04dc59ce5bad65e3d452e
|
||||
timeCreated: 1597029386
|
||||
@@ -0,0 +1,56 @@
|
||||
//using HurricaneVR.Scripts.Core;
|
||||
////using HurricaneVR.Scripts.Core.Grabbers;
|
||||
//using HurricaneVR.Scripts.Core.Utils;
|
||||
//using UnityEngine;
|
||||
|
||||
//namespace HurricaneVR.Scripts.HandPoser
|
||||
//{
|
||||
// public class HVRHandPoseRecorder : MonoBehaviour
|
||||
// {
|
||||
// public HVRPosableHand Hand;
|
||||
// //public HVRHandGrabber Grabber;
|
||||
// public HVRHandAnimator Animator;
|
||||
// //public HVRHandPhysics HandPhysics;
|
||||
|
||||
// public bool UsePhysicsPoser;
|
||||
// public bool RecordPoses;
|
||||
// public bool DisablePhysics;
|
||||
|
||||
// private bool _previousRecordPoses;
|
||||
|
||||
// void Start()
|
||||
// {
|
||||
// if (!Hand) Hand = GetComponentInChildren<HVRPosableHand>();
|
||||
// //if (!Grabber) Grabber = GetComponentInChildren<HVRHandGrabber>();
|
||||
// if (!Animator) Animator = GetComponentInChildren<HVRHandAnimator>();
|
||||
// //if (!HandPhysics) HandPhysics = GetComponentInChildren<HVRHandPhysics>();
|
||||
// }
|
||||
|
||||
// void Update()
|
||||
// {
|
||||
// if (Animator) Animator.UsePhysicsPoser = UsePhysicsPoser;
|
||||
|
||||
// if (_previousRecordPoses != RecordPoses)
|
||||
// {
|
||||
// if (RecordPoses && DisablePhysics)
|
||||
// {
|
||||
// //HandPhysics.DisableCollision();
|
||||
// }
|
||||
// else if (!RecordPoses)
|
||||
// {
|
||||
// //HandPhysics.EnableCollision();
|
||||
// }
|
||||
// _previousRecordPoses = !RecordPoses;
|
||||
// }
|
||||
|
||||
// //if (RecordPoses && Hand && Grabber && VRInputActions.GetButtonState(Hand.Side, VRButtons.Primary).JustActivated)
|
||||
// //{
|
||||
// // var closest = Grabber.GrabBag.ClosestGrabbable;
|
||||
// // if (closest && closest.VRGrabPoints)
|
||||
// // {
|
||||
// // closest.VRGrabPoints.Add(Hand);
|
||||
// // }
|
||||
// //}
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b47a4acec44dda449ba365e5d1615204
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
[Flags]
|
||||
public enum HVRHandPoseMask
|
||||
{
|
||||
None = 0, Hand = 1, Thumb = 2, Index = 4, Middle = 8, Ring = 16, Pinky = 32
|
||||
}
|
||||
|
||||
[HelpURL("https://cloudwalker2020.github.io/HurricaneVR-Docs/manual/hand_posing.html")]
|
||||
public class HVRHandPoser : MonoBehaviour
|
||||
{
|
||||
#region EditorState
|
||||
|
||||
[SerializeField]
|
||||
protected GameObject LeftHandPreview;
|
||||
|
||||
[SerializeField]
|
||||
protected GameObject RightHandPreview;
|
||||
|
||||
[SerializeField]
|
||||
protected GameObject BodyPreview;
|
||||
|
||||
[SerializeField]
|
||||
protected bool PreviewLeft;
|
||||
|
||||
[SerializeField]
|
||||
protected bool PreviewRight;
|
||||
|
||||
[SerializeField]
|
||||
protected bool LeftAutoPose;
|
||||
|
||||
[SerializeField]
|
||||
protected bool RightAutoPose;
|
||||
|
||||
[SerializeField]
|
||||
protected int SelectionIndex;
|
||||
|
||||
[SerializeField]
|
||||
public List<string> PoseNames = new List<string>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public HVRHandPoseBlend PrimaryPose;
|
||||
public List<HVRHandPoseBlend> Blends = new List<HVRHandPoseBlend>();
|
||||
|
||||
public MirrorAxis MirrorAxis = MirrorAxis.X;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (LeftHandPreview)
|
||||
{
|
||||
Destroy(LeftHandPreview);
|
||||
Debug.Log($"Left hand preview was still active. Destroyed.");
|
||||
}
|
||||
|
||||
PreviewLeft = false;
|
||||
PreviewRight = false;
|
||||
|
||||
if (RightHandPreview)
|
||||
{
|
||||
Destroy(RightHandPreview);
|
||||
Debug.Log($"Right hand preview was still active. Destroyed.");
|
||||
}
|
||||
|
||||
if (BodyPreview)
|
||||
{
|
||||
Destroy(BodyPreview);
|
||||
Debug.Log($"Full Body Preview was still active. Destroyed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public enum MirrorAxis
|
||||
{
|
||||
X, Y, Z
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96d1b667b6234620a01e126539c3f6d8
|
||||
timeCreated: 1596897615
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRHandTrigger : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 364f55a0f60644beb4e906f6db185d28
|
||||
timeCreated: 1661135657
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRIKTargets : MonoBehaviour
|
||||
{
|
||||
public Transform LeftTarget;
|
||||
public Transform RightTarget;
|
||||
|
||||
public bool IsPoser { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ed12b369b790db4595777fdcb6e72d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,551 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using HurricaneVR.Framework.Core.HandPoser.Data;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public class HVRPhysicsPoser : MonoBehaviour
|
||||
{
|
||||
[Header("Settings")]
|
||||
public int Iterations = 50;
|
||||
public int RaysPerBone = 2;
|
||||
public float SphereRadius = .008f;
|
||||
public bool FingerTipOnly;
|
||||
|
||||
|
||||
[Header("Components")]
|
||||
public HVRPosableHand Hand;
|
||||
public HVRHandPose OpenPose;
|
||||
public HVRHandPose ClosedPose;
|
||||
[Tooltip("Forward vector of the palm for aiming")]
|
||||
public Transform Palm;
|
||||
|
||||
|
||||
[Header("Debug")]
|
||||
public bool DrawSpheres;
|
||||
public bool DrawTips;
|
||||
public bool DrawCollisionPoints;
|
||||
public bool LogHitColliderNames;
|
||||
public int _fingerIndex;
|
||||
public LayerMask CurrentMask;
|
||||
|
||||
|
||||
private readonly Collider[] colliders = new Collider[5];
|
||||
private Vector3[] CollisionPoints;
|
||||
private readonly List<Vector3> _dummy = new List<Vector3>();
|
||||
private bool _validated;
|
||||
|
||||
[SerializeField]
|
||||
private List<Vector3>[] _sphereMap;
|
||||
[SerializeField]
|
||||
private bool[] _collidedBoneTracker;
|
||||
[SerializeField]
|
||||
private int[] _fingerIndices;
|
||||
|
||||
private int[] _boneIteration;
|
||||
|
||||
public bool LiveUpdate;
|
||||
public int[] FingerBends = new int[5];
|
||||
|
||||
private HVRHandPoseData OpenPoseData;
|
||||
private HVRHandPoseData ClosedPoseData;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (Application.isPlaying && Validate())
|
||||
Setup();
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
if (!Hand)
|
||||
{
|
||||
Hand = GetComponent<HVRPosableHand>();
|
||||
}
|
||||
|
||||
if (OpenPose)
|
||||
{
|
||||
OpenPoseData = OpenPose.GetPose(Hand.IsLeft);
|
||||
}
|
||||
|
||||
if (ClosedPose)
|
||||
{
|
||||
ClosedPoseData = ClosedPose.GetPose(Hand.IsLeft);
|
||||
}
|
||||
|
||||
SetupCollision();
|
||||
}
|
||||
|
||||
private void SetupBoneTracker()
|
||||
{
|
||||
if (Hand)
|
||||
{
|
||||
var count = 0;
|
||||
_fingerIndices = new int[Hand.Fingers.Length];
|
||||
for (var i = 0; i < Hand.Fingers.Length; i++)
|
||||
{
|
||||
_fingerIndices[i] = count;
|
||||
var finger = Hand.Fingers[i];
|
||||
count += finger.Bones.Count;
|
||||
}
|
||||
|
||||
_collidedBoneTracker = new bool[count];
|
||||
_sphereMap = new List<Vector3>[count];
|
||||
_boneIteration = new int[count];
|
||||
CollisionPoints = new Vector3[count];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
UpdateLive();
|
||||
}
|
||||
|
||||
private void UpdateLive()
|
||||
{
|
||||
if (LiveUpdate)
|
||||
{
|
||||
ResetCollidedBones();
|
||||
|
||||
// var watch = Stopwatch.StartNew();
|
||||
for (var f = 0; f < Hand.Fingers.Length; f++)
|
||||
{
|
||||
var finger = Hand.Fingers[f];
|
||||
var anyBoneHit = false;
|
||||
|
||||
for (var i = FingerBends[f]; i < Iterations; i++)
|
||||
{
|
||||
for (var b = 0; b < finger.Bones.Count; b++)
|
||||
{
|
||||
if (CheckBone(f, b, i))
|
||||
{
|
||||
anyBoneHit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyBoneHit)
|
||||
break;
|
||||
FingerBends[f] = i;
|
||||
}
|
||||
|
||||
|
||||
for (var i = FingerBends[f]; i >= 0; i--)
|
||||
{
|
||||
anyBoneHit = false;
|
||||
FingerBends[f] = i;
|
||||
for (var b = 0; b < finger.Bones.Count; b++)
|
||||
{
|
||||
if (CheckBone(f, b, i))
|
||||
{
|
||||
anyBoneHit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyBoneHit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHand();
|
||||
|
||||
// watch.Stop();
|
||||
//Debug.Log($"{watch.ElapsedMilliseconds}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetCollidedBones()
|
||||
{
|
||||
for (int i = 0; i < _collidedBoneTracker.Length; i++)
|
||||
{
|
||||
_collidedBoneTracker[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsFingerDone(int finger)
|
||||
{
|
||||
var i = _fingerIndices[finger];
|
||||
for (; i < Hand.Fingers[finger].Bones.Count; i++)
|
||||
{
|
||||
if (!_collidedBoneTracker[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetCollisionPoint(int finger, int bone, Vector3 point)
|
||||
{
|
||||
var i = _fingerIndices[finger];
|
||||
CollisionPoints[i + bone] = point;
|
||||
}
|
||||
|
||||
private void SetBoneCollided(int finger, int bone)
|
||||
{
|
||||
var i = _fingerIndices[finger];
|
||||
_collidedBoneTracker[i + bone] = true;
|
||||
}
|
||||
|
||||
private bool IsBoneCollided(int finger, int bone)
|
||||
{
|
||||
var i = _fingerIndices[finger];
|
||||
return _collidedBoneTracker[i + bone];
|
||||
}
|
||||
|
||||
private void SetBoneIteration(int finger, int bone, int iteration)
|
||||
{
|
||||
var i = _fingerIndices[finger];
|
||||
_boneIteration[i + bone] = iteration;
|
||||
}
|
||||
|
||||
private int GetBoneIteration(int finger, int bone)
|
||||
{
|
||||
var i = _fingerIndices[finger];
|
||||
return _boneIteration[i + bone];
|
||||
}
|
||||
|
||||
private List<Vector3> GetSpheres(int finger, int bone)
|
||||
{
|
||||
if (_fingerIndices == null || _sphereMap == null)
|
||||
return _dummy;
|
||||
|
||||
if (finger >= _fingerIndices.Length)
|
||||
return _dummy;
|
||||
|
||||
var i = _fingerIndices[finger];
|
||||
|
||||
if (i + bone >= _sphereMap.Length)
|
||||
return _dummy;
|
||||
|
||||
return _sphereMap[i + bone];
|
||||
}
|
||||
|
||||
public void SetupCollision()
|
||||
{
|
||||
if (!Hand)
|
||||
return;
|
||||
SetupBoneTracker();
|
||||
|
||||
var index = 0;
|
||||
foreach (var finger in Hand.Fingers)
|
||||
{
|
||||
if (!finger.Tip)
|
||||
continue;
|
||||
for (var i = 0; i < finger.Bones.Count; i++)
|
||||
{
|
||||
_sphereMap[index] = new List<Vector3>();
|
||||
|
||||
var current = Vector3.zero;
|
||||
var next = i == finger.Bones.Count - 1 ? finger.Tip.localPosition : finger.Bones[i + 1].Transform.localPosition;
|
||||
|
||||
for (var j = 0; j < RaysPerBone; j++)
|
||||
{
|
||||
var point = Vector3.Lerp(current, next, (j + 1f) / RaysPerBone);
|
||||
_sphereMap[index].Add(point);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenFingers()
|
||||
{
|
||||
if (Hand && OpenPose)
|
||||
{
|
||||
Hand.PoseFingers(OpenPose);
|
||||
}
|
||||
}
|
||||
|
||||
public void TestClose()
|
||||
{
|
||||
if (!Validate())
|
||||
return;
|
||||
|
||||
ResetHand();
|
||||
SetupCollision();
|
||||
|
||||
var watch = Stopwatch.StartNew();
|
||||
ResetHand();
|
||||
SimulateClose(~LayerMask.GetMask("Hand"));
|
||||
watch.Stop();
|
||||
Debug.Log(watch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
|
||||
public void SimulateClose(LayerMask mask)
|
||||
{
|
||||
if (!_validated)
|
||||
return;
|
||||
|
||||
CurrentMask = mask;
|
||||
ResetHand();
|
||||
|
||||
CloseHand();
|
||||
}
|
||||
|
||||
private void CloseHand()
|
||||
{
|
||||
for (var f = 0; f < Hand.Fingers.Length; f++)
|
||||
{
|
||||
for (int j = FingerBends[f]; j < Iterations; j++)
|
||||
{
|
||||
CheckFinger(f, ++FingerBends[f]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void NextFinger()
|
||||
{
|
||||
if (_fingerIndex + 1 < 5)
|
||||
{
|
||||
_fingerIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
public void StepIteration()
|
||||
{
|
||||
if (_fingerIndex > 4)
|
||||
return;
|
||||
|
||||
if (!Validate())
|
||||
return;
|
||||
|
||||
CheckFinger(_fingerIndex, ++FingerBends[_fingerIndex]);
|
||||
}
|
||||
|
||||
public void BackStepIteration()
|
||||
{
|
||||
if (_fingerIndex > 4)
|
||||
return;
|
||||
|
||||
if (!Validate())
|
||||
return;
|
||||
|
||||
CheckFinger(_fingerIndex, --FingerBends[_fingerIndex]);
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
_validated = false;
|
||||
if (Hand == null)
|
||||
return false;
|
||||
|
||||
if (OpenPose)
|
||||
{
|
||||
OpenPoseData = OpenPose.GetPose(Hand.IsLeft);
|
||||
}
|
||||
|
||||
if (ClosedPose)
|
||||
{
|
||||
ClosedPoseData = ClosedPose.GetPose(Hand.IsLeft);
|
||||
}
|
||||
|
||||
if (OpenPoseData == null || ClosedPoseData == null)
|
||||
{
|
||||
Debug.LogWarning($"Physics Poser Missing Open and/or Closed Poses.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (ClosedPoseData.Fingers.Length < Hand.Fingers.Length)
|
||||
{
|
||||
Debug.LogWarning($"ClosedPose Finger Count is {ClosedPoseData.Fingers.Length} while hand is {Hand.Fingers.Length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OpenPoseData.Fingers.Length < Hand.Fingers.Length)
|
||||
{
|
||||
Debug.LogWarning($"OpenPose Finger Count is {ClosedPoseData.Fingers.Length} while hand is {Hand.Fingers.Length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Hand.Fingers.Length; i++)
|
||||
{
|
||||
var finger = Hand.Fingers[i];
|
||||
|
||||
var closedFinger = ClosedPoseData.Fingers[i];
|
||||
var openFinger = OpenPoseData.Fingers[i];
|
||||
|
||||
if (finger.Bones.Count != openFinger.Bones.Count)
|
||||
{
|
||||
Debug.LogWarning($"OpenPose Finger {i} bone count incorrect: {openFinger.Bones.Count} expected {finger.Bones.Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (finger.Bones.Count != closedFinger.Bones.Count)
|
||||
{
|
||||
Debug.LogWarning($"ClosedPose Finger {i} bone count incorrect: {openFinger.Bones.Count} expected {finger.Bones.Count}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_validated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CheckFinger(int fingerIndex, int iteration)
|
||||
{
|
||||
var currentFinger = Hand.Fingers[fingerIndex];
|
||||
|
||||
for (var boneIndex = 0; boneIndex < currentFinger.Bones.Count; boneIndex++)
|
||||
{
|
||||
if (IsBoneCollided(fingerIndex, boneIndex))
|
||||
continue;
|
||||
|
||||
if (CheckBone(fingerIndex, boneIndex, iteration))
|
||||
{
|
||||
//stop moving this and any higher bones
|
||||
for (var y = boneIndex; y >= 0; y--)
|
||||
{
|
||||
SetBoneCollided(fingerIndex, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckBone(int fingerIndex, int boneIndex, int iteration)
|
||||
{
|
||||
var percent = (float)iteration / Iterations;
|
||||
|
||||
var currentFinger = Hand.Fingers[fingerIndex];
|
||||
var currentBone = currentFinger.Bones[boneIndex];
|
||||
|
||||
var currentPosition = currentBone.Transform.localPosition;
|
||||
var currentRotation = currentBone.Transform.localRotation;
|
||||
|
||||
var openBone = OpenPoseData.Fingers[fingerIndex].Bones[boneIndex];
|
||||
var closedBone = ClosedPoseData.Fingers[fingerIndex].Bones[boneIndex];
|
||||
|
||||
currentBone.Transform.localPosition = Vector3.Lerp(openBone.Position, closedBone.Position, percent);
|
||||
currentBone.Transform.localRotation = Quaternion.Lerp(openBone.Rotation, closedBone.Rotation, percent);
|
||||
|
||||
var points = GetSpheres(fingerIndex, boneIndex);
|
||||
|
||||
if (boneIndex != currentFinger.Bones.Count - 1 && FingerTipOnly)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < points.Count; i++)
|
||||
{
|
||||
var point = points[i];
|
||||
|
||||
var world = currentBone.Transform.TransformPoint(point);
|
||||
|
||||
int hits;
|
||||
//is there a better way to know if we're in scene or prefab view ? this is required to work in prefab view.
|
||||
//if (editor)
|
||||
//{
|
||||
// hits = gameObject.scene.GetPhysicsScene().OverlapSphere(world, SphereRadius, colliders, CurrentMask, QueryTriggerInteraction.Ignore);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
hits = Physics.OverlapSphereNonAlloc(world, SphereRadius, colliders, CurrentMask, QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
|
||||
if (hits > 0)
|
||||
{
|
||||
if (DrawCollisionPoints)
|
||||
{
|
||||
SetCollisionPoint(fingerIndex, boneIndex, world);
|
||||
}
|
||||
|
||||
if (LogHitColliderNames)
|
||||
{
|
||||
for (int h = 0; h < hits; h++)
|
||||
{
|
||||
Debug.Log(currentBone.Transform.name + " collided with " + colliders[h].name);
|
||||
}
|
||||
}
|
||||
|
||||
currentBone.Transform.localPosition = currentPosition;
|
||||
currentBone.Transform.localRotation = currentRotation;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DrawCollisionPoints)
|
||||
{
|
||||
SetCollisionPoint(fingerIndex, boneIndex, Vector3.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ResetHand()
|
||||
{
|
||||
for (int i = 0; i < FingerBends.Length; i++)
|
||||
{
|
||||
FingerBends[i] = 0;
|
||||
}
|
||||
|
||||
ResetCollidedBones();
|
||||
|
||||
_fingerIndex = 0;
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!Hand)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var f = 0; f < Hand.Fingers.Length; f++)
|
||||
{
|
||||
var finger = Hand.Fingers[f];
|
||||
if (DrawSpheres)
|
||||
{
|
||||
for (var b = 0; b < finger.Bones.Count; b++)
|
||||
{
|
||||
if (FingerTipOnly && b != finger.Bones.Count - 1)
|
||||
continue;
|
||||
var bone = finger.Bones[b];
|
||||
|
||||
//if (!SphereMap.TryGetValue(bone.Transform, out var points)) continue;
|
||||
|
||||
var points = GetSpheres(f, b);
|
||||
|
||||
for (var i = 0; i < points.Count; i++)
|
||||
{
|
||||
var point = points[i];
|
||||
var worldPosition = bone.Transform.TransformPoint(point);
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(worldPosition, SphereRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DrawTips)
|
||||
{
|
||||
if (finger.Tip)
|
||||
{
|
||||
Gizmos.color = Color.magenta;
|
||||
Gizmos.DrawWireSphere(finger.Tip.position, SphereRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DrawCollisionPoints)
|
||||
{
|
||||
foreach (var point in CollisionPoints)
|
||||
{
|
||||
if (point != Vector3.zero)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(point, SphereRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0afd8501793f4c08903068b0bdc79809
|
||||
timeCreated: 1597718496
|
||||
@@ -0,0 +1,190 @@
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRPosableGrabPoint : MonoBehaviour
|
||||
{
|
||||
public int PoserIndex;
|
||||
|
||||
[Header("Settings")]
|
||||
public bool IsJointAnchor;
|
||||
|
||||
public bool IsForceGrabbable = true;
|
||||
|
||||
[Tooltip("If true only one hand can grab this grabpoint")]
|
||||
public bool OneHandOnly;
|
||||
|
||||
[Range(0f, 360f)]
|
||||
public float AllowedAngleDifference = 360f;
|
||||
|
||||
public bool CheckDistance;
|
||||
public float MaxDistance = .3f;
|
||||
|
||||
[Tooltip("Can the Left hand grab this")]
|
||||
public bool LeftHand = true;
|
||||
|
||||
[Tooltip("Can the right hand grab this")]
|
||||
public bool RightHand = true;
|
||||
|
||||
[Tooltip("Grab Points in the same group will have pose rotation considered")]
|
||||
public int Group = -1;
|
||||
|
||||
[Header("Controller Tracking Offsets")]
|
||||
[FormerlySerializedAs("jointOffset")]
|
||||
public Vector3 HandRotationOffset;
|
||||
public Vector3 HandPositionOffset;
|
||||
|
||||
[Header("Transforms")]
|
||||
|
||||
[Tooltip("If populated, the grab point highlight indicator will be placed at this transform's position instead of this transform's position")]
|
||||
public Transform GrabIndicatorPosition;
|
||||
public Transform VisualGrabPoint;
|
||||
|
||||
public HVRHandPoser HandPoser;
|
||||
|
||||
[Tooltip("Auto populated from the first parent if not supplied")]
|
||||
public HVRGrabbable Grabbable;
|
||||
|
||||
|
||||
[Header("Line Grab")]
|
||||
public bool IsLineGrab;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public Transform LineStart;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public Transform LineEnd;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public bool CanLineFlip = true;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public float LooseDamper = 100;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public float LooseAngularDamper = 1;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public bool LineCanReposition = true;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public bool LineInitialCanReposition = true;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public bool LineCanRotate = true;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public bool LineFreeRotation;
|
||||
|
||||
[DrawIf("IsLineGrab", true)]
|
||||
public bool LineInitialCanRotate = true;
|
||||
|
||||
public Vector3 WorldLine => LineEnd.position - LineStart.position;
|
||||
public Vector3 WorldLineMiddle => (LineEnd.position + LineStart.position) / 2f;
|
||||
|
||||
public Quaternion LeftPoseOffset { get; private set; }
|
||||
public Quaternion RightPoseOffset { get; private set; }
|
||||
|
||||
public Vector3 LeftPosePositionOffset { get; private set; }
|
||||
|
||||
public Vector3 RightPosePositionOffset { get; private set; }
|
||||
|
||||
public List<HVRPosableGrabPoint> Others = new List<HVRPosableGrabPoint>();
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (!HandPoser)
|
||||
TryGetComponent(out HandPoser);
|
||||
|
||||
LeftPoseOffset = Quaternion.identity;
|
||||
RightPoseOffset = Quaternion.identity;
|
||||
|
||||
if (!Grabbable)
|
||||
Grabbable = GetComponentInParent<HVRGrabbable>();
|
||||
|
||||
if (HandPoser)
|
||||
{
|
||||
if (HandPoser && HandPoser.PrimaryPose != null && HandPoser.PrimaryPose.Pose && HandPoser.PrimaryPose.Pose.RightHand != null)
|
||||
{
|
||||
RightPoseOffset = Quaternion.Euler(HandPoser.PrimaryPose.Pose.RightHand.Rotation.eulerAngles);
|
||||
RightPosePositionOffset = HandPoser.PrimaryPose.Pose.RightHand.Position;
|
||||
}
|
||||
else if (RightHand)
|
||||
{
|
||||
Debug.LogWarning($"Right Hand pose missing! {(Grabbable != null ? Grabbable.name : null)}.{this.name}");
|
||||
}
|
||||
|
||||
if (HandPoser && HandPoser.PrimaryPose != null && HandPoser.PrimaryPose.Pose && HandPoser.PrimaryPose.Pose.LeftHand != null && LeftHand)
|
||||
{
|
||||
LeftPoseOffset = Quaternion.Euler(HandPoser.PrimaryPose.Pose.LeftHand.Rotation.eulerAngles);
|
||||
LeftPosePositionOffset = HandPoser.PrimaryPose.Pose.LeftHand.Position;
|
||||
}
|
||||
else if (LeftHand)
|
||||
{
|
||||
Debug.LogWarning($"Left Hand pose missing! {(Grabbable != null ? Grabbable.name : null)}.{this.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddGroupedGrabPoint(HVRPosableGrabPoint p)
|
||||
{
|
||||
if (p != this && !Others.Contains(p))
|
||||
{
|
||||
Others.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetPoseWorldPosition(HVRHandSide side)
|
||||
{
|
||||
if (side == HVRHandSide.Left)
|
||||
return transform.TransformPoint(LeftPosePositionOffset);
|
||||
return transform.TransformPoint(RightPosePositionOffset);
|
||||
}
|
||||
|
||||
public Vector3 GetPosePositionOffset(HVRHandSide side)
|
||||
{
|
||||
if (side == HVRHandSide.Left)
|
||||
return LeftPosePositionOffset;
|
||||
return RightPosePositionOffset;
|
||||
}
|
||||
|
||||
public Quaternion GetPoseRotationOffset(HVRHandSide side)
|
||||
{
|
||||
if (side == HVRHandSide.Left)
|
||||
return LeftPoseOffset;
|
||||
return RightPoseOffset;
|
||||
}
|
||||
|
||||
public Quaternion GetPoseWorldRotation(HVRHandSide side)
|
||||
{
|
||||
if (side == HVRHandSide.Left)
|
||||
return transform.rotation * LeftPoseOffset;
|
||||
return transform.rotation * RightPoseOffset;
|
||||
}
|
||||
|
||||
public Quaternion GetGrabbableRelativeRotation(HVRHandSide side)
|
||||
{
|
||||
return Quaternion.Inverse(Grabbable.transform.rotation) * GetPoseWorldRotation(side);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
// Show Grip Points
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(transform.position, 0.015f);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79cd113ec3a24073b5bab8cbfb533d1e
|
||||
timeCreated: 1597627021
|
||||
@@ -0,0 +1,883 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using HurricaneVR.Framework.Core.HandPoser.Data;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using HurricaneVR.Framework.Shared;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRPosableHand : MonoBehaviour
|
||||
{
|
||||
[Header("Settings")]
|
||||
public bool IsLeft;
|
||||
|
||||
[Tooltip("Used to match up with grab points to allowed objects to have grab points that can be grabbed by different hands.")]
|
||||
public int PoserIndex;
|
||||
|
||||
[Header("Mirroring")]
|
||||
public MirrorAxis MirrorAxis = MirrorAxis.X;
|
||||
public HVRHandMirrorSettings MirrorSettings;
|
||||
public HVRHandMirrorer HandMirrorer;
|
||||
|
||||
[Header("Hand Mirror Settings for VRIK")]
|
||||
public bool UseMatchRotation;
|
||||
|
||||
[FormerlySerializedAs("Forward")] public HVRAxis Axis1 = HVRAxis.Y;
|
||||
[FormerlySerializedAs("Up")] public HVRAxis Axis2 = HVRAxis.Z;
|
||||
|
||||
[FormerlySerializedAs("TargetAxis")] [FormerlySerializedAs("OtherForward")] public HVRAxis TargetAxis1 = HVRAxis.Y;
|
||||
[FormerlySerializedAs("OtherUp")] public HVRAxis TargetAxis2 = HVRAxis.Z;
|
||||
|
||||
[Header("Fingers")]
|
||||
|
||||
public HVRPosableFinger Thumb;
|
||||
public HVRPosableFinger Index;
|
||||
public HVRPosableFinger Middle;
|
||||
public HVRPosableFinger Ring;
|
||||
public HVRPosableFinger Pinky;
|
||||
|
||||
public HVRHandSide Side => IsLeft ? HVRHandSide.Left : HVRHandSide.Right;
|
||||
|
||||
public bool IsRight => !IsLeft;
|
||||
|
||||
[SerializeField]
|
||||
private HVRPosableFinger[] _fingers;
|
||||
|
||||
public HVRPosableFinger[] Fingers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_fingers == null || _fingers.Length == 0)
|
||||
{
|
||||
SetupFingerArray();
|
||||
}
|
||||
|
||||
return _fingers;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupFingerArray()
|
||||
{
|
||||
var fingers = new List<HVRPosableFinger>();
|
||||
|
||||
if (Thumb != null)
|
||||
{
|
||||
fingers.Add(Thumb);
|
||||
}
|
||||
|
||||
if (Index != null)
|
||||
{
|
||||
fingers.Add(Index);
|
||||
}
|
||||
|
||||
if (Middle != null)
|
||||
{
|
||||
fingers.Add(Middle);
|
||||
}
|
||||
|
||||
if (Ring != null)
|
||||
{
|
||||
fingers.Add(Ring);
|
||||
}
|
||||
|
||||
if (Pinky != null)
|
||||
{
|
||||
fingers.Add(Pinky);
|
||||
}
|
||||
|
||||
_fingers = fingers.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
SetupFingerArray();
|
||||
|
||||
if (!HandMirrorer)
|
||||
TryGetComponent(out HandMirrorer);
|
||||
}
|
||||
|
||||
public void Pose(HVRHandPose pose)
|
||||
{
|
||||
Pose(pose.GetPose(this.IsLeft));
|
||||
}
|
||||
|
||||
public void PoseFingers(HVRHandPose pose)
|
||||
{
|
||||
PoseFingers(pose.GetPose(this.IsLeft));
|
||||
}
|
||||
|
||||
public void PoseFingers(HVRHandPoseData pose)
|
||||
{
|
||||
PoseFinger(Thumb, pose.Thumb);
|
||||
PoseFinger(Index, pose.Index);
|
||||
PoseFinger(Middle, pose.Middle);
|
||||
PoseFinger(Ring, pose.Ring);
|
||||
PoseFinger(Pinky, pose.Pinky);
|
||||
}
|
||||
|
||||
public void Pose(HVRHandPoseData pose, bool poseHand = true)
|
||||
{
|
||||
if (poseHand)
|
||||
{
|
||||
transform.localPosition = pose.Position;
|
||||
transform.localRotation = pose.Rotation;
|
||||
}
|
||||
|
||||
PoseFinger(Thumb, pose.Thumb);
|
||||
PoseFinger(Index, pose.Index);
|
||||
PoseFinger(Middle, pose.Middle);
|
||||
PoseFinger(Ring, pose.Ring);
|
||||
PoseFinger(Pinky, pose.Pinky);
|
||||
}
|
||||
|
||||
public void PoseFrom(HVRPosableHand source, bool poseHand = true)
|
||||
{
|
||||
if (poseHand)
|
||||
{
|
||||
transform.position = source.transform.position;
|
||||
transform.rotation = source.transform.rotation;
|
||||
}
|
||||
|
||||
PoseFinger(Thumb, source.Thumb);
|
||||
PoseFinger(Index, source.Index);
|
||||
PoseFinger(Middle, source.Middle);
|
||||
PoseFinger(Ring, source.Ring);
|
||||
PoseFinger(Pinky, source.Pinky);
|
||||
}
|
||||
|
||||
public static Vector3 GetSignedAxisVectorToDirection(Quaternion r, Vector3 direction)
|
||||
{
|
||||
direction = direction.normalized;
|
||||
Vector3 axis = Vector3.right;
|
||||
|
||||
float dotX = Mathf.Abs(Vector3.Dot(r * Vector3.right, direction));
|
||||
float dotY = Mathf.Abs(Vector3.Dot(r * Vector3.up, direction));
|
||||
if (dotY > dotX) axis = Vector3.up;
|
||||
float dotZ = Mathf.Abs(Vector3.Dot(r * Vector3.forward, direction));
|
||||
if (dotZ > dotX && dotZ > dotY) axis = Vector3.forward;
|
||||
|
||||
if (Vector3.Dot(r * axis, direction) < 0f) axis = -axis;
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
public void PoseFinger(HVRPosableFinger finger, HVRPosableFingerData data)
|
||||
{
|
||||
if (finger == null || data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (finger.Bones == null || data.Bones == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < finger.Bones.Count; i++)
|
||||
{
|
||||
var bone = finger.Bones[i];
|
||||
if (data.Bones.Count - 1 >= i)
|
||||
{
|
||||
var boneData = data.Bones[i];
|
||||
bone.Transform.localPosition = boneData.Position;
|
||||
bone.Transform.localRotation = boneData.Rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PoseFinger(HVRPosableFinger finger, HVRPosableFinger target)
|
||||
{
|
||||
if (finger == null || target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (finger.Bones == null || target.Bones == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < finger.Bones.Count; i++)
|
||||
{
|
||||
var bone = finger.Bones[i];
|
||||
if (target.Bones.Count - 1 >= i)
|
||||
{
|
||||
var boneData = target.Bones[i];
|
||||
bone.Transform.localPosition = boneData.Transform.localPosition;
|
||||
bone.Transform.localRotation = boneData.Transform.localRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Editor
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
[Header("Finger Setup")]
|
||||
|
||||
[InspectorButton("FingerSetup")]
|
||||
public string SetupFingers;
|
||||
public void FingerSetup()
|
||||
{
|
||||
_fingers = null;
|
||||
|
||||
if (ThumbRoot)
|
||||
{
|
||||
Thumb = new HVRPosableFinger();
|
||||
Thumb.Root = ThumbRoot;
|
||||
Thumb.Tip = ThumbTip;
|
||||
FindBonesForFinger(ThumbRoot, ThumbTip, Thumb);
|
||||
}
|
||||
|
||||
if (IndexRoot)
|
||||
{
|
||||
Index = new HVRPosableFinger();
|
||||
Index.Root = IndexRoot;
|
||||
Index.Tip = IndexTip;
|
||||
FindBonesForFinger(IndexRoot, IndexTip, Index);
|
||||
}
|
||||
|
||||
if (MiddleRoot)
|
||||
{
|
||||
Middle = new HVRPosableFinger();
|
||||
Middle.Root = MiddleRoot;
|
||||
Middle.Tip = MiddleTip;
|
||||
FindBonesForFinger(MiddleRoot, MiddleTip, Middle);
|
||||
}
|
||||
|
||||
if (RingRoot)
|
||||
{
|
||||
Ring = new HVRPosableFinger();
|
||||
Ring.Root = RingRoot;
|
||||
Ring.Tip = RingTip;
|
||||
FindBonesForFinger(RingRoot, RingTip, Ring);
|
||||
}
|
||||
|
||||
if (PinkyRoot)
|
||||
{
|
||||
Pinky = new HVRPosableFinger();
|
||||
Pinky.Root = PinkyRoot;
|
||||
Pinky.Tip = PinkyTip;
|
||||
FindBonesForFinger(PinkyRoot, PinkyTip, Pinky);
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(this);
|
||||
}
|
||||
|
||||
public Transform ThumbRoot;
|
||||
public Transform ThumbTip;
|
||||
|
||||
public Transform IndexRoot;
|
||||
public Transform IndexTip;
|
||||
|
||||
public Transform MiddleRoot;
|
||||
public Transform MiddleTip;
|
||||
|
||||
public Transform RingRoot;
|
||||
public Transform RingTip;
|
||||
|
||||
public Transform PinkyRoot;
|
||||
public Transform PinkyTip;
|
||||
|
||||
[Header("Capsule Helpers (Floaty hands only!)")]
|
||||
|
||||
|
||||
public float CapsuleRadius = .01f;
|
||||
public HVRAxis CapsuleDirection;
|
||||
public bool CapsuleAddRadius = true;
|
||||
|
||||
[InspectorButton("AddThumbCapsulesPrivate")]
|
||||
public string AddThumbCapsules;
|
||||
[InspectorButton("AddIndexCapsulesPrivate")]
|
||||
public string AddIndexCapsules;
|
||||
[InspectorButton("AddMiddleCapsulesPrivate")]
|
||||
public string AddMiddleCapsules;
|
||||
[InspectorButton("AddRingCapsulesPrivate")]
|
||||
public string AddRingCapsules;
|
||||
[InspectorButton("AddPinkyCapsulesPrivate")]
|
||||
public string AddPinkyCapsules;
|
||||
|
||||
public HVRPosableHand otherhand;
|
||||
|
||||
[InspectorButton("DetectFingerAxesPrivate")]
|
||||
public string DetectFingerAxes;
|
||||
|
||||
[InspectorButton("MirrorTest")]
|
||||
public string MirrorToOtherHand;
|
||||
|
||||
[InspectorButton("ApplyPoseF")]
|
||||
public string ApplyPrimaryPose;
|
||||
|
||||
private void ApplyPoseF()
|
||||
{
|
||||
var p = gameObject.GetComponent<HVRHandPoser>();
|
||||
if (!p) return;
|
||||
var pose = p.PrimaryPose.Pose.GetPose(IsLeft);
|
||||
Pose(pose);
|
||||
EditorUtility.SetDirty(p);
|
||||
}
|
||||
|
||||
private void DetectFingerAxesPrivate()
|
||||
{
|
||||
DetectBoneAxes(otherhand, transform.parent.forward, transform.parent.up);
|
||||
}
|
||||
|
||||
private void MirrorTest()
|
||||
{
|
||||
var m = Mirror(MirrorAxis.X);
|
||||
otherhand.Pose(m, false);
|
||||
}
|
||||
|
||||
private void AddThumbCapsulesPrivate()
|
||||
{
|
||||
AddFingerCapsules(Thumb);
|
||||
}
|
||||
|
||||
private void AddIndexCapsulesPrivate()
|
||||
{
|
||||
AddFingerCapsules(Index);
|
||||
}
|
||||
|
||||
private void AddMiddleCapsulesPrivate()
|
||||
{
|
||||
AddFingerCapsules(Middle);
|
||||
}
|
||||
|
||||
private void AddRingCapsulesPrivate()
|
||||
{
|
||||
AddFingerCapsules(Ring);
|
||||
}
|
||||
|
||||
private void AddPinkyCapsulesPrivate()
|
||||
{
|
||||
AddFingerCapsules(Pinky);
|
||||
}
|
||||
|
||||
private void AddFingerCapsules(HVRPosableFinger finger)
|
||||
{
|
||||
if (finger == null || finger.Bones == null)
|
||||
return;
|
||||
|
||||
Undo.SetCurrentGroupName("AddFingerCapsules");
|
||||
|
||||
for (var i = 0; i < finger.Bones.Count; i++)
|
||||
{
|
||||
var bone = finger.Bones[i];
|
||||
var colName = "coll_" + bone.Transform.name;
|
||||
var col = bone.Transform.Find(colName);
|
||||
GameObject go;
|
||||
if (col) go = col.gameObject;
|
||||
else
|
||||
{
|
||||
go = new GameObject("coll_" + bone.Transform.name);
|
||||
go.transform.parent = bone.Transform;
|
||||
|
||||
|
||||
Undo.RegisterCreatedObjectUndo(go, "AddGO");
|
||||
}
|
||||
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localRotation = Quaternion.identity;
|
||||
go.transform.localScale = Vector3.one;
|
||||
|
||||
var capsule = go.GetComponent<CapsuleCollider>();
|
||||
if (!capsule)
|
||||
{
|
||||
capsule = go.AddComponent<CapsuleCollider>();
|
||||
}
|
||||
|
||||
Transform next;
|
||||
if (i < finger.Bones.Count - 1)
|
||||
{
|
||||
next = finger.Bones[i + 1].Transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
next = finger.Tip;
|
||||
}
|
||||
|
||||
var jointDelta = next.position - bone.Transform.position;
|
||||
var length = jointDelta.magnitude;
|
||||
if (CapsuleAddRadius && i == finger.Bones.Count - 1) length -= CapsuleRadius;
|
||||
go.transform.position += jointDelta.normalized * (length * 0.5f);
|
||||
capsule.height = length;
|
||||
if (CapsuleAddRadius)
|
||||
{
|
||||
capsule.height += CapsuleRadius * 2f;
|
||||
}
|
||||
|
||||
capsule.radius = CapsuleRadius;
|
||||
capsule.direction = (int)CapsuleDirection;
|
||||
|
||||
}
|
||||
|
||||
Undo.CollapseUndoOperations(Undo.GetCurrentGroup());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
private void FindBonesForFinger(Transform bone, Transform tip, HVRPosableFinger finger)
|
||||
{
|
||||
finger.Bones.Add(new HVRPosableBone() { Transform = bone.transform });
|
||||
|
||||
if (tip.parent == bone)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bone.childCount > 0)
|
||||
{
|
||||
FindBonesForFinger(bone.GetChild(0), tip, finger);
|
||||
}
|
||||
}
|
||||
|
||||
public HVRHandPoseData CreateHandPose(Transform transformOverride = null)
|
||||
{
|
||||
var t = transformOverride ?? transform;
|
||||
|
||||
var data = new HVRHandPoseData
|
||||
{
|
||||
Position = t.localPosition,
|
||||
Rotation = t.localRotation,
|
||||
};
|
||||
|
||||
data.Thumb = Thumb?.GetFingerData();
|
||||
data.Index = Index?.GetFingerData();
|
||||
data.Middle = Middle?.GetFingerData();
|
||||
data.Ring = Ring?.GetFingerData();
|
||||
data.Pinky = Pinky?.GetFingerData();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public void CopyHandData(HVRHandPoseData data)
|
||||
{
|
||||
data.Position = transform.localPosition;
|
||||
data.Rotation = transform.localRotation;
|
||||
|
||||
for (var i = 0; i < Fingers.Length; i++)
|
||||
{
|
||||
var finger = Fingers[i];
|
||||
for (var j = 0; j < finger.Bones.Count; j++)
|
||||
{
|
||||
var bone = finger.Bones[j];
|
||||
|
||||
data.Fingers[i].Bones[j].Position = bone.Transform.localPosition;
|
||||
data.Fingers[i].Bones[j].Rotation = bone.Transform.localRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HVRHandPose CreateFullHandPoseWorld(MirrorAxis axis)
|
||||
{
|
||||
var hand = CreateHandPose();
|
||||
hand.Position = transform.position;
|
||||
hand.Rotation = transform.rotation;
|
||||
|
||||
//var handMirror = hand.Mirror(axis, transform);
|
||||
var handMirror = Mirror(axis);
|
||||
|
||||
var left = IsLeft ? hand : handMirror;
|
||||
var right = IsLeft ? handMirror : hand;
|
||||
|
||||
var handPose = ScriptableObject.CreateInstance<HVRHandPose>();
|
||||
handPose.SnappedLeft = Side == HVRHandSide.Left;
|
||||
handPose.LeftHand = left;
|
||||
handPose.RightHand = right;
|
||||
return handPose;
|
||||
}
|
||||
|
||||
public HVRHandPose CreateFullHandPose(MirrorAxis axis, Transform transformOverride = null)
|
||||
{
|
||||
var hand = CreateHandPose(transformOverride);
|
||||
|
||||
//var handMirror = hand.Mirror(axis, transform);
|
||||
var handMirror = Mirror(axis);
|
||||
|
||||
var left = IsLeft ? hand : handMirror;
|
||||
var right = IsLeft ? handMirror : hand;
|
||||
|
||||
var handPose = ScriptableObject.CreateInstance<HVRHandPose>();
|
||||
handPose.SnappedLeft = Side == HVRHandSide.Left;
|
||||
handPose.LeftHand = left;
|
||||
handPose.RightHand = right;
|
||||
return handPose;
|
||||
}
|
||||
|
||||
public void Mirror(HVRPosableHand targetHand, MirrorAxis axis)
|
||||
{
|
||||
MirrorHand(axis, out var pos, out var rot);
|
||||
|
||||
targetHand.transform.localPosition = pos;
|
||||
targetHand.transform.localRotation = rot;
|
||||
|
||||
if (HandMirrorer)
|
||||
HandMirrorer.MirrorFingers(this, targetHand);
|
||||
}
|
||||
|
||||
public void MirrorHand(MirrorAxis axis, out Vector3 position, out Quaternion rotation, Transform transformOverride = null)
|
||||
{
|
||||
var hand = transformOverride ? transformOverride : transform; //iktarget doesn't have the HVRPosableHand on it
|
||||
|
||||
position = hand.localPosition;
|
||||
rotation = hand.localRotation;
|
||||
|
||||
Vector3 direction;
|
||||
|
||||
switch (axis)
|
||||
{
|
||||
case MirrorAxis.X:
|
||||
position.x *= -1f;
|
||||
direction = Vector3.right;
|
||||
break;
|
||||
case MirrorAxis.Y:
|
||||
position.y *= -1;
|
||||
direction = Vector3.up;
|
||||
break;
|
||||
case MirrorAxis.Z:
|
||||
position.z *= -1;
|
||||
direction = Vector3.forward;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 forward;
|
||||
Vector3 up;
|
||||
|
||||
if (hand.parent != null)
|
||||
{
|
||||
forward = hand.parent.InverseTransformDirection(hand.forward);
|
||||
up = hand.parent.InverseTransformDirection(hand.up);
|
||||
}
|
||||
else
|
||||
{
|
||||
forward = hand.forward;
|
||||
up = hand.up;
|
||||
}
|
||||
|
||||
var mirror = Vector3.Reflect(forward, direction);
|
||||
var upMirror = Vector3.Reflect(up, direction);
|
||||
rotation = Quaternion.LookRotation(mirror, upMirror);
|
||||
|
||||
if (UseMatchRotation)
|
||||
{
|
||||
rotation = MatchRotation(rotation, TargetAxis1.GetVector(), TargetAxis2.GetVector(), Axis1.GetVector(), Axis2.GetVector());
|
||||
}
|
||||
}
|
||||
|
||||
public HVRHandPoseData Mirror(MirrorAxis axis, Transform transformOverride = null)
|
||||
{
|
||||
var clone = new HVRHandPoseData();
|
||||
|
||||
MirrorHand(axis, out clone.Position, out clone.Rotation, transformOverride);
|
||||
|
||||
if (HandMirrorer)
|
||||
{
|
||||
HandMirrorer.MirrorFingers(this, clone);
|
||||
}
|
||||
else
|
||||
{
|
||||
MirrorFingers(clone);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private void MirrorFingers(HVRHandPoseData clone)
|
||||
{
|
||||
HVRJointMirrorSetting thumbOverride = null;
|
||||
HVRJointMirrorSetting indexMirror = null;
|
||||
HVRJointMirrorSetting middleMirror = null;
|
||||
HVRJointMirrorSetting ringMirror = null;
|
||||
HVRJointMirrorSetting pinkyMirror = null;
|
||||
|
||||
if (MirrorSettings)
|
||||
{
|
||||
thumbOverride = MirrorSettings.UseThumbSetting ? MirrorSettings.ThumbSetting : MirrorSettings.AllSetting;
|
||||
indexMirror = MirrorSettings.UseIndexSetting ? MirrorSettings.IndexSetting : MirrorSettings.AllSetting;
|
||||
middleMirror = MirrorSettings.UseMiddleSetting ? MirrorSettings.MiddleSetting : MirrorSettings.AllSetting;
|
||||
ringMirror = MirrorSettings.UseRingSetting ? MirrorSettings.RingSetting : MirrorSettings.AllSetting;
|
||||
pinkyMirror = MirrorSettings.UsePinkySetting ? MirrorSettings.PinkySetting : MirrorSettings.AllSetting;
|
||||
}
|
||||
|
||||
|
||||
if (Thumb != null)
|
||||
{
|
||||
clone.Thumb = MirrorFinger(Thumb, thumbOverride, MirrorSettings?.ThumbSettings);
|
||||
}
|
||||
|
||||
if (Index != null)
|
||||
{
|
||||
clone.Index = MirrorFinger(Index, indexMirror, MirrorSettings?.IndexSettings);
|
||||
}
|
||||
|
||||
if (Middle != null)
|
||||
{
|
||||
clone.Middle = MirrorFinger(Middle, middleMirror, MirrorSettings?.MiddleSettings);
|
||||
}
|
||||
|
||||
if (Ring != null)
|
||||
{
|
||||
clone.Ring = MirrorFinger(Ring, ringMirror, MirrorSettings?.RingSettings);
|
||||
}
|
||||
|
||||
if (Pinky != null)
|
||||
{
|
||||
clone.Pinky = MirrorFinger(Pinky, pinkyMirror, MirrorSettings?.PinkySettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void DetectBoneAxes(HVRPosableHand otherHand, Vector3 forward, Vector3 up)
|
||||
{
|
||||
for (var i = 0; i < Fingers.Length; i++)
|
||||
{
|
||||
var finger = Fingers[i];
|
||||
for (var j = 0; j < finger.Bones.Count; j++)
|
||||
{
|
||||
var bone = finger.Bones[j];
|
||||
var targetBone = otherHand.Fingers[i].Bones[j];
|
||||
|
||||
// Get local orthogonal axes of the right hand pointing forward and up
|
||||
var axis1 = GetSignedAxisVectorToDirection(bone.Transform.rotation, forward);
|
||||
var axis2 = GetSignedAxisVectorToDirection(bone.Transform.rotation, up);
|
||||
|
||||
var targetAxis1 = GetSignedAxisVectorToDirection(targetBone.Transform.rotation, forward);
|
||||
var targetAxis2 = GetSignedAxisVectorToDirection(targetBone.Transform.rotation, up);
|
||||
|
||||
bone.Forward = axis1;
|
||||
bone.Up = axis2;
|
||||
bone.OtherForward = targetAxis1;
|
||||
bone.OtherUp = targetAxis2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Quaternion MatchRotation(Quaternion targetRotation, Vector3 targetforwardAxis, Vector3 targetUpAxis, Vector3 forwardAxis, Vector3 upAxis)
|
||||
{
|
||||
Quaternion f = Quaternion.LookRotation(forwardAxis, upAxis);
|
||||
Quaternion fTarget = Quaternion.LookRotation(targetforwardAxis, targetUpAxis);
|
||||
|
||||
Quaternion d = targetRotation * fTarget;
|
||||
return d * Quaternion.Inverse(f);
|
||||
}
|
||||
|
||||
|
||||
private HVRPosableFingerData MirrorFinger(HVRPosableFinger finger, HVRJointMirrorSetting mirrorOverride, List<HVRJointMirrorSetting> settings)
|
||||
{
|
||||
var fingerData = new HVRPosableFingerData();
|
||||
|
||||
for (var i = 0; i < finger.Bones.Count; i++)
|
||||
{
|
||||
var bone = finger.Bones[i];
|
||||
var boneData = new HVRPosableBoneData();
|
||||
|
||||
|
||||
|
||||
HVRJointMirrorSetting mirror = null;
|
||||
|
||||
|
||||
if (settings != null && i < settings.Count)
|
||||
{
|
||||
mirror = settings[i];
|
||||
}
|
||||
else if (mirrorOverride != null)
|
||||
{
|
||||
mirror = mirrorOverride;
|
||||
}
|
||||
|
||||
if (mirror != null)
|
||||
{
|
||||
var euler = bone.Transform.localRotation.eulerAngles;
|
||||
|
||||
var xAngle = euler.x;
|
||||
var yAngle = euler.y;
|
||||
var zAngle = euler.z;
|
||||
|
||||
MirrorRotation(mirror.XRotation, ref xAngle);
|
||||
MirrorRotation(mirror.YRotation, ref yAngle);
|
||||
MirrorRotation(mirror.ZRotation, ref zAngle);
|
||||
|
||||
boneData.Position = bone.Transform.localPosition;
|
||||
boneData.Rotation = Quaternion.Euler(xAngle, yAngle, zAngle);
|
||||
|
||||
if (mirror.XPosition == FingerMirrorPosition.Opposite)
|
||||
{
|
||||
boneData.Position.x *= -1f;
|
||||
}
|
||||
|
||||
if (mirror.YPosition == FingerMirrorPosition.Opposite)
|
||||
{
|
||||
boneData.Position.y *= -1f;
|
||||
}
|
||||
|
||||
if (mirror.ZPosition == FingerMirrorPosition.Opposite)
|
||||
{
|
||||
boneData.Position.z *= -1f;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
boneData.Position = bone.Transform.localPosition * -1;
|
||||
boneData.Rotation = bone.Transform.localRotation;
|
||||
}
|
||||
|
||||
fingerData.Bones.Add(boneData);
|
||||
}
|
||||
|
||||
return fingerData;
|
||||
}
|
||||
|
||||
private void MirrorRotation(FingerMirrorRotation option, ref float angle)
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
case FingerMirrorRotation.Minus180:
|
||||
angle -= 180f;
|
||||
break;
|
||||
case FingerMirrorRotation.Plus180:
|
||||
angle += 180f;
|
||||
break;
|
||||
case FingerMirrorRotation.Same:
|
||||
break;
|
||||
case FingerMirrorRotation.Opposite:
|
||||
angle *= -1f;
|
||||
break;
|
||||
case FingerMirrorRotation.Neg180Minus:
|
||||
angle = -180f - angle;
|
||||
break;
|
||||
case FingerMirrorRotation.P180Minus:
|
||||
angle = 180 - angle;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(byte[] buffer)
|
||||
{
|
||||
using (var ms = new MemoryStream(buffer))
|
||||
{
|
||||
using (var writer = new BinaryWriter(ms))
|
||||
{
|
||||
writer.Write(transform.localPosition.x);
|
||||
writer.Write(transform.localPosition.y);
|
||||
writer.Write(transform.localPosition.z);
|
||||
|
||||
writer.Write(transform.localRotation.x);
|
||||
writer.Write(transform.localRotation.y);
|
||||
writer.Write(transform.localRotation.z);
|
||||
writer.Write(transform.localRotation.w);
|
||||
|
||||
writer.Write(Fingers.Length);
|
||||
|
||||
foreach (var finger in Fingers)
|
||||
{
|
||||
writer.Write(finger.Bones.Count);
|
||||
|
||||
foreach (var bone in finger.Bones)
|
||||
{
|
||||
writer.Write(bone.Transform.localPosition.x);
|
||||
writer.Write(bone.Transform.localPosition.y);
|
||||
writer.Write(bone.Transform.localPosition.z);
|
||||
|
||||
writer.Write(bone.Transform.localRotation.x);
|
||||
writer.Write(bone.Transform.localRotation.y);
|
||||
writer.Write(bone.Transform.localRotation.z);
|
||||
writer.Write(bone.Transform.localRotation.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FromByteArray(byte[] bytes, HVRHandSide side)
|
||||
{
|
||||
using (var ms = new MemoryStream(bytes))
|
||||
{
|
||||
using (var reader = new BinaryReader(ms))
|
||||
{
|
||||
transform.localPosition = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
transform.localRotation = new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
|
||||
var fingers = reader.ReadInt32();
|
||||
|
||||
for (var i = 0; i < fingers; i++)
|
||||
{
|
||||
var finger = Fingers[i];
|
||||
|
||||
var bones = reader.ReadInt32();
|
||||
|
||||
for (var j = 0; j < bones; j++)
|
||||
{
|
||||
var bone = finger.Bones[j];
|
||||
bone.Transform.localPosition = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
bone.Transform.localRotation = new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HVRPosableFinger
|
||||
{
|
||||
public Transform Root;
|
||||
public Transform Tip;
|
||||
public List<HVRPosableBone> Bones = new List<HVRPosableBone>();
|
||||
|
||||
public HVRPosableFingerData GetFingerData()
|
||||
{
|
||||
var finger = new HVRPosableFingerData
|
||||
{
|
||||
Bones = new List<HVRPosableBoneData>()
|
||||
};
|
||||
|
||||
foreach (var bone in Bones)
|
||||
{
|
||||
finger.Bones.Add(bone.GetBoneData());
|
||||
}
|
||||
|
||||
return finger;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HVRPosableBone
|
||||
{
|
||||
public Transform Transform;
|
||||
|
||||
public HVRPosableBoneData GetBoneData()
|
||||
{
|
||||
var bone = new HVRPosableBoneData();
|
||||
bone.Position = Transform.localPosition;
|
||||
bone.Rotation = Transform.localRotation;
|
||||
return bone;
|
||||
}
|
||||
|
||||
|
||||
public Vector3 Forward;
|
||||
public Vector3 Up;
|
||||
|
||||
public Vector3 OtherForward;
|
||||
public Vector3 OtherUp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a32cc63482144afa9752d0981ca1e93f
|
||||
timeCreated: 1596915409
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HurricaneVR.Framework.Core.Grabbers;
|
||||
using HurricaneVR.Framework.Core.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.HandPoser
|
||||
{
|
||||
public class HVRPoseZone : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Decide to ignore non trigger colliders or not on the hand for stable detection of non moving colliders.")]
|
||||
public bool UseHandTriggersOnly = true;
|
||||
|
||||
[Tooltip("Uses box overlap or sphere overlap")]
|
||||
public bool UseBox = true;
|
||||
|
||||
public Vector3 OverlapBox = new Vector3(.15f, .15f, .15f);
|
||||
public float OverlapRadius = .15f;
|
||||
|
||||
[Tooltip("Layer mask of the hand collider for performant detection")]
|
||||
public LayerMask HandMask;
|
||||
|
||||
[Tooltip("Poser that defines the pose to animate to, captured on awake from same object if not supplied.")]
|
||||
public HVRHandPoser Poser;
|
||||
|
||||
private readonly List<HVRHandGrabber> overlappedHands = new List<HVRHandGrabber>(2);
|
||||
private readonly List<HVRHandGrabber> hands = new List<HVRHandGrabber>(2);
|
||||
private readonly Collider[] _colliders = new Collider[100];
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (!Poser)
|
||||
{
|
||||
TryGetComponent(out Poser);
|
||||
}
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
hands.Clear();
|
||||
int count;
|
||||
if (UseBox)
|
||||
{
|
||||
count = Physics.OverlapBoxNonAlloc(transform.position, OverlapBox * .5f, _colliders, transform.rotation, HandMask, QueryTriggerInteraction.Collide);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = Physics.OverlapSphereNonAlloc(transform.position, OverlapRadius, _colliders, HandMask, QueryTriggerInteraction.Collide);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var c = _colliders[i];
|
||||
var hand = c.FindComponent<HVRHandGrabber>();
|
||||
if (!hand) continue;
|
||||
|
||||
if (c.isTrigger)
|
||||
{
|
||||
if (!c.TryGetComponent(out HVRHandTrigger _))
|
||||
{
|
||||
//ignore triggers that aren't marked with HVRHandTrigger or a child of the hand model so that other triggers on the hand rigidbody
|
||||
//that are used for other purposes like the distance grabber are aren't considered
|
||||
if (hand.HandAnimator && (!c.transform.IsChildOf(hand.HandAnimator.Hand.transform)) && c.transform != hand.HandAnimator.Hand.transform)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (UseHandTriggersOnly)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hands.Add(hand);
|
||||
}
|
||||
|
||||
|
||||
foreach (var hand in hands)
|
||||
{
|
||||
if (!overlappedHands.Contains(hand))
|
||||
{
|
||||
hand.SetAnimatorOverridePose(Poser);
|
||||
overlappedHands.Add(hand);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = overlappedHands.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var hand = overlappedHands[i];
|
||||
if (hand && !hands.Contains(hand))
|
||||
{
|
||||
hand.SetAnimatorOverridePose(null);
|
||||
overlappedHands.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
for (var i = overlappedHands.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var hand = overlappedHands[i];
|
||||
if (hand)
|
||||
{
|
||||
hand.SetAnimatorOverridePose(null);
|
||||
overlappedHands.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
if (UseBox)
|
||||
{
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawWireCube(Vector3.zero, OverlapBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gizmos.DrawWireSphere(transform.position, OverlapRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81ed24babdf5471dbf0b20a8d9f02ba1
|
||||
timeCreated: 1661109513
|
||||
3
Assets/HurricaneVR/Framework/Scripts/Core/Player.meta
Normal file
3
Assets/HurricaneVR/Framework/Scripts/Core/Player.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6efba9c145904ef99afdd9c4799e03eb
|
||||
timeCreated: 1600273744
|
||||
@@ -0,0 +1,25 @@
|
||||
using HurricaneVR.Framework.Shared;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Core.Player
|
||||
{
|
||||
[RequireComponent(typeof(Camera))]
|
||||
public class HVRCamera : MonoBehaviour
|
||||
{
|
||||
public Camera Camera { get; private set; }
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Camera = GetComponent<Camera>();
|
||||
if (HVRManager.Instance)
|
||||
{
|
||||
HVRManager.Instance.Camera = transform;
|
||||
}
|
||||
|
||||
var layer = LayerMask.NameToLayer(HVRLayers.Player.ToString());
|
||||
if (layer > -1)
|
||||
gameObject.layer = layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user