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