// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using UltimateXR.Avatar;
using UltimateXR.Core;
using UltimateXR.Devices;
using UnityEngine;
namespace UltimateXR.Manipulation
{
///
/// Defines a 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 , which gives flexibility
/// by allowing it to be grabbed around or along an axis passing through that point, for example.
///
[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 _avatarGripPoseEntries = new List();
[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
///
/// Gets the 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 component.
///
public int GrabberProximityTransformIndex => GrabberProximityUseDefault ? -1 : GrabberProximityIndex;
///
/// Gets how many grip pose entries there are. 1 (the default grip pose info) plus all the registered avatar ones.
///
public int GripPoseInfoCount => 1 + _avatarGripPoseEntries.Count;
///
/// Gets the registered avatars for specific grip poses and properties.
///
public List AvatarGripPoseEntries => _avatarGripPoseEntries;
///
/// 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.
///
public bool IsEditorFoldedOut
{
get => _editorFoldout;
set => _editorFoldout = value;
}
///
/// Gets or sets the grab point display name in the inspector.
///
public string EditorName
{
get => _editorName;
set => _editorName = value;
}
///
/// Gets or sets the grab mode.
///
public UxrGrabMode GrabMode
{
get => _grabMode;
set => _grabMode = value;
}
///
/// Gets or sets whether to use the default grab buttons to grab the object using the grab point.
///
public bool UseDefaultGrabButtons
{
get => _useDefaultGrabButtons;
set => _useDefaultGrabButtons = value;
}
///
/// Gets or sets whether both hands are compatible with the grab point.
///
public bool BothHandsCompatible
{
get => _bothHandsCompatible;
set => _bothHandsCompatible = value;
}
///
/// If is false, tells which hand is used to grab the object using the grab point.
///
public UxrHandSide HandSide
{
get => _handSide;
set => _handSide = value;
}
///
/// If is false, tells which buttons are used to grab the object using the grab
/// point.
///
public UxrInputButtons InputButtons
{
get => _inputButtons;
set => _inputButtons = value;
}
///
/// Gets or sets whether to hide the hand while it is grabbing the object using the grab point.
///
public bool HideHandGrabberRenderer
{
get => _hideHandGrabberRenderer;
set => _hideHandGrabberRenderer = value;
}
///
/// 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.
///
public UxrGripPoseInfo DefaultGripPoseInfo
{
get => _defaultGripPoseInfo;
set => _defaultGripPoseInfo = value;
}
///
/// Gets or sets how the object will snap to the hand when it is grabbed using the grab point.
///
public UxrSnapToHandMode SnapMode
{
get => _snapMode;
set => _snapMode = value;
}
///
/// 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.
///
public UxrHandSnapDirection SnapDirection
{
get => _snapDirection;
set => _snapDirection = value;
}
///
/// Gets or sets which reference to use for snapping when the object is grabbed using the grab point.
///
public UxrSnapReference SnapReference
{
get => _snapReference;
set => _snapReference = value;
}
///
/// Gets or sets whether to align the grab to the controller axes, useful when grabbing objects that require aiming,
/// such as weapons.
///
public bool AlignToController
{
get => _alignToController;
set => _alignToController = value;
}
///
/// Gets or sets the transform in the grabbable object to use that will align to the controller axes (x = right, y =
/// up, z = forward).
///
public Transform AlignToControllerAxes
{
get => _alignToControllerAxes;
set => _alignToControllerAxes = value;
}
///
/// Gets or sets the proximity mode to use.
///
public UxrGrabProximityMode GrabProximityMode
{
get => _grabProximityMode;
set => _grabProximityMode = value;
}
///
/// Gets or sets the box collider used when is
/// .
///
public BoxCollider GrabProximityBox
{
get => _grabProximityBox;
set => _grabProximityBox = value;
}
///
/// Gets or sets the maximum distance the object can be grabbed using this the grab point.
///
public float MaxDistanceGrab
{
get => _maxDistanceGrab;
set => _maxDistanceGrab = value;
}
///
/// Gets or sets whether to use the own transform when computing the distance to
/// components.
///
public bool GrabProximityTransformUseSelf
{
get => _grabProximityTransformUseSelf;
set => _grabProximityTransformUseSelf = value;
}
///
/// Gets or sets the that will be used to compute the distance to
/// components when is false.
///
public Transform GrabProximityTransform
{
get => _grabProximityTransform;
set => _grabProximityTransform = value;
}
///
/// Gets or sets whether to use the transform when computing the distance to the grab point.
/// Otherwise it can specify additional proximity transforms using .
///
public bool GrabberProximityUseDefault
{
get => _grabberProximityUseDefault;
set => _grabberProximityUseDefault = value;
}
///
/// Gets or sets which additional proximity transform from to use when
/// is false.
///
public int GrabberProximityIndex
{
get => _grabberProximityIndex;
set => _grabberProximityIndex = value;
}
///
/// Gets or sets the to enable or disable when the object is grabbed or not using the grab
/// point.
///
public GameObject EnableOnHandNear
{
get => _enableOnHandNear;
set => _enableOnHandNear = value;
}
#endregion
#region Public Methods
///
/// Checks whether to create a grip pose entry for the given avatar prefab.
///
/// Prefab GUID to generate a grip pose entry for
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));
}
}
///
/// Gets a given grip pose info entry.
///
/// Index to retrieve
/// Grip pose info. If the index is 0 or not valid, it will return the default grip pose info
public UxrGripPoseInfo GetGripPoseInfo(int i)
{
if (i == 0)
{
return DefaultGripPoseInfo;
}
if (i > 0 && i <= _avatarGripPoseEntries.Count)
{
return _avatarGripPoseEntries[i - 1];
}
return null;
}
///
/// Gets a given grip pose info entry.
///
/// Prefab Guid whose info to retrieve
/// Grip pose info or null if it wasn't found
public UxrGripPoseInfo GetGripPoseInfo(string prefabGuid)
{
return _avatarGripPoseEntries.FirstOrDefault(i => i.AvatarPrefabGuid == prefabGuid);
}
///
/// Gets the grip pose info for the given avatar instance or prefab.
///
/// Avatar to get the grip pose info for
///
/// 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
///
///
/// Grip pose info. If is false it will return null if the given prefab wasn't
/// found. If is true, it will return if nor the
/// prefab nor a parent prefab entry were found
///
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;
}
///
/// Gets all the grip pose infos that can be used with the given avatar.
///
/// The avatar to check
/// Whether to check for compatibility using all the parents in the prefab hierarchy
/// List of that are potentially compatible with the given avatar
public IEnumerable 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;
}
}
}
///
/// Removes the grip pose entry of a given avatar prefab.
///
/// Prefab GUID whose information to remove
public void RemoveGripPoseInfo(string avatarPrefabGuid)
{
if (string.IsNullOrEmpty(avatarPrefabGuid))
{
return;
}
_avatarGripPoseEntries.RemoveAll(e => e.AvatarPrefabGuid == avatarPrefabGuid);
}
#endregion
}
}