OpenVRLoader.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. #if UNITY_XR_MANAGEMENT
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Runtime.InteropServices;
  5. using UnityEngine;
  6. using UnityEngine.XR;
  7. using UnityEngine.Experimental.XR;
  8. using UnityEngine.XR.Management;
  9. using System.IO;
  10. using Valve.VR;
  11. using System.Runtime.CompilerServices;
  12. #if UNITY_INPUT_SYSTEM
  13. using UnityEngine.InputSystem;
  14. using UnityEngine.InputSystem.Layouts;
  15. using UnityEngine.InputSystem.XR;
  16. #endif
  17. #if UNITY_EDITOR
  18. using UnityEditor;
  19. using UnityEditor.Build;
  20. #endif
  21. namespace Unity.XR.OpenVR
  22. {
  23. #if UNITY_INPUT_SYSTEM
  24. #if UNITY_EDITOR
  25. [InitializeOnLoad]
  26. #endif
  27. static class InputLayoutLoader
  28. {
  29. static InputLayoutLoader()
  30. {
  31. RegisterInputLayouts();
  32. }
  33. public static void RegisterInputLayouts()
  34. {
  35. InputSystem.RegisterLayout<XRHMD>("OpenVRHMD",
  36. matches: new InputDeviceMatcher()
  37. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  38. .WithProduct(@"^(OpenVR Headset)|^(Vive Pro)")
  39. );
  40. InputSystem.RegisterLayout<XRController>("OpenVRControllerWMR",
  41. matches: new InputDeviceMatcher()
  42. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  43. .WithProduct(@"^(OpenVR Controller\(WindowsMR)")
  44. );
  45. InputSystem.RegisterLayout<XRController>("ViveWand",
  46. matches: new InputDeviceMatcher()
  47. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  48. .WithManufacturer("HTC")
  49. .WithProduct(@"^(OpenVR Controller\(((Vive Controller)|(VIVE Controller)))")
  50. );
  51. InputSystem.RegisterLayout<XRController>("OpenVRViveCosmosController",
  52. matches: new InputDeviceMatcher()
  53. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  54. .WithManufacturer("HTC")
  55. .WithProduct(@"^(OpenVR Controller\(((VIVE Cosmos Controller)|(Vive Cosmos Controller)|(vive_cosmos_controller)))")
  56. );
  57. InputSystem.RegisterLayout<XRController>("OpenVRControllerIndex",
  58. matches: new InputDeviceMatcher()
  59. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  60. .WithManufacturer("Valve")
  61. .WithProduct(@"^(OpenVR Controller\(Knuckles)")
  62. );
  63. InputSystem.RegisterLayout<XRController>("OpenVROculusTouchController",
  64. matches: new InputDeviceMatcher()
  65. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  66. .WithManufacturer("Oculus")
  67. .WithProduct(@"^(OpenVR Controller\(Oculus)")
  68. );
  69. InputSystem.RegisterLayout<XRController>("HandedViveTracker",
  70. matches: new InputDeviceMatcher()
  71. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  72. .WithManufacturer("HTC")
  73. .WithProduct(@"^(OpenVR Controller\(((Vive Tracker)|(VIVE Tracker)).+ - ((Left)|(Right)))")
  74. );
  75. InputSystem.RegisterLayout<XRController>("ViveTracker",
  76. matches: new InputDeviceMatcher()
  77. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  78. .WithManufacturer("HTC")
  79. .WithProduct(@"^(OpenVR Controller\(((Vive Tracker)|(VIVE Tracker)).+\)(?! - Left| - Right))")
  80. );
  81. InputSystem.RegisterLayout<XRController>("ViveTracker",
  82. matches: new InputDeviceMatcher()
  83. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  84. .WithManufacturer("HTC")
  85. .WithProduct(@"^(OpenVR Tracked Device\(((Vive Tracker)|(VIVE Tracker)).+\)(?! - Left| - Right))")
  86. );
  87. InputSystem.RegisterLayout<XRController>("LogitechStylus",
  88. matches: new InputDeviceMatcher()
  89. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  90. .WithManufacturer("Logitech")
  91. .WithProduct(@"(OpenVR Controller\(.+stylus)")
  92. );
  93. InputSystem.RegisterLayout<TrackedDevice>("ViveLighthouse",
  94. matches: new InputDeviceMatcher()
  95. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  96. .WithManufacturer("HTC")
  97. .WithProduct(@"^(OpenVR Tracking Reference\()")
  98. );
  99. InputSystem.RegisterLayout<TrackedDevice>("ValveLighthouse",
  100. matches: new InputDeviceMatcher()
  101. .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
  102. .WithManufacturer("Valve Corporation")
  103. .WithProduct(@"^(OpenVR Tracking Reference\()")
  104. );
  105. }
  106. }
  107. #endif
  108. public class OpenVRLoader : XRLoaderHelper
  109. #if UNITY_EDITOR
  110. , IXRLoaderPreInit
  111. #endif
  112. {
  113. private static List<XRDisplaySubsystemDescriptor> s_DisplaySubsystemDescriptors = new List<XRDisplaySubsystemDescriptor>();
  114. private static List<XRInputSubsystemDescriptor> s_InputSubsystemDescriptors = new List<XRInputSubsystemDescriptor>();
  115. public XRDisplaySubsystem displaySubsystem
  116. {
  117. get
  118. {
  119. return GetLoadedSubsystem<XRDisplaySubsystem>();
  120. }
  121. }
  122. public XRInputSubsystem inputSubsystem
  123. {
  124. get
  125. {
  126. return GetLoadedSubsystem<XRInputSubsystem>();
  127. }
  128. }
  129. public override bool Initialize()
  130. {
  131. #if UNITY_INPUT_SYSTEM
  132. //InputLayoutLoader.RegisterInputLayouts();
  133. #endif
  134. //this only works at the right time in editor. In builds we use a different method (reading the asset manually)
  135. #if UNITY_EDITOR
  136. OpenVRSettings settings = OpenVRSettings.GetSettings();
  137. if (settings != null)
  138. {
  139. if (string.IsNullOrEmpty(settings.EditorAppKey))
  140. {
  141. settings.EditorAppKey = settings.GenerateEditorAppKey();
  142. }
  143. UserDefinedSettings userDefinedSettings;
  144. userDefinedSettings.stereoRenderingMode = (ushort)settings.GetStereoRenderingMode();
  145. userDefinedSettings.initializationType = (ushort)settings.GetInitializationType();
  146. userDefinedSettings.applicationName = null;
  147. userDefinedSettings.editorAppKey = null;
  148. userDefinedSettings.mirrorViewMode = (ushort)settings.GetMirrorViewMode();
  149. userDefinedSettings.editorAppKey = settings.EditorAppKey; //only set the key if we're in the editor. Otherwise let steamvr set the key.
  150. if (OpenVRHelpers.IsUsingSteamVRInput())
  151. {
  152. userDefinedSettings.editorAppKey = OpenVRHelpers.GetEditorAppKeyFromPlugin();
  153. }
  154. userDefinedSettings.applicationName = string.Format("[Testing] {0}", GetEscapedApplicationName());
  155. settings.InitializeActionManifestFileRelativeFilePath();
  156. userDefinedSettings.actionManifestPath = settings.ActionManifestFileRelativeFilePath;
  157. SetUserDefinedSettings(userDefinedSettings);
  158. }
  159. #endif
  160. CreateSubsystem<XRDisplaySubsystemDescriptor, XRDisplaySubsystem>(s_DisplaySubsystemDescriptors, "OpenVR Display");
  161. EVRInitError result = GetInitializationResult();
  162. if (result != EVRInitError.None)
  163. {
  164. DestroySubsystem<XRDisplaySubsystem>();
  165. Debug.LogError("<b>[OpenVR]</b> Could not initialize OpenVR. Error code: " + result.ToString());
  166. return false;
  167. }
  168. CreateSubsystem<XRInputSubsystemDescriptor, XRInputSubsystem>(s_InputSubsystemDescriptors, "OpenVR Input");
  169. OpenVREvents.Initialize();
  170. TickCallbackDelegate callback = TickCallback;
  171. RegisterTickCallback(callback);
  172. callback(0);
  173. return displaySubsystem != null && inputSubsystem != null;
  174. }
  175. private string GetEscapedApplicationName()
  176. {
  177. if (string.IsNullOrEmpty(Application.productName))
  178. return "";
  179. return Application.productName.Replace("\\", "\\\\").Replace("\"", "\\\""); //replace \ with \\ and replace " with \" for json escaping
  180. }
  181. private void WatchForReload()
  182. {
  183. #if UNITY_EDITOR
  184. UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += DisableTickOnReload;
  185. #endif
  186. }
  187. private void CleanupReloadWatcher()
  188. {
  189. #if UNITY_EDITOR
  190. UnityEditor.AssemblyReloadEvents.beforeAssemblyReload -= DisableTickOnReload;
  191. #endif
  192. }
  193. public override bool Start()
  194. {
  195. running = true;
  196. WatchForReload();
  197. StartSubsystem<XRDisplaySubsystem>();
  198. StartSubsystem<XRInputSubsystem>();
  199. SetupFileSystemWatchers();
  200. return true;
  201. }
  202. private void SetupFileSystemWatchers()
  203. {
  204. SetupFileSystemWatcher();
  205. }
  206. private bool running = false;
  207. #if UNITY_METRO || ENABLE_IL2CPP
  208. private FileInfo watcherFile;
  209. private System.Threading.Thread watcherThread;
  210. private void SetupFileSystemWatcher()
  211. {
  212. watcherThread = new System.Threading.Thread(new System.Threading.ThreadStart(ManualFileWatcherLoop));
  213. watcherThread.Start();
  214. }
  215. private void ManualFileWatcherLoop()
  216. {
  217. watcherFile = new System.IO.FileInfo(mirrorViewPath);
  218. long lastLength = -1;
  219. while (running)
  220. {
  221. if (watcherFile.Exists)
  222. {
  223. long currentLength = watcherFile.Length;
  224. if (lastLength != currentLength)
  225. {
  226. OnChanged(null, null);
  227. lastLength = currentLength;
  228. }
  229. }
  230. else
  231. {
  232. lastLength = -1;
  233. }
  234. System.Threading.Thread.Sleep(1000);
  235. }
  236. }
  237. private void DestroyMirrorModeWatcher()
  238. {
  239. if (watcherThread != null)
  240. {
  241. watcherThread.Abort();
  242. watcherThread = null;
  243. }
  244. }
  245. #else
  246. private FileInfo watcherFile;
  247. private System.IO.FileSystemWatcher watcher;
  248. private void SetupFileSystemWatcher()
  249. {
  250. try
  251. {
  252. settings = OpenVRSettings.GetSettings();
  253. // Listen for changes in the mirror mode file
  254. if (watcher == null && running)
  255. {
  256. watcherFile = new System.IO.FileInfo(mirrorViewPath);
  257. watcher = new System.IO.FileSystemWatcher(watcherFile.DirectoryName, watcherFile.Name);
  258. watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite;
  259. watcher.Created += OnChanged;
  260. watcher.Changed += OnChanged;
  261. watcher.EnableRaisingEvents = true;
  262. if (watcherFile.Exists)
  263. OnChanged(null, null);
  264. }
  265. }
  266. catch { }
  267. }
  268. private void DestroyMirrorModeWatcher()
  269. {
  270. if (watcher != null)
  271. {
  272. watcher.Created -= OnChanged;
  273. watcher.Changed -= OnChanged;
  274. watcher.EnableRaisingEvents = false;
  275. watcher.Dispose();
  276. watcher = null;
  277. }
  278. }
  279. #endif
  280. private const string mirrorViewPath = "openvr_mirrorview.cfg";
  281. private OpenVRSettings settings;
  282. private void OnChanged(object source, System.IO.FileSystemEventArgs e)
  283. {
  284. ReadMirrorModeConfig();
  285. }
  286. /// This allows end users to switch mirror view modes at runtime with a file.
  287. /// To use place a file called openvr_mirrorview.cfg in the same directory as the executable (or root of project).
  288. /// The file should be one line with the following key/value:
  289. /// MirrorViewMode=openvr
  290. /// Acceptable values are left, right, none, and openvr. OpenVR mode is in beta but will show overlays and chaperone bounds.
  291. private void ReadMirrorModeConfig()
  292. {
  293. try
  294. {
  295. var lines = System.IO.File.ReadAllLines(mirrorViewPath);
  296. foreach (var line in lines)
  297. {
  298. var split = line.Split('=');
  299. if (split.Length == 2)
  300. {
  301. var key = split[0];
  302. if (key == "MirrorViewMode")
  303. {
  304. string stringMode = split[1];
  305. OpenVRSettings.MirrorViewModes mode = OpenVRSettings.MirrorViewModes.None;
  306. if (stringMode.Equals("left", System.StringComparison.CurrentCultureIgnoreCase))
  307. mode = OpenVRSettings.MirrorViewModes.Left;
  308. else if (stringMode.Equals("right", System.StringComparison.CurrentCultureIgnoreCase))
  309. mode = OpenVRSettings.MirrorViewModes.Right;
  310. else if (stringMode.Equals("openvr", System.StringComparison.CurrentCultureIgnoreCase))
  311. mode = OpenVRSettings.MirrorViewModes.OpenVR;
  312. else if (stringMode.Equals("none", System.StringComparison.CurrentCultureIgnoreCase))
  313. mode = OpenVRSettings.MirrorViewModes.None;
  314. else
  315. {
  316. Debug.LogError("<b>[OpenVR]</b> Invalid mode specified in openvr_mirrorview.cfg. Options are: Left, Right, None, and OpenVR.");
  317. }
  318. Debug.Log("<b>[OpenVR]</b> Mirror View Mode changed via file to: " + mode.ToString());
  319. OpenVRSettings.SetMirrorViewMode((ushort)mode); //bypass the local set.
  320. }
  321. }
  322. }
  323. }
  324. catch
  325. { }
  326. }
  327. private UnityEngine.Events.UnityEvent[] events;
  328. public override bool Stop()
  329. {
  330. running = false;
  331. CleanupTick();
  332. CleanupReloadWatcher();
  333. DestroyMirrorModeWatcher();
  334. StopSubsystem<XRInputSubsystem>();
  335. StopSubsystem<XRDisplaySubsystem>(); //display actually does vrshutdown
  336. return true;
  337. }
  338. public override bool Deinitialize()
  339. {
  340. CleanupTick();
  341. CleanupReloadWatcher();
  342. DestroyMirrorModeWatcher();
  343. DestroySubsystem<XRInputSubsystem>();
  344. DestroySubsystem<XRDisplaySubsystem>();
  345. return true;
  346. }
  347. private static void CleanupTick()
  348. {
  349. RegisterTickCallback(null);
  350. }
  351. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto )]
  352. struct UserDefinedSettings
  353. {
  354. public ushort stereoRenderingMode;
  355. public ushort initializationType;
  356. public ushort mirrorViewMode;
  357. [MarshalAs(UnmanagedType.LPStr)] public string editorAppKey;
  358. [MarshalAs(UnmanagedType.LPStr)] public string actionManifestPath;
  359. [MarshalAs(UnmanagedType.LPStr)] public string applicationName;
  360. }
  361. [DllImport("XRSDKOpenVR", CharSet = CharSet.Auto)]
  362. private static extern void SetUserDefinedSettings(UserDefinedSettings settings);
  363. [DllImport("XRSDKOpenVR", CharSet = CharSet.Auto)]
  364. static extern EVRInitError GetInitializationResult();
  365. [DllImport("XRSDKOpenVR", CharSet = CharSet.Auto)]
  366. static extern void RegisterTickCallback([MarshalAs(UnmanagedType.FunctionPtr)] TickCallbackDelegate callbackPointer);
  367. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  368. delegate void TickCallbackDelegate(int value);
  369. [AOT.MonoPInvokeCallback(typeof(TickCallbackDelegate))]
  370. public static void TickCallback(int value)
  371. {
  372. OpenVREvents.Update();
  373. }
  374. #if UNITY_EDITOR
  375. public string GetPreInitLibraryName(BuildTarget buildTarget, BuildTargetGroup buildTargetGroup)
  376. {
  377. return "XRSDKOpenVR";
  378. }
  379. private static void DisableTickOnReload()
  380. {
  381. CleanupTick();
  382. }
  383. #endif
  384. }
  385. }
  386. #endif