// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Collections.Generic; using UltimateXR.Avatar; using UltimateXR.Core.Components.Composite; using UnityEngine; using UnityEngine.XR; namespace UltimateXR.Devices { /// /// Base class for tracking devices. /// public abstract class UxrTrackingDevice : UxrAvatarComponent, IUxrTrackingDevice, IUxrTrackingUpdater { #region Inspector Properties/Serialized Fields [Header("Mixed Reality:")] [SerializeField] private bool _hideAvatarInPassthrough = true; #endregion #region Public Types & Data /// /// Default update order. /// public const int OrderStandard = 0; /// /// Default update order for post-process tracking devices such as hand-tracking. /// public const int OrderPostprocess = 10; /// /// Gets the headset device name. /// public static string HeadsetDeviceName { get { var inputDevices = new List(); InputDevices.GetDevices(inputDevices); foreach (var device in inputDevices) { if (device.characteristics.HasFlag(InputDeviceCharacteristics.HeadMounted)) { return device.name; } } return string.Empty; } } /// /// Gets the tracking update order. /// There are cases where more than one tracking device might be active. We use TrackingUpdateOrder /// for cases where there is one that should be applied after the other(s). For example an Oculus Rift /// together with a Leap Motion setup has one tracking component for each. But Leap Motion should /// override the tracking values of the rift controllers if the Leap Motion component is active. /// In this case Oculus, like most tracking devices, has a value of /// while Leap Motion has a value of so that the tracking /// devices update the avatar in the correct order. /// public virtual int TrackingUpdateOrder => OrderStandard; /// /// Gets whether the device headset renders on top of the real world. /// public virtual bool IsMixedRealityDevice => false; #endregion #region Implicit IUxrDevice /// public abstract string SDKDependency { get; } /// public event EventHandler DeviceConnected; #endregion #region Implicit IUxrTrackingDevice /// public event EventHandler SensorsUpdating; /// public event EventHandler SensorsUpdated; /// public event EventHandler AvatarUpdating; /// public event EventHandler AvatarUpdated; #endregion #region Explicit IUxrTrackingUpdater /// void IUxrTrackingUpdater.UpdateAvatar() { OnAvatarUpdating(); UpdateAvatar(); OnAvatarUpdated(); } /// void IUxrTrackingUpdater.UpdateSensors() { OnSensorsUpdating(); UpdateSensors(); OnSensorsUpdated(); } #endregion #region Public Methods /// /// Tries to get the connected headset device. /// /// Returns the headset device if found /// Whether the device was found public static bool GetHeadsetDevice(out InputDevice inputDevice) { var inputDevices = new List(); InputDevices.GetDevices(inputDevices); foreach (var device in inputDevices) { if (device.characteristics.HasFlag(InputDeviceCharacteristics.HeadMounted)) { inputDevice = device; return true; } } inputDevice = new InputDevice(); return false; } #endregion #region Unity /// /// Sets events to null in order to help remove unused references. /// protected override void OnDestroy() { base.OnDestroy(); DeviceConnected = null; SensorsUpdating = null; SensorsUpdated = null; AvatarUpdating = null; AvatarUpdated = null; } #endregion #region Event Trigger Methods /// /// Event trigger for the event. Can be used to override in child classes in order to /// use the event without subscribing to the parent. /// /// Event parameters /// Calling the base implementation is required in child classes in order for the event to propagate correctly. protected virtual void OnDeviceConnected(UxrDeviceConnectEventArgs e) { // Hide the avatar renderers in passthrough mode for mixed reality devices? if (e.IsConnected && IsMixedRealityDevice && _hideAvatarInPassthrough) { Avatar.RenderMode = UxrAvatarRenderModes.None; } DeviceConnected?.Invoke(this, e); } /// /// Event trigger for the event. Can be used to override in child classes in order to use /// the event without subscribing to the parent. /// /// Event parameters /// Calling the base implementation is required in child classes in order for the event to propagate correctly. protected virtual void OnAvatarUpdating() { AvatarUpdating?.Invoke(this, EventArgs.Empty); } /// /// Event trigger for the event. Can be used to override in child classes in order to use /// the event without subscribing to the parent. /// /// Event parameters /// Calling the base implementation is required in child classes in order for the event to propagate correctly. protected virtual void OnAvatarUpdated() { AvatarUpdated?.Invoke(this, EventArgs.Empty); } /// /// Event trigger for the event. Can be used to override in child classes in order to /// use the event without subscribing to the parent. /// /// Event parameters /// Calling the base implementation is required in child classes in order for the event to propagate correctly. protected virtual void OnSensorsUpdating() { SensorsUpdating?.Invoke(this, EventArgs.Empty); } /// /// Event trigger for the event. Can be used to override in child classes in order to use /// the event without subscribing to the parent. /// /// Event parameters /// Calling the base implementation is required in child classes in order for the event to propagate correctly. protected virtual void OnSensorsUpdated() { SensorsUpdated?.Invoke(this, EventArgs.Empty); } #endregion #region Protected Methods /// /// Overriden in child classes to implement the update of the current sensor data. /// protected virtual void UpdateSensors() { } /// /// Overriden in child classes to implement the update of the avatar using the current sensor data. /// protected virtual void UpdateAvatar() { } #endregion } }