Replace UltimateXR with HurricaneVR

This commit is contained in:
2024-08-08 17:01:07 +02:00
parent e8658374d6
commit fb21dbbb73
5932 changed files with 358362 additions and 2174150 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0f99ff4d40314101aa47047f5dfaec5d
timeCreated: 1600273619

View File

@@ -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>
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ee407dd0e26344b884367d0f6d4a806f
timeCreated: 1598308977

View File

@@ -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]);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2fb655091c7347beb9e915d00e497747
timeCreated: 1596346935

View 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]);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c9592152fa64433fa0a6766a74007789
timeCreated: 1599789992

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e55483a495124baab45ceb8df9a68da9
timeCreated: 1596346950

View File

@@ -0,0 +1 @@
//left in to prevent compile errors

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5969b23a56534b28b24c515e2b1826d9
timeCreated: 1601444721

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 18fc546c9cc5464cba366ed43c3e10b7
timeCreated: 1600273615

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace HurricaneVR.Framework.Core.Grabbers
{
public class HVRCloneDelete : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 53ae4d82f1d97b44686e84aac4f5077b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 82a1b90bd6e7417ca9833e08bd4a1ec3
timeCreated: 1598243548

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59d0d668c7d045f0914e86e73f332a4f
timeCreated: 1596310217

View File

@@ -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;
}
}
});
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a9c9943e7c254d4cb39623d0a3d5c36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce026d01d1f538044a048be125f5a85e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a27bb777100ada4459dda04ca3779632
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3480dc27077fb6a47a99066438ce883f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("HurricaneVR.PUN")]
[assembly: InternalsVisibleTo("HurricaneVR.SteamVR")]
[assembly: InternalsVisibleTo("HurricaneVR.Oculus")]

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d7e99ffe74c473aab93a12e849053f0
timeCreated: 1603423829

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18f590c92db08f642b7b64c13ff01bff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9cf9028b057d4b7db0423f5d0a26074b
timeCreated: 1674090737

View File

@@ -0,0 +1,10 @@
using HurricaneVR.Framework.Shared;
using UnityEngine;
namespace HurricaneVR.Framework.Core
{
public class HVRGrabPoints : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7737b083a4a391b49adfccaf05aeb91e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ebc937e678155e4d86feeb9248cca97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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>();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c75c5c18c01eee4ab0ab9c8cd3c3000
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 511b6afc52cd40c889e42589b4d724c0
timeCreated: 1603249821

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3258b231e1a6c448ba0ad62a5d6bb50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8bef30b910ae46c4acd561544cc1e389
timeCreated: 1674090329

View 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);
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6477d7ab11e624344bd8d26783ef171f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e342e34c74884f399b6b418e8b6b9273
timeCreated: 1666497596

View 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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 923ec526580e484ca0d4716c43ccccc8
timeCreated: 1600302382

View File

@@ -0,0 +1,10 @@
using UnityEngine;
namespace HurricaneVR.Framework.Core
{
[RequireComponent(typeof(Rigidbody))]
public class HVRPhysicsGrabbable : HVRGrabbable
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3ed6e70729ed4913846a8b06675e1581
timeCreated: 1602191380

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bbf858a0f4504aa4adf9207e33864cfe
timeCreated: 1604714079

View 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()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b3a39c3d29bcbf45b97a504fc1dedbe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96785e435f4a7a04d84021e497eeb783
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb861c724e29b4449b327730f729e990
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 516c9b05d2377ab4fba54bd370ec2157
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa807b4a09a146cd984c5554578977a7
timeCreated: 1597000245

View File

@@ -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
};
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6cdb877408734211b200721827ec211f
timeCreated: 1597000264

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 01072954f48f4b5ebb2286e938a452f8
timeCreated: 1597000256

View File

@@ -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;
// }
//}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c83ae622a0254145b072f111f9df16b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee092aa77d534bf489a538216bb710b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb138fd2e7814b38be218571e904049c
timeCreated: 1635052804

View File

@@ -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
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ebbffaca5ed8a340ae381fb8a5695d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: becb07c7d7e24b889e62802a34e26d48
timeCreated: 1606578207

View File

@@ -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));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 79648a050a4a4121bb4329449344e118
timeCreated: 1637797213

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dc30735a6f974fda88a33f70e2b17a3b
timeCreated: 1596957987

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4c0ffcacdaf04dc59ce5bad65e3d452e
timeCreated: 1597029386

View File

@@ -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);
// // }
// //}
// }
// }
//}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b47a4acec44dda449ba365e5d1615204
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 96d1b667b6234620a01e126539c3f6d8
timeCreated: 1596897615

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace HurricaneVR.Framework.Core.HandPoser
{
public class HVRHandTrigger : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 364f55a0f60644beb4e906f6db185d28
timeCreated: 1661135657

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ed12b369b790db4595777fdcb6e72d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0afd8501793f4c08903068b0bdc79809
timeCreated: 1597718496

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 79cd113ec3a24073b5bab8cbfb533d1e
timeCreated: 1597627021

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a32cc63482144afa9752d0981ca1e93f
timeCreated: 1596915409

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 81ed24babdf5471dbf0b20a8d9f02ba1
timeCreated: 1661109513

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6efba9c145904ef99afdd9c4799e03eb
timeCreated: 1600273744

View File

@@ -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