// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Core;
using UltimateXR.Core.Components;
using UltimateXR.Extensions.Unity.Math;
using UnityEngine;
namespace UltimateXR.Avatar.Controllers
{
///
/// Component that describes a box volume where any hand that gets inside automatically adopts
/// a given hand pose.
///
///
/// The finger pointing pose should only be adopted if it doesn't interfere with any other interaction, such
/// as the grab pose while grabbing an object inside the volume.
///
[RequireComponent(typeof(BoxCollider))]
public class UxrHandPoseVolume : UxrComponent
{
#region Inspector Properties/Serialized Fields
[SerializeField] private string _poseName;
[SerializeField] private bool _leftHand = true;
[SerializeField] private bool _rightHand = true;
#endregion
#region Public Types & Data
///
/// Gets the component describing the enclosed space where to adopt the pose.
///
public BoxCollider Box => GetCachedComponent();
///
/// Gets or sets whether the left hand should adopt the pose when inside.
///
public bool UseLeftHand
{
get => _leftHand;
set => _leftHand = value;
}
///
/// Gets or sets whether the right hand should adopt the pose when inside.
///
public bool UseRightHand
{
get => _rightHand;
set => _rightHand = value;
}
#endregion
#region Unity
///
/// Subscribes to the avatars updated event.
///
protected override void OnEnable()
{
base.OnEnable();
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
}
///
/// Unsubscribes from the avatars updated event.
///
protected override void OnDisable()
{
base.OnDisable();
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
}
#endregion
#region Event Handling Methods
///
/// Called every frame after the avatars have been updated. Performs the hand check.
///
private void UxrManager_AvatarsUpdated()
{
if (UxrAvatar.LocalAvatar == null)
{
return;
}
if (UxrAvatar.LocalAvatar.AvatarController is UxrStandardAvatarController avatarController)
{
}
else
{
return;
}
if (IsCompatible(UxrHandSide.Left) && IsPointInside(UxrAvatar.LocalAvatar.GetHand(UxrHandSide.Left).Wrist.position))
{
avatarController.LeftHandDefaultPoseNameOverride = _poseName;
_leftWasInside = true;
}
else if (_leftWasInside)
{
avatarController.LeftHandDefaultPoseNameOverride = null;
_leftWasInside = false;
}
if (IsCompatible(UxrHandSide.Right) && IsPointInside(UxrAvatar.LocalAvatar.GetHand(UxrHandSide.Right).Wrist.position))
{
avatarController.RightHandDefaultPoseNameOverride = _poseName;
_rightWasInside = true;
}
else if (_rightWasInside)
{
avatarController.RightHandDefaultPoseNameOverride = null;
_rightWasInside = false;
}
}
#endregion
#region Private Methods
///
/// Checks if a point is inside the attached to the this component
/// is attached to.
///
/// Point in world coordinates
/// Margin to add to the box sides
/// True if it is inside, false if not
private bool IsPointInside(Vector3 point, float margin = 0.0f)
{
return point.IsInsideBox(Box, Vector3.one * margin);
}
///
/// Checks if the volume is compatible with the given hand. This allows some volumes to work for the left or
/// right hand only.
///
/// Hand to check
/// Boolean telling whether the given hand is compatible or not
private bool IsCompatible(UxrHandSide handSide)
{
return (handSide == UxrHandSide.Left && UseLeftHand) || (handSide == UxrHandSide.Right && UseRightHand);
}
#endregion
#region Private Types & Data
private bool _leftWasInside;
private bool _rightWasInside;
#endregion
}
}