Add ultimate xr
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e89d565d8ac0453ab05ff9bf2cb3676e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrBlendPoseType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Manipulation.HandPoses
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different poses in a blend pose.
|
||||
/// </summary>
|
||||
public enum UxrBlendPoseType
|
||||
{
|
||||
/// <summary>
|
||||
/// Not a blend pose.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Pose with the open hand.
|
||||
/// </summary>
|
||||
OpenGrip,
|
||||
|
||||
/// <summary>
|
||||
/// Pose with the closed hand.
|
||||
/// </summary>
|
||||
ClosedGrip
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7be4a9e3a7f4cdbadc26754550fd0ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,167 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFingerDescriptor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core.Math;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.HandPoses
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores base-independent node orientations for a finger.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct UxrFingerDescriptor
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _hasMetacarpalInfo;
|
||||
[SerializeField] private UxrFingerNodeDescriptor _metacarpal;
|
||||
[SerializeField] private UxrFingerNodeDescriptor _proximal;
|
||||
[SerializeField] private UxrFingerNodeDescriptor _proximalNoMetacarpal;
|
||||
[SerializeField] private UxrFingerNodeDescriptor _intermediate;
|
||||
[SerializeField] private UxrFingerNodeDescriptor _distal;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether metacarpal bone information is present. Metacarpal information is optional.
|
||||
/// </summary>
|
||||
public bool HasMetacarpalInfo => _hasMetacarpalInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metacarpal bone transform information.
|
||||
/// </summary>
|
||||
public UxrFingerNodeDescriptor Metacarpal => _metacarpal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the proximal bone transform information.
|
||||
/// </summary>
|
||||
public UxrFingerNodeDescriptor Proximal => _proximal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the proximal bone transform information with respect to the wrist even if there is metacarpal information. It
|
||||
/// is used in case a pose including metacarpal information wants to be mapped to a hand that has no metacarpal bones.
|
||||
/// </summary>
|
||||
public UxrFingerNodeDescriptor ProximalNoMetacarpal => _proximalNoMetacarpal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the intermediate bone transform information.
|
||||
/// </summary>
|
||||
public UxrFingerNodeDescriptor Intermediate => _intermediate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distal bone transform information.
|
||||
/// </summary>
|
||||
public UxrFingerNodeDescriptor Distal => _distal;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Computes well-known axes systems for all finger bones, to handle transforms independently of the coordinate system
|
||||
/// being used by a hand rig.
|
||||
/// </summary>
|
||||
/// <param name="wrist">Wrist transform</param>
|
||||
/// <param name="finger">Finger rig information</param>
|
||||
/// <param name="handLocalAxes">Well-known axes system for the hand</param>
|
||||
/// <param name="fingerLocalAxes">Well-known axes system for the finger elements</param>
|
||||
/// <param name="computeRelativeMatrixOnly">Whether to compute only the relative transform to the hand</param>
|
||||
public void Compute(Transform wrist, UxrAvatarFinger finger, UxrUniversalLocalAxes handLocalAxes, UxrUniversalLocalAxes fingerLocalAxes, bool computeRelativeMatrixOnly)
|
||||
{
|
||||
if (finger.Metacarpal)
|
||||
{
|
||||
_hasMetacarpalInfo = true;
|
||||
_metacarpal.Compute(wrist, wrist, finger.Metacarpal, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_proximal.Compute(wrist, finger.Metacarpal, finger.Proximal, fingerLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_proximalNoMetacarpal.Compute(wrist, wrist, finger.Proximal, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
_hasMetacarpalInfo = false;
|
||||
_proximal.Compute(wrist, wrist, finger.Proximal, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_proximalNoMetacarpal.Compute(wrist, wrist, finger.Proximal, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
}
|
||||
|
||||
_intermediate.Compute(wrist, finger.Proximal, finger.Intermediate, fingerLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_distal.Compute(wrist, finger.Intermediate, finger.Distal, fingerLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the bone information, so that it can be used for the opposite hand.
|
||||
/// </summary>
|
||||
public void Mirror()
|
||||
{
|
||||
if (_hasMetacarpalInfo)
|
||||
{
|
||||
_metacarpal.Mirror();
|
||||
}
|
||||
|
||||
_proximal.Mirror();
|
||||
_proximalNoMetacarpal.Mirror();
|
||||
_intermediate.Mirror();
|
||||
_distal.Mirror();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates the data towards another descriptor.
|
||||
/// </summary>
|
||||
/// <param name="to">Descriptor to interpolate the data to</param>
|
||||
/// <param name="t">Interpolation factor [0.0, 1.0]</param>
|
||||
public void InterpolateTo(UxrFingerDescriptor to, float t)
|
||||
{
|
||||
if (_hasMetacarpalInfo)
|
||||
{
|
||||
_metacarpal.InterpolateTo(to._metacarpal, t);
|
||||
}
|
||||
|
||||
_proximal.InterpolateTo(to._proximal, t);
|
||||
_proximalNoMetacarpal.InterpolateTo(to._proximalNoMetacarpal, t);
|
||||
_intermediate.InterpolateTo(to._intermediate, t);
|
||||
_distal.InterpolateTo(to._distal, t);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Outputs transform information to the editor window.
|
||||
/// </summary>
|
||||
/// <param name="prefix">String to prefix the information with</param>
|
||||
public void DrawEditorDebugLabels(string prefix)
|
||||
{
|
||||
EditorGUILayout.LabelField(prefix + _proximal.Right);
|
||||
EditorGUILayout.LabelField(prefix + _proximal.Up);
|
||||
EditorGUILayout.LabelField(prefix + _proximal.Forward);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Compares the transform information with another finger.
|
||||
/// </summary>
|
||||
/// <param name="other">Finger information to compare it to</param>
|
||||
/// <returns>Whether both fingers describe the same transform information</returns>
|
||||
public bool Equals(UxrFingerDescriptor other)
|
||||
{
|
||||
if (_hasMetacarpalInfo != other._hasMetacarpalInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_hasMetacarpalInfo)
|
||||
{
|
||||
return _metacarpal.Equals(other._metacarpal) && _proximal.Equals(other._proximal) && _intermediate.Equals(other._intermediate) && _distal.Equals(other._distal);
|
||||
}
|
||||
return _proximal.Equals(other._proximal) && _intermediate.Equals(other._intermediate) && _distal.Equals(other._distal);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25fe587ebcf04b76afc04cfd1a84d413
|
||||
timeCreated: 1643749392
|
||||
@@ -0,0 +1,229 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFingerNodeDescriptor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.HandPoses
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores a bone's right, up and forward vectors in local coordinates of its parent. Right, up and forward
|
||||
/// vectors will always point to this directions independently of how the transforms have been set up in
|
||||
/// order to guarantee poses can be reused by other hands that use a different coordinate system.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct UxrFingerNodeDescriptor
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Matrix4x4 _transformRelativeToHand;
|
||||
[SerializeField] private Vector3 _right;
|
||||
[SerializeField] private Vector3 _up;
|
||||
[SerializeField] private Vector3 _forward;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original relative transform to the hand bone. We use it mainly to compute
|
||||
/// <see cref="UxrGrabbableObject" /> preview meshes more conveniently.
|
||||
/// </summary>
|
||||
public Matrix4x4 TransformRelativeToHand => _transformRelativeToHand;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal right vector. The vector that points in our well-known right direction, in the coordinate system
|
||||
/// of the finger.
|
||||
/// </summary>
|
||||
public Vector3 Right => _right;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal up vector. The vector that points in our well-known up direction, in the coordinate system of
|
||||
/// the finger.
|
||||
/// </summary>
|
||||
public Vector3 Up => _up;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the universal forward vector. The vector that points in our well-known forward direction, in the coordinate
|
||||
/// system of the finger.
|
||||
/// </summary>
|
||||
public Vector3 Forward => _forward;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Creates a well-known axes system for a node, to handle transforms independently of the coordinate system being used
|
||||
/// by a hand rig.
|
||||
/// </summary>
|
||||
/// <param name="hand">Hand node</param>
|
||||
/// <param name="parent">Parent node</param>
|
||||
/// <param name="node">Current node being created</param>
|
||||
/// <param name="parentLocalAxes">
|
||||
/// In local coordinates, which parent axes point to the well-known right, up and forward directions
|
||||
/// </param>
|
||||
/// <param name="nodeLocalAxes">
|
||||
/// In local coordinates, which node axes point to the well-known right, up and forward directions
|
||||
/// </param>
|
||||
public UxrFingerNodeDescriptor(Transform hand, Transform parent, Transform node, UxrUniversalLocalAxes parentLocalAxes, UxrUniversalLocalAxes nodeLocalAxes)
|
||||
{
|
||||
_right = Vector3.right;
|
||||
_up = Vector3.up;
|
||||
_forward = Vector3.forward;
|
||||
|
||||
_transformRelativeToHand = Matrix4x4.identity;
|
||||
|
||||
if (hand == null || parent == null || node != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Compute(hand, parent, node, parentLocalAxes, nodeLocalAxes, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a well-known axes system for a node, to handle transforms independently of the coordinate system being used
|
||||
/// by a hand rig.
|
||||
/// </summary>
|
||||
/// <param name="hand">Hand node</param>
|
||||
/// <param name="parent">Parent node</param>
|
||||
/// <param name="node">Current node being created</param>
|
||||
/// <param name="parentLocalAxes">
|
||||
/// In local coordinates, which parent axes point to the well-known right, up and forward
|
||||
/// directions
|
||||
/// </param>
|
||||
/// <param name="nodeLocalAxes">
|
||||
/// In local coordinates, which node axes point to the well-known right, up and forward
|
||||
/// directions
|
||||
/// </param>
|
||||
/// <param name="computeRelativeMatrixOnly">Whether to compute only the <see cref="TransformRelativeToHand" /> value</param>
|
||||
public void Compute(Transform hand, Transform parent, Transform node, UxrUniversalLocalAxes parentLocalAxes, UxrUniversalLocalAxes nodeLocalAxes, bool computeRelativeMatrixOnly)
|
||||
{
|
||||
_transformRelativeToHand = hand.worldToLocalMatrix * node.localToWorldMatrix;
|
||||
|
||||
if (!computeRelativeMatrixOnly)
|
||||
{
|
||||
Matrix4x4 matrixParent = new Matrix4x4();
|
||||
matrixParent.SetColumn(0, parent.TransformVector(parentLocalAxes.LocalRight));
|
||||
matrixParent.SetColumn(1, parent.TransformVector(parentLocalAxes.LocalUp));
|
||||
matrixParent.SetColumn(2, parent.TransformVector(parentLocalAxes.LocalForward));
|
||||
matrixParent.SetColumn(3, new Vector4(parent.position.x, parent.position.y, parent.position.z, 1));
|
||||
|
||||
_right = matrixParent.inverse.MultiplyVector(node.TransformVector(nodeLocalAxes.LocalRight));
|
||||
_up = matrixParent.inverse.MultiplyVector(node.TransformVector(nodeLocalAxes.LocalUp));
|
||||
_forward = matrixParent.inverse.MultiplyVector(node.TransformVector(nodeLocalAxes.LocalForward));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the descriptor. Useful to switch between left and right hand data.
|
||||
/// </summary>
|
||||
public void Mirror()
|
||||
{
|
||||
// We do not need to mirror position and rotation because we don't use them for mirroring
|
||||
|
||||
_right.x = -_right.x;
|
||||
_right = -_right;
|
||||
_up.x = -_up.x;
|
||||
_forward.x = -_forward.x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates the axes data towards another descriptor.
|
||||
/// </summary>
|
||||
/// <param name="to">Descriptor to interpolate the data to</param>
|
||||
/// <param name="t">Interpolation factor [0.0, 1.0]</param>
|
||||
public void InterpolateTo(UxrFingerNodeDescriptor to, float t)
|
||||
{
|
||||
Quaternion quatSlerp = Quaternion.Slerp(Quaternion.LookRotation(_forward, _up), Quaternion.LookRotation(to._forward, to._up), t);
|
||||
_right = quatSlerp * Vector3.right;
|
||||
_up = quatSlerp * Vector3.up;
|
||||
_forward = quatSlerp * Vector3.forward;
|
||||
|
||||
// For performance reasons, _transformRelativeToHand isn't interpolated because it is only used for grab preview poses. Interpolation is used for runtime pose blending.
|
||||
// If at any point it becomes necessary, uncomment the line below:
|
||||
|
||||
// _transformRelativeToHand = Matrix4x4Ext.Interpolate(_transformRelativeToHand, to._transformRelativeToHand, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the content of two FingerNodeDescriptors is equal (they describe the same axes).
|
||||
/// </summary>
|
||||
/// <param name="other">UxrFingerNodeDescriptor to compare it to</param>
|
||||
/// <returns>Boolean telling if the two FingerNodeDescriptors describe the same axes</returns>
|
||||
public bool Equals(UxrFingerNodeDescriptor other)
|
||||
{
|
||||
float epsilon = 0.00001f;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
if (Mathf.Abs(_transformRelativeToHand[i, j] - other._transformRelativeToHand[i, j]) > epsilon)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool equal = _right == other._right && _up == other._up && _forward == other._forward;
|
||||
/*
|
||||
double inequalityThreshold = 9.99999943962493E-11;
|
||||
|
||||
if (Right != other.Right)
|
||||
{
|
||||
double inequalityValue = GetInequalityValue(Right, other.Right);
|
||||
double inequalityMargin = inequalityValue - inequalityThreshold;
|
||||
double inequalityFactor = inequalityValue / inequalityThreshold;
|
||||
Debug.Log($"right != other.right Inequality value = {inequalityValue}, margin = {inequalityMargin}, factor {inequalityFactor}");
|
||||
}
|
||||
|
||||
if (Up != other.Up)
|
||||
{
|
||||
double inequalityValue = GetInequalityValue(Up, other.Up);
|
||||
double inequalityMargin = inequalityValue - inequalityThreshold;
|
||||
double inequalityFactor = inequalityValue / inequalityThreshold;
|
||||
Debug.Log($"up != other.up Inequality value = {inequalityValue}, margin = {inequalityMargin}, factor {inequalityFactor}");
|
||||
}
|
||||
|
||||
if (Forward != other.Forward)
|
||||
{
|
||||
double inequalityValue = GetInequalityValue(Forward, other.Forward);
|
||||
double inequalityMargin = inequalityValue - inequalityThreshold;
|
||||
double inequalityFactor = inequalityValue / inequalityThreshold;
|
||||
Debug.Log($"forward != other.forward Inequality value = {inequalityValue}, margin = {inequalityMargin}, factor {inequalityFactor}");
|
||||
}*/
|
||||
|
||||
return equal;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets an inequality value that measures how different two vectors are. It is used to provide a way to compare
|
||||
/// vectors considering floating point errors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">Vector A</param>
|
||||
/// <param name="rhs">Vector B</param>
|
||||
/// <returns>Inequality value</returns>
|
||||
private double GetInequalityValue(Vector3 lhs, Vector3 rhs)
|
||||
{
|
||||
float num1 = lhs.x - rhs.x;
|
||||
float num2 = lhs.y - rhs.y;
|
||||
float num3 = lhs.z - rhs.z;
|
||||
return num1 * (double)num1 + num2 * (double)num2 + num3 * (double)num3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f9dc40c111844888f796a51ac2e7e82
|
||||
timeCreated: 1643749470
|
||||
@@ -0,0 +1,234 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandDescriptor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.HandPoses
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores base-independent node orientations for all fingers of a hand.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrHandDescriptor
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrFingerDescriptor _index;
|
||||
[SerializeField] private UxrFingerDescriptor _middle;
|
||||
[SerializeField] private UxrFingerDescriptor _ring;
|
||||
[SerializeField] private UxrFingerDescriptor _little;
|
||||
[SerializeField] private UxrFingerDescriptor _thumb;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index finger information.
|
||||
/// </summary>
|
||||
public UxrFingerDescriptor Index => _index;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the middle finger information.
|
||||
/// </summary>
|
||||
public UxrFingerDescriptor Middle => _middle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ring finger information.
|
||||
/// </summary>
|
||||
public UxrFingerDescriptor Ring => _ring;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the little finger information.
|
||||
/// </summary>
|
||||
public UxrFingerDescriptor Little => _little;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the thumb finger information.
|
||||
/// </summary>
|
||||
public UxrFingerDescriptor Thumb => _thumb;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor()
|
||||
{
|
||||
_index = new UxrFingerDescriptor();
|
||||
_middle = new UxrFingerDescriptor();
|
||||
_ring = new UxrFingerDescriptor();
|
||||
_little = new UxrFingerDescriptor();
|
||||
_thumb = new UxrFingerDescriptor();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar whose hand to compute the descriptor for</param>
|
||||
/// <param name="handSide">Which hand to process</param>
|
||||
public UxrHandDescriptor(UxrAvatar avatar, UxrHandSide handSide)
|
||||
{
|
||||
_index = new UxrFingerDescriptor();
|
||||
_middle = new UxrFingerDescriptor();
|
||||
_ring = new UxrFingerDescriptor();
|
||||
_little = new UxrFingerDescriptor();
|
||||
_thumb = new UxrFingerDescriptor();
|
||||
|
||||
Compute(avatar, handSide);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="arm">Arm whose hand to compute the descriptor for</param>
|
||||
/// <param name="handLocalAxes">Hand axes system</param>
|
||||
/// <param name="fingerLocalAxes">Finger axes system</param>
|
||||
public UxrHandDescriptor(UxrAvatarArm arm, UxrUniversalLocalAxes handLocalAxes, UxrUniversalLocalAxes fingerLocalAxes)
|
||||
{
|
||||
_index = new UxrFingerDescriptor();
|
||||
_middle = new UxrFingerDescriptor();
|
||||
_ring = new UxrFingerDescriptor();
|
||||
_little = new UxrFingerDescriptor();
|
||||
_thumb = new UxrFingerDescriptor();
|
||||
|
||||
Compute(arm, handLocalAxes, fingerLocalAxes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the given finger.
|
||||
/// </summary>
|
||||
/// <param name="fingerType">Which finger to get</param>
|
||||
/// <returns>Finger information</returns>
|
||||
public UxrFingerDescriptor GetFinger(UxrFingerType fingerType)
|
||||
{
|
||||
switch (fingerType)
|
||||
{
|
||||
case UxrFingerType.Thumb: return Thumb;
|
||||
case UxrFingerType.Index: return Index;
|
||||
case UxrFingerType.Middle: return Middle;
|
||||
case UxrFingerType.Ring: return Ring;
|
||||
case UxrFingerType.Little: return Little;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(fingerType), fingerType, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hand data.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to compute the hand data of</param>
|
||||
/// <param name="handSide">Which hand to compute the hand data of</param>
|
||||
/// <param name="computeRelativeMatrixOnly">Whether to compute the relative transform to the hand only</param>
|
||||
public void Compute(UxrAvatar avatar, UxrHandSide handSide, bool computeRelativeMatrixOnly = false)
|
||||
{
|
||||
Compute(handSide == UxrHandSide.Left ? avatar.AvatarRig.LeftArm : avatar.AvatarRig.RightArm, avatar.AvatarRigInfo.GetArmInfo(handSide).HandUniversalLocalAxes, avatar.AvatarRigInfo.GetArmInfo(handSide).FingerUniversalLocalAxes, computeRelativeMatrixOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hand data.
|
||||
/// </summary>
|
||||
/// <param name="arm">Arm where the hand is</param>
|
||||
/// <param name="handLocalAxes">Hand axes system</param>
|
||||
/// <param name="fingerLocalAxes">Finger axes system</param>
|
||||
/// <param name="computeRelativeMatrixOnly">Whether to compute the relative transform to the hand only</param>
|
||||
public void Compute(UxrAvatarArm arm, UxrUniversalLocalAxes handLocalAxes, UxrUniversalLocalAxes fingerLocalAxes, bool computeRelativeMatrixOnly = false)
|
||||
{
|
||||
_index.Compute(arm.Hand.Wrist, arm.Hand.Index, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_middle.Compute(arm.Hand.Wrist, arm.Hand.Middle, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_ring.Compute(arm.Hand.Wrist, arm.Hand.Ring, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_little.Compute(arm.Hand.Wrist, arm.Hand.Little, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
_thumb.Compute(arm.Hand.Wrist, arm.Hand.Thumb, handLocalAxes, fingerLocalAxes, computeRelativeMatrixOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the data from another descriptor.
|
||||
/// </summary>
|
||||
/// <param name="src">Source data</param>
|
||||
public void CopyFrom(UxrHandDescriptor src)
|
||||
{
|
||||
_index = src._index;
|
||||
_middle = src._middle;
|
||||
_ring = src._ring;
|
||||
_little = src._little;
|
||||
_thumb = src._thumb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates the data towards another descriptor.
|
||||
/// </summary>
|
||||
/// <param name="to">Descriptor to interpolate the data to</param>
|
||||
/// <param name="t">Interpolation factor [0.0, 1.0]</param>
|
||||
public void InterpolateTo(UxrHandDescriptor to, float t)
|
||||
{
|
||||
_index.InterpolateTo(to._index, t);
|
||||
_middle.InterpolateTo(to._middle, t);
|
||||
_ring.InterpolateTo(to._ring, t);
|
||||
_little.InterpolateTo(to._little, t);
|
||||
_thumb.InterpolateTo(to._thumb, t);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Outputs transform data in the editor window.
|
||||
/// </summary>
|
||||
public void DrawEditorDebugLabels()
|
||||
{
|
||||
_index.DrawEditorDebugLabels("index: ");
|
||||
_middle.DrawEditorDebugLabels("middle: ");
|
||||
_ring.DrawEditorDebugLabels("ring: ");
|
||||
_little.DrawEditorDebugLabels("little: ");
|
||||
_thumb.DrawEditorDebugLabels("thumb: ");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hand descriptor with mirrored transforms, so that the data can be used for the opposite hand.
|
||||
/// </summary>
|
||||
/// <returns>Mirrored hand descriptor</returns>
|
||||
public UxrHandDescriptor Mirrored()
|
||||
{
|
||||
UxrHandDescriptor mirroredHandDescriptor = new UxrHandDescriptor();
|
||||
mirroredHandDescriptor.CopyFrom(this);
|
||||
|
||||
mirroredHandDescriptor._index.Mirror();
|
||||
mirroredHandDescriptor._middle.Mirror();
|
||||
mirroredHandDescriptor._ring.Mirror();
|
||||
mirroredHandDescriptor._little.Mirror();
|
||||
mirroredHandDescriptor._thumb.Mirror();
|
||||
|
||||
return mirroredHandDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a hand descriptor contains the same transform data.
|
||||
/// </summary>
|
||||
/// <param name="other">Hand descriptor to compare it to</param>
|
||||
/// <returns>Whether the hand descriptor contains the same transform data</returns>
|
||||
public bool Equals(UxrHandDescriptor other)
|
||||
{
|
||||
if (other != null)
|
||||
{
|
||||
return _index.Equals(other._index) && _middle.Equals(other._middle) && _ring.Equals(other._ring) && _little.Equals(other._little) && _thumb.Equals(other._thumb);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a99921d3e6b94b62bb8cd0b7a227be0a
|
||||
timeCreated: 1643749379
|
||||
@@ -0,0 +1,183 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandPoseAsset.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.HandPoses
|
||||
{
|
||||
/// <summary>
|
||||
/// ScriptableObject that stores custom hand poses. Data is stored in a well-known axes system so that poses can be
|
||||
/// exchanged between different avatars.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrHandPoseAsset : ScriptableObject
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _handPoseAssetVersion;
|
||||
[SerializeField] private UxrHandPoseType _poseType;
|
||||
[SerializeField] private UxrHandDescriptor _handDescriptorLeft;
|
||||
[SerializeField] private UxrHandDescriptor _handDescriptorRight;
|
||||
[SerializeField] private UxrHandDescriptor _handDescriptorOpenLeft;
|
||||
[SerializeField] private UxrHandDescriptor _handDescriptorOpenRight;
|
||||
[SerializeField] private UxrHandDescriptor _handDescriptorClosedLeft;
|
||||
[SerializeField] private UxrHandDescriptor _handDescriptorClosedRight;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Current data version.
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version the pose was stored in.
|
||||
/// </summary>
|
||||
public int Version
|
||||
{
|
||||
get => _handPoseAssetVersion;
|
||||
set => _handPoseAssetVersion = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pose type.
|
||||
/// </summary>
|
||||
public UxrHandPoseType PoseType
|
||||
{
|
||||
get => _poseType;
|
||||
set => _poseType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left fixed pose hand descriptor.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor HandDescriptorLeft
|
||||
{
|
||||
get => _handDescriptorLeft;
|
||||
set => _handDescriptorLeft = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right fixed pose hand descriptor.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor HandDescriptorRight
|
||||
{
|
||||
get => _handDescriptorRight;
|
||||
set => _handDescriptorRight = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left blend pose hand descriptor for the open state.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor HandDescriptorOpenLeft
|
||||
{
|
||||
get => _handDescriptorOpenLeft;
|
||||
set => _handDescriptorOpenLeft = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right blend pose hand descriptor for the open state.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor HandDescriptorOpenRight
|
||||
{
|
||||
get => _handDescriptorOpenRight;
|
||||
set => _handDescriptorOpenRight = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left blend pose hand descriptor for the closed state.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor HandDescriptorClosedLeft
|
||||
{
|
||||
get => _handDescriptorClosedLeft;
|
||||
set => _handDescriptorClosedLeft = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right blend pose hand descriptor for the closed state.
|
||||
/// </summary>
|
||||
public UxrHandDescriptor HandDescriptorClosedRight
|
||||
{
|
||||
get => _handDescriptorClosedRight;
|
||||
set => _handDescriptorClosedRight = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hand descriptor for the given hand, based on the <see cref="PoseType" />.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Hand to get the descriptor for</param>
|
||||
/// <param name="blendPoseType">
|
||||
/// If <see cref="PoseType" /> is <see cref="UxrHandPoseType.Blend" />, whether to get the open or
|
||||
/// closed pose descriptor.
|
||||
/// </param>
|
||||
/// <returns>Hand descriptor</returns>
|
||||
public UxrHandDescriptor GetHandDescriptor(UxrHandSide handSide, UxrBlendPoseType blendPoseType = UxrBlendPoseType.None)
|
||||
{
|
||||
return PoseType switch
|
||||
{
|
||||
UxrHandPoseType.Fixed => handSide == UxrHandSide.Left ? _handDescriptorLeft : _handDescriptorRight,
|
||||
UxrHandPoseType.Blend when blendPoseType == UxrBlendPoseType.OpenGrip => handSide == UxrHandSide.Left ? _handDescriptorOpenLeft : _handDescriptorOpenRight,
|
||||
UxrHandPoseType.Blend when blendPoseType == UxrBlendPoseType.ClosedGrip => handSide == UxrHandSide.Left ? _handDescriptorClosedLeft : _handDescriptorClosedRight,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hand descriptor for the given hand, based on an external <see cref="UxrHandPoseType" /> parameter.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Hand to get the descriptor for</param>
|
||||
/// <param name="poseType">The pose type to get the descriptor for</param>
|
||||
/// <param name="blendPoseType">
|
||||
/// If <see cref="PoseType" /> is <see cref="UxrHandPoseType.Blend" />, whether to get the open or
|
||||
/// closed pose descriptor.
|
||||
/// </param>
|
||||
/// <returns>Hand descriptor</returns>
|
||||
public UxrHandDescriptor GetHandDescriptor(UxrHandSide handSide, UxrHandPoseType poseType, UxrBlendPoseType blendPoseType = UxrBlendPoseType.None)
|
||||
{
|
||||
return poseType switch
|
||||
{
|
||||
UxrHandPoseType.Fixed => handSide == UxrHandSide.Left ? _handDescriptorLeft : _handDescriptorRight,
|
||||
UxrHandPoseType.Blend when blendPoseType == UxrBlendPoseType.OpenGrip => handSide == UxrHandSide.Left ? _handDescriptorOpenLeft : _handDescriptorOpenRight,
|
||||
UxrHandPoseType.Blend when blendPoseType == UxrBlendPoseType.ClosedGrip => handSide == UxrHandSide.Left ? _handDescriptorClosedLeft : _handDescriptorClosedRight,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Outputs transform debug data to the editor window.
|
||||
/// </summary>
|
||||
/// <param name="handSide">Hand to output the data for</param>
|
||||
/// <param name="blendPoseType">The blend pose type or <see cref="UxrBlendPoseType.None" /> if it is a fixed pose</param>
|
||||
public void DrawEditorDebugLabels(UxrHandSide handSide, UxrBlendPoseType blendPoseType = UxrBlendPoseType.None)
|
||||
{
|
||||
UxrHandDescriptor handDescriptor = handSide == UxrHandSide.Left ? HandDescriptorLeft : HandDescriptorRight;
|
||||
|
||||
if (blendPoseType == UxrBlendPoseType.OpenGrip)
|
||||
{
|
||||
handDescriptor = handSide == UxrHandSide.Left ? HandDescriptorOpenLeft : HandDescriptorOpenRight;
|
||||
}
|
||||
else if (blendPoseType == UxrBlendPoseType.ClosedGrip)
|
||||
{
|
||||
handDescriptor = handSide == UxrHandSide.Left ? HandDescriptorClosedLeft : HandDescriptorClosedRight;
|
||||
}
|
||||
|
||||
handDescriptor?.DrawEditorDebugLabels();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3e4dbcd197921446835b52ba65d418e
|
||||
timeCreated: 1543332642
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandPoseType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Manipulation.HandPoses
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different pose types.
|
||||
/// </summary>
|
||||
public enum UxrHandPoseType
|
||||
{
|
||||
/// <summary>
|
||||
/// Not initialized.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Fixed pose (pose with a single state).
|
||||
/// </summary>
|
||||
Fixed,
|
||||
|
||||
/// <summary>
|
||||
/// Blend pose. A blend pose has two states, open and closed, and allows to blend between them. In the grabbing system
|
||||
/// it allows using a single blend grab pose for multiple objects with different sizes.
|
||||
/// </summary>
|
||||
Blend
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1657dc842d6e4de4a6c3182e6f4efa04
|
||||
timeCreated: 1643744329
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f893437d37f64f978cd75aed2aa14320
|
||||
timeCreated: 1643993603
|
||||
@@ -0,0 +1,94 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAlignOnRelease.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Aligns an object smoothly whenever it is released to keep it leveled. Should be used on non physics-driven
|
||||
/// grabbable objects, which remain floating in the air when being released.
|
||||
/// </summary>
|
||||
public class UxrAlignOnRelease : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _onlyLevel = true;
|
||||
[SerializeField] [Range(0.0f, 1.0f)] private float _smoothFactor = 0.2f;
|
||||
[SerializeField] private List<UxrGrabbableObject> _grabbableObjects;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Caches the transform component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_selfTransform = transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the transform while the object is not being grabbed.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (!IsBeingGrabbed)
|
||||
{
|
||||
// Smoothly rotate towards horizontal orientation when not being grabbed
|
||||
|
||||
if (_onlyLevel == false)
|
||||
{
|
||||
_selfTransform.rotation = UxrInterpolator.SmoothDampRotation(_selfTransform.rotation, Quaternion.FromToRotation(_selfTransform.up, Vector3.up) * _selfTransform.rotation, _smoothFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 projectedRight = Vector3.ProjectOnPlane(transform.right, Vector3.up);
|
||||
Quaternion targetRotation = Quaternion.FromToRotation(_selfTransform.right, projectedRight) * _selfTransform.rotation;
|
||||
|
||||
if ((targetRotation * Vector3.up).y < 0.0f)
|
||||
{
|
||||
targetRotation = targetRotation * Quaternion.AngleAxis(180.0f, Vector3.forward);
|
||||
}
|
||||
|
||||
_selfTransform.rotation = UxrInterpolator.SmoothDampRotation(_selfTransform.rotation, targetRotation, _smoothFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the object is being grabbed using any of the registered grabbable objects.
|
||||
/// </summary>
|
||||
private bool IsBeingGrabbed
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (UxrGrabbableObject grabbableObject in _grabbableObjects)
|
||||
{
|
||||
if (UxrGrabManager.Instance.IsBeingGrabbed(grabbableObject))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Transform _selfTransform;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1dd4099f01318e34680aafb12edd7b4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAutoSlideInAnchor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Anchor component for <see cref="UxrAutoSlideInAnchor" />. Grabbable objects with the
|
||||
/// <see cref="UxrAutoSlideInObject" /> component will automatically attach/detach from this anchor.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrGrabbableObjectAnchor))]
|
||||
public class UxrAutoSlideInAnchor : UxrComponent<UxrAutoSlideInAnchor>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the anchor.
|
||||
/// </summary>
|
||||
public UxrGrabbableObjectAnchor Anchor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_anchor == null)
|
||||
{
|
||||
_anchor = GetComponent<UxrGrabbableObjectAnchor>();
|
||||
}
|
||||
|
||||
return _anchor;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (_anchor == null)
|
||||
{
|
||||
_anchor = GetComponent<UxrGrabbableObjectAnchor>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrGrabbableObjectAnchor _anchor;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e85a05d37194014d8a60f3f2f72936a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAutoSlideInObject.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
public partial class UxrAutoSlideInObject
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Manipulations are already handled through events, we don't serialize them in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_insertAxis), ref _insertAxis);
|
||||
SerializeStateValue(level, options, nameof(_insertOffset), ref _insertOffset);
|
||||
SerializeStateValue(level, options, nameof(_insertOffsetSign), ref _insertOffsetSign);
|
||||
SerializeStateValue(level, options, nameof(_objectLocalSize), ref _objectLocalSize);
|
||||
SerializeStateValue(level, options, nameof(_slideInTimer), ref _slideInTimer);
|
||||
SerializeStateValue(level, options, nameof(_placedAfterSlidingIn), ref _placedAfterSlidingIn);
|
||||
SerializeStateValue(level, options, nameof(_manipulationHapticFeedback), ref _manipulationHapticFeedback);
|
||||
SerializeStateValue(level, options, nameof(_minHapticAmplitude), ref _minHapticAmplitude);
|
||||
SerializeStateValue(level, options, nameof(_maxHapticAmplitude), ref _maxHapticAmplitude);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be3cda6fb2a64942a98f487a1e810b67
|
||||
timeCreated: 1706041007
|
||||
@@ -0,0 +1,369 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrAutoSlideInObject.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Haptics.Helpers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that, together with <see cref="UxrAutoSlideInAnchor" /> will add the following behaviour to a
|
||||
/// <see cref="UxrGrabbableObject" />:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// It will slide along the axis given by the grabbable object translation constraint. The constraint should
|
||||
/// be pre-configured along a single axis.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// It will be smoothly removed from the anchor and made free if dragged beyond the upper translation
|
||||
/// constraint.
|
||||
/// </item>
|
||||
/// <item>It will be smoothly placed automatically on the anchor when moved back close enough.</item>
|
||||
/// <item>It will fall back by itself when released while sliding along the axis.</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public partial class UxrAutoSlideInObject : UxrGrabbableObjectComponent<UxrAutoSlideInObject>
|
||||
{
|
||||
[SerializeField] private Vector3 _translationConstraintMin = Vector3.zero;
|
||||
[SerializeField] private Vector3 _translationConstraintMax = Vector3.forward * 0.1f;
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the object hit the end after sliding in after it was released.
|
||||
/// </summary>
|
||||
public event Action PlacedAfterSlidingIn;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the avatars updated event so that the manipulation logic is done after all manipulation
|
||||
/// logic has been updated.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the avatars updated event.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize component.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
_placedAfterSlidingIn = GrabbableObject.CurrentAnchor != null;
|
||||
|
||||
// Get slide axis
|
||||
int insertAxis = GrabbableObject.SingleTranslationAxisIndex;
|
||||
|
||||
if (insertAxis == -1 || GrabbableObject.TranslationConstraint != UxrTranslationConstraintMode.RestrictLocalOffset)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.ManipulationModule} {this}: {nameof(UxrGrabbableObject)} component needs to have a local offset translation constraint along a single axis to work properly");
|
||||
}
|
||||
|
||||
_insertAxis = UxrAxis.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
_insertAxis = insertAxis;
|
||||
}
|
||||
|
||||
// Store haptic feedback component in case it exists, to disable it while the object is out of the sliding zone
|
||||
_manipulationHapticFeedback = GetComponent<UxrManipulationHapticFeedback>();
|
||||
|
||||
// Compute the slide length
|
||||
_insertOffset = _translationConstraintMax[_insertAxis] > -_translationConstraintMin[_insertAxis] ? _translationConstraintMax[_insertAxis] : _translationConstraintMin[_insertAxis];
|
||||
|
||||
_insertOffsetSign = Mathf.Sign(_insertOffset);
|
||||
_insertOffset = Mathf.Abs(_insertOffset);
|
||||
|
||||
// Fix some object parameters if we need to
|
||||
|
||||
if (GrabbableObject.DropSnapMode != UxrSnapToAnchorMode.DontSnap)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Warnings)
|
||||
{
|
||||
Debug.LogWarning($"{UxrConstants.ManipulationModule} {this.GetPathUnderScene()}: GrabbableObject needs DropSnapMode to be DontSnap in order to work properly. Overriding.");
|
||||
}
|
||||
|
||||
GrabbableObject.DropSnapMode = UxrSnapToAnchorMode.DontSnap;
|
||||
}
|
||||
|
||||
GrabbableObject.IsPlaceable = false; // We will handle placement ourselves
|
||||
|
||||
// Compute the object size in local coordinates
|
||||
_objectLocalSize = gameObject.GetLocalBounds(true).size;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called after UltimateXR has done all the frame updating. Does the manipulation logic.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
bool grabbedByLocalAvatar = UxrGrabManager.Instance.IsBeingGrabbed(GrabbableObject) && UxrGrabManager.Instance.GetGrabbingHands(GrabbableObject).First().Avatar.AvatarMode == UxrAvatarMode.Local;
|
||||
|
||||
if (GrabbableObject.CurrentAnchor == null && grabbedByLocalAvatar)
|
||||
{
|
||||
// The object is being grabbed and is detached. Check if we need to place it on an anchor again by proximity.
|
||||
|
||||
foreach (UxrAutoSlideInAnchor anchor in UxrAutoSlideInAnchor.EnabledComponents.Where(a => a.Anchor.enabled))
|
||||
{
|
||||
// If it is inside the valid release "volume", place it in the anchor again and let it slide by re-assigning the constraints
|
||||
|
||||
if (anchor.Anchor.CurrentPlacedObject == null && anchor.Anchor.IsCompatibleObject(GrabbableObject) && IsObjectNearPlacement(anchor.Anchor))
|
||||
{
|
||||
AttachObject(anchor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GrabbableObject.CurrentAnchor != null && _insertAxis != null)
|
||||
{
|
||||
// Object can only move in a specific axis but if it is grabbed past this distance it becomes free
|
||||
|
||||
if (transform.parent != null && grabbedByLocalAvatar && Mathf.Abs(GrabbableObject.InitialLocalPosition[_insertAxis] - transform.localPosition[_insertAxis]) > _insertOffset * 0.99f)
|
||||
{
|
||||
DetachObject();
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is not being grabbed it will slide in
|
||||
|
||||
if (!GrabbableObject.IsBeingGrabbed)
|
||||
{
|
||||
// Use simple gravity to slide in. Gravity will be mapped to z axis to slide in our local coordinate system.
|
||||
|
||||
Vector3 speed = Physics.gravity * _slideInTimer;
|
||||
Vector3 pos = GrabbableObject.transform.localPosition;
|
||||
pos[_insertAxis] += Time.deltaTime * speed.y * _insertOffsetSign;
|
||||
|
||||
if ((_insertOffsetSign > 0.0f && pos[_insertAxis] < GrabbableObject.InitialLocalPosition[_insertAxis]) ||
|
||||
(_insertOffsetSign < 0.0f && pos[_insertAxis] > GrabbableObject.InitialLocalPosition[_insertAxis]))
|
||||
{
|
||||
pos[_insertAxis] = GrabbableObject.InitialLocalPosition[_insertAxis];
|
||||
|
||||
if (_placedAfterSlidingIn == false)
|
||||
{
|
||||
_placedAfterSlidingIn = true;
|
||||
OnPlacedAfterSlidingIn();
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolate other rotation/translation in case the object was released before the transition
|
||||
|
||||
float smooth = 0.1f;
|
||||
|
||||
pos[_insertAxis.Perpendicular] = UxrInterpolator.SmoothDamp(pos[_insertAxis.Perpendicular], GrabbableObject.InitialLocalPosition[_insertAxis.Perpendicular], smooth);
|
||||
pos[_insertAxis.OtherPerpendicular] = UxrInterpolator.SmoothDamp(pos[_insertAxis.OtherPerpendicular], GrabbableObject.InitialLocalPosition[_insertAxis.OtherPerpendicular], smooth);
|
||||
GrabbableObject.transform.localRotation = UxrInterpolator.SmoothDampRotation(GrabbableObject.transform.localRotation, GrabbableObject.InitialLocalRotation, smooth);
|
||||
|
||||
// Update
|
||||
|
||||
GrabbableObject.transform.localPosition = pos;
|
||||
|
||||
_slideInTimer += Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
_slideInTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object was placed at the end sliding in after it was released.
|
||||
/// Use in child classes to
|
||||
/// </summary>
|
||||
protected virtual void OnPlacedAfterSlidingIn()
|
||||
{
|
||||
PlacedAfterSlidingIn?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the object was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
if (!GrabbableObject.IsLockedInPlace)
|
||||
{
|
||||
_placedAfterSlidingIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the object was released.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectReleased(UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.GrabbableObject.CurrentAnchor != null && e.GrabbableObject.RigidBodySource)
|
||||
{
|
||||
// Force kinematic while released, so that we update the position/rotation.
|
||||
e.GrabbableObject.RigidBodySource.isKinematic = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrGrabbableObjectComponent<UxrAutoSlideInObject>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsGrabbableObjectRequired => true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Attaches the object to the anchor and assigns constraints to let it slide.
|
||||
/// </summary>
|
||||
protected void AttachObject(UxrAutoSlideInAnchor anchor)
|
||||
{
|
||||
// This method will be synchronized through network
|
||||
BeginSync();
|
||||
|
||||
// Set up constraints and place
|
||||
|
||||
GrabbableObject.TranslationConstraint = UxrTranslationConstraintMode.RestrictLocalOffset;
|
||||
GrabbableObject.RotationConstraint = UxrRotationConstraintMode.Locked;
|
||||
|
||||
GrabbableObject.TranslationLimitsMin = _translationConstraintMin;
|
||||
GrabbableObject.TranslationLimitsMax = _translationConstraintMax;
|
||||
|
||||
UxrGrabManager.Instance.PlaceObject(GrabbableObject, anchor.Anchor, UxrPlacementOptions.Smooth | UxrPlacementOptions.DontRelease, true);
|
||||
|
||||
if (_manipulationHapticFeedback)
|
||||
{
|
||||
_manipulationHapticFeedback.MinAmplitude = _minHapticAmplitude;
|
||||
_manipulationHapticFeedback.MaxAmplitude = _maxHapticAmplitude;
|
||||
}
|
||||
|
||||
EndSyncMethod(new object[] { anchor });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the object from the anchor so that it becomes free.
|
||||
/// </summary>
|
||||
protected void DetachObject()
|
||||
{
|
||||
// This method will be synchronized through network
|
||||
BeginSync();
|
||||
|
||||
if (_manipulationHapticFeedback)
|
||||
{
|
||||
_minHapticAmplitude = _manipulationHapticFeedback.MinAmplitude;
|
||||
_maxHapticAmplitude = _manipulationHapticFeedback.MaxAmplitude;
|
||||
_manipulationHapticFeedback.MinAmplitude = 0.0f;
|
||||
_manipulationHapticFeedback.MaxAmplitude = 0.0f;
|
||||
}
|
||||
|
||||
UxrGrabManager.Instance.RemoveObjectFromAnchor(GrabbableObject, true);
|
||||
GrabbableObject.TranslationConstraint = UxrTranslationConstraintMode.Free;
|
||||
GrabbableObject.RotationConstraint = UxrRotationConstraintMode.Free;
|
||||
|
||||
EndSyncMethod();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the object is close enough to the given anchor to be placed.
|
||||
/// </summary>
|
||||
/// <param name="anchor">Object anchor</param>
|
||||
/// <returns>Whether the object is close enough</returns>
|
||||
private bool IsObjectNearPlacement(UxrGrabbableObjectAnchor anchor)
|
||||
{
|
||||
if (anchor.enabled == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is it near enough in the longitudinal axis?
|
||||
|
||||
float threshold = Mathf.Min(0.03f, Mathf.Abs(_insertOffset * 0.1f));
|
||||
|
||||
Vector3 localOffset = anchor.AlignTransform.InverseTransformPoint(GrabbableObject.DropAlignTransform.position) - GrabbableObject.InitialLocalPosition;
|
||||
bool isInLongitudinalAxisRange = (_insertOffsetSign > 0.0f && localOffset[_insertAxis] < +_insertOffset - threshold && localOffset[_insertAxis] > 0.0f) ||
|
||||
(_insertOffsetSign < 0.0f && localOffset[_insertAxis] > -_insertOffset + threshold && localOffset[_insertAxis] < 0.0f);
|
||||
|
||||
// Is it near enough in both other axes?
|
||||
|
||||
float minGrabDistance = float.MaxValue;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabManager.Instance.GetGrabbingHands(GrabbableObject))
|
||||
{
|
||||
UxrGrabPointInfo grabPointInfo = GrabbableObject.GetGrabPoint(UxrGrabManager.Instance.GetGrabbedPoint(grabber));
|
||||
|
||||
if (grabPointInfo.MaxDistanceGrab < minGrabDistance)
|
||||
{
|
||||
minGrabDistance = grabPointInfo.MaxDistanceGrab;
|
||||
}
|
||||
}
|
||||
|
||||
// We use some calculations for the other axes so that it feels good.
|
||||
|
||||
float sizeOneAxis = Mathf.Min(Mathf.Max(_objectLocalSize[_insertAxis.Perpendicular], 0.1f), minGrabDistance);
|
||||
float sizeOtherAxis = Mathf.Min(Mathf.Max(_objectLocalSize[_insertAxis.OtherPerpendicular], 0.1f), minGrabDistance);
|
||||
|
||||
// Return conditions
|
||||
|
||||
return isInLongitudinalAxisRange && Mathf.Abs(localOffset[_insertAxis.Perpendicular]) < sizeOneAxis && Mathf.Abs(localOffset[_insertAxis.OtherPerpendicular]) < sizeOtherAxis;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrAxis _insertAxis;
|
||||
private float _insertOffset;
|
||||
private float _insertOffsetSign;
|
||||
private Vector3 _objectLocalSize;
|
||||
private float _slideInTimer;
|
||||
private bool _placedAfterSlidingIn;
|
||||
private UxrManipulationHapticFeedback _manipulationHapticFeedback;
|
||||
private float _minHapticAmplitude;
|
||||
private float _maxHapticAmplitude;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7264c3b787447c4596209ea4b950a39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrDependentGrabbable.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows an object be grabbed only if another object is being grabbed. For instance, it can
|
||||
/// be added to a grenade pin to make sure the pin is never grabbed unless the grenade is being grabbed too.
|
||||
/// Otherwise the pin could be removed by mistake when trying to grab the grenade.
|
||||
/// </summary>
|
||||
public class UxrDependentGrabbable : UxrGrabbableObjectComponent<UxrDependentGrabbable>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrGrabbableObject _dependentOn;
|
||||
[SerializeField] private bool _onlyOnce;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grabbable object the component depends on.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject DependentFrom
|
||||
{
|
||||
get => _dependentOn;
|
||||
set => _dependentOn = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to stop toggling the enabled state once the dependent object was grabbed. For instance, a grenade pin
|
||||
/// should remain grabbable once it has been removed from the grenade, no matter if the grenade is being grabbed or
|
||||
/// not at that point.
|
||||
/// </summary>
|
||||
public bool OnlyOnce
|
||||
{
|
||||
get => _onlyOnce;
|
||||
set => _onlyOnce = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the grabbable object state.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
GrabbableObject.enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the grabbable object state.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (GrabbableObject && DependentFrom && _check)
|
||||
{
|
||||
GrabbableObject.enabled = UxrGrabManager.Instance.IsBeingGrabbed(DependentFrom);
|
||||
|
||||
if (GrabbableObject.enabled && OnlyOnce)
|
||||
{
|
||||
_check = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the object was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectGrabbed(e);
|
||||
|
||||
_check = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _check = true;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 873afa6bf9451c3479e294d279b19ed9
|
||||
timeCreated: 1492963941
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableResizable.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
public sealed partial class UxrGrabbableResizable
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Manipulations are already handled through events, we don't serialize them in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_grabbingCount), ref _grabbingCount);
|
||||
SerializeStateValue(level, options, nameof(_grabbedCount), ref _grabbedCount);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75671c44324f466baec19cfc69507c7b
|
||||
timeCreated: 1706004361
|
||||
@@ -0,0 +1,421 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableResizable.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 67 // Disable warnings due to unused events
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Component that allows an object to be scaled by grabbing it by both sides and moving them closer or apart.
|
||||
/// The hierarchy should be as follows:
|
||||
/// </para>
|
||||
/// <code>
|
||||
/// -Root GameObject: With UxrGrabbableResizable and UxrGrabbableObject component.
|
||||
/// | The UxrGrabbableObject is a dummy grabbable parent that enables moving
|
||||
/// | this root by grabbing the child extensions. It can also have its own
|
||||
/// | grab points but they are not required.
|
||||
/// |---Root resizable: Object that will be scaled when the two extensions are moved.
|
||||
/// |---Grabbable left: Left grabbable extension with locked rotation and translation
|
||||
/// | constrained to sliding it left-right.
|
||||
/// |---Grabbable right: Right grabbable extension with locked rotation and translation
|
||||
/// constrained to sliding it left-right.
|
||||
/// </code>
|
||||
/// All objects should use an axis system with x right, y up and z forward.
|
||||
/// </summary>
|
||||
public sealed partial class UxrGrabbableResizable : UxrComponent, IUxrGrabbable
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[Header("General")] [SerializeField] private Transform _resizableRoot;
|
||||
[SerializeField] private float _startScale = 1.0f;
|
||||
|
||||
[Header("Grabbing")] [SerializeField] private UxrGrabbableObject _grabbableRoot;
|
||||
[SerializeField] private UxrGrabbableObject _grabbableExtendLeft;
|
||||
[SerializeField] private UxrGrabbableObject _grabbableExtendRight;
|
||||
|
||||
[Header("Haptics")] [SerializeField] [Range(0.0f, 1.0f)] private float _hapticsIntensity = 0.1f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Transform" /> that is going to be scaled when the two grabbable objects are moved apart.
|
||||
/// </summary>
|
||||
public Transform ResizableRoot => _resizableRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root grabbable object.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableRoot => _grabbableRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left grabbable extension.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableExtendLeft => _grabbableExtendLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right grabbable extension.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableExtendRight => _grabbableExtendRight;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrGrabbable
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsBeingGrabbed => GrabbableRoot.IsBeingGrabbed || GrabbableExtendLeft.IsBeingGrabbed || GrabbableExtendRight.IsBeingGrabbed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsGrabbable
|
||||
{
|
||||
get => GrabbableRoot.IsGrabbable || GrabbableExtendLeft.IsGrabbable || GrabbableExtendRight.IsGrabbable;
|
||||
set
|
||||
{
|
||||
BeginSync();
|
||||
|
||||
GrabbableRoot.IsGrabbable = value;
|
||||
GrabbableExtendLeft.IsGrabbable = value;
|
||||
GrabbableExtendRight.IsGrabbable = value;
|
||||
|
||||
EndSyncProperty(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsKinematic
|
||||
{
|
||||
get => GrabbableRoot.IsKinematic || GrabbableExtendLeft.IsKinematic || GrabbableExtendRight.IsKinematic;
|
||||
set
|
||||
{
|
||||
BeginSync();
|
||||
|
||||
GrabbableRoot.IsKinematic = value;
|
||||
GrabbableExtendLeft.IsKinematic = value;
|
||||
GrabbableExtendRight.IsKinematic = value;
|
||||
|
||||
EndSyncProperty(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<UxrManipulationEventArgs> Grabbing;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<UxrManipulationEventArgs> Grabbed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<UxrManipulationEventArgs> Releasing;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<UxrManipulationEventArgs> Released;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<UxrManipulationEventArgs> Placing;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<UxrManipulationEventArgs> Placed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetPositionAndState(bool propagateEvents)
|
||||
{
|
||||
// This method will be synchronized through network
|
||||
BeginSync();
|
||||
|
||||
ReleaseGrabs(true);
|
||||
GrabbableRoot.ResetPositionAndState(propagateEvents);
|
||||
GrabbableExtendLeft.ResetPositionAndState(propagateEvents);
|
||||
GrabbableExtendRight.ResetPositionAndState(propagateEvents);
|
||||
UpdateResizableScale();
|
||||
|
||||
EndSyncMethod(new object[] { propagateEvents });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ReleaseGrabs(bool propagateEvents)
|
||||
{
|
||||
// This method will be synchronized through network
|
||||
BeginSync();
|
||||
|
||||
GrabbableRoot.ReleaseGrabs(propagateEvents);
|
||||
GrabbableExtendLeft.ReleaseGrabs(propagateEvents);
|
||||
GrabbableExtendRight.ReleaseGrabs(propagateEvents);
|
||||
_grabbingCount = 0;
|
||||
_grabbedCount = 0;
|
||||
|
||||
EndSyncMethod(new object[] { propagateEvents });
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_initialGrabsSeparation = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
|
||||
_initialResizableLocalScale = _resizableRoot.transform.localScale;
|
||||
_separationToBoundsFactor = _initialGrabsSeparation / _resizableRoot.gameObject.GetLocalBounds(true).size.x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to relevant events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
|
||||
GrabbableRoot.Grabbing += Grabbable_Grabbing;
|
||||
GrabbableRoot.Grabbed += Grabbable_Grabbed;
|
||||
GrabbableRoot.Releasing += Grabbable_Releasing;
|
||||
GrabbableRoot.Released += Grabbable_Released;
|
||||
GrabbableExtendLeft.Grabbing += Grabbable_Grabbing;
|
||||
GrabbableExtendLeft.Grabbed += Grabbable_Grabbed;
|
||||
GrabbableExtendLeft.Releasing += Grabbable_Releasing;
|
||||
GrabbableExtendLeft.Released += Grabbable_Released;
|
||||
GrabbableExtendRight.Grabbed += Grabbable_Grabbed;
|
||||
GrabbableExtendRight.Grabbing += Grabbable_Grabbing;
|
||||
GrabbableExtendRight.Releasing += Grabbable_Releasing;
|
||||
GrabbableExtendRight.Released += Grabbable_Released;
|
||||
|
||||
_hapticsCoroutine = StartCoroutine(HapticsCoroutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from relevant events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
|
||||
GrabbableRoot.Grabbing -= Grabbable_Grabbing;
|
||||
GrabbableRoot.Grabbed -= Grabbable_Grabbed;
|
||||
GrabbableRoot.Releasing -= Grabbable_Releasing;
|
||||
GrabbableRoot.Released -= Grabbable_Released;
|
||||
GrabbableExtendLeft.Grabbing -= Grabbable_Grabbing;
|
||||
GrabbableExtendLeft.Grabbed -= Grabbable_Grabbed;
|
||||
GrabbableExtendLeft.Releasing -= Grabbable_Releasing;
|
||||
GrabbableExtendLeft.Released -= Grabbable_Released;
|
||||
GrabbableExtendRight.Grabbed -= Grabbable_Grabbed;
|
||||
GrabbableExtendRight.Grabbing -= Grabbable_Grabbing;
|
||||
GrabbableExtendRight.Releasing -= Grabbable_Releasing;
|
||||
GrabbableExtendRight.Released -= Grabbable_Released;
|
||||
|
||||
StopCoroutine(_hapticsCoroutine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the resizable using the initial scale if it's different than 1.0
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (!Mathf.Approximately(1.0f, _startScale))
|
||||
{
|
||||
float halfOffset = (_startScale * _initialGrabsSeparation - _initialGrabsSeparation) * 0.5f;
|
||||
GrabbableExtendLeft.transform.localPosition -= Vector3.right * halfOffset;
|
||||
GrabbableExtendRight.transform.localPosition += Vector3.right * halfOffset;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Coroutines
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that sends haptic feedback in case of scaling.
|
||||
/// </summary>
|
||||
/// <returns>Coroutine IEnumerator</returns>
|
||||
private IEnumerator HapticsCoroutine()
|
||||
{
|
||||
void SendHapticClip(UxrGrabbableObject grabbableObject, UxrHandSide handSide, float speed)
|
||||
{
|
||||
if (_hapticsIntensity < 0.001f || !UxrGrabManager.Instance.GetGrabbingHand(grabbableObject, 0, out UxrGrabber grabber) || grabber.Avatar.AvatarMode != UxrAvatarMode.Local)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float quantityPos = HapticsManipulationMaxSpeed - HapticsManipulationMinSpeed <= 0.0f ? 0.0f : (speed - HapticsManipulationMinSpeed) / (HapticsManipulationMaxSpeed - HapticsManipulationMinSpeed);
|
||||
|
||||
if (quantityPos > 0.0f)
|
||||
{
|
||||
float frequencyPos = Mathf.Lerp(HapticsManipulationMinFrequency, HapticsManipulationMaxFrequency, Mathf.Clamp01(quantityPos));
|
||||
float amplitudePos = Mathf.Lerp(0.1f, 1.0f, Mathf.Clamp01(quantityPos)) * _hapticsIntensity;
|
||||
|
||||
UxrAvatar.LocalAvatarInput.SendHapticFeedback(handSide, frequencyPos, amplitudePos, UxrConstants.InputControllers.HapticSampleDurationSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
float lastDistance = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (_grabbableExtendLeft != null && _grabbableExtendRight != null && _grabbableExtendLeft.IsBeingGrabbed && _grabbableExtendRight.IsBeingGrabbed)
|
||||
{
|
||||
float currentDistance = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
|
||||
float speed = Mathf.Abs(lastDistance - currentDistance) / UxrConstants.InputControllers.HapticSampleDurationSeconds;
|
||||
SendHapticClip(_grabbableExtendLeft, UxrHandSide.Left, speed);
|
||||
SendHapticClip(_grabbableExtendRight, UxrHandSide.Right, speed);
|
||||
lastDistance = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(UxrConstants.InputControllers.HapticSampleDurationSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the avatars and manipulation update. Scale the object at this point.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
UpdateResizableScale();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when any grabbable is about to be grabbed. It is responsible for sending the appropriate
|
||||
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Grabbable_Grabbing(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_grabbingCount++;
|
||||
|
||||
if (_grabbingCount == 1)
|
||||
{
|
||||
Grabbing?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after any grabbable was grabbed. It is responsible for sending the appropriate
|
||||
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Grabbable_Grabbed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_grabbedCount++;
|
||||
|
||||
if (_grabbedCount == 1)
|
||||
{
|
||||
Grabbed?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when any grabbable is about to be released. It is responsible for sending the appropriate
|
||||
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Grabbable_Releasing(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_grabbingCount--;
|
||||
|
||||
if (_grabbingCount == 0)
|
||||
{
|
||||
Releasing?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after any grabbable was released. It is responsible for sending the appropriate
|
||||
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Grabbable_Released(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_grabbedCount--;
|
||||
|
||||
if (_grabbedCount == 0)
|
||||
{
|
||||
Released?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the resizable scale based on the current separation between the left and right extensions.
|
||||
/// </summary>
|
||||
private void UpdateResizableScale()
|
||||
{
|
||||
float currentGrabSeparation = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
|
||||
|
||||
// Move the center in between the two extensions
|
||||
|
||||
Vector3 localCenter = transform.InverseTransformPoint((GrabbableExtendLeft.transform.position + GrabbableExtendRight.transform.position) * 0.5f);
|
||||
|
||||
Vector3 resizableLocalPos = _resizableRoot.transform.localPosition;
|
||||
resizableLocalPos.x = localCenter.x;
|
||||
_resizableRoot.transform.localPosition = resizableLocalPos;
|
||||
|
||||
// Scale the object
|
||||
|
||||
float localScaleZ = _resizableRoot.transform.localScale.z;
|
||||
Vector3 resizableLocalScale = _initialResizableLocalScale * (currentGrabSeparation / _initialGrabsSeparation * _separationToBoundsFactor);
|
||||
resizableLocalScale.z = localScaleZ;
|
||||
_resizableRoot.transform.localScale = resizableLocalScale;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const float HapticsManipulationMinSpeed = 0.03f;
|
||||
private const float HapticsManipulationMaxSpeed = 1.0f;
|
||||
private const float HapticsManipulationMinFrequency = 10;
|
||||
private const float HapticsManipulationMaxFrequency = 100;
|
||||
|
||||
private Vector3 _initialResizableLocalScale;
|
||||
private float _initialGrabsSeparation;
|
||||
private float _separationToBoundsFactor;
|
||||
private int _grabbingCount;
|
||||
private int _grabbedCount;
|
||||
private Coroutine _hapticsCoroutine;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 67
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6293fca64f4b03488a84eb8363cea23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,123 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrRestoreOnRelease.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Animation.Interpolation;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Core.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that will smoothly restore the original position and orientation of a <see cref="UxrGrabbableObject" />
|
||||
/// when released.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrGrabbableObject))]
|
||||
public class UxrRestoreOnRelease : UxrGrabbableObjectComponent<UxrRestoreOnRelease>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrEasing _transitionType = UxrEasing.Linear;
|
||||
[SerializeField] private float _transitionSeconds = 0.1f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Updates the transition if it's active.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_isTransitioning)
|
||||
{
|
||||
_transitionTimer -= Time.deltaTime;
|
||||
|
||||
float t = 1.0f;
|
||||
|
||||
if (_transitionTimer <= 0.0f)
|
||||
{
|
||||
_transitionTimer = 0.0f;
|
||||
_isTransitioning = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = UxrInterpolator.Interpolate(0.0f, 1.0f, 1.0f - _transitionTimer / _transitionSeconds, _transitionType);
|
||||
}
|
||||
|
||||
GrabbableObject.transform.localPosition = Vector3.LerpUnclamped(_initialLocalPosition, GrabbableObject.InitialLocalPosition, t);
|
||||
|
||||
if (_singleRotationAxis != -1 && t >= 0.0f && t <= 1.0f)
|
||||
{
|
||||
// Do not rotate manually here to let UxrGrabbableObject keep track of single axis rotation.
|
||||
// We allow using localRotation outside of the [0, 1] range to support overshooting since otherwise GrabbableObject.SingleRotationAxisDegrees
|
||||
// will clamp the rotation.
|
||||
GrabbableObject.SingleRotationAxisDegrees = Mathf.LerpUnclamped(_initialSingleAxisDegrees, 0.0f, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
GrabbableObject.transform.localRotation = Quaternion.SlerpUnclamped(_initialLocalRotation, GrabbableObject.InitialLocalRotation, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called by the base class whenever the object is grabbed.
|
||||
/// </summary>
|
||||
/// <param name="e">Contains all grab event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectGrabbed(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_isTransitioning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the base class whenever the object is released.
|
||||
/// </summary>
|
||||
/// <param name="e">Contains all grab event parameters</param>
|
||||
protected override void OnObjectReleased(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectReleased(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_isTransitioning = true;
|
||||
_transitionTimer = _transitionSeconds;
|
||||
_initialLocalPosition = e.GrabbableObject.transform.localPosition;
|
||||
_initialLocalRotation = e.GrabbableObject.transform.localRotation;
|
||||
_singleRotationAxis = e.GrabbableObject.SingleRotationAxisIndex;
|
||||
|
||||
if (_singleRotationAxis != -1)
|
||||
{
|
||||
_initialSingleAxisDegrees = e.GrabbableObject.SingleRotationAxisDegrees;
|
||||
}
|
||||
|
||||
// Avoid transitions getting in the way or being executed after the restore ended.
|
||||
e.GrabbableObject.FinishSmoothTransitions();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _isTransitioning;
|
||||
private float _transitionTimer = -1.0f;
|
||||
private Vector3 _initialLocalPosition;
|
||||
private Quaternion _initialLocalRotation;
|
||||
private int _singleRotationAxis;
|
||||
private float _initialSingleAxisDegrees;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a39a5225324a9c44b5248d45300663a
|
||||
timeCreated: 1532426377
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrReturnGrabbableObject.ReturnPolicy.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
public partial class UxrReturnGrabbableObject
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different policies when returning an object to a previous anchor.
|
||||
/// </summary>
|
||||
public enum ReturnPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Return to last anchor where the object was placed.
|
||||
/// </summary>
|
||||
LastAnchor = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Return to the original anchor where the object was placed.
|
||||
/// </summary>
|
||||
OriginalAnchor,
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fe66d825bdb4c3dba0d8252ba3cb221
|
||||
timeCreated: 1693091097
|
||||
@@ -0,0 +1,157 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrReturnGrabbableObject.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that will always return an <see cref="UxrGrabbableObject" /> to the
|
||||
/// <see cref="UxrGrabbableObjectAnchor" /> it was grabbed from whenever it is released.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrGrabbableObject))]
|
||||
public partial class UxrReturnGrabbableObject : UxrGrabbableObjectComponent<UxrReturnGrabbableObject>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _smoothTransition = true;
|
||||
[SerializeField] private float _returnDelaySeconds = -1.0f;
|
||||
[SerializeField] private ReturnPolicy _returnPolicy = ReturnPolicy.LastAnchor;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a return if the given object has a <see cref="UxrReturnGrabbableObject" /> component and a return
|
||||
/// programmed.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Object to try to cancel the return of</param>
|
||||
public static void CancelReturn(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (grabbableObject.gameObject.TryGetComponent<UxrReturnGrabbableObject>(out var returnComponent))
|
||||
{
|
||||
returnComponent.CancelReturn();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Coroutines
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that returns the object to the original target after some time.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Object to return</param>
|
||||
/// <param name="propagateEvents">Whether to propagate manipulation events</param>
|
||||
/// <returns>Coroutine IEnumerator</returns>
|
||||
private IEnumerator ReturnCoroutine(UxrGrabbableObject grabbableObject, bool propagateEvents)
|
||||
{
|
||||
yield return new WaitForSeconds(_returnDelaySeconds);
|
||||
|
||||
UxrGrabbableObjectAnchor anchor = GetReturnAnchor();
|
||||
|
||||
if (anchor != null && anchor.CurrentPlacedObject == null)
|
||||
{
|
||||
UxrGrabManager.Instance.PlaceObject(grabbableObject, anchor, _smoothTransition ? UxrPlacementOptions.Smooth : UxrPlacementOptions.None, propagateEvents);
|
||||
}
|
||||
|
||||
_returnCoroutine = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called by the base class whenever the object is grabbed.
|
||||
/// </summary>
|
||||
/// <param name="e">Contains all grab event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectGrabbed(e);
|
||||
|
||||
if (_returnCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_returnCoroutine);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the base class whenever the object is released.
|
||||
/// </summary>
|
||||
/// <param name="e">Contains all grab event parameters</param>
|
||||
protected override void OnObjectReleased(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectReleased(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
if (e.GrabbableAnchor != null)
|
||||
{
|
||||
_lastObjectAnchor = e.GrabbableAnchor;
|
||||
}
|
||||
|
||||
// Check also dependent grabs. We may be grabbing the object using another grip
|
||||
if (!UxrGrabManager.Instance.IsHandGrabbing(UxrAvatar.LocalAvatar, GrabbableObject, UxrHandSide.Left, true) &&
|
||||
!UxrGrabManager.Instance.IsHandGrabbing(UxrAvatar.LocalAvatar, GrabbableObject, UxrHandSide.Right, true))
|
||||
{
|
||||
if (_returnDelaySeconds <= 0.0f)
|
||||
{
|
||||
// Return to original place
|
||||
UxrGrabManager.Instance.PlaceObject(e.GrabbableObject, GetReturnAnchor(), _smoothTransition ? UxrPlacementOptions.Smooth : UxrPlacementOptions.None, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_returnCoroutine = StartCoroutine(ReturnCoroutine(e.GrabbableObject, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the anchor where the object should be returned.
|
||||
/// </summary>
|
||||
/// <returns>Destination anchor</returns>
|
||||
private UxrGrabbableObjectAnchor GetReturnAnchor()
|
||||
{
|
||||
if (_returnPolicy == ReturnPolicy.LastAnchor && _lastObjectAnchor != null && _lastObjectAnchor.CurrentPlacedObject != null)
|
||||
{
|
||||
return _lastObjectAnchor;
|
||||
}
|
||||
|
||||
return GrabbableObject.StartAnchor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a return if there was one programmed.
|
||||
/// </summary>
|
||||
private void CancelReturn()
|
||||
{
|
||||
if (_returnCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_returnCoroutine);
|
||||
_returnCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrGrabbableObjectAnchor _lastObjectAnchor;
|
||||
private Coroutine _returnCoroutine;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d08f3c02197a3f4ea71f397b2416ce4
|
||||
timeCreated: 1532426377
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
178
Assets/UltimateXR/Runtime/Scripts/Manipulation/IUxrGrabbable.cs
Normal file
178
Assets/UltimateXR/Runtime/Scripts/Manipulation/IUxrGrabbable.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrGrabbable.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for all objects that can be grabbed/manipulated using the <see cref="UxrGrabManager" />.
|
||||
/// </summary>
|
||||
public interface IUxrGrabbable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called when the object is about to be grabbed.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be grabbed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Target where the object is currently placed. Null
|
||||
/// if it isn't on an anchor.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is about to grab the object.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Grab point index of the object that is about to be
|
||||
/// grabbed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.IsMultiHands" />: true if it is already being grabbed with one hand and
|
||||
/// it will be grabbed with both hands after. False if no hand is currently grabbing it.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Grabbing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the object was grabbed. The grab event parameters use the same values as
|
||||
/// <see cref="Grabbing" />.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Grabbed;
|
||||
|
||||
/// <summary>
|
||||
/// Event called when the object is about to be released. An object is released when the last grip is released and
|
||||
/// there is no compatible <see cref="UxrGrabbableObjectAnchor" /> near enough to place it on.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be released.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object was originally grabbed
|
||||
/// from. Null if it wasn't on a target.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is about to release the object.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Grab point index of the object that is being
|
||||
/// grabbed by the <see cref="UxrGrabber" />.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.IsMultiHands" />: true if it is already being grabbed with another hand
|
||||
/// that will keep it holding. False if no other hand is currently grabbing it.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.IsSwitchHands" />: True if it was released because another
|
||||
/// <see cref="UxrGrabber" /> grabbed it, false otherwise. if
|
||||
/// <see cref="UxrManipulationEventArgs.IsMultiHands" /> is
|
||||
/// true then <see cref="UxrManipulationEventArgs.IsSwitchHands" /> will tell if it was released by both hands
|
||||
/// (false) or if it was just released by one hand and the other one still keeps it grabbed (true).
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.ReleaseVelocity" />: Velocity the object is being released with.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.ReleaseAngularVelocity" />: Angular velocity the object is being
|
||||
/// released with.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Releasing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the object was released. An object is released when the last grip is released and there is
|
||||
/// no compatible <see cref="UxrGrabbableObjectAnchor" /> near enough to place it on.
|
||||
/// The grab event parameters use the same values as <see cref="Releasing" />.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Released;
|
||||
|
||||
/// <summary>
|
||||
/// Event called when the object is about to be placed. An object is placed when the last grip is released and there is
|
||||
/// a compatible <see cref="UxrGrabbableObjectAnchor" /> near enough to place it on.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be removed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object is currently placed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is about to remove the object by grabbing it.
|
||||
/// This can be null if the object is removed through code using
|
||||
/// <see cref="UxrGrabManager.RemoveObjectFromAnchor" />,
|
||||
/// <see cref="UxrGrabbableObject.RemoveFromAnchor" /> or <see cref="UxrGrabbableObjectAnchor.RemoveObject" />>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Only if the object is being removed by grabbing it:
|
||||
/// Grab point index of the object that is about to be grabbed by the <see cref="UxrGrabber" />.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Placing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the object was placed. An object is placed when the last grip is released and there is a
|
||||
/// compatible <see cref="UxrGrabbableObjectAnchor" /> near enough to place it on.
|
||||
/// The grab event parameters use the same values as <see cref="Placed" />.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Placed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated <see cref="GameObject" />. Since all components that implement the interface will be assigned
|
||||
/// to GameObjects, this allows to access them using the interface.
|
||||
/// It doesn't follow the property PascalCase naming to make it compatible with Unity.
|
||||
/// </summary>
|
||||
public GameObject gameObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated <see cref="Transform" /> component. Since all components that implement the interface will be
|
||||
/// assigned to GameObjects, this allows to access their transform using the interface.
|
||||
/// It doesn't follow the property PascalCase naming to make it compatible with Unity.
|
||||
/// </summary>
|
||||
public Transform transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the object is being grabbed.
|
||||
/// </summary>
|
||||
public bool IsBeingGrabbed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the object can be grabbed.
|
||||
/// </summary>
|
||||
public bool IsGrabbable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the rigidbody that drives the object (if any) is kinematic.
|
||||
/// </summary>
|
||||
public bool IsKinematic { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Resets the object to its initial position/rotation and state. If the object is currently being grabbed, it will be
|
||||
/// released.
|
||||
/// </summary>
|
||||
/// <param name="propagateEvents">Should <see cref="UxrManipulationEventArgs" /> events be generated?</param>
|
||||
public void ResetPositionAndState(bool propagateEvents);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the object from all its grabs if there are any.
|
||||
/// </summary>
|
||||
/// <param name="propagateEvents">Should <see cref="UxrManipulationEventArgs" /> events be generated?</param>
|
||||
public void ReleaseGrabs(bool propagateEvents);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16baea219bfb42c5b79fb1193ec061c2
|
||||
timeCreated: 1630350158
|
||||
@@ -0,0 +1,33 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="IUxrGrabbableModifier.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that can be implemented in components that modify a <see cref="UxrGrabbableObject" /> in the same
|
||||
/// <see cref="GameObject" /> so that the inspector shows which information is being controlled by the modifier.
|
||||
/// </summary>
|
||||
public interface IUxrGrabbableModifier
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flags representing the parts of the <see cref="UxrGrabbableObject" /> that are overriden/controlled by the
|
||||
/// modifier.
|
||||
/// </summary>
|
||||
UxrGrabbableModifierFlags GrabbableModifierFlags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of additional grabbable objects affected by the modifier. These grabbable objects can only be up or
|
||||
/// down in the same hierarchy as the modifier.
|
||||
/// </summary>
|
||||
IEnumerable<UxrGrabbableObject> AdditionalTargets { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c539d9344c5836e42a7b9d91e9a94f38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrApplyConstraintsEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Event arguments for <see cref="UxrGrabbableObject" /> <see cref="UxrGrabbableObject.ConstraintsApplying" /> and
|
||||
/// <see cref="UxrGrabbableObject.ConstraintsApplied" />.
|
||||
/// </summary>
|
||||
public class UxrApplyConstraintsEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable object being constrained.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableObject { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The object being constrained</param>
|
||||
public UxrApplyConstraintsEventArgs(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
GrabbableObject = grabbableObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor is private.
|
||||
/// </summary>
|
||||
private UxrApplyConstraintsEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daad5d4facc24ae7beea828711414271
|
||||
timeCreated: 1643287360
|
||||
@@ -0,0 +1,173 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabManager.GrabbableObjectAnchorInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Serialization;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabManager
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information to handle grab events (<see cref="UxrManipulationEventArgs" />) for
|
||||
/// <see cref="UxrGrabbableObjectAnchor" /> objects:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.PlacedObjectRangeEntered" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.PlacedObjectRangeLeft" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.AnchorRangeEntered" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.AnchorRangeLeft" />
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implements <see cref="IUxrSerializable" /> to help <see cref="UxrGrabManager" />'s implementation of the
|
||||
/// <see cref="IUxrStateSave" /> interface (<see cref="UxrGrabManager.SerializeState" />).
|
||||
/// For now this information is not serialized, because most of it can be inferred at runtime on the client side, but
|
||||
/// it might get used in the future.
|
||||
/// </remarks>
|
||||
private class GrabbableObjectAnchorInfo : IUxrSerializable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the given <see cref="UxrGrabbableObjectAnchor" /> had a compatible
|
||||
/// <see cref="UxrGrabbableObject" /> within a valid drop distance the last frame.
|
||||
/// </summary>
|
||||
public bool HadCompatibleObjectNearLastFrame
|
||||
{
|
||||
get => _hadCompatibleObjectNearLastFrame;
|
||||
set => _hadCompatibleObjectNearLastFrame = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the given <see cref="UxrGrabbableObjectAnchor" /> has currently a compatible
|
||||
/// <see cref="UxrGrabbableObject" /> within a valid drop distance.
|
||||
/// </summary>
|
||||
public bool HasCompatibleObjectNear
|
||||
{
|
||||
get => _hasCompatibleObjectNear;
|
||||
set => _hasCompatibleObjectNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabber" /> that currently can grab the <see cref="UxrGrabbableObject" /> placed on
|
||||
/// the given <see cref="UxrGrabbableObjectAnchor" />. Null if there is none.
|
||||
/// </summary>
|
||||
public UxrGrabber GrabberNear
|
||||
{
|
||||
get => _grabberNear;
|
||||
set => _grabberNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabber" /> that could grab the <see cref="UxrGrabbableObject" /> placed on the
|
||||
/// given <see cref="UxrGrabbableObjectAnchor" /> during last frame. Null if there was none.
|
||||
/// </summary>
|
||||
public UxrGrabber LastValidGrabberNear
|
||||
{
|
||||
get => _lastValidGrabberNear;
|
||||
set => _lastValidGrabberNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grab point index of the <see cref="UxrGrabbableObject" /> that is placed on the given
|
||||
/// <see cref="UxrGrabbableObjectAnchor" /> that can currently be grabbed by <see cref="GrabberNear" />. -1 If there is
|
||||
/// none.
|
||||
/// </summary>
|
||||
public int GrabPointNear
|
||||
{
|
||||
get => _grabPointNear;
|
||||
set => _grabPointNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grab point index of the <see cref="UxrGrabbableObject" /> that is placed on the given
|
||||
/// <see cref="UxrGrabbableObjectAnchor" /> that could be grabbed by <see cref="GrabberNear" /> during last frame. -1
|
||||
/// if there was none.
|
||||
/// </summary>
|
||||
public int LastValidGrabPointNear
|
||||
{
|
||||
get => _lastValidGrabPointNear;
|
||||
set => _lastValidGrabPointNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabber" /> that currently is grabbing an <see cref="UxrGrabbableObject" /> that can
|
||||
/// be placed on the given <see cref="UxrGrabbableObjectAnchor" />. Null if there is none.
|
||||
/// </summary>
|
||||
public UxrGrabber FullGrabberNear
|
||||
{
|
||||
get => _fullGrabberNear;
|
||||
set => _fullGrabberNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabber" /> that currently is grabbing an <see cref="UxrGrabbableObject" /> that
|
||||
/// could be placed on the given <see cref="UxrGrabbableObjectAnchor" /> during last frame. Null if there was none.
|
||||
/// </summary>
|
||||
public UxrGrabber LastFullGrabberNear
|
||||
{
|
||||
get => _lastFullGrabberNear;
|
||||
set => _lastFullGrabberNear = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor required for serialization.
|
||||
/// </summary>
|
||||
public GrabbableObjectAnchorInfo()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializable
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SerializationVersion => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(IUxrSerializer serializer, int serializationVersion)
|
||||
{
|
||||
serializer.Serialize(ref _hadCompatibleObjectNearLastFrame);
|
||||
serializer.Serialize(ref _hasCompatibleObjectNear);
|
||||
serializer.SerializeUniqueComponent(ref _grabberNear);
|
||||
serializer.SerializeUniqueComponent(ref _lastValidGrabberNear);
|
||||
serializer.Serialize(ref _grabPointNear);
|
||||
serializer.Serialize(ref _lastValidGrabPointNear);
|
||||
serializer.SerializeUniqueComponent(ref _fullGrabberNear);
|
||||
serializer.SerializeUniqueComponent(ref _lastFullGrabberNear);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _hadCompatibleObjectNearLastFrame;
|
||||
private bool _hasCompatibleObjectNear;
|
||||
private UxrGrabber _grabberNear;
|
||||
private UxrGrabber _lastValidGrabberNear;
|
||||
private int _grabPointNear = -1;
|
||||
private int _lastValidGrabPointNear = -1;
|
||||
private UxrGrabber _fullGrabberNear;
|
||||
private UxrGrabber _lastFullGrabberNear;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45cc7569f3ff41109c9a7ee2527ecd93
|
||||
timeCreated: 1643541487
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e76b34c563814f43899427e340cc6aba
|
||||
timeCreated: 1677409671
|
||||
@@ -0,0 +1,988 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabManager.Querying.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Manipulation.HandPoses;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabManager
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an <see cref="UxrAvatar" /> can grab something using the given hand.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to check</param>
|
||||
/// <param name="handSide">Whether to check the left or right hand</param>
|
||||
/// <returns>Whether something can be grabbed</returns>
|
||||
public bool CanGrabSomething(UxrAvatar avatar, UxrHandSide handSide)
|
||||
{
|
||||
foreach (UxrGrabber grabber in UxrGrabber.GetComponents(avatar))
|
||||
{
|
||||
if (grabber.Side == handSide)
|
||||
{
|
||||
return CanGrabSomething(grabber);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a <see cref="UxrGrabber" /> can grab something using the given grabber.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to check</param>
|
||||
/// <returns>Whether something can be grabbed</returns>
|
||||
public bool CanGrabSomething(UxrGrabber grabber)
|
||||
{
|
||||
foreach (UxrGrabbableObject grabbableObject in UxrGrabbableObject.EnabledComponents)
|
||||
{
|
||||
if (grabbableObject.IsGrabbable)
|
||||
{
|
||||
for (int point = 0; point < grabbableObject.GrabPointCount; ++point)
|
||||
{
|
||||
if (grabbableObject.CanBeGrabbedByGrabber(grabber, point))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest grabbable object that can be grabbed by an <see cref="UxrAvatar" /> using the given hand.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to check</param>
|
||||
/// <param name="handSide">Whether to check the left hand or right hand</param>
|
||||
/// <param name="grabbableObject">Returns the closest grabbable object or null if none was found</param>
|
||||
/// <returns>Whether a grabbable object was found</returns>
|
||||
public bool GetClosestGrabbableObject(UxrAvatar avatar, UxrHandSide handSide, out UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
grabbableObject = null;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.GetComponents(avatar))
|
||||
{
|
||||
if (grabber.Side == handSide)
|
||||
{
|
||||
return GetClosestGrabbableObject(avatar, handSide, out grabbableObject, out int _);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest grabbable object that can be grabbed by an <see cref="UxrAvatar" /> using the given hand.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to check</param>
|
||||
/// <param name="handSide">Whether to check the left hand or right hand</param>
|
||||
/// <param name="grabbableObject">Returns the closest grabbable object or null if none was found</param>
|
||||
/// <param name="grabPoint">Returns the grab point that can be grabbed</param>
|
||||
/// <param name="candidates">List of grabbable objects to process or null to process all current enabled grabbable objects</param>
|
||||
/// <returns>Whether a grabbable object was found</returns>
|
||||
public bool GetClosestGrabbableObject(UxrAvatar avatar, UxrHandSide handSide, out UxrGrabbableObject grabbableObject, out int grabPoint, IEnumerable<UxrGrabbableObject> candidates = null)
|
||||
{
|
||||
grabbableObject = null;
|
||||
grabPoint = 0;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.GetComponents(avatar))
|
||||
{
|
||||
if (grabber.Side == handSide)
|
||||
{
|
||||
return GetClosestGrabbableObject(grabber, out grabbableObject, out grabPoint, candidates);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest grabbable object that can be grabbed by a <see cref="UxrGrabber" />.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to check</param>
|
||||
/// <param name="grabbableObject">Returns the closest grabbable object or null if none was found</param>
|
||||
/// <param name="grabPoint">Returns the grab point that can be grabbed</param>
|
||||
/// <param name="candidates">List of grabbable objects to process or null to process all current enabled grabbable objects</param>
|
||||
/// <returns>Whether a grabbable object was found</returns>
|
||||
public bool GetClosestGrabbableObject(UxrGrabber grabber, out UxrGrabbableObject grabbableObject, out int grabPoint, IEnumerable<UxrGrabbableObject> candidates = null)
|
||||
{
|
||||
int maxPriority = int.MinValue;
|
||||
float minDistanceWithoutRotation = float.MaxValue; // Between different objects we don't take orientations into account
|
||||
|
||||
grabbableObject = null;
|
||||
grabPoint = 0;
|
||||
|
||||
// Iterate over objects
|
||||
|
||||
foreach (UxrGrabbableObject candidate in candidates ?? UxrGrabbableObject.EnabledComponents)
|
||||
{
|
||||
float minDistance = float.MaxValue; // For the same object we will not just consider the distance but also how close the grabber is to the grip orientation
|
||||
|
||||
// Iterate over grab points
|
||||
for (int point = 0; point < candidate.GrabPointCount; ++point)
|
||||
{
|
||||
if (candidate.CanBeGrabbedByGrabber(grabber, point))
|
||||
{
|
||||
candidate.GetDistanceFromGrabber(grabber, point, out float distance, out float distanceWithoutRotation);
|
||||
|
||||
if (candidate.Priority > maxPriority)
|
||||
{
|
||||
grabbableObject = candidate;
|
||||
grabPoint = point;
|
||||
minDistance = distance;
|
||||
minDistanceWithoutRotation = distanceWithoutRotation;
|
||||
maxPriority = candidate.Priority;
|
||||
}
|
||||
else if (candidate.Priority == maxPriority)
|
||||
{
|
||||
if ((grabbableObject == candidate && distance < minDistance) || (grabbableObject != candidate && distanceWithoutRotation < minDistanceWithoutRotation))
|
||||
{
|
||||
grabbableObject = candidate;
|
||||
grabPoint = point;
|
||||
minDistance = distance;
|
||||
minDistanceWithoutRotation = distanceWithoutRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grabbableObject != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether grabbing a given <see cref="UxrGrabbableObject" /> using a certain <see cref="UxrGrabber" /> will make
|
||||
/// the grabber's renderer show up as hidden due to the parameters set in the inspector.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to check</param>
|
||||
/// <param name="grabbableObject">Grabbable object to check</param>
|
||||
/// <returns>Whether the renderer would be hidden when grabbed</returns>
|
||||
public bool ShouldHideHandRenderer(UxrGrabber grabber, UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
int grabPoint = manipulationInfo.GetGrabbedPoint(grabber);
|
||||
|
||||
if (grabPoint != -1)
|
||||
{
|
||||
return grabbableObject.GetGrabPoint(grabPoint).HideHandGrabberRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab pose name required when grabbing the given <see cref="UxrGrabbableObject" /> using the
|
||||
/// <see cref="UxrGrabber" />.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>Grab pose name or null to use the default grab pose specified in the avatar belonging to the grabber</returns>
|
||||
public string GetOverrideGrabPoseName(UxrGrabber grabber, UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
int grabPoint = manipulationInfo.GetGrabbedPoint(grabber);
|
||||
|
||||
if (grabPoint != -1)
|
||||
{
|
||||
UxrHandPoseAsset handPoseAsset = grabbableObject.GetGrabPoint(grabPoint).GetGripPoseInfo(grabber.Avatar).HandPose;
|
||||
return handPoseAsset != null ? handPoseAsset.name : null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blend value for the <see cref="UxrHandPoseType.Blend" /> pose used when grabbing the given
|
||||
/// <see cref="UxrGrabbableObject" /> using the <see cref="UxrGrabber" />.
|
||||
/// Blending is used to transition between different states such as open/closed or similar.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>Blending value [0.0, 1.0]</returns>
|
||||
public float GetOverrideGrabPoseBlendValue(UxrGrabber grabber, UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
int grabPoint = manipulationInfo.GetGrabbedPoint(grabber);
|
||||
|
||||
if (grabPoint != -1)
|
||||
{
|
||||
return grabbableObject.GetGrabPoint(grabPoint).GetGripPoseInfo(grabber.Avatar).PoseBlendValue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given <see cref="UxrAvatar" /> hand is currently grabbing something.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to check</param>
|
||||
/// <param name="handSide">Whether to check the left hand or right hand</param>
|
||||
/// <returns>Whether it is currently grabbing something</returns>
|
||||
public bool IsHandGrabbing(UxrAvatar avatar, UxrHandSide handSide)
|
||||
{
|
||||
foreach (UxrGrabber grabber in UxrGrabber.GetComponents(avatar))
|
||||
{
|
||||
if (grabber.Side == handSide && grabber.GrabbedObject != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an avatar's hand is grabbing a grabbable object.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to check</param>
|
||||
/// <param name="grabbableObject">Object to check if it is being grabbed</param>
|
||||
/// <param name="handSide">Whether to check the left hand or right hand</param>
|
||||
/// <param name="alsoCheckDependentGrab">
|
||||
/// Whether to also check for any parent or child <see cref="UxrGrabbableObject" /> that
|
||||
/// is physically connected.
|
||||
/// </param>
|
||||
/// <returns>Whether the object is being grabbed by the avatar using the given hand</returns>
|
||||
public bool IsHandGrabbing(UxrAvatar avatar, UxrGrabbableObject grabbableObject, UxrHandSide handSide, bool alsoCheckDependentGrab)
|
||||
{
|
||||
foreach (UxrGrabber grabber in UxrGrabber.GetComponents(avatar))
|
||||
{
|
||||
if (grabber.Side == handSide && grabber.GrabbedObject != null)
|
||||
{
|
||||
// Grabbing directly with the hand
|
||||
if (grabber.GrabbedObject == grabbableObject)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alsoCheckDependentGrab)
|
||||
{
|
||||
// Grabbing a parent/child?
|
||||
|
||||
if (GetParentsBeingGrabbedChain(grabber.GrabbedObject).Contains(grabbableObject) || GetChildrenBeingGrabbed(grabber.GrabbedObject).Contains(grabbableObject))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the objects currently being grabbed.
|
||||
/// </summary>
|
||||
/// <returns>Objects being grabbed</returns>
|
||||
public IEnumerator<UxrGrabbableObject> GetObjectsBeingGrabbed()
|
||||
{
|
||||
foreach (var manipulation in _currentManipulations)
|
||||
{
|
||||
yield return manipulation.Key;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object being grabbed by an avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to get the grabbed object of</param>
|
||||
/// <param name="handSide">Whether to check the left hand or right hand</param>
|
||||
/// <param name="grabbableObject">Returns the object being grabbed, or null if not found</param>
|
||||
/// <returns>Whether there is an object being grabbed by the avatar using the given hand</returns>
|
||||
public bool GetObjectBeingGrabbed(UxrAvatar avatar, UxrHandSide handSide, out UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
grabbableObject = null;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.GetComponents(avatar))
|
||||
{
|
||||
if (grabber.Side == handSide && grabber.GrabbedObject != null)
|
||||
{
|
||||
grabbableObject = grabber.GrabbedObject;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of hands currently grabbing an object.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="alsoCheckDependentGrabs">
|
||||
/// Also checks for hands that are grabbing other child objects that also control the direction of the given parent
|
||||
/// object.
|
||||
/// </param>
|
||||
/// <returns>Number of hands grabbing the object</returns>
|
||||
public int GetHandsGrabbingCount(UxrGrabbableObject grabbableObject, bool alsoCheckDependentGrabs = true)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.EnabledComponents)
|
||||
{
|
||||
if (grabber.GrabbedObject != null)
|
||||
{
|
||||
// Grabbing directly with the hand
|
||||
if (grabber.GrabbedObject == grabbableObject)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
else if (alsoCheckDependentGrabs)
|
||||
{
|
||||
// Grabbing a dependent grabbable?
|
||||
|
||||
if (GetParentsBeingGrabbedChain(grabber.GrabbedObject).Contains(grabbableObject) || GetChildrenBeingGrabbed(grabber.GrabbedObject).Contains(grabbableObject))
|
||||
{
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given grabbable object is being grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <returns>Whether it is being grabbed</returns>
|
||||
public bool IsBeingGrabbed(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
return _currentManipulations != null && grabbableObject != null && _currentManipulations.ContainsKey(grabbableObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given grabbable object is being grabbed using the given grab point.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">Grab point of the grabbable object to check</param>
|
||||
/// <returns>Whether the grab point is being grabbed using the given grab point</returns>
|
||||
public bool IsBeingGrabbed(UxrGrabbableObject grabbableObject, int point)
|
||||
{
|
||||
if (_currentManipulations == null || grabbableObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo) && manipulationInfo.GrabbedPoints.Contains(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given grabbable object is being grabbed by an avatar.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="avatar">The avatar to check</param>
|
||||
/// <returns>Whether it is being grabbed by the avatar</returns>
|
||||
public bool IsBeingGrabbedBy(UxrGrabbableObject grabbableObject, UxrAvatar avatar)
|
||||
{
|
||||
if (_currentManipulations == null || grabbableObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo) && manipulationInfo.Grabbers.Any(grabber => grabber.Avatar == avatar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given grabbable object is being grabbed by a specific grabber.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="grabber">The grabber to check</param>
|
||||
/// <returns>Whether it is being grabbed by the given grabber</returns>
|
||||
public bool IsBeingGrabbedBy(UxrGrabbableObject grabbableObject, UxrGrabber grabber)
|
||||
{
|
||||
if (_currentManipulations == null || grabbableObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo) && manipulationInfo.Grabbers.Any(grb => grabber == grb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given grabbable object is being grabbed using any other grab point than the specified.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">Grab point of the grabbable object not to check</param>
|
||||
/// <returns>Whether any other grab point is being grabbed</returns>
|
||||
public bool IsBeingGrabbedByOtherThan(UxrGrabbableObject grabbableObject, int point)
|
||||
{
|
||||
if (_currentManipulations == null || grabbableObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo) && manipulationInfo.GrabbedPoints.Any(grabPoint => point != grabPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given grabbable object is being grabbed using any other grab point and any other grabber than
|
||||
/// the specified.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">Point should be any other than this</param>
|
||||
/// <param name="grabber">Grabber should be any other than this</param>
|
||||
/// <returns>Whether the object is being grabbed with the specified conditions</returns>
|
||||
public bool IsBeingGrabbedByOtherThan(UxrGrabbableObject grabbableObject, int point, UxrGrabber grabber)
|
||||
{
|
||||
if (_currentManipulations == null || grabbableObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in manipulationInfo.Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber != grabber || grabInfo.GrabbedPoint != point)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hands that are grabbing an object.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="isLeft">Whether it is being grabbed using the left hand</param>
|
||||
/// <param name="isRight">Whether it is being grabbed using the right hand</param>
|
||||
/// <returns>Whether it is being grabbed</returns>
|
||||
public bool GetGrabbingHand(UxrGrabbableObject grabbableObject, out bool isLeft, out bool isRight)
|
||||
{
|
||||
isLeft = false;
|
||||
isRight = false;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.EnabledComponents)
|
||||
{
|
||||
if (grabber.GrabbedObject == grabbableObject)
|
||||
{
|
||||
if (grabber.Side == UxrHandSide.Left)
|
||||
{
|
||||
isLeft = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isRight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isLeft || isRight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabber that is grabbing an object using a specific grab point.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">Grab point to check</param>
|
||||
/// <param name="grabber">Grabber to check</param>
|
||||
/// <returns>Whether it is being grabbed with the specified conditions</returns>
|
||||
public bool GetGrabbingHand(UxrGrabbableObject grabbableObject, int point, out UxrGrabber grabber)
|
||||
{
|
||||
grabber = null;
|
||||
|
||||
foreach (UxrGrabber grabberCandidate in UxrGrabber.EnabledComponents)
|
||||
{
|
||||
if (grabberCandidate.GrabbedObject == grabbableObject)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in GetGrabs(grabbableObject))
|
||||
{
|
||||
if (grabInfo.GrabbedPoint == point)
|
||||
{
|
||||
grabber = grabInfo.Grabber;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hand grabbing the given object using a given grab point.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">The grab point used to grab the object</param>
|
||||
/// <param name="handSide">Returns the hand that is used to grab the object</param>
|
||||
/// <returns>Whether there is a hand grabbing the object</returns>
|
||||
public bool GetGrabbingHand(UxrGrabbableObject grabbableObject, int point, out UxrHandSide handSide)
|
||||
{
|
||||
handSide = UxrHandSide.Left;
|
||||
|
||||
if (GetGrabbingHand(grabbableObject, point, out UxrGrabber grabber))
|
||||
{
|
||||
handSide = grabber.Side;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbers that are grabbing the object using a specific grab point.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">The grab point or -1 to get all grabbed points</param>
|
||||
/// <param name="grabbers">
|
||||
/// Returns the list of grabbers. If the list is null a new list is created, otherwise the grabbers
|
||||
/// are added to the list.
|
||||
/// </param>
|
||||
/// <returns>Whether one or more grabbers were found</returns>
|
||||
public bool GetGrabbingHands(UxrGrabbableObject grabbableObject, int point, out List<UxrGrabber> grabbers)
|
||||
{
|
||||
grabbers = null;
|
||||
|
||||
foreach (RuntimeGrabInfo grabInfo in GetGrabs(grabbableObject))
|
||||
{
|
||||
if (grabInfo.GrabbedPoint == point || point == -1)
|
||||
{
|
||||
grabbers ??= new List<UxrGrabber>();
|
||||
|
||||
if (!grabbers.Contains(grabInfo.Grabber))
|
||||
{
|
||||
grabbers.Add(grabInfo.Grabber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grabbers != null && grabbers.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbers that are grabbing the object using a specific grab point.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">The grabbable object</param>
|
||||
/// <param name="point">The grab point or -1 to get all grabbed points</param>
|
||||
/// <returns>List of grabbers</returns>
|
||||
public IEnumerable<UxrGrabber> GetGrabbingHands(UxrGrabbableObject grabbableObject, int point = -1)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in GetGrabs(grabbableObject))
|
||||
{
|
||||
if (grabInfo.GrabbedPoint == point || point == -1)
|
||||
{
|
||||
yield return grabInfo.Grabber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab point that the <see cref="UxrGrabber" /> is currently grabbing on a <see cref="UxrGrabbableObject" />
|
||||
/// .
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to get the grabbed point from</param>
|
||||
/// <returns>Grab point index that is being grabbed or -1 if there is no object currently being grabbed</returns>
|
||||
public int GetGrabbedPoint(UxrGrabber grabber)
|
||||
{
|
||||
if (grabber && grabber.GrabbedObject != null && _currentManipulations.TryGetValue(grabber.GrabbedObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in manipulationInfo.Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber == grabber)
|
||||
{
|
||||
return grabInfo.GrabbedPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of grab points that are currently being grabbed from a <see cref="UxrGrabbableObject" />.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>Number of grab points being grabbed</returns>
|
||||
public int GetGrabbedPointCount(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
return manipulationInfo.Grabs.Count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of hands that are grabbing the given object.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="includeChildGrabs">Whether to also count dependent child grabbable objects that are being grabbed</param>
|
||||
/// <returns>Number of hands grabbing the object</returns>
|
||||
public int GetGrabbingHandCount(UxrGrabbableObject grabbableObject, bool includeChildGrabs = true)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
return manipulationInfo.Grabs.Count + (includeChildGrabs ? GetChildrenBeingGrabbed(grabbableObject).Count() : 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UxrGrabbableObjectAnchor" /> where the given <see cref="UxrGrabbableObject" /> was grabbed
|
||||
/// from.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>Anchor the grabbable object was grabbed from</returns>
|
||||
public UxrGrabbableObjectAnchor GetGrabbedObjectAnchorFrom(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
return manipulationInfo.SourceAnchor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current world-space velocity, in units per second, of an object that is being grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="smooth">Whether to smooth the velocity using a previous frame data window for improved behavior</param>
|
||||
/// <returns>Velocity in world-space units per second</returns>
|
||||
public Vector3 GetGrabbedObjectVelocity(UxrGrabbableObject grabbableObject, bool smooth = true)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo) && manipulationInfo.Grabs.Any())
|
||||
{
|
||||
return smooth ? manipulationInfo.Grabs[0].Grabber.SmoothVelocity : manipulationInfo.Grabs[0].Grabber.Velocity;
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current world-space angular velocity, in degrees per second, of an object that is being grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="smooth">Whether to smooth the velocity using a previous frame data window for improved behavior</param>
|
||||
/// <returns>Angular velocity in world-space euler angle degrees per second</returns>
|
||||
public Vector3 GetGrabbedObjectAngularVelocity(UxrGrabbableObject grabbableObject, bool smooth = true)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo) && manipulationInfo.Grabs.Any())
|
||||
{
|
||||
return smooth ? manipulationInfo.Grabs[0].Grabber.SmoothAngularVelocity : manipulationInfo.Grabs[0].Grabber.AngularVelocity;
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the grab information of a specific <see cref="UxrGrabber" />.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to get the grab information from</param>
|
||||
/// <param name="grabInfo">Returns the grab information</param>
|
||||
/// <returns>True if successful, false if the grabber is not valid or isn't grabbing any object</returns>
|
||||
private bool TryGetGrabInfo(UxrGrabber grabber, out RuntimeGrabInfo grabInfo)
|
||||
{
|
||||
grabInfo = null;
|
||||
|
||||
if (grabber && grabber.GrabbedObject && _currentManipulations.TryGetValue(grabber.GrabbedObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
grabInfo = manipulationInfo.GetGrabInfo(grabber);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates all grabs on the given grabbable object.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object to get the grab information from</param>
|
||||
/// <returns>List of grabs</returns>
|
||||
private IEnumerable<RuntimeGrabInfo> GetGrabs(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (grabbableObject != null && _currentManipulations.TryGetValue(grabbableObject, out RuntimeManipulationInfo manipulationInfo))
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in manipulationInfo.Grabs)
|
||||
{
|
||||
yield return grabInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent being grabbed of a given <see cref="UxrGrabbableObject" />.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>Parent <see cref="UxrGrabbableObject" /> that is being grabbed or null if there isn't any</returns>
|
||||
private UxrGrabbableObject GetParentBeingGrabbed(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (grabbableObject != null && grabbableObject.transform.parent != null)
|
||||
{
|
||||
UxrGrabbableObject parentGrabbableObject = grabbableObject.transform.parent.GetComponentInParent<UxrGrabbableObject>();
|
||||
|
||||
if (parentGrabbableObject != null)
|
||||
{
|
||||
if (IsBeingGrabbed(parentGrabbableObject))
|
||||
{
|
||||
return parentGrabbableObject;
|
||||
}
|
||||
|
||||
return GetParentBeingGrabbed(parentGrabbableObject);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chain of parents of a given <see cref="UxrGrabbableObject" /> that are being grabbed in bottom to top
|
||||
/// hierarchical order.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>Chain of parents being grabbed in bottom to top hierarchical order</returns>
|
||||
private IEnumerable<UxrGrabbableObject> GetParentsBeingGrabbedChain(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
UxrGrabbableObject current = grabbableObject;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
current = GetParentBeingGrabbed(current);
|
||||
|
||||
if (current != null)
|
||||
{
|
||||
yield return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="UxrGrabbableObject" />'s list of children being grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>List of children that are being grabbed</returns>
|
||||
private IEnumerable<UxrGrabbableObject> GetChildrenBeingGrabbed(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (grabbableObject == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (UxrGrabbableObject child in grabbableObject.AllChildren)
|
||||
{
|
||||
if (child != grabbableObject && _currentManipulations.ContainsKey(child))
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="UxrGrabbableObject" />'s list of direct grabbable children that are being grabbed
|
||||
/// and control the direction of the grabbable object.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>List of direct grabbable children that are being grabbed and control the grabbable object direction</returns>
|
||||
private IEnumerable<UxrGrabbableObject> GetDirectChildrenLookAtBeingGrabbed(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
foreach (RuntimeManipulationInfo childManipulation in GetDirectChildrenLookAtManipulations(grabbableObject))
|
||||
{
|
||||
yield return childManipulation.GrabbableObject;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="UxrGrabbableObject" />'s list of direct grabbable children manipulations that
|
||||
/// control the direction of the grabbable object.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <returns>
|
||||
/// List of manipulations of direct grabbable children that are being grabbed and control the grabbable object
|
||||
/// direction
|
||||
/// </returns>
|
||||
private IEnumerable<RuntimeManipulationInfo> GetDirectChildrenLookAtManipulations(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (grabbableObject == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (UxrGrabbableObject child in grabbableObject.DirectChildrenLookAts)
|
||||
{
|
||||
if (_currentManipulations.TryGetValue(child, out RuntimeManipulationInfo childManipulationInfo))
|
||||
{
|
||||
yield return childManipulationInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world-space snap position of a given <see cref="UxrGrabber" /> grabbing the object.
|
||||
/// Depending on the object's snap settings, this will be the grab point snap position (either
|
||||
/// object to hand or hand to object) or the same grip position relative to the object when the
|
||||
/// grab was performed.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to get the snap position of</param>
|
||||
/// <returns>Snap position or <see cref="Vector3.zero" /> if the grabber isn't currently grabbing an object</returns>
|
||||
private Vector3 GetGrabbedPointGrabAlignPosition(UxrGrabber grabber)
|
||||
{
|
||||
if (grabber.GrabbedObject == null || !_currentManipulations.TryGetValue(grabber.GrabbedObject, out RuntimeManipulationInfo manipulationInfo) || !TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
if (grabber.GrabbedObject.GetGrabPointSnapModeAffectsPosition(manipulationInfo.GetGrabbedPoint(grabber), UxrHandSnapDirection.ObjectToHand))
|
||||
{
|
||||
// Snap to grab point so that object goes to hand
|
||||
return TransformExt.GetWorldPosition(grabInfo.GrabAlignParentTransformUsed, grabInfo.RelativeGrabAlignPosition);
|
||||
}
|
||||
|
||||
if (grabber.GrabbedObject.GetGrabPointSnapModeAffectsPosition(manipulationInfo.GetGrabbedPoint(grabber), UxrHandSnapDirection.HandToObject))
|
||||
{
|
||||
// Snap to grab point so that hand goes to object
|
||||
Transform snapTransform = grabber.GrabbedObject.GetGrabPointGrabAlignTransform(grabber.Avatar, manipulationInfo.GetGrabbedPoint(grabber), grabber.Side);
|
||||
return snapTransform.TransformPoint(grabInfo.RelativeUsedGrabAlignPosition);
|
||||
}
|
||||
|
||||
// Keep same grip position relative to the object as when it was grabbed
|
||||
return grabber.GrabbedObject.transform.TransformPoint(GetGrabPointRelativeGrabberPosition(grabber));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world-space snap rotation of a given <see cref="UxrGrabber" /> grabbing the object.
|
||||
/// Depending on the object's snap settings, this will be the grab point snap rotation (either
|
||||
/// object to hand or hand to object) or the same grip rotation relative to the object when the
|
||||
/// grab was performed.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to get the snap rotation of</param>
|
||||
/// <returns>Snap rotation or <see cref="Quaternion.identity" /> if the grabber isn't currently grabbing an object</returns>
|
||||
private Quaternion GetGrabbedPointGrabAlignRotation(UxrGrabber grabber)
|
||||
{
|
||||
if (grabber.GrabbedObject == null || !_currentManipulations.TryGetValue(grabber.GrabbedObject, out RuntimeManipulationInfo manipulationInfo) || !TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return Quaternion.identity;
|
||||
}
|
||||
|
||||
if (grabber.GrabbedObject.GetGrabPointSnapModeAffectsRotation(manipulationInfo.GetGrabbedPoint(grabber), UxrHandSnapDirection.ObjectToHand))
|
||||
{
|
||||
// Snap to grab point so that object rotates to hand
|
||||
return TransformExt.GetWorldRotation(grabInfo.GrabAlignParentTransformUsed, grabInfo.RelativeGrabAlignRotation);
|
||||
}
|
||||
|
||||
if (grabber.GrabbedObject.GetGrabPointSnapModeAffectsRotation(manipulationInfo.GetGrabbedPoint(grabber), UxrHandSnapDirection.HandToObject))
|
||||
{
|
||||
// Snap to grab point so that hand rotates to object
|
||||
Transform snapTransform = grabber.GrabbedObject.GetGrabPointGrabAlignTransform(grabber.Avatar, manipulationInfo.GetGrabbedPoint(grabber), grabber.Side);
|
||||
return snapTransform.rotation * grabInfo.RelativeUsedGrabAlignRotation;
|
||||
}
|
||||
|
||||
// Keep same grip rotation relative to the object as when it was grabbed
|
||||
return grabber.GrabbedObject.transform.rotation * GetGrabPointRelativeGrabberRotation(grabber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position that is used to compute proximity from a <see cref="UxrGrabber" /> to the grabbed point.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to get the proximity point for</param>
|
||||
/// <returns>
|
||||
/// Position required to compute the proximity to or <see cref="Vector3.zero" /> if the grabber isn't currently
|
||||
/// grabbing an object
|
||||
/// </returns>
|
||||
private Vector3 GetGrabbedPointGrabProximityPosition(UxrGrabber grabber)
|
||||
{
|
||||
if (TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return grabber.GrabbedObject.transform.TransformPoint(grabInfo.RelativeProximityPosition);
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative position of the object to the grabber at the time it was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber with the object being grabbed</param>
|
||||
/// <returns>
|
||||
/// Relative position or <see cref="Vector3.zero" /> if the grabber isn't currently grabbing an object.
|
||||
/// </returns>
|
||||
private Vector3 GetGrabPointRelativeGrabPosition(UxrGrabber grabber)
|
||||
{
|
||||
if (TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return grabInfo.RelativeGrabPosition;
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative rotation of the object to the grabber at the time it was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber with the object being grabbed</param>
|
||||
/// <returns>
|
||||
/// Relative rotation or <see cref="Quaternion.identity" /> if the grabber isn't currently grabbing an object.
|
||||
/// </returns>
|
||||
private Quaternion GetGrabPointRelativeGrabRotation(UxrGrabber grabber)
|
||||
{
|
||||
if (TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return grabInfo.RelativeGrabRotation;
|
||||
}
|
||||
|
||||
return Quaternion.identity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative position of the grabber to the object it is grabbing at the time it was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber with the object being grabbed</param>
|
||||
/// <returns>
|
||||
/// Relative position or <see cref="Vector3.zero" /> if the grabber isn't currently grabbing an object.
|
||||
/// </returns>
|
||||
private Vector3 GetGrabPointRelativeGrabberPosition(UxrGrabber grabber)
|
||||
{
|
||||
if (TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return grabInfo.RelativeGrabberPosition;
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative rotation of the grabber to the object it is grabbing at the time it was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber with the object being grabbed</param>
|
||||
/// <returns>
|
||||
/// Relative rotation or <see cref="Quaternion.identity" /> if the grabber isn't currently grabbing an object.
|
||||
/// </returns>
|
||||
private Quaternion GetGrabPointRelativeGrabberRotation(UxrGrabber grabber)
|
||||
{
|
||||
if (TryGetGrabInfo(grabber, out RuntimeGrabInfo grabInfo))
|
||||
{
|
||||
return grabInfo.RelativeGrabberRotation;
|
||||
}
|
||||
|
||||
return Quaternion.identity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05b5882a83c34bcda86d39ab0eee3772
|
||||
timeCreated: 1677407121
|
||||
@@ -0,0 +1,563 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabManager.RuntimeGrabInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabManager
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information of a grab performed on an object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implements <see cref="IUxrSerializable" /> to help <see cref="UxrGrabManager" />'s implementation of the
|
||||
/// <see cref="IUxrStateSave" /> interface (<see cref="UxrGrabManager.SerializeState" />).
|
||||
/// </remarks>
|
||||
private sealed class RuntimeGrabInfo : IUxrSerializable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabber grabbing the <see cref="UxrGrabbableObject" />.
|
||||
/// </summary>
|
||||
public UxrGrabber Grabber
|
||||
{
|
||||
get => _grabber;
|
||||
set => _grabber = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UxrGrabbableObject" /> grabbed point.
|
||||
/// </summary>
|
||||
public int GrabbedPoint
|
||||
{
|
||||
get => _grabbedPoint;
|
||||
set => _grabbedPoint = value;
|
||||
}
|
||||
|
||||
// *************************************************************************************************************************
|
||||
// Transform information about the grip.
|
||||
// *************************************************************************************************************************
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabbableObject" /> rotation relative to the <see cref="UxrGrabber" /> at the moment
|
||||
/// it was grabbed.
|
||||
/// </summary>
|
||||
public Quaternion RelativeGrabRotation
|
||||
{
|
||||
get => _relativeGrabRotation;
|
||||
private set => _relativeGrabRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabbableObject" /> position in local <see cref="UxrGrabber" /> space at the moment
|
||||
/// it was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 RelativeGrabPosition
|
||||
{
|
||||
get => _relativeGrabPosition;
|
||||
private set => _relativeGrabPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabber" /> rotation relative to the <see cref="UxrGrabbableObject" /> at the moment
|
||||
/// it was grabbed.
|
||||
/// </summary>
|
||||
public Quaternion RelativeGrabberRotation
|
||||
{
|
||||
get => _relativeGrabberRotation;
|
||||
private set => _relativeGrabberRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabber" /> position in local <see cref="UxrGrabbableObject" /> space at the moment
|
||||
/// it was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 RelativeGrabberPosition
|
||||
{
|
||||
get => _relativeGrabberPosition;
|
||||
private set => _relativeGrabberPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transform relative to which <see cref="RelativeGrabAlignPosition" /> and
|
||||
/// <see cref="RelativeGrabAlignRotation" /> are specified.
|
||||
/// </summary>
|
||||
public Transform GrabAlignParentTransformUsed
|
||||
{
|
||||
get => _grabAlignParentTransformUsed;
|
||||
private set => _grabAlignParentTransformUsed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the snap rotation relative to the <see cref="GrabAlignParentTransformUsed" /> at the moment it was
|
||||
/// grabbed.
|
||||
/// </summary>
|
||||
public Quaternion RelativeGrabAlignRotation
|
||||
{
|
||||
get => _relativeGrabAlignRotation;
|
||||
private set => _relativeGrabAlignRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the snap position in local <see cref="GrabAlignParentTransformUsed" /> space at the moment it was
|
||||
/// grabbed.
|
||||
/// </summary>
|
||||
public Vector3 RelativeGrabAlignPosition
|
||||
{
|
||||
get => _relativeGrabAlignPosition;
|
||||
private set => _relativeGrabAlignPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the computed snap rotation relative to the object's snap rotation, which might be different if
|
||||
/// the computed snap rotation came from an <see cref="UxrGrabPointShape" />.
|
||||
/// </summary>
|
||||
public Quaternion RelativeUsedGrabAlignRotation
|
||||
{
|
||||
get => _relativeUsedGrabAlignRotation;
|
||||
private set => _relativeUsedGrabAlignRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the computed snap position relative to the object's snap position, which might be different if
|
||||
/// the computed snap position came from an <see cref="UxrGrabPointShape" />.
|
||||
/// </summary>
|
||||
public Vector3 RelativeUsedGrabAlignPosition
|
||||
{
|
||||
get => _relativeUsedGrabAlignPosition;
|
||||
private set => _relativeUsedGrabAlignPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the proximity rotation relative to the <see cref="UxrGrabbableObject" /> at the moment it was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 RelativeProximityPosition
|
||||
{
|
||||
get => _relativeProximityPosition;
|
||||
private set => _relativeProximityPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source in local <see cref="UxrGrabber" /> coordinates where the source of leverage will be
|
||||
/// computed for <see cref="UxrRotationProvider.HandPositionAroundPivot" /> manipulation. This will improve rotation
|
||||
/// behaviour when the hands are rotated because otherwise the source of leverage is the grabber itself and rotating
|
||||
/// the hand will keep the grabber more or less stationary.
|
||||
/// </summary>
|
||||
public Vector3 GrabberLocalLeverageSource
|
||||
{
|
||||
get => _grabberLocalLeverageSource;
|
||||
private set => _grabberLocalLeverageSource = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the leverage source <see cref="GrabberLocalLeverageSource" /> in local coordinates of the parent
|
||||
/// transform of the grabbable at the moment the <see cref="UxrGrabbableObject" /> was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 GrabberLocalParentLeverageSourceOnGrab
|
||||
{
|
||||
get => _grabberLocalParentLeverageSourceOnGrab;
|
||||
private set => _grabberLocalParentLeverageSourceOnGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the leverage point in local coordinates that this child grabbable will use when the parent
|
||||
/// grabbable rotation provider is HandPositionAroundPivot.
|
||||
/// </summary>
|
||||
public Vector3 ParentGrabbableLookAtLocalLeveragePoint
|
||||
{
|
||||
get => _parentGrabbableLookAtLocalLeveragePoint;
|
||||
set => _parentGrabbableLookAtLocalLeveragePoint = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the leverage point in local grabbable parent coordinates that this child grabbable will use
|
||||
/// when the parent grabbable rotation provider is HandPositionAroundPivot.
|
||||
/// </summary>
|
||||
public Vector3 ParentGrabbableLookAtParentLeveragePoint
|
||||
{
|
||||
get => _parentGrabbableLookAtParentLeveragePoint;
|
||||
set => _parentGrabbableLookAtParentLeveragePoint = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the look-at contribution in world coordinates of this child grabbable object to the parent
|
||||
/// grabbable look-at algorithm for the current frame. Only for HandPositionAroundPivot in parent grabbable objects.
|
||||
/// </summary>
|
||||
public Vector3 ParentGrabbableLeverageContribution
|
||||
{
|
||||
get => _parentGrabbableLeverageContribution;
|
||||
set => _parentGrabbableLeverageContribution = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation contribution of this object to the parent grabbable look-at algorithm for the
|
||||
/// current frame. Only for HandPositionAroundPivot in parent grabbable objects.
|
||||
/// </summary>
|
||||
public Quaternion ParentGrabbableLookAtRotationContribution
|
||||
{
|
||||
get => _parentGrabbableLookAtRotationContribution;
|
||||
set => _parentGrabbableLookAtRotationContribution = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation angle contribution, in objects constrained to a single axis rotation, during the current
|
||||
/// grab.
|
||||
/// </summary>
|
||||
public float SingleRotationAngleContribution
|
||||
{
|
||||
get => _singleRotationAngleContribution;
|
||||
set => _singleRotationAngleContribution = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="SingleRotationAngleContribution" /> value the last time it was accumulated into
|
||||
/// the object internal angle. This allows angle contributions to work using absolute values instead of delta values
|
||||
/// to have better precision.
|
||||
/// </summary>
|
||||
public float LastAccumulatedAngle
|
||||
{
|
||||
get => _lastAccumulatedAngle;
|
||||
set => _lastAccumulatedAngle = value;
|
||||
}
|
||||
|
||||
// *************************************************************************************************************************
|
||||
// Parent dependency information.
|
||||
// *************************************************************************************************************************
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab position in grabbable parent space before updating this object being grabbed. It is used to compute
|
||||
/// the lookAt contribution of this grab on the parent, when the parent is being grabbed. The grab position
|
||||
/// is computed before constraints are applied to the object to compute the contribution correctly.
|
||||
/// </summary>
|
||||
public Vector3 ParentLocalGrabPositionBeforeUpdate
|
||||
{
|
||||
get => _parentLocalGrabPositionBeforeUpdate;
|
||||
set => _parentLocalGrabPositionBeforeUpdate = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab position in grabbable parent space after updating this object being grabbed. See
|
||||
/// <see cref="ParentLocalGrabPositionBeforeUpdate" />.
|
||||
/// </summary>
|
||||
public Vector3 ParentLocalGrabPositionAfterUpdate
|
||||
{
|
||||
get => _parentLocalGrabPositionAfterUpdate;
|
||||
set => _parentLocalGrabPositionAfterUpdate = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable parent position in local grabbable child space before updating the child being grabbed.
|
||||
/// It is used to compute the contribution of a child on a parent when the parent is not being grabbed.
|
||||
/// </summary>
|
||||
public Vector3 ChildLocalParentPosition
|
||||
{
|
||||
get => _childLocalParentPosition;
|
||||
set => _childLocalParentPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable parent rotation in local grabbable child space before updating the child being grabbed.
|
||||
/// It is used to compute the contribution of a child on a parent when the parent is not being grabbed.
|
||||
/// </summary>
|
||||
public Quaternion ChildLocalParentRotation
|
||||
{
|
||||
get => _childLocalParentRotation;
|
||||
set => _childLocalParentRotation = value;
|
||||
}
|
||||
|
||||
// *************************************************************************************************************************
|
||||
// For smooth transitions from object to hand or object to target or hand to object where we want to avoid instant snapping.
|
||||
// *************************************************************************************************************************
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabbableObject" /> local position at the moment it was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 LocalPositionOnGrab
|
||||
{
|
||||
get => _localPositionOnGrab;
|
||||
private set => _localPositionOnGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="UxrGrabbableObject" /> local rotation at the moment it was grabbed.
|
||||
/// </summary>
|
||||
public Quaternion LocalRotationOnGrab
|
||||
{
|
||||
get => _localRotationOnGrab;
|
||||
private set => _localRotationOnGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the world-space snap position at the moment the <see cref="UxrGrabbableObject" /> was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 AlignPositionOnGrab
|
||||
{
|
||||
get => _alignPositionOnGrab;
|
||||
private set => _alignPositionOnGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the world-space snap rotation at the moment the <see cref="UxrGrabbableObject" /> was grabbed.
|
||||
/// </summary>
|
||||
public Quaternion AlignRotationOnGrab
|
||||
{
|
||||
get => _alignRotationOnGrab;
|
||||
private set => _alignRotationOnGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hand bone position in local avatar coordinates at the moment the <see cref="UxrGrabbableObject" />
|
||||
/// was grabbed.
|
||||
/// </summary>
|
||||
public Vector3 HandBoneLocalAvatarPositionOnGrab
|
||||
{
|
||||
get => _handBoneLocalAvatarPositionOnGrab;
|
||||
private set => _handBoneLocalAvatarPositionOnGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hand bone rotation in local avatar coordinates at the moment the <see cref="UxrGrabbableObject" />
|
||||
/// was grabbed.
|
||||
/// </summary>
|
||||
public Quaternion HandBoneLocalAvatarRotationOnGrab
|
||||
{
|
||||
get => _handBoneLocalAvatarRotationOnGrab;
|
||||
private set => _handBoneLocalAvatarRotationOnGrab = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="grabber">The grabber</param>
|
||||
/// <param name="grabbedPoint">The grabbed point</param>
|
||||
public RuntimeGrabInfo(UxrGrabber grabber, int grabbedPoint)
|
||||
{
|
||||
Grabber = grabber;
|
||||
GrabbedPoint = grabbedPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor required for serialization.
|
||||
/// </summary>
|
||||
private RuntimeGrabInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializable
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SerializationVersion => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(IUxrSerializer serializer, int serializationVersion)
|
||||
{
|
||||
serializer.SerializeUniqueComponent(ref _grabber);
|
||||
serializer.Serialize(ref _grabbedPoint);
|
||||
serializer.Serialize(ref _relativeGrabRotation);
|
||||
serializer.Serialize(ref _relativeGrabPosition);
|
||||
serializer.Serialize(ref _relativeGrabberRotation);
|
||||
serializer.Serialize(ref _relativeGrabberPosition);
|
||||
|
||||
// Trick to be able to restore _grabAlignParentTransformUsed because we can't serialize a reference without IUxrUniqueID
|
||||
{
|
||||
serializer.SerializeUniqueComponent(ref _grabbableObject);
|
||||
|
||||
if (serializer.IsReading && _grabbableObject)
|
||||
{
|
||||
Transform grabAlignTransform = _grabbableObject.GetGrabPointGrabAlignTransform(_grabber.Avatar, _grabbedPoint, _grabber.Side);
|
||||
GrabAlignParentTransformUsed = grabAlignTransform == _grabbableObject.transform ? _grabbableObject.transform : grabAlignTransform.parent;
|
||||
}
|
||||
}
|
||||
|
||||
serializer.Serialize(ref _relativeGrabAlignRotation);
|
||||
serializer.Serialize(ref _relativeGrabAlignPosition);
|
||||
serializer.Serialize(ref _relativeUsedGrabAlignRotation);
|
||||
serializer.Serialize(ref _relativeUsedGrabAlignPosition);
|
||||
serializer.Serialize(ref _relativeProximityPosition);
|
||||
serializer.Serialize(ref _grabberLocalLeverageSource);
|
||||
serializer.Serialize(ref _grabberLocalParentLeverageSourceOnGrab);
|
||||
serializer.Serialize(ref _parentGrabbableLookAtLocalLeveragePoint);
|
||||
serializer.Serialize(ref _parentGrabbableLookAtParentLeveragePoint);
|
||||
serializer.Serialize(ref _parentGrabbableLeverageContribution);
|
||||
serializer.Serialize(ref _parentGrabbableLookAtRotationContribution);
|
||||
serializer.Serialize(ref _singleRotationAngleContribution);
|
||||
serializer.Serialize(ref _lastAccumulatedAngle);
|
||||
serializer.Serialize(ref _parentLocalGrabPositionBeforeUpdate);
|
||||
serializer.Serialize(ref _parentLocalGrabPositionAfterUpdate);
|
||||
serializer.Serialize(ref _childLocalParentPosition);
|
||||
serializer.Serialize(ref _childLocalParentRotation);
|
||||
serializer.Serialize(ref _localPositionOnGrab);
|
||||
serializer.Serialize(ref _localRotationOnGrab);
|
||||
serializer.Serialize(ref _alignPositionOnGrab);
|
||||
serializer.Serialize(ref _alignRotationOnGrab);
|
||||
serializer.Serialize(ref _handBoneLocalAvatarPositionOnGrab);
|
||||
serializer.Serialize(ref _handBoneLocalAvatarRotationOnGrab);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Computes the grab information.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber responsible for grabbing the object</param>
|
||||
/// <param name="grabbableObject">The object being grabbed</param>
|
||||
/// <param name="grabPoint">Point that was grabbed</param>
|
||||
/// <param name="snapPosition">The grabber snap position to use</param>
|
||||
/// <param name="snapRotation">The grabber snap rotation to use</param>
|
||||
/// <param name="sourceGrabEventArgs">
|
||||
/// If non-null, the grab will use the information on the event to ensure that
|
||||
/// it is performed in exactly the same way. This is used in multi-player environments.
|
||||
/// </param>
|
||||
public void Compute(UxrGrabber grabber, UxrGrabbableObject grabbableObject, int grabPoint, Vector3 snapPosition, Quaternion snapRotation, UxrManipulationEventArgs sourceGrabEventArgs = null)
|
||||
{
|
||||
Transform grabAlignTransform = grabbableObject.GetGrabPointGrabAlignTransform(grabber.Avatar, grabPoint, grabber.Side);
|
||||
Vector3 originalPosition = grabbableObject.transform.position;
|
||||
Quaternion originalRotation = grabbableObject.transform.rotation;
|
||||
|
||||
if (sourceGrabEventArgs != null)
|
||||
{
|
||||
// Grab is synchronizing with external grab. Position object momentarily in the exact same relative position with the grabber as the source external data.
|
||||
grabbableObject.transform.position = grabber.transform.TransformPoint(sourceGrabEventArgs.GrabberLocalObjectPosition);
|
||||
grabbableObject.transform.rotation = grabber.transform.rotation * sourceGrabEventArgs.GrabberLocalObjectRotation;
|
||||
}
|
||||
|
||||
// Update snap position/orientation if it's an external grab, to keep it in sync with exactly the same grip
|
||||
|
||||
if (sourceGrabEventArgs != null)
|
||||
{
|
||||
snapPosition = grabber.transform.TransformPoint(sourceGrabEventArgs.GrabberLocalSnapPosition);
|
||||
snapRotation = grabber.transform.rotation * sourceGrabEventArgs.GrabberLocalSnapRotation;
|
||||
}
|
||||
|
||||
Matrix4x4 snapMatrix = Matrix4x4.TRS(snapPosition, snapRotation, grabAlignTransform.lossyScale);
|
||||
Vector3 localProximity = grabAlignTransform.InverseTransformPoint(grabbableObject.GetGrabPointGrabProximityTransform(grabber, grabPoint).position);
|
||||
|
||||
RelativeGrabRotation = Quaternion.Inverse(grabber.transform.rotation) * grabbableObject.transform.rotation;
|
||||
RelativeGrabPosition = grabber.transform.InverseTransformPoint(grabbableObject.transform.position);
|
||||
RelativeGrabberRotation = Quaternion.Inverse(grabbableObject.transform.rotation) * grabber.transform.rotation;
|
||||
RelativeGrabberPosition = grabbableObject.transform.InverseTransformPoint(grabber.transform.position);
|
||||
GrabAlignParentTransformUsed = grabAlignTransform == grabbableObject.transform ? grabbableObject.transform : grabAlignTransform.parent;
|
||||
RelativeGrabAlignPosition = TransformExt.GetLocalPosition(GrabAlignParentTransformUsed, snapPosition);
|
||||
RelativeGrabAlignRotation = TransformExt.GetLocalRotation(GrabAlignParentTransformUsed, snapRotation);
|
||||
RelativeUsedGrabAlignRotation = Quaternion.Inverse(grabAlignTransform.rotation) * snapRotation;
|
||||
RelativeUsedGrabAlignPosition = grabAlignTransform.InverseTransformPoint(snapPosition);
|
||||
RelativeProximityPosition = grabbableObject.transform.InverseTransformPoint(snapMatrix.MultiplyPoint(localProximity));
|
||||
GrabberLocalLeverageSource = Vector3.zero;
|
||||
|
||||
grabbableObject.CheckComputeAutoRotationProvider(snapPosition);
|
||||
|
||||
if (grabbableObject.RotationProvider == UxrRotationProvider.HandPositionAroundPivot && grabbableObject.GetGrabPointSnapModeAffectsRotation(grabPoint))
|
||||
{
|
||||
// Check if the leverage is provided by the inner side of the palm (where the thumb is) or the outer side.
|
||||
// We do that by checking the difference in distance of both to the rotation pivot. If it is above a threshold, it is provided by either one of the two.
|
||||
// If it is below a threshold it is provide by the grabber itself.
|
||||
|
||||
float separation = UxrConstants.Hand.HandWidth;
|
||||
float distanceInner = Vector3.Distance(grabbableObject.transform.position, snapPosition + snapRotation * grabber.LocalPalmThumbDirection * (separation * 0.5f));
|
||||
float distanceOuter = Vector3.Distance(grabbableObject.transform.position, snapPosition - snapRotation * grabber.LocalPalmThumbDirection * (separation * 0.5f));
|
||||
|
||||
if (Mathf.Abs(distanceInner - distanceOuter) > separation * 0.5f)
|
||||
{
|
||||
GrabberLocalLeverageSource = grabber.LocalPalmThumbDirection * (separation * 0.5f * (distanceInner > distanceOuter ? 1.0f : -1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
GrabberLocalParentLeverageSourceOnGrab = TransformExt.GetLocalPosition(grabbableObject.transform.parent, grabber.transform.TransformPoint(GrabberLocalLeverageSource));
|
||||
LocalPositionOnGrab = grabbableObject.transform.localPosition;
|
||||
LocalRotationOnGrab = grabbableObject.transform.localRotation;
|
||||
AlignPositionOnGrab = snapPosition;
|
||||
AlignRotationOnGrab = snapRotation;
|
||||
HandBoneLocalAvatarPositionOnGrab = grabber.Avatar.transform.InverseTransformPoint(grabber.HandBone.position);
|
||||
HandBoneLocalAvatarRotationOnGrab = Quaternion.Inverse(grabber.Avatar.transform.rotation) * grabber.HandBone.rotation;
|
||||
|
||||
if (grabbableObject.UsesGrabbableParentDependency && grabbableObject.ControlParentDirection)
|
||||
{
|
||||
// Compute leverage point in local grabbable parent coordinates when parent is rotated using the children.
|
||||
// We will use the largest vector of these two: (leverage point, local child position).
|
||||
|
||||
Vector3 localParentLeveragePosition = grabbableObject.GrabbableParent.transform.InverseTransformPoint(grabber.transform.TransformPoint(GrabberLocalLeverageSource));
|
||||
Vector3 localParentChildPosition = grabbableObject.GrabbableParent.transform.InverseTransformPoint(grabbableObject.transform.position);
|
||||
|
||||
bool useLeveragePosition = localParentLeveragePosition.magnitude > localParentChildPosition.magnitude;
|
||||
|
||||
ParentGrabbableLookAtParentLeveragePoint = useLeveragePosition ? localParentLeveragePosition : localParentChildPosition;
|
||||
ParentGrabbableLookAtLocalLeveragePoint = useLeveragePosition ? grabbableObject.transform.InverseTransformPoint(grabber.transform.TransformPoint(GrabberLocalLeverageSource)) : Vector3.zero;
|
||||
}
|
||||
|
||||
SingleRotationAngleContribution = 0.0f;
|
||||
LastAccumulatedAngle = 0.0f;
|
||||
|
||||
if (sourceGrabEventArgs != null)
|
||||
{
|
||||
// Place back again.
|
||||
grabbableObject.transform.position = originalPosition;
|
||||
grabbableObject.transform.rotation = originalRotation;
|
||||
}
|
||||
|
||||
// Additional help for serialization
|
||||
_grabbableObject = grabbableObject;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrGrabber _grabber;
|
||||
private int _grabbedPoint;
|
||||
private Quaternion _relativeGrabRotation;
|
||||
private Vector3 _relativeGrabPosition;
|
||||
private Quaternion _relativeGrabberRotation;
|
||||
private Vector3 _relativeGrabberPosition;
|
||||
private Transform _grabAlignParentTransformUsed;
|
||||
private Quaternion _relativeGrabAlignRotation;
|
||||
private Vector3 _relativeGrabAlignPosition;
|
||||
private Quaternion _relativeUsedGrabAlignRotation;
|
||||
private Vector3 _relativeUsedGrabAlignPosition;
|
||||
private Vector3 _relativeProximityPosition;
|
||||
private Vector3 _grabberLocalLeverageSource;
|
||||
private Vector3 _grabberLocalParentLeverageSourceOnGrab;
|
||||
private Vector3 _parentGrabbableLookAtLocalLeveragePoint;
|
||||
private Vector3 _parentGrabbableLookAtParentLeveragePoint;
|
||||
private Vector3 _parentGrabbableLeverageContribution;
|
||||
private Quaternion _parentGrabbableLookAtRotationContribution;
|
||||
private float _singleRotationAngleContribution;
|
||||
private float _lastAccumulatedAngle;
|
||||
private Vector3 _parentLocalGrabPositionBeforeUpdate;
|
||||
private Vector3 _parentLocalGrabPositionAfterUpdate;
|
||||
private Vector3 _childLocalParentPosition;
|
||||
private Quaternion _childLocalParentRotation;
|
||||
private Vector3 _localPositionOnGrab;
|
||||
private Quaternion _localRotationOnGrab;
|
||||
private Vector3 _alignPositionOnGrab;
|
||||
private Quaternion _alignRotationOnGrab;
|
||||
private Vector3 _handBoneLocalAvatarPositionOnGrab;
|
||||
private Quaternion _handBoneLocalAvatarRotationOnGrab;
|
||||
|
||||
// To be able to retrieve _grabAlignParentTransformUsed when serializing, because it doesn't have any way to serialize the reference:
|
||||
private UxrGrabbableObject _grabbableObject;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3608ac3c02642aa8d90a0cdd133d495
|
||||
timeCreated: 1677343619
|
||||
@@ -0,0 +1,445 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabManager.RuntimeManipulationInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Extensions.System.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabManager
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information of grabs performed on a <see cref="UxrGrabbableObject" /> at runtime.
|
||||
/// An object being manipulated can have multiple grabs, registered in <see cref="Grabs" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Implements <see cref="IUxrSerializable" /> to help <see cref="UxrGrabManager" />'s implementation of the
|
||||
/// <see cref="IUxrStateSave" /> interface (<see cref="UxrGrabManager.SerializeState" />).
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
private sealed class RuntimeManipulationInfo : IUxrSerializable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbers currently manipulating the object.
|
||||
/// </summary>
|
||||
public IEnumerable<UxrGrabber> Grabbers
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
yield return grabInfo.Grabber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the points currently being grabbed on the object.
|
||||
/// </summary>
|
||||
public IEnumerable<int> GrabbedPoints
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
yield return grabInfo.GrabbedPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current grabs manipulating the object.
|
||||
/// </summary>
|
||||
public List<RuntimeGrabInfo> Grabs => _grabs;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target from where the <see cref="UxrGrabbableObject" /> was grabbed.
|
||||
/// </summary>
|
||||
public UxrGrabbableObjectAnchor SourceAnchor => _sourceAnchor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current rotation angle, in objects constrained to a single rotation axis, contributed by all the grabbers
|
||||
/// manipulating the object.
|
||||
/// </summary>
|
||||
public float CurrentSingleRotationAngleContributions
|
||||
{
|
||||
get
|
||||
{
|
||||
float accumulation = 0.0f;
|
||||
int contributionCount = 0;
|
||||
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
accumulation += grabInfo.SingleRotationAngleContribution - grabInfo.LastAccumulatedAngle;
|
||||
contributionCount++;
|
||||
}
|
||||
|
||||
if (contributionCount > 1)
|
||||
{
|
||||
accumulation /= contributionCount;
|
||||
}
|
||||
|
||||
return accumulation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbed object.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableObject => _grabbableObject;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation pivot when child grabbable objects manipulate this object's orientation.
|
||||
/// </summary>
|
||||
public Vector3 LocalManipulationRotationPivot
|
||||
{
|
||||
get => _localManipulationRotationPivot;
|
||||
set => _localManipulationRotationPivot = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber of the grab</param>
|
||||
/// <param name="grabPoint">Grab point index of the <see cref="UxrGrabbableObject" /> that was grabbed.</param>
|
||||
/// <param name="sourceAnchor">Target if the grabbed object was placed on any.</param>
|
||||
public RuntimeManipulationInfo(UxrGrabber grabber, int grabPoint, UxrGrabbableObjectAnchor sourceAnchor = null)
|
||||
{
|
||||
Grabs.Add(new RuntimeGrabInfo(grabber, grabPoint));
|
||||
|
||||
_grabbableObject = grabber.GrabbedObject;
|
||||
_sourceAnchor = sourceAnchor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor required for serialization.
|
||||
/// </summary>
|
||||
private RuntimeManipulationInfo()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializable
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SerializationVersion => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(IUxrSerializer serializer, int serializationVersion)
|
||||
{
|
||||
serializer.Serialize(ref _grabs);
|
||||
serializer.SerializeUniqueComponent(ref _sourceAnchor);
|
||||
serializer.SerializeUniqueComponent(ref _grabbableObject);
|
||||
serializer.Serialize(ref _localManipulationRotationPivot);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new grab.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber that performed the grab</param>
|
||||
/// <param name="grabPoint">The point of the <see cref="UxrGrabbableObject" /> that was grabbed.</param>
|
||||
/// <param name="append">
|
||||
/// Whether to append or insert at the beginning. If there is more than one grab point and none of
|
||||
/// them is the 0 index (main grab), the main grab will be the first one in the list.
|
||||
/// </param>
|
||||
/// <returns>The newly created grab info entry</returns>
|
||||
public RuntimeGrabInfo RegisterNewGrab(UxrGrabber grabber, int grabPoint, bool append = true)
|
||||
{
|
||||
RuntimeGrabInfo runtimeGrabInfo = new RuntimeGrabInfo(grabber, grabPoint);
|
||||
|
||||
if (append)
|
||||
{
|
||||
Grabs.Add(runtimeGrabInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grabs.Insert(0, runtimeGrabInfo);
|
||||
}
|
||||
|
||||
return runtimeGrabInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a grab.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber that released the grab</param>
|
||||
public void RemoveGrab(UxrGrabber grabber)
|
||||
{
|
||||
Grabs.RemoveAll(g => g.Grabber == grabber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all grabs registered.
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
Grabs.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies a the start of a new grab, in order to compute all required data.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber responsible for grabbing the object</param>
|
||||
/// <param name="grabbableObject">The object being grabbed</param>
|
||||
/// <param name="grabPoint">Point that was grabbed</param>
|
||||
/// <param name="snapPosition">The grabber snap position to use</param>
|
||||
/// <param name="snapRotation">The grabber snap rotation to use</param>
|
||||
/// <param name="sourceGrabEventArgs">
|
||||
/// If non-null, the grab will use the information on the event to ensure that
|
||||
/// it is performed in exactly the same way. This is used in multi-player environments.
|
||||
/// </param>
|
||||
/// <returns>Grab information</returns>
|
||||
public RuntimeGrabInfo NotifyBeginGrab(UxrGrabber grabber, UxrGrabbableObject grabbableObject, int grabPoint, Vector3 snapPosition, Quaternion snapRotation, UxrManipulationEventArgs sourceGrabEventArgs = null)
|
||||
{
|
||||
RuntimeGrabInfo grabInfo = GetGrabInfo(grabber);
|
||||
|
||||
if (grabInfo == null)
|
||||
{
|
||||
grabInfo = RegisterNewGrab(grabber, grabPoint);
|
||||
}
|
||||
|
||||
// If it's an object constrained to a single rotation axis, accumulate current contributions first
|
||||
|
||||
AccumulateSingleRotationAngle(grabbableObject);
|
||||
|
||||
// Compute data
|
||||
|
||||
grabbableObject.NotifyBeginGrab(grabber, grabPoint, snapPosition, snapRotation);
|
||||
grabInfo.Compute(grabber, grabbableObject, grabPoint, snapPosition, snapRotation, sourceGrabEventArgs);
|
||||
|
||||
// Smooth transitions
|
||||
|
||||
if (Instance.Features.HasFlag(UxrManipulationFeatures.SmoothTransitions))
|
||||
{
|
||||
grabbableObject.StartSmoothManipulationTransition();
|
||||
grabber.StartSmoothManipulationTransition();
|
||||
|
||||
if (grabbableObject.GrabbableParent != null && grabbableObject.UsesGrabbableParentDependency && grabbableObject.ControlParentDirection)
|
||||
{
|
||||
grabbableObject.GrabbableParent.StartSmoothManipulationTransition();
|
||||
}
|
||||
}
|
||||
|
||||
return grabInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the end of a grab.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber that released the object</param>
|
||||
/// <param name="grabbableObject">Object that was released</param>
|
||||
/// <param name="grabPoint">Grab point that was released</param>
|
||||
public void NotifyEndGrab(UxrGrabber grabber, UxrGrabbableObject grabbableObject, int grabPoint)
|
||||
{
|
||||
// If it's an object constrained to a single rotation axis, accumulate current contributions first
|
||||
|
||||
AccumulateSingleRotationAngle(grabbableObject);
|
||||
|
||||
// Notify object
|
||||
|
||||
grabbableObject.NotifyEndGrab(grabber, grabPoint);
|
||||
|
||||
// Smooth transitions
|
||||
|
||||
if (Instance.Features.HasFlag(UxrManipulationFeatures.SmoothTransitions))
|
||||
{
|
||||
grabbableObject.StartSmoothManipulationTransition();
|
||||
grabber.StartSmoothManipulationTransition();
|
||||
|
||||
if (grabbableObject.GrabbableParent != null && grabbableObject.UsesGrabbableParentDependency && grabbableObject.ControlParentDirection)
|
||||
{
|
||||
grabbableObject.GrabbableParent.StartSmoothManipulationTransition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab information of a specific grabber.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <returns>Grab info or null if not found</returns>
|
||||
public RuntimeGrabInfo GetGrabInfo(UxrGrabber grabber)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber == grabber)
|
||||
{
|
||||
return grabInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point grabbed by the given grabber.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <returns>Grabbed point in <see cref="UxrGrabbableObject" /> or -1 if not found</returns>
|
||||
public int GetGrabbedPoint(UxrGrabber grabber)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber == grabber)
|
||||
{
|
||||
return grabInfo.GrabbedPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabber grabbing the given point.
|
||||
/// </summary>
|
||||
/// <param name="grabPoint">Grab point in <see cref="UxrGrabbableObject" /></param>
|
||||
/// <returns>Grabber grabbing the given point or null if not found</returns>
|
||||
public UxrGrabber GetGrabberGrabbingPoint(int grabPoint)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.GrabbedPoint == grabPoint)
|
||||
{
|
||||
return grabInfo.Grabber;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given grabber is being used to manipulate the object.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to check</param>
|
||||
/// <returns>Whether the given grabber is being used to manipulate the object</returns>
|
||||
public bool IsGrabberUsed(UxrGrabber grabber)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber == grabber)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given grab point is being grabbed on the object.
|
||||
/// </summary>
|
||||
/// <param name="grabPoint">Grab point to check</param>
|
||||
/// <returns>Whether the given grab point is being grabbed on the object</returns>
|
||||
public bool IsPointGrabbed(int grabPoint)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.GrabbedPoint == grabPoint)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a grabber swap to indicate that a different hand is now grabbing the point.
|
||||
/// </summary>
|
||||
/// <param name="oldGrabber">Old grabber that was grabbing</param>
|
||||
/// <param name="newGrabber">New grabber that the grab switched to</param>
|
||||
public void SwapGrabber(UxrGrabber oldGrabber, UxrGrabber newGrabber)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber == oldGrabber)
|
||||
{
|
||||
grabInfo.Grabber = newGrabber;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a grabber swap to indicate that a different hand is now grabbing another point.
|
||||
/// </summary>
|
||||
/// <param name="oldGrabber">Old grabber that was grabbing</param>
|
||||
/// <param name="oldGrabPoint">Old grab point of the <see cref="UxrGrabbableObject" /> grabbed by the old grabber</param>
|
||||
/// <param name="newGrabber">New grabber that the grab switched to</param>
|
||||
/// <param name="newGrabPoint">New grab point of the <see cref="UxrGrabbableObject" /> the grab switched to</param>
|
||||
public void SwapGrabber(UxrGrabber oldGrabber, int oldGrabPoint, UxrGrabber newGrabber, int newGrabPoint)
|
||||
{
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
if (grabInfo.Grabber == oldGrabber)
|
||||
{
|
||||
grabInfo.Grabber = newGrabber;
|
||||
grabInfo.GrabbedPoint = newGrabPoint;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accumulates the contributions of all grabbers in an object constrained to a single angle rotation.
|
||||
/// </summary>
|
||||
public void AccumulateSingleRotationAngle(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
int singleRotationAxisIndex = grabbableObject.SingleRotationAxisIndex;
|
||||
|
||||
if (singleRotationAxisIndex == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float accumulation = 0.0f;
|
||||
int contributionCount = 0;
|
||||
|
||||
foreach (RuntimeGrabInfo grabInfo in Grabs)
|
||||
{
|
||||
accumulation += grabInfo.SingleRotationAngleContribution - grabInfo.LastAccumulatedAngle;
|
||||
grabInfo.LastAccumulatedAngle = grabInfo.SingleRotationAngleContribution;
|
||||
contributionCount++;
|
||||
}
|
||||
|
||||
if (contributionCount > 0)
|
||||
{
|
||||
accumulation /= contributionCount;
|
||||
grabbableObject.SingleRotationAngleCumulative = (grabbableObject.SingleRotationAngleCumulative + accumulation).Clamped(grabbableObject.RotationAngleLimitsMin[singleRotationAxisIndex],
|
||||
grabbableObject.RotationAngleLimitsMax[singleRotationAxisIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private List<RuntimeGrabInfo> _grabs = new List<RuntimeGrabInfo>();
|
||||
private UxrGrabbableObjectAnchor _sourceAnchor;
|
||||
private UxrGrabbableObject _grabbableObject;
|
||||
private Vector3 _localManipulationRotationPivot;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82ee23bcf0d63fc479a4229506949086
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabManager.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabManager
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Manipulations are already handled through events, we don't serialize them in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
// We don't want to compare dictionaries, we save the manipulations info always by using null as name to avoid overhead.
|
||||
SerializeStateValue(level, options, null, ref _currentManipulations);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e159c4422a8ab5441a9bef231f06f8ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
989
Assets/UltimateXR/Runtime/Scripts/Manipulation/UxrGrabManager.cs
Normal file
989
Assets/UltimateXR/Runtime/Scripts/Manipulation/UxrGrabManager.cs
Normal file
@@ -0,0 +1,989 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabManager.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Singleton;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Manager that takes care of updating all the manipulation mechanics. The manipulation system handles three main
|
||||
/// types of entities:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabber" />: Components usually assigned to each hand of an <see cref="UxrAvatar" /> and
|
||||
/// that are able to grab objects
|
||||
/// </item>
|
||||
/// <item><see cref="UxrGrabbableObject" />: Objects that can be grabbed</item>
|
||||
/// <item><see cref="UxrGrabbableObjectAnchor" />: Anchors where grabbable objects can be placed</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public partial class UxrGrabManager : UxrSingleton<UxrGrabManager>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever a <see cref="UxrGrabber" /> component is about to try to grab something (a hand is beginning
|
||||
/// to close). If it ends up grabbing something will depend on whether there is a <see cref="UxrGrabbableObject" /> in
|
||||
/// reach.
|
||||
/// Properties available:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that tried to grab.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> GrabTrying;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever a <see cref="UxrGrabber" /> component is about to grab a <see cref="UxrGrabbableObject" />.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be grabbed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object is currently placed. Null
|
||||
/// if it isn't placed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is about to grab the object.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Grab point index of the object that is about to be
|
||||
/// grabbed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.IsMultiHands" />: true if it is already being grabbed and
|
||||
/// will be grabbed with one more hand after. False if no hand is currently grabbing it.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectGrabbing;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="ObjectGrabbing" /> but called right after the object was grabbed.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectGrabbed;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever a <see cref="UxrGrabber" /> component is about to release the
|
||||
/// <see cref="UxrGrabbableObject" /> that it is holding and there is no <see cref="UxrGrabbableObjectAnchor" /> nearby
|
||||
/// to place it on.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be released.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object was originally grabbed
|
||||
/// from. Null if it wasn't grabbed from an anchor.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is about to release the object.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Grab point index of the object that is being
|
||||
/// grabbed by the <see cref="UxrGrabber" />.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.IsMultiHands" />: true if it is already being grabbed with another hand
|
||||
/// that will keep holding it. False if no other hand is currently grabbing it.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.IsSwitchHands" />: True if it was released because another
|
||||
/// <see cref="UxrGrabber" /> grabbed it, false otherwise. if
|
||||
/// <see cref="UxrManipulationEventArgs.IsMultiHands" /> is
|
||||
/// true then <see cref="UxrManipulationEventArgs.IsSwitchHands" /> will tell if it was released by all hands
|
||||
/// (false) or if it was just released by one hand and the other(s) still keep the grab (true).
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.ReleaseVelocity" />: Velocity the object is being released with.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.ReleaseAngularVelocity" />: Angular velocity the object is being
|
||||
/// released with.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the object is being released on a <see cref="UxrGrabbableObjectAnchor" /> that can hold it, it will
|
||||
/// generate a <see cref="ObjectPlacing" /> event instead. Whenever an object is released it will either generate
|
||||
/// either a Place or Release event, but not both.
|
||||
/// </remarks>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectReleasing;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="ObjectReleasing" /> but called right after the object was released.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectReleased;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever a <see cref="UxrGrabbableObject" /> is about to be placed on an
|
||||
/// <see cref="UxrGrabbableObjectAnchor" />.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be placed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object is about to be placed on.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is placing the object.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Grab point index of the object that is being
|
||||
/// grabbed by the <see cref="UxrGrabber" />.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the object is being placed it will not generate a <see cref="ObjectReleasing" /> event. Whenever an object is
|
||||
/// released it will either generate either a Place or Release event, but not both.
|
||||
/// </remarks>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectPlacing;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="ObjectPlacing" /> but called right after the object was placed.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectPlaced;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever a <see cref="UxrGrabbableObject" /> is about to be removed from an
|
||||
/// <see cref="UxrGrabbableObjectAnchor" />.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is about to be removed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object is currently placed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is about to remove the object by grabbing it.
|
||||
/// This can be null if the object is removed through code using <see cref="RemoveObjectFromAnchor" />,
|
||||
/// <see cref="UxrGrabbableObject.RemoveFromAnchor" /> or <see cref="UxrGrabbableObjectAnchor.RemoveObject" />>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Only if the object is being removed by grabbing it:
|
||||
/// Grab point index of the object that is about to be grabbed by the <see cref="UxrGrabber" />.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectRemoving;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="ObjectRemoving" /> but called right after the object was removed.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> ObjectRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever an <see cref="UxrGrabbableObject" /> being grabbed by a <see cref="UxrGrabber" /> entered the
|
||||
/// valid placement range (distance) of a compatible <see cref="UxrGrabbableObjectAnchor" />.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that entered the valid placement range.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object can potentially be placed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that is holding the object. If more than one
|
||||
/// grabber is holding it, it will indicate the first one to grab it.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only enter/leave events will be generated. To check if an object can be placed on an anchor use
|
||||
/// <see cref="UxrGrabbableObject.CanBePlacedOnAnchor" />.
|
||||
/// </remarks>
|
||||
/// <seealso cref="AnchorRangeLeft" />
|
||||
public event EventHandler<UxrManipulationEventArgs> AnchorRangeEntered;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="AnchorRangeEntered" /> but when leaving the valid range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only enter/leave events will be generated. To check if an object can be placed on an anchor use
|
||||
/// <see cref="UxrGrabbableObject.CanBePlacedOnAnchor" />.
|
||||
/// </remarks>
|
||||
/// <seealso cref="AnchorRangeEntered" />
|
||||
public event EventHandler<UxrManipulationEventArgs> AnchorRangeLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever a <see cref="UxrGrabber" /> enters the valid grab range (distance) of a
|
||||
/// <see cref="UxrGrabbableObject" /> placed on an <see cref="UxrGrabbableObjectAnchor" />.
|
||||
/// The following properties from <see cref="UxrManipulationEventArgs" /> will contain meaningful data:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableObject" />: Object that is within reach.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabbableAnchor" />: Anchor where the object is placed.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.Grabber" />: Grabber that entered the valid grab range.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrManipulationEventArgs.GrabPointIndex" />: Grab point index that is within reach.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only enter/leave events will be generated. To check if an object can be grabbed use
|
||||
/// <see cref="UxrGrabbableObject.CanBeGrabbedByGrabber" />.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PlacedObjectRangeLeft" />
|
||||
public event EventHandler<UxrManipulationEventArgs> PlacedObjectRangeEntered;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="PlacedObjectRangeEntered" /> but when leaving the valid range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only enter/leave events will be generated. To check if an object can be grabbed use
|
||||
/// <see cref="UxrGrabbableObject.CanBeGrabbedByGrabber" />.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PlacedObjectRangeEntered" />
|
||||
public event EventHandler<UxrManipulationEventArgs> PlacedObjectRangeLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the manipulation features that are used when the manager is updated.
|
||||
/// </summary>
|
||||
public UxrManipulationFeatures Features { get; set; } = UxrManipulationFeatures.All;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the grab manager to the current frame.
|
||||
/// </summary>
|
||||
internal void UpdateManager()
|
||||
{
|
||||
// Initializes the variables for a manipulation frame update computation.
|
||||
InitializeManipulationFrame();
|
||||
|
||||
// Updates the grabbable objects based on manipulation logic
|
||||
UpdateManipulation();
|
||||
|
||||
if (Features.HasFlag(UxrManipulationFeatures.Affordances))
|
||||
{
|
||||
// Updates visual feedback states (objects that can be grabbed, anchors where a grabbed object can be placed on, etc.)
|
||||
UpdateAffordances();
|
||||
}
|
||||
|
||||
// Perform operations that need to be done at the end of the updating process.
|
||||
FinalizeManipulationFrame();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the manager and subscribes to global events.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
UxrGrabbableObjectAnchor.GlobalEnabled += GrabbableObjectAnchor_Enabled;
|
||||
UxrGrabbableObjectAnchor.GlobalDisabled += GrabbableObjectAnchor_Disabled;
|
||||
UxrGrabbableObject.GlobalDisabled += GrabbableObject_Disabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from global events.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
UxrGrabbableObjectAnchor.GlobalEnabled -= GrabbableObjectAnchor_Enabled;
|
||||
UxrGrabbableObjectAnchor.GlobalDisabled -= GrabbableObjectAnchor_Disabled;
|
||||
UxrGrabbableObject.GlobalDisabled += GrabbableObject_Disabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrAvatar.GlobalAvatarMoved += UxrAvatar_GlobalAvatarMoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrAvatar.GlobalAvatarMoved -= UxrAvatar_GlobalAvatarMoved;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when a grabbable object anchor was enabled. Adds it to the internal list.
|
||||
/// </summary>
|
||||
/// <param name="anchor">Anchor that was enabled</param>
|
||||
private void GrabbableObjectAnchor_Enabled(UxrGrabbableObjectAnchor anchor)
|
||||
{
|
||||
_grabbableObjectAnchors.Add(anchor, new GrabbableObjectAnchorInfo());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a grabbable object anchor was disabled. Removes it from the internal list.
|
||||
/// </summary>
|
||||
/// <param name="anchor">Anchor that was disabled</param>
|
||||
private void GrabbableObjectAnchor_Disabled(UxrGrabbableObjectAnchor anchor)
|
||||
{
|
||||
_grabbableObjectAnchors.Remove(anchor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a grabbable object was disabled. Removes it from current grabs if present.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object that was disabled</param>
|
||||
private void GrabbableObject_Disabled(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
if (_currentManipulations.ContainsKey(grabbableObject))
|
||||
{
|
||||
_currentManipulations.Remove(grabbableObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an avatar was moved due to regular movement or teleportation. It is used to process the objects that
|
||||
/// are being grabbed to the avatar to keep it in the same relative position/orientation.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void UxrAvatar_GlobalAvatarMoved(object sender, UxrAvatarMoveEventArgs e)
|
||||
{
|
||||
UxrAvatar avatar = sender as UxrAvatar;
|
||||
|
||||
if (avatar == null || avatar.AvatarMode == UxrAvatarMode.UpdateExternally)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create anonymous pairs of grabbable objects and their grabs that are affected by the avatar position change
|
||||
var dependencies = _currentManipulations.Where(pair => pair.Value.Grabbers.Any(g => g.Avatar == avatar)).Select(pair => new { GrabbableObject = pair.Key, Grabs = pair.Value.Grabs.Where(g => g.Grabber.Avatar == avatar) });
|
||||
|
||||
foreach (var dependency in dependencies)
|
||||
{
|
||||
UxrGrabbableObject grabbableObject = dependency.GrabbableObject;
|
||||
|
||||
// Move grabbed objects without being parented to avatar to new position/orientation to avoid rubber-band effects
|
||||
if (!grabbableObject.transform.HasParent(avatar.transform))
|
||||
{
|
||||
UxrGrabbableObject grabbableRoot = grabbableObject.AllParents.LastOrDefault() ?? grabbableObject;
|
||||
|
||||
// Use this handy method to make the grabbable object keep the relative positioning to the avatar
|
||||
e.ReorientRelativeToAvatar(grabbableRoot.transform);
|
||||
|
||||
grabbableRoot.LocalPositionBeforeUpdate = grabbableRoot.transform.localPosition;
|
||||
grabbableRoot.LocalRotationBeforeUpdate = grabbableRoot.transform.localRotation;
|
||||
ConstrainTransform(grabbableRoot);
|
||||
KeepGripsInPlace(grabbableRoot);
|
||||
|
||||
foreach (UxrGrabbableObject grabbableChild in grabbableRoot.AllChildren)
|
||||
{
|
||||
grabbableChild.LocalPositionBeforeUpdate = grabbableChild.transform.localPosition;
|
||||
grabbableChild.LocalRotationBeforeUpdate = grabbableChild.transform.localRotation;
|
||||
ConstrainTransform(grabbableChild);
|
||||
KeepGripsInPlace(grabbableChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="GrabTrying" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnGrabTrying(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Verbose)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.ManipulationModule} Trying to grab using {e.Grabber}.");
|
||||
}
|
||||
|
||||
if (propagateEvent)
|
||||
{
|
||||
GrabTrying?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectGrabbing" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectGrabbing(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.ManipulationModule} {e.ToString(UxrGlobalSettings.Instance.LogLevelManipulation == UxrLogLevel.Verbose)}");
|
||||
}
|
||||
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectGrabbing?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectGrabbed" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectGrabbed(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectGrabbed?.Invoke(this, e);
|
||||
}
|
||||
|
||||
if (e.GrabbableObject)
|
||||
{
|
||||
e.GrabbableObject.UpdateGrabbableDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectReleasing" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectReleasing(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.ManipulationModule} {e.ToString(UxrGlobalSettings.Instance.LogLevelManipulation == UxrLogLevel.Verbose)}");
|
||||
}
|
||||
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectReleasing?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectReleased" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectReleased(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectReleased?.Invoke(this, e);
|
||||
}
|
||||
|
||||
if (e.GrabbableObject)
|
||||
{
|
||||
e.GrabbableObject.UpdateGrabbableDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectPlacing" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectPlacing(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.ManipulationModule} {e.ToString(UxrGlobalSettings.Instance.LogLevelManipulation == UxrLogLevel.Verbose)}");
|
||||
}
|
||||
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectPlacing?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectPlaced" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectPlaced(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectPlaced?.Invoke(this, e);
|
||||
}
|
||||
|
||||
if (e.GrabbableObject)
|
||||
{
|
||||
e.GrabbableObject.UpdateGrabbableDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectRemoving" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectRemoving(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelManipulation >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.ManipulationModule} {e.ToString(UxrGlobalSettings.Instance.LogLevelManipulation == UxrLogLevel.Verbose)}");
|
||||
}
|
||||
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectRemoving?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ObjectRemoved" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnObjectRemoved(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
ObjectRemoved?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="AnchorRangeEntered" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnAnchorRangeEntered(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
AnchorRangeEntered?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="AnchorRangeLeft" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnAnchorRangeLeft(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
AnchorRangeLeft?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="PlacedObjectRangeEntered" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnPlacedObjectRangeEntered(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
PlacedObjectRangeEntered?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="PlacedObjectRangeLeft" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
/// <param name="propagateEvent">Whether to propagate the event</param>
|
||||
private void OnPlacedObjectRangeLeft(UxrManipulationEventArgs e, bool propagateEvent)
|
||||
{
|
||||
if (propagateEvent)
|
||||
{
|
||||
PlacedObjectRangeLeft?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the variables for a manipulation frame update computation.
|
||||
/// </summary>
|
||||
private void InitializeManipulationFrame()
|
||||
{
|
||||
// Store the unprocessed grabber positions for this update.
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.AllComponents)
|
||||
{
|
||||
grabber.UnprocessedGrabberPosition = grabber.transform.position;
|
||||
grabber.UnprocessedGrabberRotation = grabber.transform.rotation;
|
||||
}
|
||||
|
||||
// Update grabbable object information
|
||||
|
||||
void InitializeGrabbableData(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
grabbableObject.DirectLookAtChildProcessedCount = 0;
|
||||
grabbableObject.DirectLookAtChildGrabbedCount = grabbableObject.DirectChildrenLookAts.Count(IsBeingGrabbed);
|
||||
grabbableObject.LocalPositionBeforeUpdate = grabbableObject.transform.localPosition;
|
||||
grabbableObject.LocalRotationBeforeUpdate = grabbableObject.transform.localRotation;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<UxrGrabbableObject, RuntimeManipulationInfo> manipulationInfoPair in _currentManipulations)
|
||||
{
|
||||
if (manipulationInfoPair.Key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UxrGrabbableObject grabbableParent = manipulationInfoPair.Key.GrabbableParent;
|
||||
|
||||
InitializeGrabbableData(manipulationInfoPair.Key);
|
||||
|
||||
if (grabbableParent != null)
|
||||
{
|
||||
InitializeGrabbableData(grabbableParent);
|
||||
}
|
||||
|
||||
foreach (UxrGrabbableObject child in manipulationInfoPair.Key.DirectChildrenLookAts)
|
||||
{
|
||||
InitializeGrabbableData(child);
|
||||
}
|
||||
|
||||
manipulationInfoPair.Value.LocalManipulationRotationPivot = Vector3.zero;
|
||||
}
|
||||
|
||||
// Initialize some anchor variables for later
|
||||
|
||||
foreach (KeyValuePair<UxrGrabbableObjectAnchor, GrabbableObjectAnchorInfo> anchorPair in _grabbableObjectAnchors)
|
||||
{
|
||||
if (anchorPair.Key.CurrentPlacedObject == null)
|
||||
{
|
||||
anchorPair.Value.HadCompatibleObjectNearLastFrame = anchorPair.Value.HasCompatibleObjectNear;
|
||||
anchorPair.Value.HasCompatibleObjectNear = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
anchorPair.Value.GrabberNear = null;
|
||||
}
|
||||
|
||||
anchorPair.Value.FullGrabberNear = null;
|
||||
anchorPair.Value.GrabPointNear = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs operations that require to be done at the end of the manipulation update pipeline.
|
||||
/// </summary>
|
||||
private void FinalizeManipulationFrame()
|
||||
{
|
||||
// Update grabbers
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.EnabledComponents)
|
||||
{
|
||||
grabber.UpdateThrowPhysicsInfo();
|
||||
grabber.UpdateHandGrabberRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates visual feedback states (objects that can be grabbed, anchors where a grabbed object can be placed on,
|
||||
/// etc.).
|
||||
/// </summary>
|
||||
private void UpdateAffordances()
|
||||
{
|
||||
// Look for grabbed objects that can be placed on anchors
|
||||
|
||||
foreach (KeyValuePair<UxrGrabbableObject, RuntimeManipulationInfo> manipulationInfoPair in _currentManipulations)
|
||||
{
|
||||
UxrGrabbableObjectAnchor anchorTargetCandidate = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
if (manipulationInfoPair.Key.UsesGrabbableParentDependency == false && manipulationInfoPair.Key.IsPlaceable)
|
||||
{
|
||||
foreach (KeyValuePair<UxrGrabbableObjectAnchor, GrabbableObjectAnchorInfo> anchorPair in _grabbableObjectAnchors)
|
||||
{
|
||||
if (manipulationInfoPair.Key.CanBePlacedOnAnchor(anchorPair.Key, out float distance) && distance < minDistance)
|
||||
{
|
||||
anchorTargetCandidate = anchorPair.Key;
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is there a compatible anchor if we would release it? store the grabber for later
|
||||
|
||||
if (anchorTargetCandidate != null)
|
||||
{
|
||||
_grabbableObjectAnchors[anchorTargetCandidate].HasCompatibleObjectNear = true;
|
||||
_grabbableObjectAnchors[anchorTargetCandidate].FullGrabberNear = manipulationInfoPair.Value.Grabs[0].Grabber;
|
||||
_grabbableObjectAnchors[anchorTargetCandidate].LastFullGrabberNear = manipulationInfoPair.Value.Grabs[0].Grabber;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for objects that can be grabbed to update feedback objects (blinks, labels...).
|
||||
// First pass: get closest candidate for each grabber.
|
||||
|
||||
Dictionary<UxrGrabbableObject, List<int>> possibleGrabs = null;
|
||||
|
||||
foreach (UxrGrabber grabber in UxrGrabber.EnabledComponents)
|
||||
{
|
||||
if (grabber.GrabbedObject == null)
|
||||
{
|
||||
if (GetClosestGrabbableObject(grabber, out UxrGrabbableObject grabbableCandidate, out int grabPointCandidate) && !IsBeingGrabbed(grabbableCandidate, grabPointCandidate))
|
||||
{
|
||||
if (possibleGrabs == null)
|
||||
{
|
||||
possibleGrabs = new Dictionary<UxrGrabbableObject, List<int>>();
|
||||
}
|
||||
|
||||
if (possibleGrabs.ContainsKey(grabbableCandidate))
|
||||
{
|
||||
possibleGrabs[grabbableCandidate].Add(grabPointCandidate);
|
||||
}
|
||||
else
|
||||
{
|
||||
possibleGrabs.Add(grabbableCandidate, new List<int> { grabPointCandidate });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: update visual feedback objects for grabbable objects.
|
||||
|
||||
foreach (UxrGrabbableObject grabbable in UxrGrabbableObject.EnabledComponents)
|
||||
{
|
||||
// First disable all needed, then enable them in another pass because some points may share the same object
|
||||
for (int point = 0; point < grabbable.GrabPointCount; ++point)
|
||||
{
|
||||
GameObject enableOnHandNear = grabbable.GetGrabPoint(point).EnableOnHandNear;
|
||||
|
||||
if (enableOnHandNear)
|
||||
{
|
||||
bool enableObject = false;
|
||||
List<int> grabPoints = null;
|
||||
|
||||
if (possibleGrabs != null && possibleGrabs.TryGetValue(grabbable, out grabPoints))
|
||||
{
|
||||
enableObject = grabPoints.Contains(point);
|
||||
}
|
||||
|
||||
if (!enableObject && enableOnHandNear.activeSelf)
|
||||
{
|
||||
// Try to find first if other point needs to enable it
|
||||
bool foundEnable = false;
|
||||
|
||||
for (int pointOther = 0; pointOther < grabbable.GrabPointCount; ++pointOther)
|
||||
{
|
||||
GameObject enableOnHandNearOther = grabbable.GetGrabPoint(pointOther).EnableOnHandNear;
|
||||
|
||||
if (enableOnHandNear == enableOnHandNearOther)
|
||||
{
|
||||
if (possibleGrabs != null && possibleGrabs.TryGetValue(grabbable, out List<int> grabPointsOther))
|
||||
{
|
||||
foundEnable = grabPoints.Contains(pointOther);
|
||||
|
||||
if (foundEnable)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundEnable)
|
||||
{
|
||||
enableOnHandNear.SetActive(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int point = 0; point < grabbable.GrabPointCount; ++point)
|
||||
{
|
||||
GameObject enableOnHandNear = grabbable.GetGrabPoint(point).EnableOnHandNear;
|
||||
|
||||
if (enableOnHandNear)
|
||||
{
|
||||
bool enableObject = false;
|
||||
|
||||
if (possibleGrabs != null && possibleGrabs.TryGetValue(grabbable, out List<int> grabPoints))
|
||||
{
|
||||
enableObject = grabPoints.Contains(point);
|
||||
}
|
||||
|
||||
if (enableObject && !enableOnHandNear.activeSelf)
|
||||
{
|
||||
enableOnHandNear.SetActive(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for empty hand being able to grab something from an anchor to update anchor visual feedback objects later and also raise events. First pass: gather info.
|
||||
foreach (UxrGrabber grabber in UxrGrabber.EnabledComponents)
|
||||
{
|
||||
if (grabber.GrabbedObject == null)
|
||||
{
|
||||
UxrGrabbableObjectAnchor anchorCandidate = null;
|
||||
int grabPointCandidate = 0;
|
||||
int maxPriority = int.MinValue;
|
||||
float minDistanceWithoutRotation = float.MaxValue; // Between different objects we don't take orientations into account
|
||||
|
||||
foreach (KeyValuePair<UxrGrabbableObjectAnchor, GrabbableObjectAnchorInfo> anchorPair in _grabbableObjectAnchors)
|
||||
{
|
||||
UxrGrabbableObjectAnchor grabbableAnchor = anchorPair.Key;
|
||||
|
||||
if (grabbableAnchor.CurrentPlacedObject != null)
|
||||
{
|
||||
// For the same object we will not just consider the distance but also how close the grabber is to the grip orientation
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
for (int point = 0; point < grabbableAnchor.CurrentPlacedObject.GrabPointCount; ++point)
|
||||
{
|
||||
if (grabbableAnchor.CurrentPlacedObject.CanBeGrabbedByGrabber(grabber, point))
|
||||
{
|
||||
grabbableAnchor.CurrentPlacedObject.GetDistanceFromGrabber(grabber, point, out float distance, out float distanceWithoutRotation);
|
||||
|
||||
if (grabbableAnchor.CurrentPlacedObject.Priority > maxPriority)
|
||||
{
|
||||
anchorCandidate = grabbableAnchor;
|
||||
grabPointCandidate = point;
|
||||
minDistance = distance;
|
||||
minDistanceWithoutRotation = distanceWithoutRotation;
|
||||
maxPriority = grabbableAnchor.CurrentPlacedObject.Priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((anchorCandidate == grabbableAnchor && distance < minDistance) || (anchorCandidate != grabbableAnchor && distanceWithoutRotation < minDistanceWithoutRotation))
|
||||
{
|
||||
anchorCandidate = grabbableAnchor;
|
||||
grabPointCandidate = point;
|
||||
minDistance = distance;
|
||||
minDistanceWithoutRotation = distanceWithoutRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anchorCandidate != null)
|
||||
{
|
||||
_grabbableObjectAnchors[anchorCandidate].GrabberNear = null;
|
||||
_grabbableObjectAnchors[anchorCandidate].GrabPointNear = grabPointCandidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: update object states and raise events.
|
||||
foreach (KeyValuePair<UxrGrabbableObjectAnchor, GrabbableObjectAnchorInfo> anchorPair in _grabbableObjectAnchors)
|
||||
{
|
||||
if (anchorPair.Key.CurrentPlacedObject == null)
|
||||
{
|
||||
if (anchorPair.Value.LastValidGrabberNear != null)
|
||||
{
|
||||
OnPlacedObjectRangeEntered(UxrManipulationEventArgs.FromOther(UxrManipulationEventType.PlacedObjectRangeEntered, anchorPair.Key.CurrentPlacedObject, anchorPair.Key, anchorPair.Value.LastValidGrabberNear, anchorPair.Value.LastValidGrabPointNear), true);
|
||||
|
||||
anchorPair.Value.LastValidGrabberNear = null;
|
||||
anchorPair.Value.LastValidGrabPointNear = -1;
|
||||
}
|
||||
|
||||
if (anchorPair.Value.HasCompatibleObjectNear && !anchorPair.Value.HadCompatibleObjectNearLastFrame)
|
||||
{
|
||||
OnAnchorRangeEntered(UxrManipulationEventArgs.FromOther(UxrManipulationEventType.AnchorRangeEntered, anchorPair.Value.FullGrabberNear.GrabbedObject, anchorPair.Key, anchorPair.Value.FullGrabberNear), true);
|
||||
}
|
||||
|
||||
if (!anchorPair.Value.HasCompatibleObjectNear && anchorPair.Value.HadCompatibleObjectNearLastFrame)
|
||||
{
|
||||
OnAnchorRangeLeft(UxrManipulationEventArgs.FromOther(UxrManipulationEventType.AnchorRangeLeft, anchorPair.Value.LastFullGrabberNear.GrabbedObject, anchorPair.Key, anchorPair.Value.LastFullGrabberNear), true);
|
||||
}
|
||||
|
||||
if (anchorPair.Key.ActivateOnCompatibleNear)
|
||||
{
|
||||
anchorPair.Key.ActivateOnCompatibleNear.SetActive(anchorPair.Value.HasCompatibleObjectNear);
|
||||
}
|
||||
|
||||
if (anchorPair.Key.ActivateOnCompatibleNotNear)
|
||||
{
|
||||
anchorPair.Key.ActivateOnCompatibleNotNear.SetActive(!anchorPair.Value.HasCompatibleObjectNear);
|
||||
}
|
||||
|
||||
if (anchorPair.Key.ActivateOnHandNearAndGrabbable)
|
||||
{
|
||||
anchorPair.Key.ActivateOnHandNearAndGrabbable.SetActive(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anchorPair.Value.GrabberNear != anchorPair.Value.LastValidGrabberNear)
|
||||
{
|
||||
if (anchorPair.Value.GrabberNear != null)
|
||||
{
|
||||
OnPlacedObjectRangeEntered(UxrManipulationEventArgs.FromOther(UxrManipulationEventType.PlacedObjectRangeEntered, anchorPair.Key.CurrentPlacedObject, anchorPair.Key, anchorPair.Value.GrabberNear, anchorPair.Value.GrabPointNear), true);
|
||||
}
|
||||
else if (anchorPair.Value.LastValidGrabberNear != null)
|
||||
{
|
||||
OnPlacedObjectRangeLeft(UxrManipulationEventArgs.FromOther(UxrManipulationEventType.PlacedObjectRangeLeft, anchorPair.Key.CurrentPlacedObject, anchorPair.Key, anchorPair.Value.LastValidGrabberNear, anchorPair.Value.GrabPointNear), true);
|
||||
}
|
||||
|
||||
anchorPair.Value.LastValidGrabberNear = anchorPair.Value.GrabberNear;
|
||||
anchorPair.Value.LastValidGrabPointNear = anchorPair.Value.GrabPointNear;
|
||||
}
|
||||
|
||||
if (anchorPair.Key.ActivateOnHandNearAndGrabbable)
|
||||
{
|
||||
anchorPair.Key.ActivateOnHandNearAndGrabbable.SetActive(anchorPair.Value.GrabberNear != null);
|
||||
}
|
||||
|
||||
if (anchorPair.Key.ActivateOnPlaced)
|
||||
{
|
||||
anchorPair.Key.ActivateOnPlaced.SetActive(true);
|
||||
}
|
||||
|
||||
if (anchorPair.Key.ActivateOnEmpty)
|
||||
{
|
||||
anchorPair.Key.ActivateOnEmpty.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Dictionary<UxrGrabbableObject, RuntimeManipulationInfo> _currentManipulations = new Dictionary<UxrGrabbableObject, RuntimeManipulationInfo>();
|
||||
private readonly Dictionary<UxrGrabbableObjectAnchor, GrabbableObjectAnchorInfo> _grabbableObjectAnchors = new Dictionary<UxrGrabbableObjectAnchor, GrabbableObjectAnchorInfo>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25d17f0adba76654ea7135bc31b42b79
|
||||
timeCreated: 1493366419
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// What controller input we need to grab and release.
|
||||
/// </summary>
|
||||
public enum UxrGrabMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Object is grabbed while the grab button is pressed.
|
||||
/// </summary>
|
||||
GrabWhilePressed,
|
||||
|
||||
/// <summary>
|
||||
/// One click on the grab button to grab, and another click to release it.
|
||||
/// </summary>
|
||||
GrabToggle,
|
||||
|
||||
/// <summary>
|
||||
/// Object will keep being grabbed. It can be released manually through <see cref="UxrGrabbableObject.ReleaseGrabs" />.
|
||||
/// </summary>
|
||||
GrabAndKeepAlways
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4334c8f6f8d646af8dcee070b1e9d8a7
|
||||
timeCreated: 1643287323
|
||||
@@ -0,0 +1,119 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabPointIndex.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables nicer formatting of the grab point this shape is bound to in the editor. It will show strings like Main,
|
||||
/// Additional 0,
|
||||
/// Additional 1... etc. because there is an CustomPropertyDrawer for this class (see GrabPointIndexDrawer).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrGrabPointIndex
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _index;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="index">Grab point index</param>
|
||||
public UxrGrabPointIndex(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of a given grabbable object index.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="index">Grab point index</param>
|
||||
/// <returns>Display name</returns>
|
||||
public static string GetIndexDisplayName(UxrGrabbableObject grabbableObject, int index)
|
||||
{
|
||||
// If we have a custom name set up in the editor, use it
|
||||
|
||||
UxrGrabPointInfo grabPointInfo = grabbableObject.GetGrabPoint(index);
|
||||
|
||||
if (grabPointInfo != null && !string.IsNullOrEmpty(grabPointInfo.EditorName))
|
||||
{
|
||||
return grabPointInfo.EditorName;
|
||||
}
|
||||
|
||||
// Use better formatted default name
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
return MainGrabPointName;
|
||||
}
|
||||
|
||||
return AdditionalGrabPointPrefix + " " + (index - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab point index of a given display name.
|
||||
/// </summary>
|
||||
/// <param name="name">Display name to get the index for</param>
|
||||
/// <returns>Grab point index</returns>
|
||||
public static int GetIndexFromDisplayName(string name)
|
||||
{
|
||||
if (name == MainGrabPointName)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (name.StartsWith(AdditionalGrabPointPrefix))
|
||||
{
|
||||
return int.Parse(name.Remove(0, AdditionalGrabPointPrefix.Length + 1)) + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts from <see cref="UxrGrabPointIndex" /> to integer.
|
||||
/// </summary>
|
||||
/// <param name="grabPointIndex">Grab point index object</param>
|
||||
/// <returns>Integer</returns>
|
||||
public static implicit operator int(UxrGrabPointIndex grabPointIndex)
|
||||
{
|
||||
return grabPointIndex._index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from integer to <see cref="UxrGrabPointIndex" />.
|
||||
/// </summary>
|
||||
/// <param name="index">Grab point index</param>
|
||||
/// <returns>Grab point index object</returns>
|
||||
public static implicit operator UxrGrabPointIndex(int index)
|
||||
{
|
||||
return new UxrGrabPointIndex(index);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const string MainGrabPointName = "Main Grab Point";
|
||||
private const string AdditionalGrabPointPrefix = "Additional Grab Point";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f0f83509c4a40c8bc7289cc2afc553e
|
||||
timeCreated: 1647620152
|
||||
@@ -0,0 +1,410 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabPointInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Devices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a <see cref="UxrGrabbableObject" /> grab point. A grab point describes a point of an object which
|
||||
/// can be grabbed. Objects can have multiple grab points to allow it to be grabbed from different angles.
|
||||
/// Grab points can be further expanded by using a <see cref="UxrGrabPointShape" />, which gives flexibility
|
||||
/// by allowing it to be grabbed around or along an axis passing through that point, for example.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrGrabPointInfo
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _editorFoldout = true;
|
||||
[SerializeField] private string _editorName = "";
|
||||
[SerializeField] private UxrGrabMode _grabMode = UxrGrabMode.GrabWhilePressed;
|
||||
[SerializeField] private bool _useDefaultGrabButtons = true;
|
||||
[SerializeField] private bool _bothHandsCompatible = true;
|
||||
[SerializeField] private UxrHandSide _handSide = UxrHandSide.Left;
|
||||
[SerializeField] private UxrInputButtons _inputButtons = UxrInputButtons.Grip;
|
||||
[SerializeField] private bool _hideHandGrabberRenderer;
|
||||
[SerializeField] private UxrGripPoseInfo _defaultGripPoseInfo = new UxrGripPoseInfo(null);
|
||||
[SerializeField] private UxrSnapToHandMode _snapMode = UxrSnapToHandMode.PositionAndRotation;
|
||||
[SerializeField] private UxrHandSnapDirection _snapDirection = UxrHandSnapDirection.ObjectToHand;
|
||||
[SerializeField] private UxrSnapReference _snapReference = UxrSnapReference.UseOtherTransform;
|
||||
[SerializeField] private List<UxrGripPoseInfo> _avatarGripPoseEntries = new List<UxrGripPoseInfo>();
|
||||
[SerializeField] private bool _alignToController;
|
||||
[SerializeField] private Transform _alignToControllerAxes;
|
||||
[SerializeField] private UxrGrabProximityMode _grabProximityMode = UxrGrabProximityMode.UseProximity;
|
||||
[SerializeField] private BoxCollider _grabProximityBox;
|
||||
[SerializeField] private float _maxDistanceGrab = 0.2f;
|
||||
[SerializeField] private bool _grabProximityTransformUseSelf = true;
|
||||
[SerializeField] private Transform _grabProximityTransform;
|
||||
[SerializeField] private bool _grabberProximityUseDefault = true;
|
||||
[SerializeField] private int _grabberProximityIndex;
|
||||
[SerializeField] private GameObject _enableOnHandNear;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UxrGrabber" /> proximity index used to compute the distance to the object. -1 for default (the
|
||||
/// grabber itself) or any other value for additional transforms in the <see cref="UxrGrabber" /> component.
|
||||
/// </summary>
|
||||
public int GrabberProximityTransformIndex => GrabberProximityUseDefault ? -1 : GrabberProximityIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets how many grip pose entries there are. 1 (the default grip pose info) plus all the registered avatar ones.
|
||||
/// </summary>
|
||||
public int GripPoseInfoCount => 1 + _avatarGripPoseEntries.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registered avatars for specific grip poses and properties.
|
||||
/// </summary>
|
||||
public List<UxrGripPoseInfo> AvatarGripPoseEntries => _avatarGripPoseEntries;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether foldout control for a given grab point is folded out or not. We use this in the
|
||||
/// editor to check if we need to render the preview grab pose meshes for a given grab point.
|
||||
/// Grab points that are not folded out are not rendered.
|
||||
/// </summary>
|
||||
public bool IsEditorFoldedOut
|
||||
{
|
||||
get => _editorFoldout;
|
||||
set => _editorFoldout = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grab point display name in the inspector.
|
||||
/// </summary>
|
||||
public string EditorName
|
||||
{
|
||||
get => _editorName;
|
||||
set => _editorName = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grab mode.
|
||||
/// </summary>
|
||||
public UxrGrabMode GrabMode
|
||||
{
|
||||
get => _grabMode;
|
||||
set => _grabMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to use the default grab buttons to grab the object using the grab point.
|
||||
/// </summary>
|
||||
public bool UseDefaultGrabButtons
|
||||
{
|
||||
get => _useDefaultGrabButtons;
|
||||
set => _useDefaultGrabButtons = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether both hands are compatible with the grab point.
|
||||
/// </summary>
|
||||
public bool BothHandsCompatible
|
||||
{
|
||||
get => _bothHandsCompatible;
|
||||
set => _bothHandsCompatible = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="BothHandsCompatible" /> is false, tells which hand is used to grab the object using the grab point.
|
||||
/// </summary>
|
||||
public UxrHandSide HandSide
|
||||
{
|
||||
get => _handSide;
|
||||
set => _handSide = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="UseDefaultGrabButtons" /> is false, tells which buttons are used to grab the object using the grab
|
||||
/// point.
|
||||
/// </summary>
|
||||
public UxrInputButtons InputButtons
|
||||
{
|
||||
get => _inputButtons;
|
||||
set => _inputButtons = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to hide the hand while it is grabbing the object using the grab point.
|
||||
/// </summary>
|
||||
public bool HideHandGrabberRenderer
|
||||
{
|
||||
get => _hideHandGrabberRenderer;
|
||||
set => _hideHandGrabberRenderer = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default grip pose info, which is the grip pose info used when an avatar interacts with an object
|
||||
/// and is not registered to have specific properties.
|
||||
/// </summary>
|
||||
public UxrGripPoseInfo DefaultGripPoseInfo
|
||||
{
|
||||
get => _defaultGripPoseInfo;
|
||||
set => _defaultGripPoseInfo = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the object will snap to the hand when it is grabbed using the grab point.
|
||||
/// </summary>
|
||||
public UxrSnapToHandMode SnapMode
|
||||
{
|
||||
get => _snapMode;
|
||||
set => _snapMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the object will snap to the hand or the hand will snap to the object when it is grabbed using
|
||||
/// the grab point. Only used when any kind of snapping is enabled.
|
||||
/// </summary>
|
||||
public UxrHandSnapDirection SnapDirection
|
||||
{
|
||||
get => _snapDirection;
|
||||
set => _snapDirection = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which reference to use for snapping when the object is grabbed using the grab point.
|
||||
/// </summary>
|
||||
public UxrSnapReference SnapReference
|
||||
{
|
||||
get => _snapReference;
|
||||
set => _snapReference = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to align the grab to the controller axes, useful when grabbing objects that require aiming,
|
||||
/// such as weapons.
|
||||
/// </summary>
|
||||
public bool AlignToController
|
||||
{
|
||||
get => _alignToController;
|
||||
set => _alignToController = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transform in the grabbable object to use that will align to the controller axes (x = right, y =
|
||||
/// up, z = forward).
|
||||
/// </summary>
|
||||
public Transform AlignToControllerAxes
|
||||
{
|
||||
get => _alignToControllerAxes;
|
||||
set => _alignToControllerAxes = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the proximity mode to use.
|
||||
/// </summary>
|
||||
public UxrGrabProximityMode GrabProximityMode
|
||||
{
|
||||
get => _grabProximityMode;
|
||||
set => _grabProximityMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the box collider used when <see cref="GrabProximityMode" /> is
|
||||
/// <see cref="UxrGrabProximityMode.BoxConstrained" />.
|
||||
/// </summary>
|
||||
public BoxCollider GrabProximityBox
|
||||
{
|
||||
get => _grabProximityBox;
|
||||
set => _grabProximityBox = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum distance the object can be grabbed using this the grab point.
|
||||
/// </summary>
|
||||
public float MaxDistanceGrab
|
||||
{
|
||||
get => _maxDistanceGrab;
|
||||
set => _maxDistanceGrab = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to use the own <see cref="UxrGrabbableObject" /> transform when computing the distance to
|
||||
/// <see cref="UxrGrabber" /> components.
|
||||
/// </summary>
|
||||
public bool GrabProximityTransformUseSelf
|
||||
{
|
||||
get => _grabProximityTransformUseSelf;
|
||||
set => _grabProximityTransformUseSelf = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Transform" /> that will be used to compute the distance to <see cref="UxrGrabber" />
|
||||
/// components when <see cref="GrabberProximityUseDefault" /> is false.
|
||||
/// </summary>
|
||||
public Transform GrabProximityTransform
|
||||
{
|
||||
get => _grabProximityTransform;
|
||||
set => _grabProximityTransform = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to use the <see cref="UxrGrabber" /> transform when computing the distance to the grab point.
|
||||
/// Otherwise it can specify additional proximity transforms using <see cref="GrabberProximityIndex" />.
|
||||
/// </summary>
|
||||
public bool GrabberProximityUseDefault
|
||||
{
|
||||
get => _grabberProximityUseDefault;
|
||||
set => _grabberProximityUseDefault = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which additional proximity transform from <see cref="UxrGrabber" /> to use when
|
||||
/// <see cref="GrabberProximityUseDefault" /> is false.
|
||||
/// </summary>
|
||||
public int GrabberProximityIndex
|
||||
{
|
||||
get => _grabberProximityIndex;
|
||||
set => _grabberProximityIndex = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="GameObject" /> to enable or disable when the object is grabbed or not using the grab
|
||||
/// point.
|
||||
/// </summary>
|
||||
public GameObject EnableOnHandNear
|
||||
{
|
||||
get => _enableOnHandNear;
|
||||
set => _enableOnHandNear = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether to create a grip pose entry for the given avatar prefab.
|
||||
/// </summary>
|
||||
/// <param name="avatarGuid">Prefab GUID to generate a grip pose entry for</param>
|
||||
public void CheckAddGripPoseInfo(string avatarGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(avatarGuid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only add if the given avatar prefab isn't found. If any parent prefabs are also registered, the new registered prefab will prevail over the parent prefab entries.
|
||||
|
||||
if (_avatarGripPoseEntries.All(e => e.AvatarPrefabGuid != avatarGuid))
|
||||
{
|
||||
_avatarGripPoseEntries.Add(new UxrGripPoseInfo(avatarGuid));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a given grip pose info entry.
|
||||
/// </summary>
|
||||
/// <param name="i">Index to retrieve</param>
|
||||
/// <returns>Grip pose info. If the index is 0 or not valid, it will return the default grip pose info</returns>
|
||||
public UxrGripPoseInfo GetGripPoseInfo(int i)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return DefaultGripPoseInfo;
|
||||
}
|
||||
|
||||
if (i > 0 && i <= _avatarGripPoseEntries.Count)
|
||||
{
|
||||
return _avatarGripPoseEntries[i - 1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a given grip pose info entry.
|
||||
/// </summary>
|
||||
/// <param name="prefabGuid">Prefab Guid whose info to retrieve</param>
|
||||
/// <returns>Grip pose info or null if it wasn't found</returns>
|
||||
public UxrGripPoseInfo GetGripPoseInfo(string prefabGuid)
|
||||
{
|
||||
return _avatarGripPoseEntries.FirstOrDefault(i => i.AvatarPrefabGuid == prefabGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grip pose info for the given avatar instance or prefab.
|
||||
/// </summary>
|
||||
/// <param name="avatar">Avatar to get the grip pose info for</param>
|
||||
/// <param name="usePrefabInheritance">
|
||||
/// If the given avatar prefab info wasn't found, whether to look for the pose info for any prefab above the first
|
||||
/// prefab in the hierarchy. This allows child prefabs to inherit poses and manipulation settings of parent prefabs
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Grip pose info. If <see cref="usePrefabInheritance" /> is false it will return null if the given prefab wasn't
|
||||
/// found. If <see cref="usePrefabInheritance" /> is true, it will return <see cref="DefaultGripPoseInfo" /> if nor the
|
||||
/// prefab nor a parent prefab entry were found
|
||||
/// </returns>
|
||||
public UxrGripPoseInfo GetGripPoseInfo(UxrAvatar avatar, bool usePrefabInheritance = true)
|
||||
{
|
||||
foreach (string avatarPrefabGuid in avatar.GetPrefabGuidChain())
|
||||
{
|
||||
foreach (UxrGripPoseInfo gripPoseInfo in _avatarGripPoseEntries)
|
||||
{
|
||||
if (gripPoseInfo.AvatarPrefabGuid == avatarPrefabGuid)
|
||||
{
|
||||
return gripPoseInfo;
|
||||
}
|
||||
}
|
||||
|
||||
if (!usePrefabInheritance)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultGripPoseInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the grip pose infos that can be used with the given avatar.
|
||||
/// </summary>
|
||||
/// <param name="avatar">The avatar to check</param>
|
||||
/// <param name="usePrefabInheritance">Whether to check for compatibility using all the parents in the prefab hierarchy</param>
|
||||
/// <returns>List of <see cref="UxrGripPoseInfo" /> that are potentially compatible with the given avatar</returns>
|
||||
public IEnumerable<UxrGripPoseInfo> GetCompatibleGripPoseInfos(UxrAvatar avatar, bool usePrefabInheritance = true)
|
||||
{
|
||||
foreach (string avatarPrefabGuid in avatar.GetPrefabGuidChain())
|
||||
{
|
||||
foreach (UxrGripPoseInfo gripPoseInfo in _avatarGripPoseEntries)
|
||||
{
|
||||
if (gripPoseInfo.AvatarPrefabGuid == avatarPrefabGuid)
|
||||
{
|
||||
yield return gripPoseInfo;
|
||||
}
|
||||
}
|
||||
|
||||
if (!usePrefabInheritance)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the grip pose entry of a given avatar prefab.
|
||||
/// </summary>
|
||||
/// <param name="avatarPrefabGuid">Prefab GUID whose information to remove</param>
|
||||
public void RemoveGripPoseInfo(string avatarPrefabGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(avatarPrefabGuid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_avatarGripPoseEntries.RemoveAll(e => e.AvatarPrefabGuid == avatarPrefabGuid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d63d8d35d6f4c2dbbd7deea167440b0
|
||||
timeCreated: 1643287822
|
||||
@@ -0,0 +1,77 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabPointShape.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class to create more advanced grips (cylindrical, box...).
|
||||
/// An <see cref="UxrGrabbableObject" /> enables grabbing an object. A grabPoint inside the
|
||||
/// <see cref="UxrGrabbableObject" /> defines where and how the object will snap to the hand. Additionally, if there is
|
||||
/// an <see cref="UxrGrabPointShape" /> based component on the same object, it will "expand" the snapping from a
|
||||
/// single point to a more complex shape like a an axis, a cylinder, a box... This way an object can be picked up from
|
||||
/// many different places just by specifying a snap point and some additional properties.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrGrabbableObject))]
|
||||
public abstract class UxrGrabPointShape : UxrComponent<UxrGrabbableObject, UxrGrabPointShape>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] protected UxrGrabPointIndex _grabPointIndex = new UxrGrabPointIndex(0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab point from the <see cref="UxrGrabbableObject" /> this object extends.
|
||||
/// </summary>
|
||||
public UxrGrabPointIndex GrabPoint => _grabPointIndex;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance from a <see cref="UxrGrabber" /> to a grab point, defined by transform used for snapping and the
|
||||
/// transform used to compute proximity.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to compute the distance from</param>
|
||||
/// <param name="snapTransform">The <see cref="Transform" /> on the grabbable object that is used to align to the grabber</param>
|
||||
/// <param name="objectDistanceTransform">
|
||||
/// The <see cref="Transform" /> on the grabbable object that is used to compute the
|
||||
/// distance to the <see cref="UxrGrabber" />
|
||||
/// </param>
|
||||
/// <param name="grabberDistanceTransform">
|
||||
/// The <see cref="Transform" /> on the grabber that is used to compute the distance
|
||||
/// to the <see cref="UxrGrabbableObject" />
|
||||
/// </param>
|
||||
/// <returns>Distance value</returns>
|
||||
public abstract float GetDistanceFromGrabber(UxrGrabber grabber, Transform snapTransform, Transform objectDistanceTransform, Transform grabberDistanceTransform);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest snap position and rotation that should be used when a <see cref="UxrGrabber" /> tries to a grab
|
||||
/// point, defined by transform used for snapping and the transform used to compute proximity.
|
||||
/// </summary>
|
||||
/// <param name="grabber">Grabber to compute the snapping for</param>
|
||||
/// <param name="snapTransform">The <see cref="Transform" /> on the grabbable object that is used to align to the grabber</param>
|
||||
/// <param name="distanceTransform">
|
||||
/// The <see cref="Transform" /> on the grabbable object that is used to compute the
|
||||
/// distance to the grabber
|
||||
/// </param>
|
||||
/// <param name="grabberDistanceTransform">
|
||||
/// The <see cref="Transform" /> on the grabber that is used to compute the distance
|
||||
/// to the <see cref="UxrGrabbableObject" />
|
||||
/// </param>
|
||||
/// <param name="position">Snap position</param>
|
||||
/// <param name="rotation">Snap rotation</param>
|
||||
/// <returns>Distance value</returns>
|
||||
public abstract void GetClosestSnap(UxrGrabber grabber, Transform snapTransform, Transform distanceTransform, Transform grabberDistanceTransform, out Vector3 position, out Quaternion rotation);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53bb82318421f864ba18e07e56c095b7
|
||||
timeCreated: 1538560526
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,217 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabPointShapeAxisAngle.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 414 // Disable warnings due to unused values
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Grab shape used to grab cylindrical objects. The cylinder is described by an axis and a length. It is possible to
|
||||
/// specify if the object can be grabbed in both directions or a direction only.
|
||||
/// </summary>
|
||||
public class UxrGrabPointShapeAxisAngle : UxrGrabPointShape
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Transform _center;
|
||||
[SerializeField] private UxrAxis _centerAxis = UxrAxis.Z;
|
||||
[SerializeField] private bool _bidirectional;
|
||||
[SerializeField] private float _angleMin = -180.0f;
|
||||
[SerializeField] private float _angleMax = 180.0f;
|
||||
[SerializeField] private float _angleInterval = 0.01f;
|
||||
[SerializeField] private float _offsetMin = -0.1f;
|
||||
[SerializeField] private float _offsetMax = 0.1f;
|
||||
[SerializeField] private float _offsetInterval = 0.001f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the axis center.
|
||||
/// </summary>
|
||||
public Transform Center => _center != null ? _center : transform;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the axis around which the grab can rotate.
|
||||
/// </summary>
|
||||
public UxrAxis CenterAxis
|
||||
{
|
||||
get => _centerAxis;
|
||||
set => _centerAxis = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum angle the grip, defined by the <see cref="UxrGrabbableObject" />, can rotate around
|
||||
/// <see cref="CenterAxis" />.
|
||||
/// </summary>
|
||||
public float AngleMin
|
||||
{
|
||||
get => _angleMin;
|
||||
set => _angleMin = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum angle the grip, defined by the <see cref="UxrGrabbableObject" />, can rotate around
|
||||
/// <see cref="CenterAxis" />.
|
||||
/// </summary>
|
||||
public float AngleMax
|
||||
{
|
||||
get => _angleMax;
|
||||
set => _angleMax = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discrete angle interval steps the grip can rotate around <see cref="CenterAxis" />.
|
||||
/// </summary>
|
||||
public float AngleInterval
|
||||
{
|
||||
get => _angleInterval;
|
||||
set => _angleInterval = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum offset from the center, and along <see cref="CenterAxis" />, the grip can move.
|
||||
/// </summary>
|
||||
public float OffsetMin
|
||||
{
|
||||
get => _offsetMin;
|
||||
set => _offsetMin = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum offset from the center, and along <see cref="CenterAxis" />, the grip can move.
|
||||
/// </summary>
|
||||
public float OffsetMax
|
||||
{
|
||||
get => _offsetMax;
|
||||
set => _offsetMax = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discrete offset steps along along <see cref="CenterAxis" /> the grip can move.
|
||||
/// </summary>
|
||||
public float OffsetInterval
|
||||
{
|
||||
get => _offsetInterval;
|
||||
set => _offsetInterval = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides UxrGrabPointShape
|
||||
|
||||
/// <inheritdoc />
|
||||
public override float GetDistanceFromGrabber(UxrGrabber grabber, Transform snapTransform, Transform objectDistanceTransform, Transform grabberDistanceTransform)
|
||||
{
|
||||
// TODO: Consider rotation difference
|
||||
return grabberDistanceTransform.position.DistanceToSegment(GetSegmentA(objectDistanceTransform.position), GetSegmentB(objectDistanceTransform.position));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetClosestSnap(UxrGrabber grabber, Transform snapTransform, Transform distanceTransform, Transform grabberDistanceTransform, out Vector3 position, out Quaternion rotation)
|
||||
{
|
||||
// Compute best fitting rotation
|
||||
|
||||
Vector3 worldAxis = Center.TransformDirection(CenterAxis);
|
||||
Vector3 localSnapAxis = snapTransform.InverseTransformDirection(worldAxis);
|
||||
Vector3 worldGrabberAxis = grabber.transform.TransformDirection(localSnapAxis);
|
||||
bool reverseGrip = _bidirectional && Vector3.Angle(worldGrabberAxis, -worldAxis) < Vector3.Angle(worldGrabberAxis, worldAxis);
|
||||
|
||||
// worldGrabberAxis contains the axis in world coordinates if it was being grabbed with the current grabber orientation
|
||||
// projection contains the rotation that the grabber would need to rotate to align to the axis using the closest angle
|
||||
|
||||
Quaternion projection = Quaternion.FromToRotation(worldGrabberAxis, reverseGrip ? -worldAxis : worldAxis);
|
||||
/*
|
||||
if (reverseGrip)
|
||||
{
|
||||
Vector3 right = projection * Vector3.right;
|
||||
Vector3 up = projection * Vector3.up;
|
||||
Vector3 forward = projection * Vector3.forward;
|
||||
}*/
|
||||
|
||||
// Compute the rotation required to rotate the grabber to the best suited grip on the axis with the given properties
|
||||
|
||||
rotation = projection * grabber.transform.rotation;
|
||||
|
||||
// Compute perpendicular vectors to the axis to get the angle from snap rotation to projected snap rotation.
|
||||
|
||||
Vector3 worldPerpendicular = Center.TransformDirection(CenterAxis.Perpendicular);
|
||||
Vector3 localPerpendicular = snapTransform.InverseTransformDirection(worldPerpendicular);
|
||||
|
||||
Quaternion grabberRotation = grabber.transform.rotation;
|
||||
grabber.transform.rotation = rotation;
|
||||
|
||||
// Compute angle and clamp it.
|
||||
|
||||
float angle = Vector3.SignedAngle(worldPerpendicular, grabber.transform.TransformDirection(localPerpendicular), worldAxis);
|
||||
float clampedAngle = Mathf.Clamp(angle, AngleMin, AngleMax);
|
||||
rotation = Quaternion.AngleAxis(clampedAngle - angle, worldAxis) * rotation;
|
||||
|
||||
// TODO: use _angleInterval
|
||||
grabber.transform.rotation = grabberRotation;
|
||||
|
||||
// Compute grabber position by rotating the snap position around the axis
|
||||
|
||||
Vector3 projectedSnap = snapTransform.position.ProjectOnLine(Center.position, worldAxis);
|
||||
Vector3 fromAxisToSnap = snapTransform.position - projectedSnap;
|
||||
Vector3 grabberPos = grabber.transform.position;
|
||||
|
||||
if (reverseGrip)
|
||||
{
|
||||
fromAxisToSnap = Quaternion.AngleAxis(180.0f, worldPerpendicular) * fromAxisToSnap;
|
||||
}
|
||||
|
||||
position = grabberPos.ProjectOnSegment(GetSegmentA(projectedSnap), GetSegmentB(projectedSnap)) + fromAxisToSnap.GetRotationAround(worldAxis, clampedAngle);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object is selected, to draw the gizmos in the scene window.
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
UxrGrabbableObject grabbableObject = GetComponent<UxrGrabbableObject>();
|
||||
|
||||
if (_grabPointIndex >= 0 && _grabPointIndex < grabbableObject.GrabPointCount)
|
||||
{
|
||||
Gizmos.DrawLine(GetSegmentA(transform.position), GetSegmentB(transform.position));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets one side of the grabbable segment in world space if it started in <paramref name="center" />.
|
||||
/// </summary>
|
||||
/// <param name="center">Center in world space to consider</param>
|
||||
private Vector3 GetSegmentA(Vector3 center)
|
||||
{
|
||||
return center + Center.TransformDirection(CenterAxis) * OffsetMin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other side of the grabbable segment in world space if it started in <paramref name="center" />.
|
||||
/// </summary>
|
||||
/// <param name="center">Center in world space to consider</param>
|
||||
private Vector3 GetSegmentB(Vector3 center)
|
||||
{
|
||||
return center + Center.TransformDirection(CenterAxis) * OffsetMax;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 414
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f163104d7494aa8478b336859f93866b
|
||||
timeCreated: 1538560526
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabProximityMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates how the distance from a <see cref="UxrGrabber" /> to a <see cref="UxrGrabbableObject" /> can be
|
||||
/// computed.
|
||||
/// </summary>
|
||||
public enum UxrGrabProximityMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the proximity transform in the <see cref="UxrGrabber" />.
|
||||
/// </summary>
|
||||
UseProximity,
|
||||
|
||||
/// <summary>
|
||||
/// Use a <see cref="BoxCollider" /> inside which the <see cref="UxrGrabber" /> needs to be in order to grab the
|
||||
/// object.
|
||||
/// </summary>
|
||||
BoxConstrained
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bf3239359e64939a0569b1e7239fd17
|
||||
timeCreated: 1643287251
|
||||
@@ -0,0 +1,29 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableModifierFlags.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags that represent parts in an <see cref="UxrGrabbableObject" /> that can be modified/hidden by components in the
|
||||
/// same <see cref="GameObject" /> that implement the <see cref="IUxrGrabbableModifier" /> interface.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrGrabbableModifierFlags
|
||||
{
|
||||
None = 0,
|
||||
DummyParentGrabbable = 1 << 0,
|
||||
ParentControl = 1 << 1,
|
||||
Priority = 1 << 2,
|
||||
MultiGrab = 1 << 3,
|
||||
TranslationConstraint = 1 << 8,
|
||||
RotationConstraint = 1 << 12,
|
||||
TranslationResistance = 1 << 16,
|
||||
RotationResistance = 1 << 17,
|
||||
Anchored = 1 << 18
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ce8d8e6c71c72a47afaa54519830af0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObject.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabbableObject
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override UxrTransformSpace TransformStateSaveSpace => GetLocalTransformIfParentedOr(UxrTransformSpace.Local);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool RequiresTransformSerialization(UxrStateSaveLevel level)
|
||||
{
|
||||
// Save always
|
||||
return level >= UxrStateSaveLevel.ChangesSincePreviousSave;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
if (level <= UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
// All the variables below are not needed in incremental snapshots.
|
||||
// In a replay system, these variables will be updated by events.
|
||||
return;
|
||||
}
|
||||
|
||||
// Backing fields for public properties
|
||||
|
||||
SerializeStateValue(level, options, nameof(_currentAnchor), ref _currentAnchor);
|
||||
SerializeStateValue(level, options, nameof(_isPlaceable), ref _isPlaceable);
|
||||
SerializeStateValue(level, options, nameof(_isLockedInPlace), ref _isLockedInPlace);
|
||||
SerializeStateValue(level, options, nameof(_priority), ref _priority);
|
||||
SerializeStateValue(level, options, nameof(_useParenting), ref _useParenting);
|
||||
SerializeStateValue(level, options, nameof(_tag), ref _tag);
|
||||
SerializeStateValue(level, options, nameof(_translationConstraintMode), ref _translationConstraintMode);
|
||||
SerializeStateValue(level, options, nameof(_translationLimitsMin), ref _translationLimitsMin);
|
||||
SerializeStateValue(level, options, nameof(_translationLimitsMax), ref _translationLimitsMax);
|
||||
SerializeStateValue(level, options, nameof(_rotationConstraintMode), ref _rotationConstraintMode);
|
||||
SerializeStateValue(level, options, nameof(_rotationAngleLimitsMin), ref _rotationAngleLimitsMin);
|
||||
SerializeStateValue(level, options, nameof(_rotationAngleLimitsMax), ref _rotationAngleLimitsMax);
|
||||
SerializeStateValue(level, options, nameof(_translationResistance), ref _translationResistance);
|
||||
SerializeStateValue(level, options, nameof(_rotationResistance), ref _rotationResistance);
|
||||
SerializeStateValue(level, options, nameof(_dropSnapMode), ref _dropSnapMode);
|
||||
|
||||
// Backing fields for internal properties
|
||||
|
||||
SerializeStateValue(level, options, nameof(_singleRotationAngleCumulative), ref _singleRotationAngleCumulative);
|
||||
SerializeStateValue(level, options, nameof(_placementOptions), ref _placementOptions);
|
||||
|
||||
// Backing fields for IUxrGrabbable interface
|
||||
|
||||
SerializeStateValue(level, options, nameof(_isGrabbable), ref _isGrabbable);
|
||||
|
||||
bool isKinematic = _rigidBodySource && _rigidBodySource.isKinematic;
|
||||
bool isSleeping = _rigidBodySource && _rigidBodySource.IsSleeping();
|
||||
|
||||
if (!isReading && options.HasFlag(UxrStateSaveOptions.FirstFrame))
|
||||
{
|
||||
// To avoid changes, because IsSleeping() will return false the first frame. We force it to be sleeping the first frame.
|
||||
isSleeping = true;
|
||||
}
|
||||
|
||||
SerializeStateValue(level, options, nameof(isKinematic), ref isKinematic);
|
||||
SerializeStateValue(level, options, nameof(isSleeping), ref isSleeping);
|
||||
|
||||
if (isReading && _rigidBodySource)
|
||||
{
|
||||
_rigidBodySource.isKinematic = isKinematic;
|
||||
|
||||
if (isSleeping)
|
||||
{
|
||||
_rigidBodySource.Sleep();
|
||||
}
|
||||
}
|
||||
|
||||
if (isReading)
|
||||
{
|
||||
LocalPositionBeforeUpdate = transform.localPosition;
|
||||
LocalRotationBeforeUpdate = transform.localRotation;
|
||||
}
|
||||
|
||||
// Private vars
|
||||
|
||||
SerializeStateValue(level, options, nameof(_grabPointEnabledStates), ref _grabPointEnabledStates);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40079bae94fc4122b55073ddcc7d1ab1
|
||||
timeCreated: 1705483727
|
||||
2662
Assets/UltimateXR/Runtime/Scripts/Manipulation/UxrGrabbableObject.cs
Normal file
2662
Assets/UltimateXR/Runtime/Scripts/Manipulation/UxrGrabbableObject.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79f5a30897a3c024c805c866226dd743
|
||||
timeCreated: 1492963941
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObjectAnchor.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabbableObjectAnchor
|
||||
{
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Manipulations are already handled through events, we don't serialize them in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_currentPlacedObject), ref _currentPlacedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d47f4c201c93468da2061e15ce8d6263
|
||||
timeCreated: 1706169124
|
||||
@@ -0,0 +1,422 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObjectAnchor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that, added to a <see cref="GameObject" />, will enable <see cref="UxrGrabbableObject" /> objects to be
|
||||
/// placed on it.
|
||||
/// Some of the main features of grabbable object anchors are:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// Placement mechanics are handled automatically by the <see cref="UxrGrabManager" />. There is no special
|
||||
/// requirement to set it up in a scene, the grab manager will be available as soon as it is required.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Compatible tags allow to model which objects can be placed on the anchor. If the list is empty, the
|
||||
/// anchor is compatible with all other <see cref="UxrGrabbableObject" /> that do not have a tag.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Events such as <see cref="Placed" /> and <see cref="Removed" /> allow to write logic when a user interacts
|
||||
/// with the anchor. Each one has pre and post events.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="ActivateOnCompatibleNear" />, <see cref="ActivateOnCompatibleNotNear" />,
|
||||
/// <see cref="ActivateOnHandNearAndGrabbable" />, <see cref="ActivateOnPlaced" /> and
|
||||
/// <see cref="ActivateOnEmpty" /> can be used to activate/deactivate objects on manipulation events. They can
|
||||
/// be assigned during edit-time using the inspector and also at runtime.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="AddPlacingValidator" /> and <see cref="RemovePlacingValidator" /> can be used to model complex
|
||||
/// compatibility behaviour that changes at runtime.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public partial class UxrGrabbableObjectAnchor : UxrComponent<UxrGrabbableObjectAnchor>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private List<string> _compatibleTags = new List<string>();
|
||||
[SerializeField] private float _maxPlaceDistance = 0.1f;
|
||||
[SerializeField] private bool _alignTransformUseSelf = true;
|
||||
[SerializeField] private Transform _alignTransform;
|
||||
[SerializeField] private bool _dropProximityTransformUseSelf = true;
|
||||
[SerializeField] private Transform _dropProximityTransform;
|
||||
[SerializeField] private GameObject _activateOnCompatibleNear;
|
||||
[SerializeField] private GameObject _activateOnCompatibleNotNear;
|
||||
[SerializeField] private GameObject _activateOnHandNearAndGrabbable;
|
||||
[SerializeField] private GameObject _activateOnPlaced;
|
||||
[SerializeField] private GameObject _activateOnEmpty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called right before an object is placed on the anchor.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Placing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after an object was placed on the anchor.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Placed;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right before the currently placed object is removed from the anchor.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Removing;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the currently placed object is removed from the anchor.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> Removed;
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after an object that was placed on the anchor ended its smooth placing transition.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrManipulationEventArgs> SmoothPlaceTransitionEnded;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Transform" /> that will be used to snap the <see cref="UxrGrabbableObject" /> placed on it.
|
||||
/// </summary>
|
||||
public Transform AlignTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_alignTransform == null || _alignTransformUseSelf)
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
|
||||
return _alignTransform;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Transform" /> that will be used to compute the distance to <see cref="UxrGrabbableObject" />
|
||||
/// that can be placed on it, in order to determine if they are close enough.
|
||||
/// </summary>
|
||||
public Transform DropProximityTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dropProximityTransform == null || _dropProximityTransformUseSelf)
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
|
||||
return _dropProximityTransform;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UxrGrabbableObject" /> that is currently placed on the anchor.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject CurrentPlacedObject
|
||||
{
|
||||
get => _currentPlacedObject;
|
||||
internal set => _currentPlacedObject = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum distance from which an object that is released will be placed on the anchor.
|
||||
/// </summary>
|
||||
public float MaxPlaceDistance
|
||||
{
|
||||
get => _maxPlaceDistance;
|
||||
set => _maxPlaceDistance = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object that will be enabled or disabled depending on if there is a grabbed compatible
|
||||
/// <see cref="UxrGrabbableObject" /> close enough to be placed on it.
|
||||
/// </summary>
|
||||
public GameObject ActivateOnCompatibleNear
|
||||
{
|
||||
get => _activateOnCompatibleNear;
|
||||
set => _activateOnCompatibleNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object that will be enabled or disabled depending on if there isn't a grabbed compatible
|
||||
/// <see cref="UxrGrabbableObject" /> close enough to be placed on it.
|
||||
/// </summary>
|
||||
public GameObject ActivateOnCompatibleNotNear
|
||||
{
|
||||
get => _activateOnCompatibleNotNear;
|
||||
set => _activateOnCompatibleNotNear = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object that will be enabled or disabled depending on if there is a
|
||||
/// <see cref="UxrGrabbableObject" /> currently placed on the anchor and a <see cref="UxrGrabber" /> close enough to
|
||||
/// grab it.
|
||||
/// </summary>
|
||||
public GameObject ActivateOnHandNearAndGrabbable
|
||||
{
|
||||
get => _activateOnHandNearAndGrabbable;
|
||||
set => _activateOnHandNearAndGrabbable = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object that will be enabled or disabled depending on if there is a
|
||||
/// <see cref="UxrGrabbableObject" /> currently placed on the anchor.
|
||||
/// </summary>
|
||||
public GameObject ActivateOnPlaced
|
||||
{
|
||||
get => _activateOnPlaced;
|
||||
set => _activateOnPlaced = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object that will be enabled or disabled depending on if there isn't a
|
||||
/// <see cref="UxrGrabbableObject" /> currently placed on the anchor.
|
||||
/// </summary>
|
||||
public GameObject ActivateOnEmpty
|
||||
{
|
||||
get => _activateOnEmpty;
|
||||
set => _activateOnEmpty = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds compatible tags to the list of compatible tags that control which objects can be placed on the anchor.
|
||||
/// </summary>
|
||||
/// <param name="tags">Tags to add</param>
|
||||
/// <exception cref="ArgumentNullException">tags is null</exception>
|
||||
public void AddCompatibleTags(params string[] tags)
|
||||
{
|
||||
if (tags == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tags));
|
||||
}
|
||||
|
||||
_compatibleTags.AddRange(tags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes compatible tags from the list of compatible tags that control which objects can be placed on the anchor.
|
||||
/// </summary>
|
||||
/// <param name="tags">Tags to remove</param>
|
||||
/// <exception cref="ArgumentNullException">tags is null</exception>
|
||||
public void RemoveCompatibleTags(params string[] tags)
|
||||
{
|
||||
if (tags == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tags));
|
||||
}
|
||||
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
_compatibleTags.Remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the currently placed object, if there is any, from the anchor.
|
||||
/// </summary>
|
||||
/// <param name="propagateEvents">Whether the call should generate any events or not</param>
|
||||
public void RemoveObject(bool propagateEvents)
|
||||
{
|
||||
if (CurrentPlacedObject != null)
|
||||
{
|
||||
UxrGrabManager.Instance.RemoveObjectFromAnchor(CurrentPlacedObject, propagateEvents);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a placing validator to the internal list of validators. Placing validators are functions that are used in
|
||||
/// addition to compatibility tags in order to determine if a <see cref="UxrGrabbableObject" /> can be placed on the
|
||||
/// anchor.
|
||||
/// An object can be placed on an anchor if the tag is compatible and if it is allowed by all of the placing
|
||||
/// validators.
|
||||
/// </summary>
|
||||
/// <param name="validator">
|
||||
/// New placing validator function to add. It takes an <see cref="UxrGrabbableObject" /> as input
|
||||
/// and returns a boolean telling whether it can be placed or not
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">The validator function is null</exception>
|
||||
public void AddPlacingValidator(Func<UxrGrabbableObject, bool> validator)
|
||||
{
|
||||
if (validator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validator));
|
||||
}
|
||||
|
||||
_placingValidators.Add(validator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a placing validator added using <see cref="AddPlacingValidator" />.
|
||||
/// </summary>
|
||||
/// <param name="validator">Validator to remove</param>
|
||||
/// <exception cref="ArgumentNullException">the validator function is null</exception>
|
||||
public void RemovePlacingValidator(Func<UxrGrabbableObject, bool> validator)
|
||||
{
|
||||
if (validator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validator));
|
||||
}
|
||||
|
||||
_placingValidators.Remove(validator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given <see cref="UxrGrabbableObject" /> is compatible with the anchor, which means that it can
|
||||
/// potentially be placed on it if there is no other object placed.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Object to check</param>
|
||||
/// <returns>Whether the object is compatible with the anchor</returns>
|
||||
public bool IsCompatibleObject(UxrGrabbableObject grabbableObject)
|
||||
{
|
||||
return grabbableObject != null && _placingValidators.All(v => v(grabbableObject)) && IsCompatibleObjectTag(grabbableObject.Tag);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
UxrGrabManager.Instance.Poke();
|
||||
|
||||
if (_activateOnCompatibleNear != null)
|
||||
{
|
||||
_activateOnCompatibleNear.SetActive(false);
|
||||
}
|
||||
|
||||
if (_activateOnCompatibleNotNear != null)
|
||||
{
|
||||
_activateOnCompatibleNotNear.SetActive(false);
|
||||
}
|
||||
|
||||
if (_activateOnHandNearAndGrabbable != null)
|
||||
{
|
||||
_activateOnHandNearAndGrabbable.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the validators.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
_placingValidators.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs additional initialization.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (_activateOnPlaced != null)
|
||||
{
|
||||
_activateOnPlaced.SetActive(CurrentPlacedObject != null);
|
||||
}
|
||||
|
||||
if (_activateOnEmpty != null)
|
||||
{
|
||||
_activateOnEmpty.SetActive(CurrentPlacedObject == null);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="Placing" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaisePlacingEvent(UxrManipulationEventArgs e)
|
||||
{
|
||||
Placing?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="Placed" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaisePlacedEvent(UxrManipulationEventArgs e)
|
||||
{
|
||||
Placed?.Invoke(this, e);
|
||||
_smoothPlaceEventArgs = e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="Removing" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseRemovingEvent(UxrManipulationEventArgs e)
|
||||
{
|
||||
Removing?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="Removed" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseRemovedEvent(UxrManipulationEventArgs e)
|
||||
{
|
||||
Removed?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="SmoothPlaceTransitionEnded" />.
|
||||
/// </summary>
|
||||
internal void RaiseSmoothTransitionPlaceEnded()
|
||||
{
|
||||
SmoothPlaceTransitionEnded?.Invoke(this, _smoothPlaceEventArgs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checking whether the given tag is compatible with the anchor.
|
||||
/// </summary>
|
||||
/// <param name="otherTag">Tag to check whether it is compatible</param>
|
||||
/// <returns>Whether the tag is compatible</returns>
|
||||
private bool IsCompatibleObjectTag(string otherTag)
|
||||
{
|
||||
if (_compatibleTags == null || _compatibleTags.Count == 0)
|
||||
{
|
||||
return string.IsNullOrEmpty(otherTag);
|
||||
}
|
||||
|
||||
return _compatibleTags.Contains(otherTag);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private readonly List<Func<UxrGrabbableObject, bool>> _placingValidators = new List<Func<UxrGrabbableObject, bool>>();
|
||||
private UxrManipulationEventArgs _smoothPlaceEventArgs;
|
||||
private UxrGrabbableObject _currentPlacedObject;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7b06a52e9ced0e46a369c9a6e98a92c
|
||||
timeCreated: 1492963957
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObjectPreviewMesh.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Component used internally by the editor. They are added to keep track of grab pose preview meshes and delete them
|
||||
/// when the preview is no longer needed.
|
||||
/// These work together with <see cref="UxrGrabbableObjectPreviewMeshProxy" /> to avoid non-uniform scaling problems
|
||||
/// when previewing grab poses in UxrGrabbableObject hierarchies.
|
||||
/// </summary>
|
||||
public class UxrGrabbableObjectPreviewMesh : UxrComponent
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
public MeshFilter MeshFilterComponent => PreviewMeshProxy.MeshFilterComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preview mesh object used by the editor (editor type UxrPreviewHandGripMesh).
|
||||
/// </summary>
|
||||
public object PreviewMesh
|
||||
{
|
||||
get => PreviewMeshProxy.PreviewMesh;
|
||||
set => PreviewMeshProxy.PreviewMesh = value;
|
||||
}
|
||||
|
||||
public UxrGrabbableObjectPreviewMeshProxy PreviewMeshProxy { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 892bad91ee54a3645a13bf83f4bf9d2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObjectPreviewMeshProxy.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Component used internally by the editor. They are added to keep track of grab pose preview meshes and
|
||||
/// delete them when the preview is no longer needed.
|
||||
/// <see cref="UxrGrabbableObjectPreviewMesh" /> components will be hidden hanging from each
|
||||
/// <see cref="UxrGrabbableObjectSnapTransform" />. They could have the mesh themselves but when dealing with
|
||||
/// non-uniform scaling in grabbable hierarchies, the preview mesh would be distorted when hanging directly.
|
||||
/// Instead, each <see cref="UxrGrabbableObjectPreviewMesh" /> component will additionally point to a hidden
|
||||
/// root GameObject with a <see cref="UxrGrabbableObjectPreviewMeshProxy" /> that will have the mesh data.
|
||||
/// Being a root GameObject will avoid non-uniform scaling problems.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
public class UxrGrabbableObjectPreviewMeshProxy : UxrComponent
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mesh filter.
|
||||
/// </summary>
|
||||
public MeshFilter MeshFilterComponent => GetCachedComponent<MeshFilter>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preview mesh component that the proxy is following.
|
||||
/// </summary>
|
||||
public UxrGrabbableObjectPreviewMesh PreviewMeshComponent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preview mesh object used by the editor (editor type UxrPreviewHandGripMesh).
|
||||
/// </summary>
|
||||
public object PreviewMesh { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure to hide the GameObject initially during play mode when working from the editor.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (Application.isEditor && Application.isPlaying)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Follow the source object and monitors deletion.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (PreviewMeshComponent == null)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.SetPositionAndRotation(PreviewMeshComponent.transform.position, PreviewMeshComponent.transform.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private MeshFilter _meshFilterComponent;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18483e5bbc7fc314e9f60f6d48d4c2f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabbableObjectSnapTransform.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Component used internally by the editor. It renders grab pose meshes in the Scene Window when a grab point's snap
|
||||
/// transform is selected and moved/rotated.
|
||||
/// It also allows to modify the blend factor in blend poses and gives access to some handy repositioning tools.
|
||||
/// </summary>
|
||||
public class UxrGrabbableObjectSnapTransform : UxrComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 212ce82ef45e9684daf388b699410086
|
||||
timeCreated: 1492963941
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,112 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabber.PhysicsSample.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabber
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores physics data of a frame to perform smooth throw computations.
|
||||
/// </summary>
|
||||
private class PhysicsSample
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampled center of mass world position.
|
||||
/// </summary>
|
||||
public Vector3 CenterOfMass { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampled finger tip world position.
|
||||
/// </summary>
|
||||
public Vector3 Tip { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampled rotation.
|
||||
/// </summary>
|
||||
public Quaternion Rotation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampled angular speed in Euler angles.
|
||||
/// </summary>
|
||||
public Vector3 EulerSpeed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampled linear velocity in world space.
|
||||
/// </summary>
|
||||
public Vector3 Velocity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampled total velocity in world space. It is the result of combining the linear velocity plus the velocity
|
||||
/// due to the throw axis angular speed. This should be used to compute throw release velocity.
|
||||
/// </summary>
|
||||
public Vector3 TotalVelocity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the elapsed time in seconds with respect to the previous sample.
|
||||
/// </summary>
|
||||
public float DeltaTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sample age in seconds.
|
||||
/// </summary>
|
||||
public float Age { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="lastSample">Last frame data, to compute velocities</param>
|
||||
/// <param name="sampledTransform">Transform, from the grabbed object if there is one currently being grabbed, otherwise from the grabber</param>
|
||||
/// <param name="centerOfMass">World position of the throwing center of mass</param>
|
||||
/// <param name="tip">World position of the finger tip approximation, to account for angular velocity in the throw</param>
|
||||
/// <param name="deltaTime">Time in seconds since last frame</param>
|
||||
public PhysicsSample(PhysicsSample lastSample, Transform sampledTransform, Vector3 centerOfMass, Vector3 tip, float deltaTime)
|
||||
{
|
||||
Age = 0.0f;
|
||||
Rotation = sampledTransform.rotation;
|
||||
DeltaTime = deltaTime;
|
||||
CenterOfMass = centerOfMass;
|
||||
Tip = tip;
|
||||
|
||||
if (lastSample != null)
|
||||
{
|
||||
// Angular
|
||||
|
||||
Vector3 v1 = lastSample.Tip - lastSample.CenterOfMass;
|
||||
Vector3 v2 = tip - centerOfMass;
|
||||
Vector3 rotationAxis = Vector3.Cross(v2, v1);
|
||||
|
||||
Quaternion relative = Quaternion.Inverse(lastSample.Rotation) * sampledTransform.rotation;
|
||||
relative.ToAngleAxis(out float angle, out Vector3 axis);
|
||||
EulerSpeed = (angle * sampledTransform.TransformDirection(axis)) / deltaTime;
|
||||
|
||||
// Linear. TODO: Improve using a mix of linear and angular components?
|
||||
|
||||
Velocity = ((tip - lastSample.Tip) / deltaTime);
|
||||
TotalVelocity = Velocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
EulerSpeed = Vector3.zero;
|
||||
Velocity = Vector3.zero;
|
||||
TotalVelocity = Velocity;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ffa327fd18544c3a2a9885c574182b7
|
||||
timeCreated: 1656494341
|
||||
@@ -0,0 +1,29 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabber.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
public partial class UxrGrabber
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Manipulations are already handled through events, we don't serialize them in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_grabbedObject), ref _grabbedObject);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5dd692b0dfad41d689221fe0ae33564e
|
||||
timeCreated: 1705937331
|
||||
529
Assets/UltimateXR/Runtime/Scripts/Manipulation/UxrGrabber.cs
Normal file
529
Assets/UltimateXR/Runtime/Scripts/Manipulation/UxrGrabber.cs
Normal file
@@ -0,0 +1,529 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrabber.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Avatar.Rig;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Component that added to an <see cref="UxrAvatar" /> allows to interact with <see cref="UxrGrabbableObject" />
|
||||
/// entities. Normally there are two per avatar, one on each hand. They are usually added to the hand object since
|
||||
/// it is the <see cref="UxrGrabber" /> transform where grabbable objects will be snapped to when snapping is used.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// By default, the grabber transform is also used to compute distances to grabbable objects. Additional proximity
|
||||
/// transforms can be specified on the grabber so that grabbable objects can choose which one is used. This can be
|
||||
/// useful in some scenarios: In an aircraft cockpit most knobs and buttons will prefer the distance from the tip
|
||||
/// of the index finger, while bigger objects will prefer from the palm of the hand.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public partial class UxrGrabber : UxrAvatarComponent<UxrGrabber>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Renderer _handRenderer;
|
||||
[SerializeField] private GameObject[] _objectsToDisableOnGrab;
|
||||
[SerializeField] private List<Transform> _optionalProximityTransforms;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets from all the positive and negative axes in the grabber's transform, the axis in local-space that is pointing
|
||||
/// to the fingers, excluding the thumb.
|
||||
/// </summary>
|
||||
public Vector3 LocalFingerDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Avatar == null || Avatar.AvatarRigInfo == null)
|
||||
{
|
||||
return transform.forward;
|
||||
}
|
||||
|
||||
return transform.GetClosestLocalAxis(Avatar.AvatarRigInfo.GetArmInfo(Side).HandUniversalLocalAxes.WorldForward);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets from all the positive and negative axes in the grabber's transform, the axis in world-space that is pointing
|
||||
/// to the fingers, excluding the thumb.
|
||||
/// </summary>
|
||||
public Vector3 FingerDirection => transform.TransformDirection(LocalFingerDirection);
|
||||
|
||||
/// <summary>
|
||||
/// Gets from all the positive and negative axes in the grabber's transform, the axis in local-space that is pointing
|
||||
/// outwards from the palm.
|
||||
/// </summary>
|
||||
public Vector3 LocalPalmOutDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Avatar == null || Avatar.AvatarRigInfo == null)
|
||||
{
|
||||
return -transform.up;
|
||||
}
|
||||
|
||||
return transform.GetClosestLocalAxis(-Avatar.AvatarRigInfo.GetArmInfo(Side).HandUniversalLocalAxes.WorldUp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets from all the positive and negative axes in the grabber's transform, the axis in world-space that is pointing
|
||||
/// outwards from the palm..
|
||||
/// </summary>
|
||||
public Vector3 PalmOutDirection => transform.TransformDirection(LocalPalmOutDirection);
|
||||
|
||||
/// <summary>
|
||||
/// Gets from all the positive and negative axes in the grabber's transform, the axis in local-space that is pointing
|
||||
/// towards the thumb.
|
||||
/// </summary>
|
||||
public Vector3 LocalPalmThumbDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3 direction = transform.right;
|
||||
|
||||
if (Avatar != null && Avatar.AvatarRigInfo != null)
|
||||
{
|
||||
direction = transform.GetClosestLocalAxis(Avatar.AvatarRigInfo.GetArmInfo(Side).HandUniversalLocalAxes.WorldRight);
|
||||
}
|
||||
|
||||
return Side == UxrHandSide.Left ? direction : -direction;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets from all the positive and negative axes in the grabber's transform, the axis in world-space that is pointing
|
||||
/// towards the thumb.
|
||||
/// </summary>
|
||||
public Vector3 PalmThumbDirection => transform.TransformDirection(LocalPalmThumbDirection);
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Gets, based on <see cref="FingerDirection" /> and <see cref="PalmOutDirection" />, which mirroring snap
|
||||
/// transforms
|
||||
/// should use with the grabber if they want to be mirrored.
|
||||
/// </para>
|
||||
/// Snap transforms are GameObjects in <see cref="UxrGrabbableObject" /> that determine where the hand should be placed
|
||||
/// during grabs by making the <see cref="UxrGrabber" />'s transform align with the snap <see cref="Transform" />.
|
||||
/// Mirroring snap transforms is used to quickly create/modify grab positions/orientations.
|
||||
/// </summary>
|
||||
/// <returns>Which mirroring TransformExt.ApplyMirroring() should use</returns>
|
||||
public TransformExt.MirrorType RequiredMirrorType
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3 other = Vector3.Cross(LocalPalmOutDirection, LocalFingerDirection);
|
||||
|
||||
if (Mathf.Abs(other.z) > 0.5)
|
||||
{
|
||||
return TransformExt.MirrorType.MirrorXY;
|
||||
}
|
||||
if (Mathf.Abs(other.y) > 0.5)
|
||||
{
|
||||
return TransformExt.MirrorType.MirrorXZ;
|
||||
}
|
||||
|
||||
return TransformExt.MirrorType.MirrorYZ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the grabber component is on the left or right hand.
|
||||
/// </summary>
|
||||
public UxrHandSide OppositeSide => Side == UxrHandSide.Left ? UxrHandSide.Right : UxrHandSide.Left;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the grabber component is on the left or right hand.
|
||||
/// </summary>
|
||||
public UxrHandSide Side
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_sideInitialized || (Application.isEditor && !Application.isPlaying))
|
||||
{
|
||||
InitializeSide();
|
||||
}
|
||||
|
||||
return _side;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_side = value;
|
||||
_sideInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the avatar hand bone that corresponds to the grabber.
|
||||
/// </summary>
|
||||
public Transform HandBone => Avatar.GetHandBone(Side);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative position of the hand bone to the grabber.
|
||||
/// </summary>
|
||||
public Vector3 HandBoneRelativePos => HandBone != null ? transform.InverseTransformPoint(HandBone.position) : Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative rotation of the hand bone to the grabber.
|
||||
/// </summary>
|
||||
public Quaternion HandBoneRelativeRot => HandBone != null ? Quaternion.Inverse(transform.rotation) * HandBone.rotation : Quaternion.identity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hand renderer.
|
||||
/// </summary>
|
||||
public Renderer HandRenderer
|
||||
{
|
||||
get
|
||||
{
|
||||
// Try to get it automatically if it is unassigned or disabled.
|
||||
|
||||
if ((_handRenderer == null || !_handRenderer.gameObject.activeInHierarchy) && Avatar != null)
|
||||
{
|
||||
SkinnedMeshRenderer handRenderer = UxrAvatarRig.TryToGetHandRenderer(Avatar, Side);
|
||||
|
||||
if (handRenderer != null)
|
||||
{
|
||||
_handRenderer = handRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
return _handRenderer;
|
||||
}
|
||||
set => _handRenderer = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opposite hand grabber in the same avatar.
|
||||
/// </summary>
|
||||
public UxrGrabber OppositeHandGrabber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unprocessed grabber position. This is the position the grabber has taking only the hand controller tracking
|
||||
/// sensor into account.
|
||||
/// The hand position is updated by the <see cref="UxrGrabManager" /> and may be forced into a certain position if the
|
||||
/// object being grabbed has constraints, altering also the <see cref="UxrGrabber" /> position. Sometimes it is
|
||||
/// preferred to use the unprocessed grabber position.
|
||||
/// </summary>
|
||||
public Vector3 UnprocessedGrabberPosition { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unprocessed grabber rotation. See <see cref="UnprocessedGrabberPosition" />.
|
||||
/// </summary>
|
||||
public Quaternion UnprocessedGrabberRotation { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently grabbed object if there is one. null if no object is being grabbed.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbedObject
|
||||
{
|
||||
get => _grabbedObject;
|
||||
set
|
||||
{
|
||||
_grabbedObject = value;
|
||||
|
||||
if (_objectsToDisableOnGrab != null)
|
||||
{
|
||||
foreach (GameObject go in _objectsToDisableOnGrab)
|
||||
{
|
||||
go.SetActive(value == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="UxrGrabber" />'s current frame velocity.
|
||||
/// </summary>
|
||||
public Vector3 Velocity { get; private set; } = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="UxrGrabber" />'s current frame angular velocity.
|
||||
/// </summary>
|
||||
public Vector3 AngularVelocity { get; private set; } = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="UxrGrabber" />'s velocity smoothed using averaged previous frame data.
|
||||
/// </summary>
|
||||
public Vector3 SmoothVelocity { get; private set; } = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="UxrGrabber" />'s angular velocity smoothed using averaged previous frame data.
|
||||
/// </summary>
|
||||
public Vector3 SmoothAngularVelocity { get; private set; } = Vector3.zero;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the grabber is currently being smoothly interpolated in an object manipulation.
|
||||
/// </summary>
|
||||
internal bool IsInSmoothManipulationTransition => SmoothManipulationTimer >= 0.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides Object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
string avatarName = Avatar != null ? $"{Avatar.name} " : string.Empty;
|
||||
return $"{avatarName}{Side.ToString().ToLower()} hand grabber";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the given proximity transform, used to compute distances to<see cref="UxrGrabbableObject" /> entities
|
||||
/// </summary>
|
||||
/// <param name="proximityIndex">
|
||||
/// Proximity transform index. -1 for the default (the grabber's transform) and 0 to n for any
|
||||
/// optional proximity transform.
|
||||
/// </param>
|
||||
/// <returns>Proximity transform. If the index is out of range it will return the default transform</returns>
|
||||
public Transform GetProximityTransform(int proximityIndex = -1)
|
||||
{
|
||||
if (proximityIndex >= 0 && proximityIndex < _optionalProximityTransforms.Count)
|
||||
{
|
||||
return _optionalProximityTransforms[proximityIndex];
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hand renderer enabled state.
|
||||
/// </summary>
|
||||
internal void UpdateHandGrabberRenderer()
|
||||
{
|
||||
if (_handRenderer != null && Avatar && (Avatar.RenderMode == UxrAvatarRenderModes.Avatar || Avatar.RenderMode == UxrAvatarRenderModes.AllControllersAndAvatar))
|
||||
{
|
||||
if (GrabbedObject == null)
|
||||
{
|
||||
_handRenderer.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_handRenderer.enabled = !UxrGrabManager.Instance.ShouldHideHandRenderer(this, GrabbedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the throw physics information.
|
||||
/// </summary>
|
||||
internal void UpdateThrowPhysicsInfo()
|
||||
{
|
||||
Transform sampledTransform = GrabbedObject != null ? GrabbedObject.transform : transform;
|
||||
Vector3 centerOfMassPosition = transform.TransformPoint(ThrowCenterOfMassLocalPosition);
|
||||
Vector3 throwTipPosition = transform.TransformPoint(ThrowTipLocalPosition);
|
||||
PhysicsSample newSample = new PhysicsSample(_physicsSampleWindow.LastOrDefault(), sampledTransform, centerOfMassPosition, throwTipPosition, Time.deltaTime);
|
||||
|
||||
// Update timers
|
||||
_physicsSampleWindow.ForEach(s => s.Age += Time.deltaTime);
|
||||
|
||||
// Remove samples out of the time window
|
||||
_physicsSampleWindow.RemoveAll(s => s.Age > SampleWindowSeconds);
|
||||
|
||||
// Add new sample
|
||||
_physicsSampleWindow.Add(newSample);
|
||||
|
||||
// Compute instant and smoothed values:
|
||||
Velocity = newSample.Velocity;
|
||||
AngularVelocity = newSample.EulerSpeed;
|
||||
SmoothVelocity = Vector3Ext.Average(_physicsSampleWindow.Select(s => s.TotalVelocity));
|
||||
|
||||
Quaternion relative = Quaternion.Inverse(_physicsSampleWindow.First().Rotation) * _physicsSampleWindow.Last().Rotation;
|
||||
relative.ToAngleAxis(out float angle, out Vector3 axis);
|
||||
|
||||
SmoothAngularVelocity = angle * sampledTransform.TransformDirection(axis) / _physicsSampleWindow.First().Age;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a smooth manipulation transition in a grab or a release, to make sure the hand transitions smoothly.
|
||||
/// </summary>
|
||||
internal void StartSmoothManipulationTransition()
|
||||
{
|
||||
SmoothManipulationTimer = UxrConstants.SmoothManipulationTransitionSeconds;
|
||||
|
||||
SmoothTransitionLocalAvatarHandBonePos = Avatar.transform.InverseTransformPoint(transform.TransformPoint(HandBoneRelativePos));
|
||||
SmoothTransitionLocalAvatarHandBoneRot = Quaternion.Inverse(Avatar.transform.rotation) * transform.rotation * HandBoneRelativeRot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the smooth manipulation transitions if they exist.
|
||||
/// </summary>
|
||||
internal void UpdateSmoothManipulationTransition(float deltaTime)
|
||||
{
|
||||
if (SmoothManipulationTimer >= 0.0f)
|
||||
{
|
||||
SmoothManipulationTimer -= deltaTime;
|
||||
|
||||
if (SmoothManipulationTimer > 0.0f)
|
||||
{
|
||||
float t = SmoothManipulationT;
|
||||
|
||||
HandBone.SetPositionAndRotation(Vector3.Lerp(Avatar.transform.TransformPoint(SmoothTransitionLocalAvatarHandBonePos), transform.TransformPoint(HandBoneRelativePos), t),
|
||||
Quaternion.Slerp(Avatar.transform.rotation * SmoothTransitionLocalAvatarHandBoneRot, transform.rotation * HandBoneRelativeRot, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
UxrAvatarRig.GetHandSide(transform, out UxrHandSide handSide);
|
||||
Side = handSide;
|
||||
|
||||
if (Avatar != null)
|
||||
{
|
||||
// Compute grabber info
|
||||
|
||||
UxrGrabber[] avatarGrabbers = Avatar.GetComponentsInChildren<UxrGrabber>();
|
||||
|
||||
OppositeHandGrabber = avatarGrabbers.FirstOrDefault(g => g != null && Side != g.Side);
|
||||
GrabbedObject = null;
|
||||
|
||||
// Compute throw physics info
|
||||
|
||||
if (Avatar.GetHand(handSide).GetPalmCenter(out Vector3 palmCenter) && Avatar.GetHand(handSide).GetPalmToFingerDirection(out Vector3 palmToFinger))
|
||||
{
|
||||
ThrowCenterOfMassLocalPosition = transform.InverseTransformPoint(palmCenter);
|
||||
ThrowTipLocalPosition = transform.InverseTransformPoint(palmCenter + palmToFinger * ThrowAxisLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object is destroyed. Releases any grabbed objects.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (GrabbedObject != null)
|
||||
{
|
||||
UxrGrabManager.Instance.ReleaseObject(this, GrabbedObject, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object is disabled. Releases any grabbed objects.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
if (GrabbedObject != null)
|
||||
{
|
||||
UxrGrabManager.Instance.ReleaseObject(this, GrabbedObject, true);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the hand the grabber belongs to.
|
||||
/// </summary>
|
||||
private void InitializeSide()
|
||||
{
|
||||
if (UxrAvatarRig.GetHandSide(transform, out UxrHandSide handSide))
|
||||
{
|
||||
Side = handSide;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the smooth manipulation transition interpolation value.
|
||||
/// </summary>
|
||||
private float SmoothManipulationT
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SmoothManipulationTimer <= 0.0f)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return 1.0f - Mathf.Clamp01(SmoothManipulationTimer / UxrConstants.SmoothManipulationTransitionSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decreasing smooth manipulation transition timer.
|
||||
/// </summary>
|
||||
private float SmoothManipulationTimer { get; set; } = -1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the hand bone in local avatar space at the start of a smooth transition.
|
||||
/// </summary>
|
||||
private Vector3 SmoothTransitionLocalAvatarHandBonePos { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation of the hand bone in local avatar space at the start of a smooth transition.
|
||||
/// </summary>
|
||||
private Quaternion SmoothTransitionLocalAvatarHandBoneRot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the throw center of mass (palm center) in the grabber's local coordinate system.
|
||||
/// </summary>
|
||||
private Vector3 ThrowCenterOfMassLocalPosition { get; set; } = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the throw center of mass (palm center) in the grabber local coordinate system.
|
||||
/// </summary>
|
||||
private Vector3 ThrowTipLocalPosition { get; set; } = Vector3.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Distance from the center of mass (palm) to the fingers to compute throw angular speed.
|
||||
/// </summary>
|
||||
private const float ThrowAxisLength = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// History physics sample window in seconds.
|
||||
/// </summary>
|
||||
private const float SampleWindowSeconds = 0.15f;
|
||||
|
||||
private readonly List<PhysicsSample> _physicsSampleWindow = new List<PhysicsSample>();
|
||||
|
||||
private bool _sideInitialized;
|
||||
private UxrHandSide _side;
|
||||
private UxrGrabbableObject _grabbedObject;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a89958cfe8683aa4b910cbe234c93f11
|
||||
timeCreated: 1492964946
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGripPoseInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Manipulation.HandPoses;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes how an object is grabbed. It tells the pose that will be used and how it will be snapped to the hand.
|
||||
/// The key is stored in the object, ideally we would have Dictionary(key, GripPoseInfo) but since Unity does not
|
||||
/// serialize Dictionaries we use a List(GripPoseInfo) containing the key (<see cref="AvatarPrefabGuid" />) as well.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrGripPoseInfo
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private string _avatarPrefabGuid;
|
||||
[SerializeField] private UxrHandPoseAsset _handPose;
|
||||
[SerializeField] private float _poseBlendValue;
|
||||
[SerializeField] private Transform _gripAlignTransformHandLeft;
|
||||
[SerializeField] private Transform _gripAlignTransformHandRight;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GUID of the avatar prefab the grip pose info belongs to.
|
||||
/// </summary>
|
||||
public string AvatarPrefabGuid => _avatarPrefabGuid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the left grab pose preview mesh.
|
||||
/// </summary>
|
||||
public Mesh GrabPoseMeshLeft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the right grab pose preview mesh.
|
||||
/// </summary>
|
||||
public Mesh GrabPoseMeshRight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pose that will be used when grabbing.
|
||||
/// </summary>
|
||||
public UxrHandPoseAsset HandPose
|
||||
{
|
||||
get => _handPose;
|
||||
set => _handPose = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pose blend value if the pose has the possibility of blending. Blending is used to blend between
|
||||
/// open/closed grips or other animations.
|
||||
/// </summary>
|
||||
public float PoseBlendValue
|
||||
{
|
||||
get => _poseBlendValue;
|
||||
set => _poseBlendValue = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Transform" /> that will be used to align the object grab point to the left
|
||||
/// <see cref="UxrGrabber" /> that grabbed it.
|
||||
/// </summary>
|
||||
public Transform GripAlignTransformHandLeft
|
||||
{
|
||||
get => _gripAlignTransformHandLeft;
|
||||
set => _gripAlignTransformHandLeft = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Transform" /> that will be used to align the object grab point to the right
|
||||
/// <see cref="UxrGrabber" /> that grabbed it.
|
||||
/// </summary>
|
||||
public Transform GripAlignTransformHandRight
|
||||
{
|
||||
get => _gripAlignTransformHandRight;
|
||||
set => _gripAlignTransformHandRight = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="avatarPrefabGuid">
|
||||
/// Avatar prefab GUID. Using prefabs allows to share poses among instances and also prefab variants to inherit poses
|
||||
/// from their parent prefabs in the chain
|
||||
/// </param>
|
||||
public UxrGripPoseInfo(string avatarPrefabGuid)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(avatarPrefabGuid))
|
||||
{
|
||||
_avatarPrefabGuid = avatarPrefabGuid;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b91d5f23482c403b95cc973ed54d668f
|
||||
timeCreated: 1643287841
|
||||
@@ -0,0 +1,24 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrHandSnapDirection.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different ways snapping can be handled when grabbing an object.
|
||||
/// For constrained objects, this will always be hand to the object
|
||||
/// </summary>
|
||||
public enum UxrHandSnapDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// The object will snap to the hand.
|
||||
/// </summary>
|
||||
ObjectToHand,
|
||||
|
||||
/// <summary>
|
||||
/// The hand will snap to the object.
|
||||
/// </summary>
|
||||
HandToObject
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3afe97ed32147e8b534c0a083cae927
|
||||
timeCreated: 1643287266
|
||||
@@ -0,0 +1,580 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrManipulationEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Event parameters for most manipulation events:
|
||||
/// </para>
|
||||
/// <see cref="UxrGrabManager" />:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.GrabTrying" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectGrabbing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectGrabbed" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectReleasing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectReleased" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectPlacing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectPlaced" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectRemoving" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.ObjectRemoved" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.AnchorRangeEntered" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.AnchorRangeLeft" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.PlacedObjectRangeEntered" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabManager.PlacedObjectRangeLeft" />
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// <see cref="UxrGrabbableObject" />:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObject.Grabbing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObject.Grabbed" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObject.Releasing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObject.Released" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObject.Placing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObject.Placed" />
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// <see cref="UxrGrabbableObjectAnchor" />:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObjectAnchor.Placing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObjectAnchor.Placed" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObjectAnchor.Removing" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObjectAnchor.Removed" />
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="UxrGrabbableObjectAnchor.SmoothPlaceTransitionEnded" />
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public class UxrManipulationEventArgs : EventArgs, IUxrSerializable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the manipulation changed an object from not being grabbed at all to being grabbed or vice-versa.
|
||||
/// This is useful to filter out grabs or releases on an object that is still being grabbed using another hand.<br/>
|
||||
/// <see cref="IsGrabbedStateChanged" /> is true if <see cref="IsMultiHands" /> and <see cref="IsSwitchHands" /> are
|
||||
/// both false.
|
||||
/// </summary>
|
||||
public bool IsGrabbedStateChanged => !IsMultiHands && !IsSwitchHands;
|
||||
|
||||
/// <summary>
|
||||
/// The type of event.
|
||||
/// </summary>
|
||||
public UxrManipulationEventType EventType
|
||||
{
|
||||
get => _eventType;
|
||||
private set => _eventType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable object related to the event. Can be null if the event doesn't use this property. Check the event
|
||||
/// documentation to see how the property is used.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject GrabbableObject
|
||||
{
|
||||
get => _grabbableObject;
|
||||
private set => _grabbableObject = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable object anchor related to the event. Can be null if the event doesn't use this property. Check
|
||||
/// the event documentation to see how the property is used.
|
||||
/// </summary>
|
||||
public UxrGrabbableObjectAnchor GrabbableAnchor
|
||||
{
|
||||
get => _grabbableAnchor;
|
||||
private set => _grabbableAnchor = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabber related to the event. Can be null if the event doesn't use this property. Check the event
|
||||
/// documentation to see how the property is used.
|
||||
/// </summary>
|
||||
public UxrGrabber Grabber
|
||||
{
|
||||
get => _grabber;
|
||||
private set => _grabber = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grabbable object's grab point index related to the event. Can be meaningless if the event doesn't use this
|
||||
/// property. Check the event documentation to see how the property is used.
|
||||
/// </summary>
|
||||
public int GrabPointIndex
|
||||
{
|
||||
get => _grabPointIndex;
|
||||
private set => _grabPointIndex = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the manipulation used more than one hand. Can be meaningless if the event doesn't use this property.
|
||||
/// Check the event documentation to see how the property is used.
|
||||
/// </summary>
|
||||
public bool IsMultiHands
|
||||
{
|
||||
get => _isMultiHands;
|
||||
private set => _isMultiHands = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the event was the result of passing the object from one hand to the other. Can be meaningless if the
|
||||
/// event doesn't use this property. Check the event documentation to see how the property is used.
|
||||
/// </summary>
|
||||
public bool IsSwitchHands
|
||||
{
|
||||
get => _isSwitchHands;
|
||||
private set => _isSwitchHands = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the release velocity for release events.
|
||||
/// </summary>
|
||||
public Vector3 ReleaseVelocity
|
||||
{
|
||||
get => _releaseVelocity;
|
||||
private set => _releaseVelocity = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the release angular velocity for release events.
|
||||
/// </summary>
|
||||
public Vector3 ReleaseAngularVelocity
|
||||
{
|
||||
get => _releaseAngularVelocity;
|
||||
private set => _releaseAngularVelocity = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the placement flags in place events.
|
||||
/// </summary>
|
||||
public UxrPlacementOptions PlacementOptions
|
||||
{
|
||||
get => _placementOptions;
|
||||
private set => _placementOptions = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UxrGrabbableObject position in local UxrGrabber space at the moment of grabbing.
|
||||
/// This is used in multi-player environments to make sure to reproduce the same grab action.
|
||||
/// </summary>
|
||||
internal Vector3 GrabberLocalObjectPosition
|
||||
{
|
||||
get => _grabberLocalObjectPosition;
|
||||
private set => _grabberLocalObjectPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UxrGrabbableObject rotation in local UxrGrabber space at the moment of grabbing.
|
||||
/// This is used in multi-player environments to make sure to reproduce the same grab action.
|
||||
/// </summary>
|
||||
internal Quaternion GrabberLocalObjectRotation
|
||||
{
|
||||
get => _grabberLocalObjectRotation;
|
||||
private set => _grabberLocalObjectRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab snap position in local UxrGrabber space at the moment of grabbing.
|
||||
/// This is used in multi-player environments to make sure to reproduce the same grab action.
|
||||
/// </summary>
|
||||
internal Vector3 GrabberLocalSnapPosition
|
||||
{
|
||||
get => _grabberLocalSnapPosition;
|
||||
private set => _grabberLocalSnapPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grab snap rotation in local UxrGrabber space at the moment of grabbing.
|
||||
/// This is used in multi-player environments to make sure to reproduce the same grab action.
|
||||
/// </summary>
|
||||
internal Quaternion GrabberLocalSnapRotation
|
||||
{
|
||||
get => _grabberLocalSnapRotation;
|
||||
private set => _grabberLocalSnapRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the release start position for release events.
|
||||
/// </summary>
|
||||
internal Vector3 ReleaseStartPosition
|
||||
{
|
||||
get => _releaseStartPosition;
|
||||
private set => _releaseStartPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the release start rotation for release events.
|
||||
/// </summary>
|
||||
internal Quaternion ReleaseStartRotation
|
||||
{
|
||||
get => _releaseStartRotation;
|
||||
private set => _releaseStartRotation = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor is private.
|
||||
/// </summary>
|
||||
private UxrManipulationEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that initializes the event type.
|
||||
/// </summary>
|
||||
private UxrManipulationEventArgs(UxrManipulationEventType eventType)
|
||||
{
|
||||
EventType = eventType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Explicit IUxrSerializable
|
||||
|
||||
/// <inheritdoc />
|
||||
int IUxrSerializable.SerializationVersion => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
void IUxrSerializable.Serialize(IUxrSerializer serializer, int serializationVersion)
|
||||
{
|
||||
serializer.SerializeEnum(ref _eventType);
|
||||
serializer.SerializeUniqueComponent(ref _grabbableObject);
|
||||
serializer.SerializeUniqueComponent(ref _grabbableAnchor);
|
||||
serializer.SerializeUniqueComponent(ref _grabber);
|
||||
serializer.Serialize(ref _grabPointIndex);
|
||||
serializer.Serialize(ref _isMultiHands);
|
||||
serializer.Serialize(ref _isSwitchHands);
|
||||
|
||||
if (EventType == UxrManipulationEventType.Grab)
|
||||
{
|
||||
serializer.Serialize(ref _grabberLocalSnapPosition);
|
||||
serializer.Serialize(ref _grabberLocalSnapRotation);
|
||||
serializer.Serialize(ref _grabberLocalObjectPosition);
|
||||
serializer.Serialize(ref _grabberLocalObjectRotation);
|
||||
}
|
||||
else if (EventType == UxrManipulationEventType.Release)
|
||||
{
|
||||
serializer.Serialize(ref _releaseStartPosition);
|
||||
serializer.Serialize(ref _releaseStartRotation);
|
||||
serializer.Serialize(ref _releaseVelocity);
|
||||
serializer.Serialize(ref _releaseAngularVelocity);
|
||||
}
|
||||
else if (EventType == UxrManipulationEventType.Place)
|
||||
{
|
||||
serializer.SerializeEnum(ref _placementOptions);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Grab events.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="grabbableAnchor">Grabbable object anchor</param>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabPointIndex">Grab point index</param>
|
||||
/// <param name="isMultiHands">Whether the object was already grabbed with one or more hands</param>
|
||||
/// <param name="isSwitchHands">Whether the event was a result of passing the grabbable object from one hand to the other</param>
|
||||
/// <param name="grabberLocalSnapPosition">Grab snap position in local UxrGrabber space at the moment of grabbing</param>
|
||||
/// <param name="grabberLocalSnapRotation">Grab snap rotation in local UxrGrabber space at the moment of grabbing</param>
|
||||
public static UxrManipulationEventArgs FromGrab(UxrGrabbableObject grabbableObject,
|
||||
UxrGrabbableObjectAnchor grabbableAnchor,
|
||||
UxrGrabber grabber,
|
||||
int grabPointIndex,
|
||||
bool isMultiHands,
|
||||
bool isSwitchHands,
|
||||
Vector3 grabberLocalSnapPosition,
|
||||
Quaternion grabberLocalSnapRotation)
|
||||
{
|
||||
UxrManipulationEventArgs eventArgs = new UxrManipulationEventArgs(UxrManipulationEventType.Grab);
|
||||
|
||||
eventArgs.GrabbableObject = grabbableObject;
|
||||
eventArgs.GrabbableAnchor = grabbableAnchor;
|
||||
eventArgs.Grabber = grabber;
|
||||
eventArgs.GrabPointIndex = grabPointIndex;
|
||||
eventArgs.IsMultiHands = isMultiHands;
|
||||
eventArgs.IsSwitchHands = isSwitchHands;
|
||||
|
||||
// Internal vars
|
||||
|
||||
if (grabbableObject != null && grabber != null)
|
||||
{
|
||||
eventArgs.GrabberLocalSnapPosition = grabberLocalSnapPosition;
|
||||
eventArgs.GrabberLocalSnapRotation = grabberLocalSnapRotation;
|
||||
eventArgs.GrabberLocalObjectPosition = grabber.transform.InverseTransformPoint(grabbableObject.transform.position);
|
||||
eventArgs.GrabberLocalObjectRotation = Quaternion.Inverse(grabber.transform.rotation) * grabbableObject.transform.rotation;
|
||||
}
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Release events.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="grabbableAnchor">Grabbable object anchor</param>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabPointIndex">Grab point index</param>
|
||||
/// <param name="isMultiHands">Whether the object will still be grabbed with one or more hands</param>
|
||||
/// <param name="isSwitchHands">Whether the event was a result of passing the grabbable object from one hand to the other</param>
|
||||
/// <param name="releaseVelocity">The release velocity</param>
|
||||
/// <param name="releaseAngularVelocity">The release angular velocity</param>
|
||||
public static UxrManipulationEventArgs FromRelease(UxrGrabbableObject grabbableObject,
|
||||
UxrGrabbableObjectAnchor grabbableAnchor,
|
||||
UxrGrabber grabber,
|
||||
int grabPointIndex,
|
||||
bool isMultiHands,
|
||||
bool isSwitchHands,
|
||||
Vector3 releaseVelocity = default(Vector3),
|
||||
Vector3 releaseAngularVelocity = default(Vector3))
|
||||
|
||||
{
|
||||
UxrManipulationEventArgs eventArgs = new UxrManipulationEventArgs(UxrManipulationEventType.Release);
|
||||
|
||||
eventArgs.GrabbableObject = grabbableObject;
|
||||
eventArgs.GrabbableAnchor = grabbableAnchor;
|
||||
eventArgs.Grabber = grabber;
|
||||
eventArgs.GrabPointIndex = grabPointIndex;
|
||||
eventArgs.IsMultiHands = isMultiHands;
|
||||
eventArgs.IsSwitchHands = isSwitchHands;
|
||||
eventArgs.ReleaseVelocity = releaseVelocity;
|
||||
eventArgs.ReleaseAngularVelocity = releaseAngularVelocity;
|
||||
|
||||
// Internal vars
|
||||
|
||||
if (grabbableObject != null)
|
||||
{
|
||||
eventArgs.ReleaseStartPosition = grabbableObject.transform.position;
|
||||
eventArgs.ReleaseStartRotation = grabbableObject.transform.rotation;
|
||||
}
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Place events.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="grabbableAnchor">Grabbable object anchor</param>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabPointIndex">Grab point index</param>
|
||||
/// <param name="placementOptions">The placement options flags</param>
|
||||
public static UxrManipulationEventArgs FromPlace(UxrGrabbableObject grabbableObject,
|
||||
UxrGrabbableObjectAnchor grabbableAnchor,
|
||||
UxrGrabber grabber,
|
||||
int grabPointIndex,
|
||||
UxrPlacementOptions placementOptions)
|
||||
{
|
||||
UxrManipulationEventArgs eventArgs = new UxrManipulationEventArgs(UxrManipulationEventType.Place);
|
||||
|
||||
eventArgs.GrabbableObject = grabbableObject;
|
||||
eventArgs.GrabbableAnchor = grabbableAnchor;
|
||||
eventArgs.Grabber = grabber;
|
||||
eventArgs.GrabPointIndex = grabPointIndex;
|
||||
eventArgs.IsMultiHands = false;
|
||||
eventArgs.IsSwitchHands = false;
|
||||
eventArgs.PlacementOptions = placementOptions;
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Release events.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="grabbableAnchor">Grabbable object anchor</param>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabPointIndex">Grab point index</param>
|
||||
/// <param name="isMultiHands">Whether the event was a result of a manipulation with more than one hand</param>
|
||||
/// <param name="isSwitchHands">Whether the event was a result of passing the grabbable object from one hand to the other</param>
|
||||
public static UxrManipulationEventArgs FromRemove(UxrGrabbableObject grabbableObject,
|
||||
UxrGrabbableObjectAnchor grabbableAnchor,
|
||||
UxrGrabber grabber,
|
||||
int grabPointIndex = 0,
|
||||
bool isMultiHands = false,
|
||||
bool isSwitchHands = false)
|
||||
{
|
||||
UxrManipulationEventArgs eventArgs = new UxrManipulationEventArgs(UxrManipulationEventType.Remove);
|
||||
|
||||
eventArgs.GrabbableObject = grabbableObject;
|
||||
eventArgs.GrabbableAnchor = grabbableAnchor;
|
||||
eventArgs.Grabber = grabber;
|
||||
eventArgs.GrabPointIndex = grabPointIndex;
|
||||
eventArgs.IsMultiHands = isMultiHands;
|
||||
eventArgs.IsSwitchHands = isSwitchHands;
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for PlacedObjectRangeEntered/Left, AnchorRangeEntered/Left and GrabTrying events.
|
||||
/// </summary>
|
||||
/// <param name="grabbableObject">Grabbable object</param>
|
||||
/// <param name="grabbableAnchor">Grabbable object anchor</param>
|
||||
/// <param name="grabber">Grabber</param>
|
||||
/// <param name="grabPointIndex">Grab point index</param>
|
||||
/// <param name="isMultiHands">Whether the event was a result of a manipulation with more than one hand</param>
|
||||
/// <param name="isSwitchHands">Whether the event was a result of passing the grabbable object from one hand to the other</param>
|
||||
public static UxrManipulationEventArgs FromOther(UxrManipulationEventType eventType,
|
||||
UxrGrabbableObject grabbableObject,
|
||||
UxrGrabbableObjectAnchor grabbableAnchor,
|
||||
UxrGrabber grabber,
|
||||
int grabPointIndex = 0,
|
||||
bool isMultiHands = false,
|
||||
bool isSwitchHands = false)
|
||||
{
|
||||
UxrManipulationEventArgs eventArgs = new UxrManipulationEventArgs(eventType);
|
||||
|
||||
eventArgs.GrabbableObject = grabbableObject;
|
||||
eventArgs.GrabbableAnchor = grabbableAnchor;
|
||||
eventArgs.Grabber = grabber;
|
||||
eventArgs.GrabPointIndex = grabPointIndex;
|
||||
eventArgs.IsMultiHands = isMultiHands;
|
||||
eventArgs.IsSwitchHands = isSwitchHands;
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string that describes the event.
|
||||
/// </summary>
|
||||
/// <param name="includeIds">Whether to include information of the component unique IDs</param>
|
||||
/// <returns>String with a description of the event</returns>
|
||||
public string ToString(bool includeIds = false)
|
||||
{
|
||||
switch (EventType)
|
||||
{
|
||||
case UxrManipulationEventType.Grab: return $"Grabbing {GetGrabbableObjectLogInfo(this, includeIds)}{GetGrabberLogInfo(this, includeIds)}";
|
||||
case UxrManipulationEventType.Release: return $"Releasing {GetGrabbableObjectLogInfo(this, includeIds)}{GetGrabberLogInfo(this, includeIds)}";
|
||||
case UxrManipulationEventType.Place: return $"Placing {GetGrabbableObjectLogInfo(this, includeIds)} on {GetAnchorLogInfo(this, includeIds)}{GetGrabberLogInfo(this, includeIds)}";
|
||||
case UxrManipulationEventType.Remove: return $"Removing {GetGrabbableObjectLogInfo(this, includeIds)} from {GetAnchorLogInfo(this, includeIds)}{GetGrabberLogInfo(this, includeIds)}";
|
||||
}
|
||||
|
||||
return "Unknown event";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log string describing the grabber.
|
||||
/// </summary>
|
||||
/// <param name="e">Event with the grabber to log</param>
|
||||
/// <param name="includeIds">Whether to include information of the component unique IDs</param>
|
||||
/// <returns>Log string</returns>
|
||||
private static string GetGrabberLogInfo(UxrManipulationEventArgs e, bool includeIds)
|
||||
{
|
||||
string id = e.Grabber != null && includeIds ? $" (id {e.Grabber.UniqueId})" : string.Empty;
|
||||
return e.Grabber != null ? $" using {e.Grabber}{id}" : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log string describing the grabber.
|
||||
/// </summary>
|
||||
/// <param name="e">Event with the grabber to log</param>
|
||||
/// <param name="includeIds">Whether to include information of the component unique IDs</param>
|
||||
/// <returns>Log string</returns>
|
||||
private static string GetAnchorLogInfo(UxrManipulationEventArgs e, bool includeIds)
|
||||
{
|
||||
string id = e.GrabbableAnchor != null && includeIds ? $" (id {e.GrabbableAnchor.UniqueId})" : string.Empty;
|
||||
return e.GrabbableAnchor != null ? $"{e.GrabbableAnchor.name}{id}" : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the log string describing the grabbable object.
|
||||
/// </summary>
|
||||
/// <param name="e">Event with the grabbable object to log</param>
|
||||
/// <param name="includeIds">Whether to include information of the component unique IDs</param>
|
||||
/// <returns>Log string</returns>
|
||||
private static string GetGrabbableObjectLogInfo(UxrManipulationEventArgs e, bool includeIds)
|
||||
{
|
||||
string id = e.GrabbableObject != null && includeIds ? $" (id {e.GrabbableObject.UniqueId})" : string.Empty;
|
||||
string grabPointInfo = e.GrabbableObject != null && e.GrabbableObject.GrabPointCount > 1 && e.GrabPointIndex >= 0 ? $" (grab point {e.GrabPointIndex})" : string.Empty;
|
||||
return e.GrabbableObject != null ? $"{e.GrabbableObject.name}{id}{grabPointInfo}" : string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrManipulationEventType _eventType;
|
||||
private UxrGrabbableObject _grabbableObject;
|
||||
private UxrGrabbableObjectAnchor _grabbableAnchor;
|
||||
private UxrGrabber _grabber;
|
||||
private int _grabPointIndex;
|
||||
private bool _isMultiHands;
|
||||
private bool _isSwitchHands;
|
||||
private Vector3 _releaseVelocity;
|
||||
private Vector3 _releaseAngularVelocity;
|
||||
private UxrPlacementOptions _placementOptions;
|
||||
private Vector3 _grabberLocalObjectPosition;
|
||||
private Quaternion _grabberLocalObjectRotation;
|
||||
private Vector3 _grabberLocalSnapPosition;
|
||||
private Quaternion _grabberLocalSnapRotation;
|
||||
private Vector3 _releaseStartPosition;
|
||||
private Quaternion _releaseStartRotation;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c45448cc1fe54193ab3ce3edf21184c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrManipulationEventType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different manipulation event types.
|
||||
/// </summary>
|
||||
public enum UxrManipulationEventType
|
||||
{
|
||||
Grab,
|
||||
Release,
|
||||
Place,
|
||||
Remove,
|
||||
GrabTrying,
|
||||
AnchorRangeEntered,
|
||||
AnchorRangeLeft,
|
||||
PlacedObjectRangeEntered,
|
||||
PlacedObjectRangeLeft
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49a06d54a230465b91af5ffa68d67600
|
||||
timeCreated: 1650790693
|
||||
@@ -0,0 +1,59 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrManipulationFeatures.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different manipulation features that can be used when the <see cref="UxrGrabManager" /> is being
|
||||
/// updated.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrManipulationFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Update the transform of <see cref="UxrGrabbableObject" /> objects based on user interactions using grabbers.
|
||||
/// </summary>
|
||||
ObjectManipulation = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Applies constraints defined in the <see cref="UxrGrabbableObject" /> component.
|
||||
/// </summary>
|
||||
ObjectConstraints = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Applies resistance defined by <see cref="UxrGrabbableObject.TranslationResistance" /> and
|
||||
/// <see cref="UxrGrabbableObject.RotationResistance" />.
|
||||
/// </summary>
|
||||
ObjectResistance = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Applies constraints defined by users through <see cref="UxrGrabbableObject.ConstraintsApplying" />/
|
||||
/// <see cref="UxrGrabbableObject.ConstraintsApplied" />/<see cref="UxrGrabbableObject.ConstraintsFinished" />.
|
||||
/// </summary>
|
||||
UserConstraints = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Forces to keep the grips in place to avoid hands drifting from an object that has constraints applied.
|
||||
/// </summary>
|
||||
KeepGripsInPlace = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Smooth transitions in grabbing hands and objects that are being manipulated.
|
||||
/// </summary>
|
||||
SmoothTransitions = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Updates the affordances.
|
||||
/// </summary>
|
||||
Affordances = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Uses all features.
|
||||
/// </summary>
|
||||
All = 0x7FFFFFFF
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57b24cb1c4044e20b5d05970658e667a
|
||||
timeCreated: 1709380090
|
||||
@@ -0,0 +1,42 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrPlacementOptions.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace UltimateXR.Manipulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different ways a <see cref="UxrGrabbableObject" /> can transition when being placed on an
|
||||
/// <see cref="UxrGrabbableObjectAnchor" />.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UxrPlacementOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Place immediately. If the object is being grabbed, release it.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Place using smooth transition (interpolation).
|
||||
/// </summary>
|
||||
Smooth = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Do not release the object when placing.
|
||||
/// </summary>
|
||||
DontRelease = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Overrides <see cref="UxrSnapToAnchorMode"/> so that it forces to snap the position.
|
||||
/// </summary>
|
||||
ForceSnapPosition = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Overrides <see cref="UxrSnapToAnchorMode"/> so that it forces to snap the rotation.
|
||||
/// </summary>
|
||||
ForceSnapRotation = 1 << 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1110ad82fc5745bcb1e9e1d1251915ac
|
||||
timeCreated: 1643645683
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user