/**************************************************************************** * Copyright 2019 Nreal Techonology Limited. All rights reserved. * * This file is part of NRSDK. * * https://www.nreal.ai/ * *****************************************************************************/ namespace NRKernal { using System; using UnityEngine; /// Enumeration of handedness. public enum ControllerHandEnum { /// An enum constant representing the right option. Right = 0, /// An enum constant representing the left option. Left = 1 } /// Enumeration of raycast mode. Normally, suggest using "Laser" mode. public enum RaycastModeEnum { /// An enum constant representing the gaze option. Gaze, /// An enum constant representing the laser option. Laser } /// Enumeration of input source type. public enum InputSourceEnum { /// An enum constant representing the hands option. Hands, /// An enum constant representing the controller option. Controller } /// Enumeration of controller visual types. public enum ControllerVisualType { /// An enum constant representing the none option. None = 0, /// An enum constant representing the nreal light option. NrealLight = 1, /// An enum constant representing the phone option. Phone = 2 } /// /// The main class to handle controller related things, such as to get controller states, update /// controller states Through this class, application would create a controllerProvider which /// could be custom, then the controllerProvider iteself would define how to update the /// controller states, so that every frame NRInput could get the right states.There are max two /// states for one controllerProvider. [HelpURL("https://developer.nreal.ai/develop/unity/controller")] [ScriptOrder(NativeConstants.NRINPUT_ORDER)] public partial class NRInput : SingletonBehaviour { public GameObject rightGame; public GameObject leftGame; /// True to emulate virtual display in editor. [Tooltip("If enable this, phone virtual controller would be shown in Unity Editor")] [SerializeField] private bool m_EmulateVirtualDisplayInEditor; /// The override camera center. [SerializeField] private Transform m_OverrideCameraCenter; /// The anchor helper. [SerializeField] private ControllerAnchorsHelper m_AnchorHelper; /// The raycast mode. [SerializeField] private RaycastModeEnum m_RaycastMode = RaycastModeEnum.Laser; /// The current input source type. [SerializeField] private InputSourceEnum m_InputSourceType = InputSourceEnum.Controller; /// The click interval. [SerializeField] private float m_ClickInterval = 0.3f; /// The drag threshold. [SerializeField] private float m_DragThreshold = 0.02f; /// Manager for visual. private ControllerVisualManager m_VisualManager; /// Number of last controllers. private int m_LastControllerCount; /// True to reticle visual active. private bool m_ReticleVisualActive = true; /// True to laser visual active. private bool m_LaserVisualActive = true; /// True to controller visual active. private bool m_ControllerVisualActive = true; /// True to enable, false to disable the haptic vibration. private bool m_HapticVibrationEnabled = true; /// True to active, false to disactive the gameobjects of raycasters. private bool m_RaycastersActive = true; /// Whether has checked the camera center. private bool m_HasCheckedCameraCenter; /// True means will do something OnValidate. private bool m_IsListeningToEditorValidateEvents = false; /// The cached input source type in Editor. private InputSourceEnum m_EditorCachedInputSourceType = InputSourceEnum.Controller; /// True to ignore recenter callback. private static bool m_IgnoreRecenterCallback = false; /// The domain hand. private static ControllerHandEnum m_DomainHand = ControllerHandEnum.Right; /// The controller provider. private static ControllerProviderBase m_ControllerProvider; /// The states. private static ControllerState[] m_States = new ControllerState[MAX_CONTROLLER_STATE_COUNT] { new ControllerState(), new ControllerState() }; /// Max count of controllerstates supported per frame. public const int MAX_CONTROLLER_STATE_COUNT = 2; /// Event invoked whenever the domain hand has changed. public static Action OnDomainHandChanged; /// Event invoked whenever a controller device is connected. public static Action OnControllerConnected; /// Event invoked whenever a controller device is disconnected. public static Action OnControllerDisconnected; /// Event invoked before controller devices are going to recenter. public static Action OnBeforeControllerRecenter; /// Event invoked whenever controller devices are recentering. internal static Action OnControllerRecentering; /// Event invoked whenever controller devices are recentered. public static Action OnControllerRecentered; /// Event invoked whenever controller devices states are updated. public static Action OnControllerStatesUpdated; /// /// Determine whether to show reticle visuals, could be get and set at runtime. /// True if reticle visual active, false if not. public static bool ReticleVisualActive { get { return Instance.m_ReticleVisualActive; } set { Instance.m_ReticleVisualActive = value; } } /// Determine whether to show laser visuals, could be get and set at runtime. /// True if laser visual active, false if not. public static bool LaserVisualActive { get { return Instance.m_LaserVisualActive; } set { Instance.m_LaserVisualActive = value; } } /// /// Determine whether to show controller visuals, could be get and set at runtime. /// True if controller visual active, false if not. public static bool ControllerVisualActive { get { return Instance.m_ControllerVisualActive; } set { Instance.m_ControllerVisualActive = value; } } /// Determine whether enable haptic vibration. /// True if haptic vibration enabled, false if not. public static bool HapticVibrationEnabled { get { return Instance.m_HapticVibrationEnabled; } set { Instance.m_HapticVibrationEnabled = value; } } /// /// Determine whether to active raycaster gameobjects, could be get and set at runtime. /// True if active raycaster gameobjects, false if not. public static bool RaycastersActive { get { return Instance.m_RaycastersActive; } set { Instance.m_RaycastersActive = value; } } /// Determine whether emulate phone virtual display in Unity Editor. /// True if emulate virtual display in editor, false if not. public static bool EmulateVirtualDisplayInEditor { get { return Instance ? Instance.m_EmulateVirtualDisplayInEditor : false; } } /// It's a helper to get controller anchors which are frequently used. /// The anchors helper. public static ControllerAnchorsHelper AnchorsHelper { get { return Instance.m_AnchorHelper; } } /// Get the current enumeration of handedness. /// The domain hand. public static ControllerHandEnum DomainHand { get { return m_DomainHand; } } /// Determine which raycast mode to use. /// The raycast mode. public static RaycastModeEnum RaycastMode { get { return Instance.m_RaycastMode; } set { Instance.m_RaycastMode = value; } } /// Get the current input source type. /// The input source type. public static InputSourceEnum CurrentInputSourceType { get { return Instance.m_InputSourceType; } } /// Get and set button click interval. /// The click interval. public static float ClickInterval { get { return Instance.m_ClickInterval; } set { Instance.m_ClickInterval = value; } } /// Get and set pointer drag threshold. /// The drag threshold. public static float DragThreshold { get { return Instance.m_DragThreshold; } set { Instance.m_DragThreshold = value; } } /// Get the transform of the camera which controllers are following. /// The camera center. public static Transform CameraCenter { get { return Instance.GetCameraCenter(); } } /// The HandsManager which controls the hand-tracking. public static NRHandsManager Hands = new NRHandsManager(); /// Starts this object. private void Start() { if (isDirty) { return; } Init(); Invoke("showContorl",1f); } void showContorl() { leftGame.SetActive(true); rightGame.SetActive(true); } /// Executes the 'update' action. private void OnUpdate() { if (m_ControllerProvider == null) return; UpdateControllerProvider(); } /// Updates the controller provider. private void UpdateControllerProvider() { if (m_ControllerProvider.Inited) { m_ControllerProvider.Update(); if (OnControllerStatesUpdated != null) { OnControllerStatesUpdated(); } CheckControllerConnection(); CheckControllerRecentered(); CheckControllerButtonEvents(); } else { m_ControllerProvider.Update(); #if !UNITY_EDITOR if (m_ControllerProvider is NRControllerProvider) { m_DomainHand = ((NRControllerProvider)m_ControllerProvider).GetHandednessType(); NRDebugger.Info("[NRInput] Set default domain hand:" + m_DomainHand); } #endif } } /// Executes the 'enable' action. private void OnEnable() { if (isDirty) { return; } NRKernalUpdater.OnPostUpdate += OnUpdate; m_ControllerProvider?.OnResume(); } /// Executes the 'disable' action. private void OnDisable() { if (isDirty) { return; } NRKernalUpdater.OnPostUpdate -= OnUpdate; m_ControllerProvider?.OnPause(); } #if UNITY_EDITOR /// Executes the 'validate' action. private void OnValidate() { if (!m_IsListeningToEditorValidateEvents) return; if (m_EditorCachedInputSourceType != m_InputSourceType) { SetInputSource(m_InputSourceType); } } #endif /// Gets a version. /// Zero-based index of the. /// The version. public string GetVersion(int index) { if (m_ControllerProvider is NRControllerProvider) { return ((NRControllerProvider)m_ControllerProvider).GetVersion(index); } else { return "0.0.0"; } } /// Destroys this object. internal static void Destroy() { if (m_ControllerProvider != null) { m_ControllerProvider.OnDestroy(); m_ControllerProvider = null; } } /// /// Base OnDestroy method that destroys the Singleton's unique instance. Called by Unity when /// destroying a MonoBehaviour. Scripts that extend Singleton should be sure to call /// base.OnDestroy() to ensure the underlying static Instance reference is properly cleaned up. new void OnDestroy() { if (isDirty) { return; } base.OnDestroy(); Destroy(); } /// Check controller connection. private void CheckControllerConnection() { int currentControllerCount = GetAvailableControllersCount(); if (m_LastControllerCount < currentControllerCount) { if (OnControllerConnected != null) { OnControllerConnected(); } } else if (m_LastControllerCount > currentControllerCount) { if (OnControllerDisconnected != null) { OnControllerDisconnected(); } } m_LastControllerCount = currentControllerCount; } /// Check controller recentered. private void CheckControllerRecentered() { if (GetControllerState(DomainHand).recentered) { if (m_IgnoreRecenterCallback == false && OnBeforeControllerRecenter != null) { OnBeforeControllerRecenter(); } if (OnControllerRecentering != null) { OnControllerRecentering(); } if (m_IgnoreRecenterCallback == false && OnControllerRecentered != null) { OnControllerRecentered(); } m_IgnoreRecenterCallback = false; } } /// Check controller button events. private void CheckControllerButtonEvents() { int currentControllerCount = GetAvailableControllersCount(); for (int i = 0; i < currentControllerCount; i++) { m_States[i].CheckButtonEvents(); } } /// Executes the 'application pause' action. /// True if paused. private void OnApplicationPause(bool paused) { if (m_ControllerProvider == null || !m_ControllerProvider.Inited) return; if (paused) { m_ControllerProvider.OnPause(); } else { m_ControllerProvider.OnResume(); m_IgnoreRecenterCallback = true; m_ControllerProvider.Recenter(); } } /// Initializes this object. private void Init() { NRDebugger.Info("[NRInput] Init"); NRDevice.Instance.Init(); m_VisualManager = gameObject.AddComponent(); m_VisualManager.Init(m_States); SwitchControllerProvider(ControllerProviderFactory.controllerProviderType); #if UNITY_EDITOR InitEmulator(); m_IsListeningToEditorValidateEvents = true; #endif SetInputSourceSafely(m_InputSourceType); } #if UNITY_EDITOR private void InitEmulator() { if (!NREmulatorManager.Inited && !GameObject.Find("NREmulatorManager")) { NREmulatorManager.Inited = true; GameObject.Instantiate(Resources.Load("Prefabs/NREmulatorManager")); } if (!GameObject.Find("NREmulatorController")) { Instantiate(Resources.Load("Prefabs/NREmulatorController")); } } #endif /// Gets camera center. /// The camera center. private Transform GetCameraCenter() { if (m_OverrideCameraCenter == null) { m_HasCheckedCameraCenter = true; return NRSessionManager.Instance.CenterCameraAnchor; } else { if (!m_HasCheckedCameraCenter) { CheckCameraCenter(); } return m_OverrideCameraCenter; } } /// To guarantee the camera center was right. private void CheckCameraCenter() { if (m_OverrideCameraCenter != null && NRSessionManager.Instance != null && NRSessionManager.Instance.NRSessionBehaviour != null) { var cameraRigTransform = NRSessionManager.Instance.NRSessionBehaviour.transform; if (m_OverrideCameraCenter.parent == cameraRigTransform) { m_OverrideCameraCenter = NRSessionManager.Instance.CenterCameraAnchor; } } m_HasCheckedCameraCenter = true; } /// Convert hand to index. /// . /// The hand converted to index. private static int ConvertHandToIndex(ControllerHandEnum handEnum) { if (GetAvailableControllersCount() < 2) { return DomainHand == handEnum ? 0 : 1; } else { return (int)handEnum; } } /// Gets controller state. /// The hand. /// The controller state. private static ControllerState GetControllerState(ControllerHandEnum hand) { return m_States[ConvertHandToIndex(hand)]; } /// /// Set the current input source with fallback /// /// private static void SetInputSourceSafely(InputSourceEnum inputSourceType) { var adaptInputSourceType = AdaptInputSource(inputSourceType); if (adaptInputSourceType != inputSourceType) { NRDebugger.Warning("[NRInput] AutoAdaptInputSource : {0} => {1}", inputSourceType, adaptInputSourceType); inputSourceType = adaptInputSourceType; } if (SetInputSource(inputSourceType)) { return; } var fallbackInputSourceType = InputSourceEnum.Controller; NRDebugger.Info("[NRInput] Set Input Source To {0} Failed. Now Set Input Source To Fallback: {1}", inputSourceType, fallbackInputSourceType); SetInputSource(fallbackInputSourceType); } /// Auto adaption for inputSource based on supported feature on current device. /// Fallback inputSource. private static InputSourceEnum AdaptInputSource(InputSourceEnum inputSourceType) { if (inputSourceType == InputSourceEnum.Hands && !NRDevice.Subsystem.IsFeatureSupported(NRSupportedFeature.NR_FEATURE_HANDTRACKING)) return InputSourceEnum.Controller; return inputSourceType; } /// /// To swith the controller provider /// /// internal static void SwitchControllerProvider(Type providerType) { if (m_ControllerProvider != null && m_ControllerProvider.GetType() == providerType) return; var nextControllerProvider = ControllerProviderFactory.GetOrCreateControllerProvider(providerType, m_States); if (nextControllerProvider == null) return; if (m_ControllerProvider != null) { m_ControllerProvider.OnPause(); } m_ControllerProvider = nextControllerProvider; if (m_ControllerProvider != null) { m_ControllerProvider.OnResume(); } } /// Set the current enumeration of handedness. /// . public static void SetDomainHandMode(ControllerHandEnum handEnum) { if (m_DomainHand == handEnum) return; m_DomainHand = handEnum; if (OnDomainHandChanged != null) { OnDomainHandChanged(m_DomainHand); } } /// Set the current input source. /// /// The result of setting input source. public static bool SetInputSource(InputSourceEnum inputSourceType) { NRDebugger.Info("[NRInput] Set Input Source: " + inputSourceType); if (Instance == null) { return false; } bool success = true; switch (inputSourceType) { case InputSourceEnum.Hands: success = Hands.StartHandTracking(); break; case InputSourceEnum.Controller: success = Hands.StopHandTracking(); break; default: break; } if (success) { Instance.m_InputSourceType = inputSourceType; #if UNITY_EDITOR Instance.m_EditorCachedInputSourceType = inputSourceType; #endif } NRDebugger.Info("[NRInput] Input Source Set. Current Input Source = " + CurrentInputSourceType); return success; } /// Get the current count of controllers which are connected and available. /// The available controllers count. public static int GetAvailableControllersCount() { if (m_ControllerProvider == null) { return 0; } return m_ControllerProvider.ControllerCount; } /// Get the ControllerType of current controller. /// The controller type. public static ControllerType GetControllerType() { return GetControllerState(DomainHand).controllerType; } /// Get the ConnectionState of current controller. /// The controller connection state. public static ControllerConnectionState GetControllerConnectionState() { return GetControllerState(DomainHand).connectionState; } /// Returns true if the controller is available. /// . /// True if it succeeds, false if it fails. public static bool CheckControllerAvailable(ControllerHandEnum handEnum) { if (m_ControllerProvider is NRHandControllerProvider) { return Hands.GetHandState(handEnum == ControllerHandEnum.Right ? HandEnum.RightHand : HandEnum.LeftHand).pointerPoseValid; } int availableCount = GetAvailableControllersCount(); if (availableCount == 2) { return true; } if (availableCount == 1) { return handEnum == DomainHand; } return false; } /// Returns true if the current controller supports the certain feature. /// The feature. /// True if it succeeds, false if it fails. public static bool GetControllerAvailableFeature(ControllerAvailableFeature feature) { if (GetAvailableControllersCount() == 0) return false; return GetControllerState(m_DomainHand).IsFeatureAvailable(feature); } /// Returns true if the button is currently pressed this frame. /// The button. /// True if it succeeds, false if it fails. public static bool GetButton(ControllerButton button) { return GetButton(m_DomainHand, button); } /// Returns true if the button was pressed down this frame. /// The button. /// True if it succeeds, false if it fails. public static bool GetButtonDown(ControllerButton button) { return GetButtonDown(m_DomainHand, button); } /// Returns true if the button was released this frame. /// The button. /// True if it succeeds, false if it fails. public static bool GetButtonUp(ControllerButton button) { return GetButtonUp(m_DomainHand, button); } /// Returns true if the touchpad is being touched. /// True if touching, false if not. public static bool IsTouching() { return IsTouching(m_DomainHand); } /// /// Returns a Vector2 touch position on touchpad of the domain controller, range: x(-1f ~ 1f), y(- /// 1f ~ 1f) /// The touch. public static Vector2 GetTouch() { return GetTouch(m_DomainHand); } /// Returns a Vector2 delta touch value on touchpad of the domain controller. /// The delta touch. public static Vector2 GetDeltaTouch() { return GetDeltaTouch(m_DomainHand); } /// /// Returns the current position of the domain controller if 6dof, otherwise returns Vector3.zero. /// The position. public static Vector3 GetPosition() { return GetPosition(m_DomainHand); } /// Returns the current rotation of the domain controller. /// The rotation. public static Quaternion GetRotation() { return GetRotation(m_DomainHand); } /// Returns the gyro sensor value of the domain controller. /// The gyro. public static Vector3 GetGyro() { return GetGyro(m_DomainHand); } /// Returns the accel sensor value of the domain controller. /// The accel. public static Vector3 GetAccel() { return GetAccel(m_DomainHand); } /// Returns the magnetic sensor value of the domain controller. /// The magnitude. public static Vector3 GetMag() { return GetMag(m_DomainHand); } /// Returns the battery level of the domain controller. /// The controller battery. public static int GetControllerBattery() { return GetControllerBattery(DomainHand); } /// Trigger vibration of the domain controller. /// (Optional) The duration in seconds. /// (Optional) The frequency. /// (Optional) The amplitude. public static void TriggerHapticVibration(float durationSeconds = 0.1f, float frequency = 200f, float amplitude = 0.8f) { TriggerHapticVibration(m_DomainHand, durationSeconds, frequency, amplitude); } /// /// Returns true if the button is currently pressed this frame on a certain handedness /// controller. /// The hand. /// The button. /// True if it succeeds, false if it fails. public static bool GetButton(ControllerHandEnum hand, ControllerButton button) { return GetControllerState(hand).GetButton(button); } /// /// Returns true if the button was pressed down this frame on a certain handedness controller. /// The hand. /// The button. /// True if it succeeds, false if it fails. public static bool GetButtonDown(ControllerHandEnum hand, ControllerButton button) { return GetControllerState(hand).GetButtonDown(button); } /// /// Returns true if the button was released this frame on a certain handedness controller. /// The hand. /// The button. /// True if it succeeds, false if it fails. public static bool GetButtonUp(ControllerHandEnum hand, ControllerButton button) { return GetControllerState(hand).GetButtonUp(button); } /// /// Returns true if the touchpad is being touched this frame on a certain handedness controller. /// The hand. /// True if touching, false if not. public static bool IsTouching(ControllerHandEnum hand) { return GetControllerState(hand).isTouching; } /// /// Returns a Vector2 touch position on touchpad of a certain handedness controller, range: x(-1f /// ~ 1f), y(-1f ~ 1f) /// The hand. /// The touch. public static Vector2 GetTouch(ControllerHandEnum hand) { return GetControllerState(hand).touchPos; } /// /// Returns a Vector2 delta touch value on touchpad of a certain handedness controller. /// The hand. /// The delta touch. public static Vector2 GetDeltaTouch(ControllerHandEnum hand) { return GetControllerState(hand).deltaTouch; } /// /// Returns the current position of a certain handedness controller if 6dof, otherwise returns /// Vector3.zero. /// The hand. /// The position. public static Vector3 GetPosition(ControllerHandEnum hand) { return GetControllerState(hand).position; } /// Returns the current rotation of a certain handedness controller. /// The hand. /// The rotation. public static Quaternion GetRotation(ControllerHandEnum hand) { return GetControllerState(hand).rotation; } /// Returns the gyro sensor value of a certain handedness controller. /// The hand. /// The gyro. public static Vector3 GetGyro(ControllerHandEnum hand) { return GetControllerState(hand).gyro; } /// Returns the accel sensor value of a certain handedness controller. /// The hand. /// The accel. public static Vector3 GetAccel(ControllerHandEnum hand) { return GetControllerState(hand).accel; } /// Returns the magnetic sensor value of a certain handedness controller. /// The hand. /// The magnitude. public static Vector3 GetMag(ControllerHandEnum hand) { return GetControllerState(hand).mag; } /// /// Returns the battery level of a certain handedness controller, range from 0 to 100. /// The hand. /// The controller battery. public static int GetControllerBattery(ControllerHandEnum hand) { return GetControllerState(hand).batteryLevel; } /// Trigger vibration of a certain handedness controller. /// The hand. /// (Optional) The duration in seconds. /// (Optional) The frequency. /// (Optional) The amplitude. public static void TriggerHapticVibration(ControllerHandEnum hand, float durationSeconds = 0.1f, float frequency = 200f, float amplitude = 0.8f) { if (!HapticVibrationEnabled) return; if (GetAvailableControllersCount() == 0) return; m_ControllerProvider.TriggerHapticVibration(ConvertHandToIndex(hand), durationSeconds, frequency, amplitude); } /// Recenter controller. public static void RecenterController() { if (GetAvailableControllersCount() == 0) return; m_IgnoreRecenterCallback = false; m_ControllerProvider.Recenter(); } /// Add button down event listerner. /// The hand. /// The button. /// The action. public static void AddDownListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).AddButtonListener(ButtonEventType.Down, button, action); } /// Remove button down event listerner. /// The hand. /// The button. /// The action. public static void RemoveDownListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).RemoveButtonListener(ButtonEventType.Down, button, action); } /// Add button pressing event listerner. /// The hand. /// The button. /// The action. public static void AddPressingListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).AddButtonListener(ButtonEventType.Pressing, button, action); } /// Remove button pressing event listerner. /// The hand. /// The button. /// The action. public static void RemovePressingListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).RemoveButtonListener(ButtonEventType.Pressing, button, action); } /// Add button up event listerner. /// The hand. /// The button. /// The action. public static void AddUpListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).AddButtonListener(ButtonEventType.Up, button, action); } /// Remove button up event listerner. /// The hand. /// The button. /// The action. public static void RemoveUpListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).RemoveButtonListener(ButtonEventType.Up, button, action); } /// Add button click event listerner. /// The hand. /// The button. /// The action. public static void AddClickListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).AddButtonListener(ButtonEventType.Click, button, action); } /// Remove button click event listerner. /// The hand. /// The button. /// The action. public static void RemoveClickListener(ControllerHandEnum hand, ControllerButton button, Action action) { GetControllerState(hand).RemoveButtonListener(ButtonEventType.Click, button, action); } } }