Add ultimate xr

This commit is contained in:
2024-08-06 21:58:35 +02:00
parent 864033bf10
commit 7165bacd9d
3952 changed files with 2162037 additions and 35 deletions

View File

@@ -0,0 +1,421 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrGrabbableResizable.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections;
using UltimateXR.Avatar;
using UltimateXR.Core;
using UltimateXR.Core.Components;
using UltimateXR.Extensions.Unity;
using UnityEngine;
#pragma warning disable 67 // Disable warnings due to unused events
namespace UltimateXR.Manipulation.Helpers
{
/// <summary>
/// <para>
/// Component that allows an object to be scaled by grabbing it by both sides and moving them closer or apart.
/// The hierarchy should be as follows:
/// </para>
/// <code>
/// -Root GameObject: With UxrGrabbableResizable and UxrGrabbableObject component.
/// | The UxrGrabbableObject is a dummy grabbable parent that enables moving
/// | this root by grabbing the child extensions. It can also have its own
/// | grab points but they are not required.
/// |---Root resizable: Object that will be scaled when the two extensions are moved.
/// |---Grabbable left: Left grabbable extension with locked rotation and translation
/// | constrained to sliding it left-right.
/// |---Grabbable right: Right grabbable extension with locked rotation and translation
/// constrained to sliding it left-right.
/// </code>
/// All objects should use an axis system with x right, y up and z forward.
/// </summary>
public sealed partial class UxrGrabbableResizable : UxrComponent, IUxrGrabbable
{
#region Inspector Properties/Serialized Fields
[Header("General")] [SerializeField] private Transform _resizableRoot;
[SerializeField] private float _startScale = 1.0f;
[Header("Grabbing")] [SerializeField] private UxrGrabbableObject _grabbableRoot;
[SerializeField] private UxrGrabbableObject _grabbableExtendLeft;
[SerializeField] private UxrGrabbableObject _grabbableExtendRight;
[Header("Haptics")] [SerializeField] [Range(0.0f, 1.0f)] private float _hapticsIntensity = 0.1f;
#endregion
#region Public Types & Data
/// <summary>
/// Gets the <see cref="Transform" /> that is going to be scaled when the two grabbable objects are moved apart.
/// </summary>
public Transform ResizableRoot => _resizableRoot;
/// <summary>
/// Gets the root grabbable object.
/// </summary>
public UxrGrabbableObject GrabbableRoot => _grabbableRoot;
/// <summary>
/// Gets the left grabbable extension.
/// </summary>
public UxrGrabbableObject GrabbableExtendLeft => _grabbableExtendLeft;
/// <summary>
/// Gets the right grabbable extension.
/// </summary>
public UxrGrabbableObject GrabbableExtendRight => _grabbableExtendRight;
#endregion
#region Implicit IUxrGrabbable
/// <inheritdoc />
public bool IsBeingGrabbed => GrabbableRoot.IsBeingGrabbed || GrabbableExtendLeft.IsBeingGrabbed || GrabbableExtendRight.IsBeingGrabbed;
/// <inheritdoc />
public bool IsGrabbable
{
get => GrabbableRoot.IsGrabbable || GrabbableExtendLeft.IsGrabbable || GrabbableExtendRight.IsGrabbable;
set
{
BeginSync();
GrabbableRoot.IsGrabbable = value;
GrabbableExtendLeft.IsGrabbable = value;
GrabbableExtendRight.IsGrabbable = value;
EndSyncProperty(value);
}
}
/// <inheritdoc />
public bool IsKinematic
{
get => GrabbableRoot.IsKinematic || GrabbableExtendLeft.IsKinematic || GrabbableExtendRight.IsKinematic;
set
{
BeginSync();
GrabbableRoot.IsKinematic = value;
GrabbableExtendLeft.IsKinematic = value;
GrabbableExtendRight.IsKinematic = value;
EndSyncProperty(value);
}
}
/// <inheritdoc />
public event EventHandler<UxrManipulationEventArgs> Grabbing;
/// <inheritdoc />
public event EventHandler<UxrManipulationEventArgs> Grabbed;
/// <inheritdoc />
public event EventHandler<UxrManipulationEventArgs> Releasing;
/// <inheritdoc />
public event EventHandler<UxrManipulationEventArgs> Released;
/// <inheritdoc />
public event EventHandler<UxrManipulationEventArgs> Placing;
/// <inheritdoc />
public event EventHandler<UxrManipulationEventArgs> Placed;
/// <inheritdoc />
public void ResetPositionAndState(bool propagateEvents)
{
// This method will be synchronized through network
BeginSync();
ReleaseGrabs(true);
GrabbableRoot.ResetPositionAndState(propagateEvents);
GrabbableExtendLeft.ResetPositionAndState(propagateEvents);
GrabbableExtendRight.ResetPositionAndState(propagateEvents);
UpdateResizableScale();
EndSyncMethod(new object[] { propagateEvents });
}
/// <inheritdoc />
public void ReleaseGrabs(bool propagateEvents)
{
// This method will be synchronized through network
BeginSync();
GrabbableRoot.ReleaseGrabs(propagateEvents);
GrabbableExtendLeft.ReleaseGrabs(propagateEvents);
GrabbableExtendRight.ReleaseGrabs(propagateEvents);
_grabbingCount = 0;
_grabbedCount = 0;
EndSyncMethod(new object[] { propagateEvents });
}
#endregion
#region Unity
/// <summary>
/// Initializes the component.
/// </summary>
protected override void Awake()
{
base.Awake();
_initialGrabsSeparation = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
_initialResizableLocalScale = _resizableRoot.transform.localScale;
_separationToBoundsFactor = _initialGrabsSeparation / _resizableRoot.gameObject.GetLocalBounds(true).size.x;
}
/// <summary>
/// Subscribes to relevant events.
/// </summary>
protected override void OnEnable()
{
base.OnEnable();
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
GrabbableRoot.Grabbing += Grabbable_Grabbing;
GrabbableRoot.Grabbed += Grabbable_Grabbed;
GrabbableRoot.Releasing += Grabbable_Releasing;
GrabbableRoot.Released += Grabbable_Released;
GrabbableExtendLeft.Grabbing += Grabbable_Grabbing;
GrabbableExtendLeft.Grabbed += Grabbable_Grabbed;
GrabbableExtendLeft.Releasing += Grabbable_Releasing;
GrabbableExtendLeft.Released += Grabbable_Released;
GrabbableExtendRight.Grabbed += Grabbable_Grabbed;
GrabbableExtendRight.Grabbing += Grabbable_Grabbing;
GrabbableExtendRight.Releasing += Grabbable_Releasing;
GrabbableExtendRight.Released += Grabbable_Released;
_hapticsCoroutine = StartCoroutine(HapticsCoroutine());
}
/// <summary>
/// Unsubscribes from relevant events.
/// </summary>
protected override void OnDisable()
{
base.OnDisable();
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
GrabbableRoot.Grabbing -= Grabbable_Grabbing;
GrabbableRoot.Grabbed -= Grabbable_Grabbed;
GrabbableRoot.Releasing -= Grabbable_Releasing;
GrabbableRoot.Released -= Grabbable_Released;
GrabbableExtendLeft.Grabbing -= Grabbable_Grabbing;
GrabbableExtendLeft.Grabbed -= Grabbable_Grabbed;
GrabbableExtendLeft.Releasing -= Grabbable_Releasing;
GrabbableExtendLeft.Released -= Grabbable_Released;
GrabbableExtendRight.Grabbed -= Grabbable_Grabbed;
GrabbableExtendRight.Grabbing -= Grabbable_Grabbing;
GrabbableExtendRight.Releasing -= Grabbable_Releasing;
GrabbableExtendRight.Released -= Grabbable_Released;
StopCoroutine(_hapticsCoroutine);
}
/// <summary>
/// Scales the resizable using the initial scale if it's different than 1.0
/// </summary>
protected override void Start()
{
base.Start();
if (!Mathf.Approximately(1.0f, _startScale))
{
float halfOffset = (_startScale * _initialGrabsSeparation - _initialGrabsSeparation) * 0.5f;
GrabbableExtendLeft.transform.localPosition -= Vector3.right * halfOffset;
GrabbableExtendRight.transform.localPosition += Vector3.right * halfOffset;
}
}
#endregion
#region Coroutines
/// <summary>
/// Coroutine that sends haptic feedback in case of scaling.
/// </summary>
/// <returns>Coroutine IEnumerator</returns>
private IEnumerator HapticsCoroutine()
{
void SendHapticClip(UxrGrabbableObject grabbableObject, UxrHandSide handSide, float speed)
{
if (_hapticsIntensity < 0.001f || !UxrGrabManager.Instance.GetGrabbingHand(grabbableObject, 0, out UxrGrabber grabber) || grabber.Avatar.AvatarMode != UxrAvatarMode.Local)
{
return;
}
float quantityPos = HapticsManipulationMaxSpeed - HapticsManipulationMinSpeed <= 0.0f ? 0.0f : (speed - HapticsManipulationMinSpeed) / (HapticsManipulationMaxSpeed - HapticsManipulationMinSpeed);
if (quantityPos > 0.0f)
{
float frequencyPos = Mathf.Lerp(HapticsManipulationMinFrequency, HapticsManipulationMaxFrequency, Mathf.Clamp01(quantityPos));
float amplitudePos = Mathf.Lerp(0.1f, 1.0f, Mathf.Clamp01(quantityPos)) * _hapticsIntensity;
UxrAvatar.LocalAvatarInput.SendHapticFeedback(handSide, frequencyPos, amplitudePos, UxrConstants.InputControllers.HapticSampleDurationSeconds);
}
}
float lastDistance = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
while (true)
{
if (_grabbableExtendLeft != null && _grabbableExtendRight != null && _grabbableExtendLeft.IsBeingGrabbed && _grabbableExtendRight.IsBeingGrabbed)
{
float currentDistance = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
float speed = Mathf.Abs(lastDistance - currentDistance) / UxrConstants.InputControllers.HapticSampleDurationSeconds;
SendHapticClip(_grabbableExtendLeft, UxrHandSide.Left, speed);
SendHapticClip(_grabbableExtendRight, UxrHandSide.Right, speed);
lastDistance = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
}
yield return new WaitForSeconds(UxrConstants.InputControllers.HapticSampleDurationSeconds);
}
}
#endregion
#region Event Handling Methods
/// <summary>
/// Called right after the avatars and manipulation update. Scale the object at this point.
/// </summary>
private void UxrManager_AvatarsUpdated()
{
UpdateResizableScale();
}
/// <summary>
/// Called when any grabbable is about to be grabbed. It is responsible for sending the appropriate
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
/// </summary>
/// <param name="sender">Event sender</param>
/// <param name="e">Event parameters</param>
private void Grabbable_Grabbing(object sender, UxrManipulationEventArgs e)
{
if (e.IsGrabbedStateChanged)
{
_grabbingCount++;
if (_grabbingCount == 1)
{
Grabbing?.Invoke(this, e);
}
}
}
/// <summary>
/// Called right after any grabbable was grabbed. It is responsible for sending the appropriate
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
/// </summary>
/// <param name="sender">Event sender</param>
/// <param name="e">Event parameters</param>
private void Grabbable_Grabbed(object sender, UxrManipulationEventArgs e)
{
if (e.IsGrabbedStateChanged)
{
_grabbedCount++;
if (_grabbedCount == 1)
{
Grabbed?.Invoke(this, e);
}
}
}
/// <summary>
/// Called when any grabbable is about to be released. It is responsible for sending the appropriate
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
/// </summary>
/// <param name="sender">Event sender</param>
/// <param name="e">Event parameters</param>
private void Grabbable_Releasing(object sender, UxrManipulationEventArgs e)
{
if (e.IsGrabbedStateChanged)
{
_grabbingCount--;
if (_grabbingCount == 0)
{
Releasing?.Invoke(this, e);
}
}
}
/// <summary>
/// Called right after any grabbable was released. It is responsible for sending the appropriate
/// <see cref="UxrGrabbableResizable" /> manipulation events if necessary.
/// </summary>
/// <param name="sender">Event sender</param>
/// <param name="e">Event parameters</param>
private void Grabbable_Released(object sender, UxrManipulationEventArgs e)
{
if (e.IsGrabbedStateChanged)
{
_grabbedCount--;
if (_grabbedCount == 0)
{
Released?.Invoke(this, e);
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Updates the resizable scale based on the current separation between the left and right extensions.
/// </summary>
private void UpdateResizableScale()
{
float currentGrabSeparation = Vector3.Distance(GrabbableExtendLeft.transform.position, GrabbableExtendRight.transform.position);
// Move the center in between the two extensions
Vector3 localCenter = transform.InverseTransformPoint((GrabbableExtendLeft.transform.position + GrabbableExtendRight.transform.position) * 0.5f);
Vector3 resizableLocalPos = _resizableRoot.transform.localPosition;
resizableLocalPos.x = localCenter.x;
_resizableRoot.transform.localPosition = resizableLocalPos;
// Scale the object
float localScaleZ = _resizableRoot.transform.localScale.z;
Vector3 resizableLocalScale = _initialResizableLocalScale * (currentGrabSeparation / _initialGrabsSeparation * _separationToBoundsFactor);
resizableLocalScale.z = localScaleZ;
_resizableRoot.transform.localScale = resizableLocalScale;
}
#endregion
#region Private Types & Data
private const float HapticsManipulationMinSpeed = 0.03f;
private const float HapticsManipulationMaxSpeed = 1.0f;
private const float HapticsManipulationMinFrequency = 10;
private const float HapticsManipulationMaxFrequency = 100;
private Vector3 _initialResizableLocalScale;
private float _initialGrabsSeparation;
private float _separationToBoundsFactor;
private int _grabbingCount;
private int _grabbedCount;
private Coroutine _hapticsCoroutine;
#endregion
}
}
#pragma warning restore 67