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