CurvedUIInputModule.cs 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. using UnityEngine;
  2. using System.Collections;
  3. using UnityEngine.EventSystems;
  4. using UnityEngine.UI;
  5. using System.Collections.Generic;
  6. using CurvedUI;
  7. using UnityEngine.Serialization;
  8. #if CURVEDUI_UNITY_XR
  9. using UnityEngine.XR;
  10. using UnityEngine.XR.Interaction.Toolkit;
  11. #endif
  12. #if CURVEDUI_STEAMVR_LEGACY || CURVEDUI_STEAMVR_2
  13. using Valve.VR;
  14. #endif
  15. [assembly: CurvedUI.OptionalDependency("Valve.VR.InteractionSystem.Player", "CURVEDUI_STEAMVR_INT")]
  16. [ExecuteInEditMode]
  17. #if CURVEDUI_GOOGLEVR
  18. public class CurvedUIInputModule : GvrPointerInputModule {
  19. #else
  20. public class CurvedUIInputModule : StandaloneInputModule {
  21. #endif
  22. //SETTINGS-------------------------------------------------//
  23. #region SETTINGS
  24. #pragma warning disable 414, 0649
  25. //Common
  26. [SerializeField]
  27. CUIControlMethod controlMethod;
  28. [SerializeField]
  29. string submitButtonName = "Fire1";
  30. [SerializeField]
  31. Camera mainEventCamera;
  32. [SerializeField]
  33. LayerMask raycastLayerMask = 1 << 5;
  34. //Gaze
  35. [SerializeField]
  36. bool gazeUseTimedClick = false;
  37. [SerializeField]
  38. float gazeClickTimer = 2.0f;
  39. [SerializeField]
  40. float gazeClickTimerDelay = 1.0f;
  41. [SerializeField]
  42. Image gazeTimedClickProgressImage;
  43. //World Space Mouse
  44. [SerializeField]
  45. float worldSpaceMouseSensitivity = 1;
  46. //SteamVR and Oculus
  47. [SerializeField]
  48. Hand usedHand = Hand.Right;
  49. [FormerlySerializedAs("controllerTransformOverride")] [SerializeField]
  50. Transform pointerTransformOverride;
  51. //SteamVR 2.0 specific
  52. #if CURVEDUI_STEAMVR_2
  53. [SerializeField]
  54. SteamVR_Action_Boolean m_steamVRClickAction;
  55. #endif
  56. //hidden
  57. static bool disableOtherInputModulesOnStart = true; //default true
  58. #endregion
  59. //---------------------------------------------------------//
  60. //COMMON VARIABLES-----------------------------------------//
  61. #region VARIABLES
  62. //Support Variables - common
  63. static CurvedUIInputModule instance;
  64. GameObject currentDragging;
  65. GameObject currentPointedAt;
  66. //Support Variables - handheld controllers
  67. GameObject m_rightController;
  68. GameObject m_leftController;
  69. //Support Variables - gaze
  70. float gazeTimerProgress;
  71. //Support variables - custom ray
  72. Ray customControllerRay;
  73. //support variables - other
  74. float dragThreshold = 10.0f;
  75. bool pressedDown = false;
  76. bool pressedLastFrame = false;
  77. //support variables - world space mouse
  78. Vector3 lastMouseOnScreenPos = Vector2.zero;
  79. Vector2 worldSpaceMouseInCanvasSpace = Vector2.zero;
  80. Vector2 lastWorldSpaceMouseOnCanvas = Vector2.zero;
  81. Vector2 worldSpaceMouseOnCanvasDelta = Vector2.zero;
  82. //---------------------------------------------------------//
  83. //PLATFORM DEPENDANT VARIABLES AND SETTINGS----------------//
  84. #if CURVEDUI_STEAMVR_LEGACY
  85. //Settings & References - SteamVR
  86. [SerializeField]
  87. SteamVR_ControllerManager steamVRControllerManager;
  88. //Support Variables - SteamVR
  89. private static SteamVR_ControllerManager controllerManager;
  90. private static CurvedUIViveController rightCont;
  91. private static CurvedUIViveController leftCont;
  92. private CurvedUIPointerEventData rightControllerData;
  93. private CurvedUIPointerEventData leftControllerData;
  94. #endif
  95. #if CURVEDUI_STEAMVR_2
  96. [SerializeField]
  97. SteamVR_PlayArea steamVRPlayArea;
  98. #endif
  99. #if CURVEDUI_OCULUSVR
  100. //Settings & References - Oculus SDK
  101. [SerializeField]
  102. Transform TouchControllerTransform;
  103. [SerializeField]
  104. OVRInput.Button InteractionButton = OVRInput.Button.PrimaryIndexTrigger;
  105. [SerializeField]
  106. OVRCameraRig oculusCameraRig;
  107. //Support variables - Touch
  108. private OVRInput.Controller activeCont;
  109. #endif
  110. #if CURVEDUI_UNITY_XR
  111. [SerializeField] private XRController rightXRController;
  112. [SerializeField] private XRController leftXRController;
  113. #endif
  114. #pragma warning restore 414, 0649
  115. #endregion
  116. //---------------------------------------------------------//
  117. #if !CURVEDUI_GOOGLEVR
  118. #region LIFECYCLE
  119. protected override void Awake()
  120. {
  121. forceModuleActive = true;
  122. if (!Application.isPlaying) return;
  123. Instance = this;
  124. base.Awake();
  125. //component setup
  126. EventCamera = mainEventCamera == null ? Camera.main : EventCamera;
  127. //Gaze setup
  128. if (gazeTimedClickProgressImage != null)
  129. gazeTimedClickProgressImage.fillAmount = 0;
  130. //SDK setup
  131. #if CURVEDUI_STEAMVR_LEGACY
  132. if(ControlMethod == CUIControlMethod.STEAMVR_LEGACY) SetupViveControllers();
  133. #elif CURVEDUI_STEAMVR_2
  134. if (ControlMethod == CUIControlMethod.STEAMVR_2) SetupSteamVR2Controllers();
  135. #elif CURVEDUI_UNITY_XR
  136. if (ControlMethod == CUIControlMethod.UNITY_XR) SetupUnityXRControllers();
  137. #endif
  138. }
  139. protected override void Start()
  140. {
  141. if (!Application.isPlaying) return;
  142. base.Start();
  143. //OculusVR setup
  144. #if CURVEDUI_OCULUSVR
  145. if (oculusCameraRig == null)
  146. {
  147. //find the oculus rig - via manager or by findObjectOfType, if unavailable
  148. if (OVRManager.instance != null) oculusCameraRig = OVRManager.instance.GetComponent<OVRCameraRig>();
  149. if (oculusCameraRig == null) oculusCameraRig = Object.FindObjectOfType<OVRCameraRig>();
  150. if (oculusCameraRig == null && ControlMethod == CUIControlMethod.OCULUSVR)Debug.LogError("CURVEDUI: OVRCameraRig prefab required. Import Oculus Utilities and drag OVRCameraRig prefab onto the scene.");
  151. }
  152. #endif
  153. }
  154. protected virtual void Update()
  155. {
  156. //find camera, if we lost it
  157. if (mainEventCamera == null && Application.isPlaying)
  158. EventCamera = Camera.main;
  159. if (Time.frameCount % 120 == 0)//do it only once every 120 frames
  160. {
  161. //check if we don't have extra eventSystem on the scene, as this may mess up interactions.
  162. if (EventSystem.current != null && EventSystem.current.gameObject != this.gameObject)
  163. Debug.LogError("CURVEDUI: Second EventSystem component detected. This can make UI unusable. Make sure there is only one EventSystem component on the scene. Click on this message to have the extra one selected.", EventSystem.current.gameObject);
  164. }
  165. }
  166. #endregion // LIFECYCLE
  167. #region EVENT PROCESSING - GENERAL
  168. /// <summary>
  169. /// Process() is called by UI system to process events
  170. /// </summary>
  171. public override void Process()
  172. {
  173. switch (controlMethod)
  174. {
  175. case CUIControlMethod.MOUSE: base.Process(); break;
  176. case CUIControlMethod.GAZE: ProcessGaze(); break;
  177. case CUIControlMethod.STEAMVR_LEGACY: ProcessViveControllers(); break;
  178. case CUIControlMethod.STEAMVR_2: ProcessSteamVR2Controllers(); break;
  179. case CUIControlMethod.OCULUSVR: ProcessOculusVRController();break;
  180. case CUIControlMethod.CUSTOM_RAY: ProcessCustomRayController(); break;
  181. case CUIControlMethod.UNITY_XR: ProcessUnityXRController(); break;
  182. case CUIControlMethod.WORLD_MOUSE:
  183. {
  184. //touch can also be used as a world space mouse,
  185. //although its probably not the best experience
  186. //Use standard mouse controller with touch.
  187. if (Input.touchCount > 0)
  188. worldSpaceMouseOnCanvasDelta = Input.GetTouch(0).deltaPosition * worldSpaceMouseSensitivity;
  189. else {
  190. worldSpaceMouseOnCanvasDelta = new Vector2((Input.mousePosition - lastMouseOnScreenPos).x, (Input.mousePosition - lastMouseOnScreenPos).y) * worldSpaceMouseSensitivity;
  191. lastMouseOnScreenPos = Input.mousePosition;
  192. }
  193. lastWorldSpaceMouseOnCanvas = worldSpaceMouseInCanvasSpace;
  194. worldSpaceMouseInCanvasSpace += worldSpaceMouseOnCanvasDelta;
  195. base.Process();
  196. break;
  197. }
  198. default: goto case CUIControlMethod.MOUSE;
  199. }
  200. //save button pressed state for reference in next frame
  201. pressedLastFrame = pressedDown;
  202. }
  203. #endregion // EVENT PROCESSING - GENERAL
  204. #region EVENT PROCESSING - GAZE
  205. protected virtual void ProcessGaze()
  206. {
  207. bool usedEvent = SendUpdateEventToSelectedObject();
  208. if (eventSystem.sendNavigationEvents)
  209. {
  210. if (!usedEvent) usedEvent |= SendMoveEventToSelectedObject();
  211. if (!usedEvent) SendSubmitEventToSelectedObject();
  212. }
  213. ProcessMouseEvent();
  214. }
  215. #endregion // EVENT PROCESSING - GAZE
  216. #region EVENT PROCESSING - CUSTOM RAY
  217. protected virtual void ProcessCustomRayController() {
  218. this.ProcessMouseEvent();
  219. }
  220. protected override MouseState GetMousePointerEventData(int id)
  221. {
  222. MouseState ret = base.GetMousePointerEventData(id);
  223. if(ControlMethod != CUIControlMethod.MOUSE && ControlMethod != CUIControlMethod.WORLD_MOUSE)
  224. ret.SetButtonState(PointerEventData.InputButton.Left, CustomRayFramePressedState(), ret.GetButtonState(PointerEventData.InputButton.Left).eventData.buttonData);
  225. return ret;
  226. }
  227. PointerEventData.FramePressState CustomRayFramePressedState()
  228. {
  229. if (pressedDown && !pressedLastFrame)
  230. return PointerEventData.FramePressState.Pressed;
  231. else if (!pressedDown && pressedLastFrame)
  232. return PointerEventData.FramePressState.Released;
  233. else return PointerEventData.FramePressState.NotChanged;
  234. }
  235. #endregion // EVENT PROCESSING - CUSTOM RAY
  236. #region EVENT PROCESSING - STEAMVR LEGACY
  237. protected virtual void ProcessViveControllers()
  238. {
  239. #if CURVEDUI_STEAMVR_LEGACY
  240. switch (usedHand)
  241. {
  242. case Hand.Right:
  243. {
  244. //in case only one controller is turned on, it will still be used to call events.
  245. if (controllerManager.right.activeInHierarchy)
  246. ProcessController(controllerManager.right);
  247. else if (controllerManager.left.activeInHierarchy)
  248. ProcessController(controllerManager.left);
  249. break;
  250. }
  251. case Hand.Left:
  252. {
  253. //in case only one controller is turned on, it will still be used to call events.
  254. if (controllerManager.left.activeInHierarchy)
  255. ProcessController(controllerManager.left);
  256. else if (controllerManager.right.activeInHierarchy)
  257. ProcessController(controllerManager.right);
  258. break;
  259. }
  260. case Hand.Both:
  261. {
  262. ProcessController(controllerManager.left);
  263. ProcessController(controllerManager.right);
  264. break;
  265. }
  266. default: goto case Hand.Right;
  267. }
  268. }
  269. /// <summary>
  270. /// Processes Events from given controller.
  271. /// </summary>
  272. /// <param name="myController"></param>
  273. void ProcessController(GameObject myController)
  274. {
  275. //do not process events from this controller if it's off or not visible by base stations.
  276. if (!myController.gameObject.activeInHierarchy) return;
  277. //get the assistant or add it if its missing.
  278. CurvedUIViveController myControllerAssitant = myController.AddComponentIfMissing<CurvedUIViveController>();
  279. // send update events if there is a selected object - this is important for InputField to receive keyboard events
  280. SendUpdateEventToSelectedObject();
  281. // see if there is a UI element that is currently being pointed at
  282. PointerEventData ControllerData;
  283. if (myControllerAssitant == Right)
  284. ControllerData = GetControllerPointerData(myControllerAssitant, ref rightControllerData);
  285. else
  286. ControllerData = GetControllerPointerData(myControllerAssitant, ref leftControllerData);
  287. currentPointedAt = ControllerData.pointerCurrentRaycast.gameObject;
  288. ProcessDownRelease(ControllerData, myControllerAssitant.IsTriggerDown, myControllerAssitant.IsTriggerUp);
  289. //Process move and drag if trigger is pressed
  290. if (!myControllerAssitant.IsTriggerUp)
  291. {
  292. ProcessMove(ControllerData);
  293. ProcessDrag(ControllerData);
  294. }
  295. if (!Mathf.Approximately(ControllerData.scrollDelta.sqrMagnitude, 0.0f))
  296. {
  297. var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(ControllerData.pointerCurrentRaycast.gameObject);
  298. ExecuteEvents.ExecuteHierarchy(scrollHandler, ControllerData, ExecuteEvents.scrollHandler);
  299. // Debug.Log("executing scroll handler");
  300. }
  301. }
  302. /// <summary>
  303. /// Sends trigger down / trigger released events to gameobjects under the pointer.
  304. /// </summary>
  305. protected virtual void ProcessDownRelease(PointerEventData eventData, bool down, bool released)
  306. {
  307. var currentOverGo = eventData.pointerCurrentRaycast.gameObject;
  308. // PointerDown notification
  309. if (down)
  310. {
  311. eventData.eligibleForClick = true;
  312. eventData.delta = Vector2.zero;
  313. eventData.dragging = false;
  314. eventData.useDragThreshold = true;
  315. eventData.pressPosition = eventData.position;
  316. eventData.pointerPressRaycast = eventData.pointerCurrentRaycast;
  317. DeselectIfSelectionChanged(currentOverGo, eventData);
  318. if (eventData.pointerEnter != currentOverGo)
  319. {
  320. // send a pointer enter to the touched element if it isn't the one to select...
  321. HandlePointerExitAndEnter(eventData, currentOverGo);
  322. eventData.pointerEnter = currentOverGo;
  323. }
  324. // search for the control that will receive the press
  325. // if we can't find a press handler set the press
  326. // handler to be what would receive a click.
  327. var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.pointerDownHandler);
  328. // didnt find a press handler... search for a click handler
  329. if (newPressed == null)
  330. newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
  331. float time = Time.unscaledTime;
  332. if (newPressed == eventData.lastPress)
  333. {
  334. var diffTime = time - eventData.clickTime;
  335. if (diffTime < 0.3f)
  336. ++eventData.clickCount;
  337. else
  338. eventData.clickCount = 1;
  339. eventData.clickTime = time;
  340. }
  341. else
  342. {
  343. eventData.clickCount = 1;
  344. }
  345. eventData.pointerPress = newPressed;
  346. eventData.rawPointerPress = currentOverGo;
  347. eventData.clickTime = time;
  348. // Save the drag handler as well
  349. eventData.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
  350. if (eventData.pointerDrag != null)
  351. ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.initializePotentialDrag);
  352. }
  353. // PointerUp notification
  354. if (released)
  355. {
  356. ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
  357. // see if we mouse up on the same element that we clicked on...
  358. var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
  359. // PointerClick and Drop events
  360. if (eventData.pointerPress == pointerUpHandler && eventData.eligibleForClick)
  361. {
  362. ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerClickHandler);
  363. //Debug.Log("click");
  364. }
  365. else if (eventData.pointerDrag != null && eventData.dragging)
  366. {
  367. ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.dropHandler);
  368. //Debug.Log("drop");
  369. }
  370. eventData.eligibleForClick = false;
  371. eventData.pointerPress = null;
  372. eventData.rawPointerPress = null;
  373. if (eventData.pointerDrag != null && eventData.dragging)
  374. {
  375. ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.endDragHandler);
  376. //Debug.Log("end drag");
  377. }
  378. eventData.dragging = false;
  379. eventData.pointerDrag = null;
  380. // send exit events as we need to simulate this on touch up on touch device
  381. ExecuteEvents.ExecuteHierarchy(eventData.pointerEnter, eventData, ExecuteEvents.pointerExitHandler);
  382. eventData.pointerEnter = null;
  383. }
  384. }
  385. /// <summary>
  386. /// Create a pointerEventData that stores all the data associated with Vive controller.
  387. /// </summary>
  388. private CurvedUIPointerEventData GetControllerPointerData(CurvedUIViveController controller, ref CurvedUIPointerEventData ControllerData)
  389. {
  390. if (ControllerData == null)
  391. ControllerData = new CurvedUIPointerEventData(eventSystem);
  392. ControllerData.Reset();
  393. ControllerData.delta = Vector2.one; // to trick into moving
  394. ControllerData.position = Vector2.zero; // this will be overriden by raycaster
  395. ControllerData.Controller = controller.gameObject; // raycaster will use this object to override pointer position on screen. Keep it safe.
  396. ControllerData.scrollDelta = controller.TouchPadAxis - ControllerData.TouchPadAxis; // calcualte scroll delta
  397. ControllerData.TouchPadAxis = controller.TouchPadAxis; // assign finger position on touchpad
  398. eventSystem.RaycastAll(ControllerData, m_RaycastResultCache); //Raycast all the things!. Position will be overridden here by CurvedUIRaycaster
  399. //Get a current raycast to find if we're pointing at GUI object.
  400. ControllerData.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);
  401. m_RaycastResultCache.Clear();
  402. return ControllerData;
  403. }
  404. private bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold)
  405. {
  406. if (!useDragThreshold)
  407. return true;
  408. //this always returns false if override pointereventdata in curveduiraycster.cs is set to false. There is no past pointereventdata to compare with then.
  409. return (pressPos - currentPos).sqrMagnitude >= threshold * threshold;
  410. }
  411. /// <summary>
  412. /// Force selection of a gameobject.
  413. /// </summary>
  414. private void Select(GameObject go)
  415. {
  416. ClearSelection();
  417. if (ExecuteEvents.GetEventHandler<ISelectHandler>(go))
  418. {
  419. eventSystem.SetSelectedGameObject(go);
  420. }
  421. }
  422. /// <summary>
  423. /// Adds necessary components to Vive controller gameobjects. These will let us know what inputs are used on them.
  424. /// </summary>
  425. private void SetupViveControllers()
  426. {
  427. //find controller reference
  428. if (controllerManager == null)
  429. controllerManager = steamVRControllerManager;
  430. //Find Controller manager on the scene.
  431. if (controllerManager == null)
  432. {
  433. SteamVR_ControllerManager[] potentialManagers = Object.FindObjectsOfType<SteamVR_ControllerManager>();
  434. controllerManager = null;
  435. //ignore external camera created by externalcamera.cfg for mixed reality videos
  436. if (potentialManagers.GetLength(0) > 0)
  437. {
  438. for (int i = 0; i < potentialManagers.GetLength(0); i++)
  439. {
  440. if (potentialManagers[i].gameObject.name != "External Camera")
  441. controllerManager = potentialManagers[i];
  442. }
  443. }
  444. if (controllerManager == null)
  445. Debug.LogError("Can't find SteamVR_ControllerManager on scene. It is required to use VIVE control method. Make sure all SteamVR prefabs are present.");
  446. }
  447. #endif
  448. }
  449. #endregion
  450. #region EVENT PROCESSING - OCULUS TOUCH
  451. protected virtual void ProcessOculusVRController()
  452. {
  453. #if CURVEDUI_OCULUSVR
  454. activeCont = OVRInput.GetActiveController();
  455. //Find the currently used HandAnchor----------------------//
  456. //and set direction ray using its transform
  457. switch (activeCont)
  458. {
  459. //Oculus Touch
  460. case OVRInput.Controller.RTouch: CustomControllerRay = new Ray(oculusCameraRig.rightHandAnchor.position, oculusCameraRig.rightHandAnchor.forward); break;
  461. case OVRInput.Controller.LTouch: CustomControllerRay = new Ray(oculusCameraRig.leftHandAnchor.position, oculusCameraRig.leftHandAnchor.forward); break;
  462. //Oculus Go controller
  463. case OVRInput.Controller.RHand: goto case OVRInput.Controller.RTouch;
  464. case OVRInput.Controller.LHand: goto case OVRInput.Controller.LTouch;
  465. //edge cases
  466. default: CustomControllerRay = new Ray(OculusTouchUsedControllerTransform.position, OculusTouchUsedControllerTransform.forward); break;
  467. }
  468. //Check if interaction button is pressed ---------------//
  469. //find if we're using Rift with touch. If yes, we'll have to check if the interaction button is pressed on the proper hand.
  470. bool touchControllersUsed = (activeCont == OVRInput.Controller.Touch || activeCont == OVRInput.Controller.LTouch || activeCont == OVRInput.Controller.RTouch);
  471. if (usedHand == Hand.Both || !touchControllersUsed)
  472. {
  473. //check if this button is pressed on any controller. Handles GearVR controller and Oculus Go controller.
  474. CustomControllerButtonState = OVRInput.Get(InteractionButton);
  475. }
  476. else if (usedHand == Hand.Right) // Right Oculus Touch
  477. {
  478. CustomControllerButtonState = OVRInput.Get(InteractionButton, OVRInput.Controller.RTouch);
  479. }
  480. else if (usedHand == Hand.Left) // Left Oculus Touch
  481. {
  482. CustomControllerButtonState = OVRInput.Get(InteractionButton, OVRInput.Controller.LTouch);
  483. }
  484. //process all events based on this data--------------//
  485. ProcessCustomRayController();
  486. #endif // EVENT PROCESSING - CURVEDUI_OCULUSVR
  487. }
  488. #endregion
  489. #region EVENT PROCESSING - STEAMVR_2
  490. void ProcessSteamVR2Controllers()
  491. #if CURVEDUI_STEAMVR_2
  492. {
  493. if(m_steamVRClickAction != null)
  494. {
  495. CustomControllerButtonState = m_steamVRClickAction.GetState(SteamVRInputSource);
  496. CustomControllerRay = new Ray(ControllerTransform.transform.position, ControllerTransform.transform.forward);
  497. ProcessCustomRayController();
  498. }
  499. else
  500. {
  501. Debug.LogError("CURVEDUI: Choose which SteamVR_Action will be used for a Click on CurvedUISettings component.");
  502. }
  503. }
  504. void SetupSteamVR2Controllers()
  505. {
  506. if (steamVRPlayArea == null)
  507. steamVRPlayArea = FindObjectOfType<SteamVR_PlayArea>();
  508. if (steamVRPlayArea != null)
  509. {
  510. foreach (SteamVR_Behaviour_Pose poseComp in steamVRPlayArea.GetComponentsInChildren<SteamVR_Behaviour_Pose>(true))
  511. {
  512. if (poseComp.inputSource == SteamVR_Input_Sources.RightHand)
  513. m_rightController = poseComp.gameObject;
  514. else if (poseComp.inputSource == SteamVR_Input_Sources.LeftHand)
  515. m_leftController = poseComp.gameObject;
  516. }
  517. }
  518. else
  519. {
  520. #if CURVEDUI_STEAMVR_INT
  521. //Optional - SteamVR Interaction System
  522. Valve.VR.InteractionSystem.Player PlayerComponent = FindObjectOfType<Valve.VR.InteractionSystem.Player>();
  523. if(PlayerComponent != null)
  524. {
  525. m_rightController = PlayerComponent.rightHand.gameObject;
  526. m_leftController = PlayerComponent.leftHand.gameObject;
  527. }
  528. else
  529. #endif
  530. Debug.LogError("CURVEDUI: Can't find SteamVR_PlayArea component or InteractionSystem.Player component on the scene. One of these is required. Add a reference to it manually to CurvedUIInputModule on EventSystem gameobject.", this.gameObject);
  531. }
  532. if (m_steamVRClickAction == null)
  533. Debug.LogError("CURVEDUI: No SteamVR action to use for button interactions. Choose the action you want to use to click the buttons on CurvedUISettings component.");
  534. }
  535. #else
  536. { }
  537. #endif //end of CURVEDUI_STEAMVR_2
  538. #endregion // end of EVENT PROCESSING - STEAMVR_2
  539. #region EVENT PROCESSING - UNITY XR
  540. protected virtual void ProcessUnityXRController()
  541. #if CURVEDUI_UNITY_XR
  542. {
  543. bool pressed;
  544. switch (usedHand)
  545. {
  546. case Hand.Right: rightXRController.inputDevice.IsPressed(rightXRController.uiPressUsage,
  547. out pressed, rightXRController.axisToPressThreshold);
  548. break;
  549. case Hand.Left: leftXRController.inputDevice.IsPressed(leftXRController.uiPressUsage,
  550. out pressed, leftXRController.axisToPressThreshold);
  551. break;
  552. default: goto case Hand.Right;
  553. }
  554. CustomControllerButtonState = pressed;
  555. CustomControllerRay = new Ray(ControllerTransform.transform.position, ControllerTransform.transform.forward);
  556. ProcessCustomRayController();
  557. Debug.Log("XR Button: " + rightXRController.uiPressUsage.ToString() + " on " + usedHand.ToString() +" - " + CustomControllerButtonState );
  558. }
  559. private void SetupUnityXRControllers()
  560. {
  561. if (rightXRController != null && leftXRController != null) return;
  562. foreach (var controller in GameObject.FindObjectsOfType<XRController>())
  563. {
  564. if (rightXRController == null && controller.controllerNode == XRNode.RightHand)
  565. rightXRController = controller;
  566. if (leftXRController == null && controller.controllerNode == XRNode.LeftHand)
  567. leftXRController = controller;
  568. }
  569. }
  570. #else
  571. { }
  572. #endif //end of CURVEDUI_UNITY_XR if
  573. #endregion
  574. #endif // END OF CURVEDUI_GOOGLEVR IF - GOOGLEVR INPUT MODULE OVERRIDES
  575. #region HELPER FUNCTIONS
  576. static T EnableInputModule<T>() where T : BaseInputModule
  577. {
  578. bool moduleMissing = true;
  579. EventSystem eventGO = GameObject.FindObjectOfType<EventSystem>();
  580. if (eventGO == null)
  581. {
  582. Debug.LogError("CurvedUI: Your EventSystem component is missing from the scene! Unity Canvas will not track interactions without it.");
  583. return null as T;
  584. }
  585. foreach (BaseInputModule module in eventGO.GetComponents<BaseInputModule>())
  586. {
  587. if (module is T) {
  588. moduleMissing = false;
  589. module.enabled = true;
  590. } else if (disableOtherInputModulesOnStart) {
  591. module.enabled = false;
  592. #if CURVEDUI_GOOGLEVR //on GVR, we have to completely destroy the module because the code looks for first instance, even if it's disabled
  593. if(module is GvrPointerInputModule){
  594. if(Application.isPlaying){
  595. Destroy(module);
  596. Debug.LogError("CurvedUI: Fixed bad Input Module. Restart Play mode to continue.");
  597. } else {
  598. DestroyImmediate(module);
  599. }
  600. }
  601. #endif
  602. }
  603. }
  604. if (moduleMissing)
  605. eventGO.gameObject.AddComponent<T>();
  606. return eventGO.GetComponent<T>();
  607. }
  608. #endregion // HELPER FUNCTIONS
  609. #region SETTERS AND GETTERS - GENERAL
  610. public static CurvedUIInputModule Instance
  611. {
  612. get {
  613. if (instance == null) instance = EnableInputModule<CurvedUIInputModule>();
  614. return instance;
  615. }
  616. private set { instance = value; }
  617. }
  618. /// <summary>
  619. /// Current controller mode. Decides how user can interact with the canvas.
  620. /// </summary>
  621. public static CUIControlMethod ControlMethod
  622. {
  623. get { return Instance.controlMethod; }
  624. set
  625. {
  626. if (Instance.controlMethod != value)
  627. {
  628. Instance.controlMethod = value;
  629. #if CURVEDUI_STEAMVR_LEGACY
  630. if(value == CUIControlMethod.STEAMVR_LEGACY)
  631. Instance.SetupViveControllers();
  632. #endif
  633. }
  634. }
  635. }
  636. /// <summary>
  637. /// Layermask used by Raycaster classes to perform a Physics.Raycast() in order to find
  638. /// where user is pointing at the canvas.
  639. /// </summary>
  640. public LayerMask RaycastLayerMask
  641. {
  642. get { return raycastLayerMask; }
  643. set { raycastLayerMask = value; }
  644. }
  645. /// <summary>
  646. /// Which hand can be used to interact with canvas. Left, Right or Both. Default Right.
  647. /// Used in control methods that differentiate hands (STEAMVR, OCULUSVR)
  648. /// </summary>
  649. public Hand UsedHand
  650. {
  651. get { return usedHand; }
  652. set { usedHand = value; }
  653. }
  654. /// <summary>
  655. /// Gameobject of the handheld controller used for interactions - Oculus Touch, GearVR remote etc.
  656. /// If ControllerTransformOverride is set, that transform will be returned instead.
  657. /// Used in STEAMVR, STEAMVR_LEGACY, OCULUSVR and GOOGLEVR control methods.
  658. /// </summary>
  659. public Transform ControllerTransform {
  660. get
  661. {
  662. //use override, if available.
  663. if (PointerTransformOverride != null) return PointerTransformOverride;
  664. #if CURVEDUI_OCULUSVR
  665. return UsedHand == Hand.Left ? oculusCameraRig.leftHandAnchor : oculusCameraRig.rightHandAnchor;
  666. #elif CURVEDUI_STEAMVR_LEGACY
  667. return UsedHand == Hand.Left ? leftCont.transform : rightCont.transform;
  668. #elif CURVEDUI_STEAMVR_2
  669. return UsedHand == Hand.Left ? m_leftController.transform : m_rightController.transform;
  670. #elif CURVEDUI_GOOGLEVR
  671. return Pointer.PointerTransform;
  672. #elif CURVEDUI_UNITY_XR
  673. return UsedHand == Hand.Left ? leftXRController.transform : rightXRController.transform;
  674. #else
  675. Debug.LogWarning("CURVEDUI: CurvedUIInputModule.ActiveController will only return proper gameobject in STEAMVR, STEAMVR_LEGACY, OCULUSVR, UNITY_XR or GOOGLEVR control methods.");
  676. return null;
  677. #endif
  678. }
  679. }
  680. /// <summary>
  681. /// Direction where the handheld controller points. Forward (blue) direction of the controller transform.
  682. /// If ControllerTransformOverride is set, its forward direction will be returned instead.
  683. /// Used in STEAMVR, STEAMVR_LEGACY, OCULUSVR and GOOGLEVR control methods.
  684. /// </summary>
  685. public Vector3 ControllerPointingDirection {
  686. get
  687. {
  688. #if CURVEDUI_STEAMVR_LEGACY || CURVEDUI_STEAMVR_2 || CURVEDUI_GOOGLEVR || CURVEDUI_OCULUSVR || CURVEDUI_UNITY_XR
  689. return ControllerTransform.forward;
  690. #else
  691. Debug.LogWarning("CURVEDUI: CurvedUIInputModule.PointingDirection will only return proper direction in STEAMVR, STEAMVR_LEGACY, OCULUSVR, UNITY_XR or GOOGLEVR control methods.");
  692. return Vector3.forward;
  693. #endif
  694. }
  695. }
  696. /// <summary>
  697. /// World Space position where the pointing ray starts. Usually the location of controller transform.
  698. /// If ControllerTransformOverride is set, its position will be returned instead.
  699. /// Used in STEAMVR, STEAMVR_LEGACY, OCULUSVR and GOOGLEVR control methods.
  700. /// </summary>
  701. public Vector3 ControllerPointingOrigin {
  702. get
  703. {
  704. #if CURVEDUI_STEAMVR_LEGACY || CURVEDUI_STEAMVR_2 || CURVEDUI_GOOGLEVR || CURVEDUI_OCULUSVR || CURVEDUI_UNITY_XR
  705. return ControllerTransform.position;
  706. #else
  707. Debug.LogWarning("CURVEDUI: CurvedUIInputModule.PointingOrigin will only return proper position in STEAMVR, STEAMVR_LEGACY, OCULUSVR, UNITY_XR or GOOGLEVR control methods.");
  708. return Vector3.zero;
  709. #endif
  710. }
  711. }
  712. /// <summary>
  713. /// If not null, this transform will be used as the Pointer.
  714. /// Its position will be used as PointingOrigin and its forward (blue) direction as PointingDirection.
  715. /// </summary>
  716. public Transform PointerTransformOverride {
  717. get { return instance.pointerTransformOverride; }
  718. set { instance.pointerTransformOverride = value; }
  719. }
  720. /// <summary>
  721. /// Gameobject we're currently pointing at.
  722. /// Updated every frame.
  723. /// </summary>
  724. public GameObject CurrentPointedAt {
  725. get { return currentPointedAt; }
  726. }
  727. public Camera EventCamera {
  728. get { return mainEventCamera; }
  729. set
  730. {
  731. mainEventCamera = value;
  732. //add physics raycaster to event camera, so we can click on 3d objects
  733. if (mainEventCamera != null) mainEventCamera.AddComponentIfMissing<CurvedUIPhysicsRaycaster>();
  734. }
  735. }
  736. /// <summary>
  737. ///Get a ray to raycast with. Depends in EventCamera and current Control Method
  738. /// </summary>
  739. /// <returns></returns>
  740. public Ray GetEventRay(Camera eventCam = null) {
  741. if (eventCam == null) eventCam = mainEventCamera;
  742. switch (ControlMethod)
  743. {
  744. case CUIControlMethod.MOUSE:
  745. {
  746. // Get a ray from the camera through the point on the screen - used for mouse input
  747. return eventCam.ScreenPointToRay(Input.mousePosition);
  748. }
  749. case CUIControlMethod.GAZE:
  750. {
  751. //get a ray from the center of world camera. used for gaze input
  752. return new Ray(eventCam.transform.position, eventCam.transform.forward);
  753. }
  754. //case CUIControlMethod.WORLD_MOUSE: //processed in CurvedUIRaycaster instead
  755. case CUIControlMethod.GOOGLEVR:
  756. {
  757. if(pointerTransformOverride)
  758. return new Ray(pointerTransformOverride.position, pointerTransformOverride.forward);
  759. return new Ray(ControllerPointingOrigin, ControllerPointingDirection);
  760. }
  761. case CUIControlMethod.CUSTOM_RAY:
  762. {
  763. if(pointerTransformOverride)
  764. return new Ray(pointerTransformOverride.position, pointerTransformOverride.forward);
  765. return CustomControllerRay;
  766. }
  767. case CUIControlMethod.STEAMVR_LEGACY: goto case CUIControlMethod.GOOGLEVR;
  768. case CUIControlMethod.STEAMVR_2: goto case CUIControlMethod.CUSTOM_RAY;
  769. case CUIControlMethod.OCULUSVR: goto case CUIControlMethod.CUSTOM_RAY;
  770. case CUIControlMethod.UNITY_XR: goto case CUIControlMethod.CUSTOM_RAY;
  771. default: goto case CUIControlMethod.CUSTOM_RAY;
  772. }
  773. }
  774. #endregion
  775. #region SETTERS AND GETTERS - CUSTOM RAY
  776. /// <summary>
  777. /// When in CUSTOM_RAY controller mode, Canvas Raycaster will use this worldspace Ray to determine which Canvas objects are being selected.
  778. /// </summary>
  779. public static Ray CustomControllerRay
  780. {
  781. get { return Instance.customControllerRay; }
  782. set { Instance.customControllerRay = value; }
  783. }
  784. /// <summary>
  785. /// Tell CurvedUI if controller button is pressed when in CUSTOM_RAY controller mode. Input module will use this to interact with canvas.
  786. /// </summary>
  787. public static bool CustomControllerButtonState
  788. {
  789. get { return Instance.pressedDown; }
  790. set { Instance.pressedDown = value; }
  791. }
  792. [System.Obsolete("Use CustomControllerButtonState instead.")]
  793. public static bool CustomControllerButtonDown
  794. {
  795. get { return Instance.pressedDown; }
  796. set { Instance.pressedDown = value; }
  797. }
  798. #endregion
  799. #region SETTERS AND GETTERS - WORLD SPACE MOUSE
  800. /// <summary>
  801. /// Returns the position of the world space pointer in Canvas' local space.
  802. /// You can use it to position an image on world space mouse pointer's position.
  803. /// </summary>
  804. public Vector2 WorldSpaceMouseInCanvasSpace
  805. {
  806. get { return worldSpaceMouseInCanvasSpace; }
  807. set
  808. {
  809. worldSpaceMouseInCanvasSpace = value;
  810. lastWorldSpaceMouseOnCanvas = value;
  811. }
  812. }
  813. /// <summary>
  814. /// The change in position of the world space mouse in canvas' units.
  815. /// Counted since the last frame.
  816. /// </summary>
  817. public Vector2 WorldSpaceMouseInCanvasSpaceDelta
  818. {
  819. get { return worldSpaceMouseInCanvasSpace - lastWorldSpaceMouseOnCanvas; }
  820. }
  821. /// <summary>
  822. /// How many units in Canvas space equals one unit in screen space.
  823. /// </summary>
  824. public float WorldSpaceMouseSensitivity
  825. {
  826. get { return worldSpaceMouseSensitivity; }
  827. set { worldSpaceMouseSensitivity = value; }
  828. }
  829. #endregion // SETTERS AND GETTERS - WORLD SPACE MOUSE
  830. #region SETTERS AND GETTERS - GAZE
  831. /// <summary>
  832. /// Gaze Control Method. Should execute OnClick events on button after user points at them?
  833. /// </summary>
  834. public bool GazeUseTimedClick
  835. {
  836. get { return gazeUseTimedClick; }
  837. set { gazeUseTimedClick = value; }
  838. }
  839. /// <summary>
  840. /// Gaze Control Method. How long after user points on a button should we click it? Default 2 seconds.
  841. /// </summary>
  842. public float GazeClickTimer
  843. {
  844. get { return gazeClickTimer; }
  845. set { gazeClickTimer = Mathf.Max(value, 0); }
  846. }
  847. /// <summary>
  848. /// Gaze Control Method. How long after user looks at a button should we start the timer? Default 1 second.
  849. /// </summary>
  850. public float GazeClickTimerDelay
  851. {
  852. get { return gazeClickTimerDelay; }
  853. set { gazeClickTimerDelay = Mathf.Max(value, 0); }
  854. }
  855. /// <summary>
  856. /// Gaze Control Method. How long till Click method is executed on Buttons under gaze? Goes 0-1.
  857. /// </summary>
  858. public float GazeTimerProgress
  859. {
  860. get { return gazeTimerProgress; }
  861. }
  862. /// <summary>
  863. /// Gaze Control Method. This Images's fill will be animated 0-1 when OnClick events are about
  864. /// to be executed on buttons under the gaze.
  865. /// </summary>
  866. public Image GazeTimedClickProgressImage
  867. {
  868. get { return gazeTimedClickProgressImage; }
  869. set { gazeTimedClickProgressImage = value; }
  870. }
  871. #endregion // SETTERS AND GETTERS - GAZE
  872. #region SETTERS AND GETTERS - STEAMVR_LEGACY
  873. #if CURVEDUI_STEAMVR_LEGACY
  874. /// <summary>
  875. /// Scene's controller manager. Used to get references for Vive controllers.
  876. /// </summary>
  877. public SteamVR_ControllerManager SteamVRControllerManager {
  878. get { return steamVRControllerManager; }
  879. set {
  880. if (steamVRControllerManager != value) {
  881. steamVRControllerManager = value;
  882. }
  883. }
  884. }
  885. /// <summary>
  886. /// Get or Set controller manager used by this input module.
  887. /// </summary>
  888. public SteamVR_ControllerManager ControllerManager {
  889. get { return controllerManager; }
  890. set {
  891. controllerManager = value;
  892. SetupViveControllers();
  893. }
  894. }
  895. /// <summary>
  896. /// Returns Right SteamVR Controller. Ask this component for any button states.;
  897. /// </summary>
  898. public static CurvedUIViveController Right {
  899. get {
  900. if (!rightCont) rightCont = controllerManager.right.AddComponentIfMissing<CurvedUIViveController>();
  901. return rightCont ;
  902. }
  903. }
  904. /// <summary>
  905. /// Returns Left SteamVR Controller. Ask this component for any button states.;
  906. /// </summary>
  907. public static CurvedUIViveController Left {
  908. get {
  909. if (!leftCont) leftCont = controllerManager.left.AddComponentIfMissing<CurvedUIViveController>();
  910. return leftCont;
  911. }
  912. }
  913. #endif // CURVEDUI_STEAMVR_LEGACY
  914. #endregion // end of SETTERS AND GETTERS - STEAMVR_LEGACY
  915. #region SETTERS AND GETTERS - STEAMVR_2
  916. #if CURVEDUI_STEAMVR_2
  917. public SteamVR_PlayArea SteamVRPlayArea {
  918. get { return steamVRPlayArea; }
  919. set { steamVRPlayArea = value; }
  920. }
  921. /// <summary>
  922. /// Currently used SteamVR Input Source, based on used Hand.
  923. /// </summary>
  924. public SteamVR_Input_Sources SteamVRInputSource {
  925. get { return (UsedHand == Hand.Left ? Valve.VR.SteamVR_Input_Sources.LeftHand : Valve.VR.SteamVR_Input_Sources.RightHand); }
  926. }
  927. /// <summary>
  928. /// SteamVR 2.0 Action that should be used to click on UI elements.
  929. /// </summary>
  930. public SteamVR_Action_Boolean SteamVRClickAction {
  931. get { return m_steamVRClickAction; }
  932. set { m_steamVRClickAction = value; }
  933. }
  934. #endif // end of STEAMVR2
  935. #endregion // SETTERS AND GETTERS - STEAMVR_2
  936. #region SETTERS AND GETTERS - OCULUSVR
  937. #if CURVEDUI_OCULUSVR
  938. public OVRCameraRig OculusCameraRig {
  939. get { return oculusCameraRig; }
  940. set { oculusCameraRig = value; }
  941. }
  942. public OVRInput.Button OculusTouchInteractionButton {
  943. get { return InteractionButton; }
  944. set { InteractionButton = value; }
  945. }
  946. public Transform OculusTouchUsedControllerTransform {
  947. get { return UsedHand == Hand.Left ? oculusCameraRig.leftHandAnchor : oculusCameraRig.rightHandAnchor; }
  948. }
  949. #endif // end of CURVEDUI_OCULUSVR
  950. #endregion // SETTERS AND GETTERS - OCULUSVR
  951. #region SETTERS AND GETTERS - UNITY_XR
  952. #if CURVEDUI_UNITY_XR
  953. public XRController RightXRController {
  954. get => rightXRController;
  955. set => rightXRController = value;
  956. }
  957. public XRController LeftXRController {
  958. get => leftXRController;
  959. set => leftXRController = value;
  960. }
  961. #endif // end of CURVEDUI_UNITY_XR
  962. #endregion // SETTERS AND GETTERS - UNITY_XR
  963. #region ENUMS
  964. public enum CUIControlMethod
  965. {
  966. MOUSE = 0,
  967. GAZE = 1,
  968. WORLD_MOUSE = 2,
  969. CUSTOM_RAY = 3,
  970. STEAMVR_LEGACY = 4, //SDK version 1.2.3 or earlier
  971. OCULUSVR = 5,
  972. //DAYDREAM = 6,//deprecated, GoogleVR is now used for daydream.
  973. GOOGLEVR = 7,
  974. STEAMVR_2 = 8, //SDK version 2.0 or later
  975. UNITY_XR = 9,
  976. }
  977. public enum Hand
  978. {
  979. Both = 0,
  980. Right = 1,
  981. Left = 2,
  982. }
  983. #endregion // ENUMS
  984. }