// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- 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 /// /// Stores information of grabs performed on a at runtime. /// An object being manipulated can have multiple grabs, registered in . /// /// /// Implements to help 's implementation of the /// interface (). /// [Serializable] private sealed class RuntimeManipulationInfo : IUxrSerializable { #region Public Types & Data /// /// Gets the grabbers currently manipulating the object. /// public IEnumerable Grabbers { get { foreach (RuntimeGrabInfo grabInfo in Grabs) { yield return grabInfo.Grabber; } } } /// /// Gets the points currently being grabbed on the object. /// public IEnumerable GrabbedPoints { get { foreach (RuntimeGrabInfo grabInfo in Grabs) { yield return grabInfo.GrabbedPoint; } } } /// /// Gets the current grabs manipulating the object. /// public List Grabs => _grabs; /// /// Gets the target from where the was grabbed. /// public UxrGrabbableObjectAnchor SourceAnchor => _sourceAnchor; /// /// Gets the current rotation angle, in objects constrained to a single rotation axis, contributed by all the grabbers /// manipulating the object. /// 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; } } /// /// Gets the grabbed object. /// public UxrGrabbableObject GrabbableObject => _grabbableObject; /// /// Gets the rotation pivot when child grabbable objects manipulate this object's orientation. /// public Vector3 LocalManipulationRotationPivot { get => _localManipulationRotationPivot; set => _localManipulationRotationPivot = value; } #endregion #region Constructors & Finalizer /// /// Constructor. /// /// Grabber of the grab /// Grab point index of the that was grabbed. /// Target if the grabbed object was placed on any. public RuntimeManipulationInfo(UxrGrabber grabber, int grabPoint, UxrGrabbableObjectAnchor sourceAnchor = null) { Grabs.Add(new RuntimeGrabInfo(grabber, grabPoint)); _grabbableObject = grabber.GrabbedObject; _sourceAnchor = sourceAnchor; } /// /// Default constructor required for serialization. /// private RuntimeManipulationInfo() { } #endregion #region Implicit IUxrSerializable /// public int SerializationVersion => 0; /// 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 /// /// Registers a new grab. /// /// Grabber that performed the grab /// The point of the that was grabbed. /// /// 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. /// /// The newly created grab info entry 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; } /// /// Removes a grab. /// /// Grabber that released the grab public void RemoveGrab(UxrGrabber grabber) { Grabs.RemoveAll(g => g.Grabber == grabber); } /// /// Removes all grabs registered. /// public void RemoveAll() { Grabs.Clear(); } /// /// Notifies a the start of a new grab, in order to compute all required data. /// /// 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. /// /// Grab information 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; } /// /// Notifies the end of a grab. /// /// Grabber that released the object /// Object that was released /// Grab point that was released 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(); } } } /// /// Gets the grab information of a specific grabber. /// /// Grabber /// Grab info or null if not found public RuntimeGrabInfo GetGrabInfo(UxrGrabber grabber) { foreach (RuntimeGrabInfo grabInfo in Grabs) { if (grabInfo.Grabber == grabber) { return grabInfo; } } return null; } /// /// Gets the point grabbed by the given grabber. /// /// Grabber /// Grabbed point in or -1 if not found public int GetGrabbedPoint(UxrGrabber grabber) { foreach (RuntimeGrabInfo grabInfo in Grabs) { if (grabInfo.Grabber == grabber) { return grabInfo.GrabbedPoint; } } return -1; } /// /// Gets the grabber grabbing the given point. /// /// Grab point in /// Grabber grabbing the given point or null if not found public UxrGrabber GetGrabberGrabbingPoint(int grabPoint) { foreach (RuntimeGrabInfo grabInfo in Grabs) { if (grabInfo.GrabbedPoint == grabPoint) { return grabInfo.Grabber; } } return null; } /// /// Checks if the given grabber is being used to manipulate the object. /// /// Grabber to check /// Whether the given grabber is being used to manipulate the object public bool IsGrabberUsed(UxrGrabber grabber) { foreach (RuntimeGrabInfo grabInfo in Grabs) { if (grabInfo.Grabber == grabber) { return true; } } return false; } /// /// Checks if the given grab point is being grabbed on the object. /// /// Grab point to check /// Whether the given grab point is being grabbed on the object public bool IsPointGrabbed(int grabPoint) { foreach (RuntimeGrabInfo grabInfo in Grabs) { if (grabInfo.GrabbedPoint == grabPoint) { return true; } } return false; } /// /// Registers a grabber swap to indicate that a different hand is now grabbing the point. /// /// Old grabber that was grabbing /// New grabber that the grab switched to public void SwapGrabber(UxrGrabber oldGrabber, UxrGrabber newGrabber) { foreach (RuntimeGrabInfo grabInfo in Grabs) { if (grabInfo.Grabber == oldGrabber) { grabInfo.Grabber = newGrabber; return; } } } /// /// Registers a grabber swap to indicate that a different hand is now grabbing another point. /// /// Old grabber that was grabbing /// Old grab point of the grabbed by the old grabber /// New grabber that the grab switched to /// New grab point of the the grab switched to 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; } } } /// /// Accumulates the contributions of all grabbers in an object constrained to a single angle rotation. /// 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 _grabs = new List(); private UxrGrabbableObjectAnchor _sourceAnchor; private UxrGrabbableObject _grabbableObject; private Vector3 _localManipulationRotationPivot; #endregion } #endregion } }