// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System.Collections; using UltimateXR.Avatar; using UltimateXR.Core; using UltimateXR.Core.Components.Composite; using UltimateXR.Manipulation; using UnityEngine; namespace UltimateXR.Devices.Visualization { /// /// UltimateXR has support for rendering avatars in 'controllers mode' (where the currently /// active controllers are rendered with optional hands on top) or full Avatar mode, where the actual /// visual representation of the user is rendered. /// 'Controllers mode' can be useful during tutorials or menus while the full Avatar mode will be used /// mainly for the core game/simulation. /// When we developed the big example scene we included a small gallery with all supported controllers /// so the user could grab and inspect them. But there was no link between the user input and how the /// hands and the controllers behaved (like the 'controllers mode'). /// Since one of the coolest UltimateXR features is mapping user input to controllers and having IK drive /// the hands we also wanted to link the grabbing mechanics to this. In short, what we did was to add /// UxrControllerHand components to the regular avatar hands as well, and then at runtime link them /// to the currently grabbed controller and feed the avatar input. This way we are now able to not /// just grab to controller, but also see how the hand and the controller respond to the user input. /// [RequireComponent(typeof(UxrGrabbableObject))] public class UxrMapControllerToHand : UxrGrabbableObjectComponent { #region Inspector Properties/Serialized Fields [SerializeField] private bool _ambidextrous; #endregion #region Unity /// /// Cache components. /// protected override void Start() { base.Start(); _controller3DModel = GetComponentInChildren(); } #endregion #region Coroutines /// /// Initializes the hand: /// /// /// Block input mainly to prevent locomotion while pressing the controllers. We want the user to be able to move /// the joysticks and press buttons without triggering unwanted events. /// /// /// Use the current hand pose, which is used to grab the controller, to initialize the /// component. /// /// /// Grabber that was used to grab the controller /// The controller hand component /// Coroutine IEnumerator private IEnumerator InitializeHandCoroutine(UxrGrabber grabber, UxrControllerHand controllerHand) { yield return null; controllerHand.InitializeFromCurrentHandPose(grabber.Avatar, grabber.Side); if (grabber.Side == UxrHandSide.Left) { _ignoreInputLeft = UxrControllerInput.GetIgnoreControllerInput(UxrHandSide.Left); UxrControllerInput.SetIgnoreControllerInput(UxrHandSide.Left, true); } else { _ignoreInputRight = UxrControllerInput.GetIgnoreControllerInput(UxrHandSide.Right); UxrControllerInput.SetIgnoreControllerInput(UxrHandSide.Right, true); } } #endregion #region Event Handling Methods /// /// This callback will be executed at the end of all the UltimateXR frame pipeline when the user is grabbing the /// controller. We feed both the controller and the hand's IK the user input. /// private void UxrManager_AvatarsUpdated() { _controller3DModel.UpdateFromInput(UxrAvatar.LocalAvatarInput); if (_controller3DModel.ControllerHand) { _controller3DModel.ControllerHand.UpdateIKManually(); } if (_controller3DModel.ControllerHandLeft) { _controller3DModel.ControllerHandLeft.UpdateIKManually(); } if (_controller3DModel.ControllerHandRight) { _controller3DModel.ControllerHandRight.UpdateIKManually(); } } #endregion #region Event Trigger Methods /// /// Called when the user grabs the controller. We bind the controller to the /// hand that grabbed it and subscribe to events. /// /// Grab parameters protected override void OnObjectGrabbed(UxrManipulationEventArgs e) { UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated; UxrControllerHand controllerHand = GetControllerHand(e.Grabber); if (controllerHand) { if (_controller3DModel.NeedsBothHands) { // Controller can be grabbed with both hands simultaneously if (e.Grabber.Side == UxrHandSide.Left) { _controller3DModel.ControllerHandLeft = controllerHand; } else { _controller3DModel.ControllerHandRight = controllerHand; } } else { // Some controllers are grabbed with one hand only but can be grabbed with both. Switch the component handedness // if necessary to adapt to the hand that is grabbing it if (_ambidextrous) { if (e.Grabber.Side != _controller3DModel.HandSide) { _controller3DModel.SwitchHandedness(); } } // Assign hand _controller3DModel.ControllerHand = controllerHand; } // Initialize the hand using a coroutine so that it is performed the next frame. StartCoroutine(InitializeHandCoroutine(e.Grabber, controllerHand)); controllerHand.enabled = true; } } /// protected override void OnObjectReleased(UxrManipulationEventArgs e) { OnControllerReleaseOrPlace(e); } /// protected override void OnObjectPlaced(UxrManipulationEventArgs e) { OnControllerReleaseOrPlace(e); } /// /// Called when the user releases or places this controller. We remove the connection /// with the controller and the hand that grabbed it, and unsubscribe from events. /// /// Grab parameters private void OnControllerReleaseOrPlace(UxrManipulationEventArgs e) { UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated; UxrControllerHand controllerHand = GetControllerHand(e.Grabber); if (controllerHand) { controllerHand.enabled = false; if (_controller3DModel.NeedsBothHands) { if (e.Grabber.Side == UxrHandSide.Left) { _controller3DModel.ControllerHandLeft = null; } else { _controller3DModel.ControllerHandRight = null; } } else { _controller3DModel.ControllerHand = null; } // Restore blocked input UxrControllerInput.SetIgnoreControllerInput(e.Grabber.Side, e.Grabber.Side == UxrHandSide.Left ? _ignoreInputLeft : _ignoreInputRight); } } #endregion #region Private Methods /// /// Gets the UxrControllerHand component that belongs to the grabber passed. This basically /// just checks if it is the left or right one and looks for the UxrControllerHand component /// in the hand bone object. /// /// The grabber we want to get the UxrControllerHand for /// UxrControllerHand or null if none was found private UxrControllerHand GetControllerHand(UxrGrabber grabber) { if (grabber && grabber.Side == UxrHandSide.Left && grabber.Avatar != null) { return grabber.Avatar.LeftHandBone.GetComponent(); } if (grabber && grabber.Side == UxrHandSide.Right && grabber.Avatar != null) { return grabber.Avatar.RightHandBone.GetComponent(); } return null; } #endregion #region Private Types & Data private UxrController3DModel _controller3DModel; private bool _ignoreInputLeft; private bool _ignoreInputRight; #endregion } }