CurvedUIInputModule.cs 41 KB

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