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