// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
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
///
/// Stores information of a grab performed on an object.
///
///
/// Implements to help 's implementation of the
/// interface ().
///
private sealed class RuntimeGrabInfo : IUxrSerializable
{
#region Public Types & Data
///
/// Gets the grabber grabbing the .
///
public UxrGrabber Grabber
{
get => _grabber;
set => _grabber = value;
}
///
/// Gets the grabbed point.
///
public int GrabbedPoint
{
get => _grabbedPoint;
set => _grabbedPoint = value;
}
// *************************************************************************************************************************
// Transform information about the grip.
// *************************************************************************************************************************
///
/// Gets or sets the rotation relative to the at the moment
/// it was grabbed.
///
public Quaternion RelativeGrabRotation
{
get => _relativeGrabRotation;
private set => _relativeGrabRotation = value;
}
///
/// Gets or sets the position in local space at the moment
/// it was grabbed.
///
public Vector3 RelativeGrabPosition
{
get => _relativeGrabPosition;
private set => _relativeGrabPosition = value;
}
///
/// Gets or sets the rotation relative to the at the moment
/// it was grabbed.
///
public Quaternion RelativeGrabberRotation
{
get => _relativeGrabberRotation;
private set => _relativeGrabberRotation = value;
}
///
/// Gets or sets the position in local space at the moment
/// it was grabbed.
///
public Vector3 RelativeGrabberPosition
{
get => _relativeGrabberPosition;
private set => _relativeGrabberPosition = value;
}
///
/// Gets or sets the transform relative to which and
/// are specified.
///
public Transform GrabAlignParentTransformUsed
{
get => _grabAlignParentTransformUsed;
private set => _grabAlignParentTransformUsed = value;
}
///
/// Gets or sets the snap rotation relative to the at the moment it was
/// grabbed.
///
public Quaternion RelativeGrabAlignRotation
{
get => _relativeGrabAlignRotation;
private set => _relativeGrabAlignRotation = value;
}
///
/// Gets or sets the snap position in local space at the moment it was
/// grabbed.
///
public Vector3 RelativeGrabAlignPosition
{
get => _relativeGrabAlignPosition;
private set => _relativeGrabAlignPosition = value;
}
///
/// 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 .
///
public Quaternion RelativeUsedGrabAlignRotation
{
get => _relativeUsedGrabAlignRotation;
private set => _relativeUsedGrabAlignRotation = value;
}
///
/// 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 .
///
public Vector3 RelativeUsedGrabAlignPosition
{
get => _relativeUsedGrabAlignPosition;
private set => _relativeUsedGrabAlignPosition = value;
}
///
/// Gets or sets the proximity rotation relative to the at the moment it was grabbed.
///
public Vector3 RelativeProximityPosition
{
get => _relativeProximityPosition;
private set => _relativeProximityPosition = value;
}
///
/// Gets or sets the source in local coordinates where the source of leverage will be
/// computed for 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.
///
public Vector3 GrabberLocalLeverageSource
{
get => _grabberLocalLeverageSource;
private set => _grabberLocalLeverageSource = value;
}
///
/// Gets or sets the leverage source in local coordinates of the parent
/// transform of the grabbable at the moment the was grabbed.
///
public Vector3 GrabberLocalParentLeverageSourceOnGrab
{
get => _grabberLocalParentLeverageSourceOnGrab;
private set => _grabberLocalParentLeverageSourceOnGrab = value;
}
///
/// Gets or sets the leverage point in local coordinates that this child grabbable will use when the parent
/// grabbable rotation provider is HandPositionAroundPivot.
///
public Vector3 ParentGrabbableLookAtLocalLeveragePoint
{
get => _parentGrabbableLookAtLocalLeveragePoint;
set => _parentGrabbableLookAtLocalLeveragePoint = value;
}
///
/// 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.
///
public Vector3 ParentGrabbableLookAtParentLeveragePoint
{
get => _parentGrabbableLookAtParentLeveragePoint;
set => _parentGrabbableLookAtParentLeveragePoint = value;
}
///
/// 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.
///
public Vector3 ParentGrabbableLeverageContribution
{
get => _parentGrabbableLeverageContribution;
set => _parentGrabbableLeverageContribution = value;
}
///
/// 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.
///
public Quaternion ParentGrabbableLookAtRotationContribution
{
get => _parentGrabbableLookAtRotationContribution;
set => _parentGrabbableLookAtRotationContribution = value;
}
///
/// Gets or sets the rotation angle contribution, in objects constrained to a single axis rotation, during the current
/// grab.
///
public float SingleRotationAngleContribution
{
get => _singleRotationAngleContribution;
set => _singleRotationAngleContribution = value;
}
///
/// Gets or sets the 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.
///
public float LastAccumulatedAngle
{
get => _lastAccumulatedAngle;
set => _lastAccumulatedAngle = value;
}
// *************************************************************************************************************************
// Parent dependency information.
// *************************************************************************************************************************
///
/// 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.
///
public Vector3 ParentLocalGrabPositionBeforeUpdate
{
get => _parentLocalGrabPositionBeforeUpdate;
set => _parentLocalGrabPositionBeforeUpdate = value;
}
///
/// Gets the grab position in grabbable parent space after updating this object being grabbed. See
/// .
///
public Vector3 ParentLocalGrabPositionAfterUpdate
{
get => _parentLocalGrabPositionAfterUpdate;
set => _parentLocalGrabPositionAfterUpdate = value;
}
///
/// 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.
///
public Vector3 ChildLocalParentPosition
{
get => _childLocalParentPosition;
set => _childLocalParentPosition = value;
}
///
/// 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.
///
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.
// *************************************************************************************************************************
///
/// Gets or sets the local position at the moment it was grabbed.
///
public Vector3 LocalPositionOnGrab
{
get => _localPositionOnGrab;
private set => _localPositionOnGrab = value;
}
///
/// Gets or sets the local rotation at the moment it was grabbed.
///
public Quaternion LocalRotationOnGrab
{
get => _localRotationOnGrab;
private set => _localRotationOnGrab = value;
}
///
/// Gets or sets the world-space snap position at the moment the was grabbed.
///
public Vector3 AlignPositionOnGrab
{
get => _alignPositionOnGrab;
private set => _alignPositionOnGrab = value;
}
///
/// Gets or sets the world-space snap rotation at the moment the was grabbed.
///
public Quaternion AlignRotationOnGrab
{
get => _alignRotationOnGrab;
private set => _alignRotationOnGrab = value;
}
///
/// Gets or sets the hand bone position in local avatar coordinates at the moment the
/// was grabbed.
///
public Vector3 HandBoneLocalAvatarPositionOnGrab
{
get => _handBoneLocalAvatarPositionOnGrab;
private set => _handBoneLocalAvatarPositionOnGrab = value;
}
///
/// Gets or sets the hand bone rotation in local avatar coordinates at the moment the
/// was grabbed.
///
public Quaternion HandBoneLocalAvatarRotationOnGrab
{
get => _handBoneLocalAvatarRotationOnGrab;
private set => _handBoneLocalAvatarRotationOnGrab = value;
}
#endregion
#region Constructors & Finalizer
///
/// Constructor.
///
/// The grabber
/// The grabbed point
public RuntimeGrabInfo(UxrGrabber grabber, int grabbedPoint)
{
Grabber = grabber;
GrabbedPoint = grabbedPoint;
}
///
/// Default constructor required for serialization.
///
private RuntimeGrabInfo()
{
}
#endregion
#region Implicit IUxrSerializable
///
public int SerializationVersion => 0;
///
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
///
/// Computes the grab information.
///
/// Grabber responsible for grabbing the object
/// The object being grabbed
/// Point that was grabbed
/// The grabber snap position to use
/// The grabber snap rotation to use
///
/// 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.
///
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
}
}