SteamVR.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Access to SteamVR system (hmd) and compositor (distort) interfaces.
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using Valve.VR;
  8. public class SteamVR : System.IDisposable
  9. {
  10. // Use this to check if SteamVR is currently active without attempting
  11. // to activate it in the process.
  12. public static bool active { get { return _instance != null; } }
  13. // Set this to false to keep from auto-initializing when calling SteamVR.instance.
  14. private static bool _enabled = true;
  15. public static bool enabled
  16. {
  17. get
  18. {
  19. if (!UnityEngine.XR.XRSettings.enabled)
  20. enabled = false;
  21. return _enabled;
  22. }
  23. set
  24. {
  25. _enabled = value;
  26. if (!_enabled)
  27. SafeDispose();
  28. }
  29. }
  30. private static SteamVR _instance;
  31. public static SteamVR instance
  32. {
  33. get
  34. {
  35. #if UNITY_EDITOR
  36. if (!Application.isPlaying)
  37. return null;
  38. #endif
  39. if (!enabled)
  40. return null;
  41. if (_instance == null)
  42. {
  43. _instance = CreateInstance();
  44. // If init failed, then auto-disable so scripts don't continue trying to re-initialize things.
  45. if (_instance == null)
  46. _enabled = false;
  47. }
  48. return _instance;
  49. }
  50. }
  51. public static bool usingNativeSupport
  52. {
  53. get { return UnityEngine.XR.XRDevice.GetNativePtr() != System.IntPtr.Zero; }
  54. }
  55. static SteamVR CreateInstance()
  56. {
  57. try
  58. {
  59. var error = EVRInitError.None;
  60. if (!SteamVR.usingNativeSupport)
  61. {
  62. Debug.Log("OpenVR initialization failed. Ensure 'Virtual Reality Supported' is checked in Player Settings, and OpenVR is added to the list of Virtual Reality SDKs.");
  63. return null;
  64. }
  65. // Verify common interfaces are valid.
  66. OpenVR.GetGenericInterface(OpenVR.IVRCompositor_Version, ref error);
  67. if (error != EVRInitError.None)
  68. {
  69. ReportError(error);
  70. return null;
  71. }
  72. OpenVR.GetGenericInterface(OpenVR.IVROverlay_Version, ref error);
  73. if (error != EVRInitError.None)
  74. {
  75. ReportError(error);
  76. return null;
  77. }
  78. }
  79. catch (System.Exception e)
  80. {
  81. Debug.LogError(e);
  82. return null;
  83. }
  84. return new SteamVR();
  85. }
  86. static void ReportError(EVRInitError error)
  87. {
  88. switch (error)
  89. {
  90. case EVRInitError.None:
  91. break;
  92. case EVRInitError.VendorSpecific_UnableToConnectToOculusRuntime:
  93. Debug.Log("SteamVR Initialization Failed! Make sure device is on, Oculus runtime is installed, and OVRService_*.exe is running.");
  94. break;
  95. case EVRInitError.Init_VRClientDLLNotFound:
  96. Debug.Log("SteamVR drivers not found! They can be installed via Steam under Library > Tools. Visit http://steampowered.com to install Steam.");
  97. break;
  98. case EVRInitError.Driver_RuntimeOutOfDate:
  99. Debug.Log("SteamVR Initialization Failed! Make sure device's runtime is up to date.");
  100. break;
  101. default:
  102. Debug.Log(OpenVR.GetStringForHmdError(error));
  103. break;
  104. }
  105. }
  106. // native interfaces
  107. public CVRSystem hmd { get; private set; }
  108. public CVRCompositor compositor { get; private set; }
  109. public CVROverlay overlay { get; private set; }
  110. // tracking status
  111. static public bool initializing { get; private set; }
  112. static public bool calibrating { get; private set; }
  113. static public bool outOfRange { get; private set; }
  114. static public bool[] connected = new bool[OpenVR.k_unMaxTrackedDeviceCount];
  115. // render values
  116. public float sceneWidth { get; private set; }
  117. public float sceneHeight { get; private set; }
  118. public float aspect { get; private set; }
  119. public float fieldOfView { get; private set; }
  120. public Vector2 tanHalfFov { get; private set; }
  121. public VRTextureBounds_t[] textureBounds { get; private set; }
  122. public SteamVR_Utils.RigidTransform[] eyes { get; private set; }
  123. public ETextureType textureType;
  124. // hmd properties
  125. public string hmd_TrackingSystemName { get { return GetStringProperty(ETrackedDeviceProperty.Prop_TrackingSystemName_String); } }
  126. public string hmd_ModelNumber { get { return GetStringProperty(ETrackedDeviceProperty.Prop_ModelNumber_String); } }
  127. public string hmd_SerialNumber { get { return GetStringProperty(ETrackedDeviceProperty.Prop_SerialNumber_String); } }
  128. public float hmd_SecondsFromVsyncToPhotons { get { return GetFloatProperty(ETrackedDeviceProperty.Prop_SecondsFromVsyncToPhotons_Float); } }
  129. public float hmd_DisplayFrequency { get { return GetFloatProperty(ETrackedDeviceProperty.Prop_DisplayFrequency_Float); } }
  130. public string GetTrackedDeviceString(uint deviceId)
  131. {
  132. var error = ETrackedPropertyError.TrackedProp_Success;
  133. var capacity = hmd.GetStringTrackedDeviceProperty(deviceId, ETrackedDeviceProperty.Prop_AttachedDeviceId_String, null, 0, ref error);
  134. if (capacity > 1)
  135. {
  136. var result = new System.Text.StringBuilder((int)capacity);
  137. hmd.GetStringTrackedDeviceProperty(deviceId, ETrackedDeviceProperty.Prop_AttachedDeviceId_String, result, capacity, ref error);
  138. return result.ToString();
  139. }
  140. return null;
  141. }
  142. public string GetStringProperty(ETrackedDeviceProperty prop, uint deviceId = OpenVR.k_unTrackedDeviceIndex_Hmd)
  143. {
  144. var error = ETrackedPropertyError.TrackedProp_Success;
  145. var capactiy = hmd.GetStringTrackedDeviceProperty(deviceId, prop, null, 0, ref error);
  146. if (capactiy > 1)
  147. {
  148. var result = new System.Text.StringBuilder((int)capactiy);
  149. hmd.GetStringTrackedDeviceProperty(deviceId, prop, result, capactiy, ref error);
  150. return result.ToString();
  151. }
  152. return (error != ETrackedPropertyError.TrackedProp_Success) ? error.ToString() : "<unknown>";
  153. }
  154. public float GetFloatProperty(ETrackedDeviceProperty prop, uint deviceId = OpenVR.k_unTrackedDeviceIndex_Hmd)
  155. {
  156. var error = ETrackedPropertyError.TrackedProp_Success;
  157. return hmd.GetFloatTrackedDeviceProperty(deviceId, prop, ref error);
  158. }
  159. #region Event callbacks
  160. private void OnInitializing(bool initializing)
  161. {
  162. SteamVR.initializing = initializing;
  163. }
  164. private void OnCalibrating(bool calibrating)
  165. {
  166. SteamVR.calibrating = calibrating;
  167. }
  168. private void OnOutOfRange(bool outOfRange)
  169. {
  170. SteamVR.outOfRange = outOfRange;
  171. }
  172. private void OnDeviceConnected(int i, bool connected)
  173. {
  174. SteamVR.connected[i] = connected;
  175. }
  176. private void OnNewPoses(TrackedDevicePose_t[] poses)
  177. {
  178. // Update eye offsets to account for IPD changes.
  179. eyes[0] = new SteamVR_Utils.RigidTransform(hmd.GetEyeToHeadTransform(EVREye.Eye_Left));
  180. eyes[1] = new SteamVR_Utils.RigidTransform(hmd.GetEyeToHeadTransform(EVREye.Eye_Right));
  181. for (int i = 0; i < poses.Length; i++)
  182. {
  183. var connected = poses[i].bDeviceIsConnected;
  184. if (connected != SteamVR.connected[i])
  185. {
  186. SteamVR_Events.DeviceConnected.Send(i, connected);
  187. }
  188. }
  189. if (poses.Length > OpenVR.k_unTrackedDeviceIndex_Hmd)
  190. {
  191. var result = poses[OpenVR.k_unTrackedDeviceIndex_Hmd].eTrackingResult;
  192. var initializing = result == ETrackingResult.Uninitialized;
  193. if (initializing != SteamVR.initializing)
  194. {
  195. SteamVR_Events.Initializing.Send(initializing);
  196. }
  197. var calibrating =
  198. result == ETrackingResult.Calibrating_InProgress ||
  199. result == ETrackingResult.Calibrating_OutOfRange;
  200. if (calibrating != SteamVR.calibrating)
  201. {
  202. SteamVR_Events.Calibrating.Send(calibrating);
  203. }
  204. var outOfRange =
  205. result == ETrackingResult.Running_OutOfRange ||
  206. result == ETrackingResult.Calibrating_OutOfRange;
  207. if (outOfRange != SteamVR.outOfRange)
  208. {
  209. SteamVR_Events.OutOfRange.Send(outOfRange);
  210. }
  211. }
  212. }
  213. #endregion
  214. private SteamVR()
  215. {
  216. hmd = OpenVR.System;
  217. Debug.Log("Connected to " + hmd_TrackingSystemName + ":" + hmd_SerialNumber);
  218. compositor = OpenVR.Compositor;
  219. overlay = OpenVR.Overlay;
  220. // Setup render values
  221. uint w = 0, h = 0;
  222. hmd.GetRecommendedRenderTargetSize(ref w, ref h);
  223. sceneWidth = (float)w;
  224. sceneHeight = (float)h;
  225. float l_left = 0.0f, l_right = 0.0f, l_top = 0.0f, l_bottom = 0.0f;
  226. hmd.GetProjectionRaw(EVREye.Eye_Left, ref l_left, ref l_right, ref l_top, ref l_bottom);
  227. float r_left = 0.0f, r_right = 0.0f, r_top = 0.0f, r_bottom = 0.0f;
  228. hmd.GetProjectionRaw(EVREye.Eye_Right, ref r_left, ref r_right, ref r_top, ref r_bottom);
  229. tanHalfFov = new Vector2(
  230. Mathf.Max(-l_left, l_right, -r_left, r_right),
  231. Mathf.Max(-l_top, l_bottom, -r_top, r_bottom));
  232. textureBounds = new VRTextureBounds_t[2];
  233. textureBounds[0].uMin = 0.5f + 0.5f * l_left / tanHalfFov.x;
  234. textureBounds[0].uMax = 0.5f + 0.5f * l_right / tanHalfFov.x;
  235. textureBounds[0].vMin = 0.5f - 0.5f * l_bottom / tanHalfFov.y;
  236. textureBounds[0].vMax = 0.5f - 0.5f * l_top / tanHalfFov.y;
  237. textureBounds[1].uMin = 0.5f + 0.5f * r_left / tanHalfFov.x;
  238. textureBounds[1].uMax = 0.5f + 0.5f * r_right / tanHalfFov.x;
  239. textureBounds[1].vMin = 0.5f - 0.5f * r_bottom / tanHalfFov.y;
  240. textureBounds[1].vMax = 0.5f - 0.5f * r_top / tanHalfFov.y;
  241. // Grow the recommended size to account for the overlapping fov
  242. sceneWidth = sceneWidth / Mathf.Max(textureBounds[0].uMax - textureBounds[0].uMin, textureBounds[1].uMax - textureBounds[1].uMin);
  243. sceneHeight = sceneHeight / Mathf.Max(textureBounds[0].vMax - textureBounds[0].vMin, textureBounds[1].vMax - textureBounds[1].vMin);
  244. aspect = tanHalfFov.x / tanHalfFov.y;
  245. fieldOfView = 2.0f * Mathf.Atan(tanHalfFov.y) * Mathf.Rad2Deg;
  246. eyes = new SteamVR_Utils.RigidTransform[] {
  247. new SteamVR_Utils.RigidTransform(hmd.GetEyeToHeadTransform(EVREye.Eye_Left)),
  248. new SteamVR_Utils.RigidTransform(hmd.GetEyeToHeadTransform(EVREye.Eye_Right)) };
  249. switch (SystemInfo.graphicsDeviceType)
  250. {
  251. #if (UNITY_5_4)
  252. case UnityEngine.Rendering.GraphicsDeviceType.OpenGL2:
  253. #endif
  254. case UnityEngine.Rendering.GraphicsDeviceType.OpenGLCore:
  255. case UnityEngine.Rendering.GraphicsDeviceType.OpenGLES2:
  256. case UnityEngine.Rendering.GraphicsDeviceType.OpenGLES3:
  257. textureType = ETextureType.OpenGL;
  258. break;
  259. #if !(UNITY_5_4)
  260. case UnityEngine.Rendering.GraphicsDeviceType.Vulkan:
  261. textureType = ETextureType.Vulkan;
  262. break;
  263. #endif
  264. default:
  265. textureType = ETextureType.DirectX;
  266. break;
  267. }
  268. SteamVR_Events.Initializing.Listen(OnInitializing);
  269. SteamVR_Events.Calibrating.Listen(OnCalibrating);
  270. SteamVR_Events.OutOfRange.Listen(OnOutOfRange);
  271. SteamVR_Events.DeviceConnected.Listen(OnDeviceConnected);
  272. SteamVR_Events.NewPoses.Listen(OnNewPoses);
  273. }
  274. ~SteamVR()
  275. {
  276. Dispose(false);
  277. }
  278. public void Dispose()
  279. {
  280. Dispose(true);
  281. System.GC.SuppressFinalize(this);
  282. }
  283. private void Dispose(bool disposing)
  284. {
  285. SteamVR_Events.Initializing.Remove(OnInitializing);
  286. SteamVR_Events.Calibrating.Remove(OnCalibrating);
  287. SteamVR_Events.OutOfRange.Remove(OnOutOfRange);
  288. SteamVR_Events.DeviceConnected.Remove(OnDeviceConnected);
  289. SteamVR_Events.NewPoses.Remove(OnNewPoses);
  290. _instance = null;
  291. }
  292. // Use this interface to avoid accidentally creating the instance in the process of attempting to dispose of it.
  293. public static void SafeDispose()
  294. {
  295. if (_instance != null)
  296. _instance.Dispose();
  297. }
  298. }