Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

560 lines
22 KiB

  1. // SDK Setup|Utilities|90030
  2. namespace VRTK
  3. {
  4. using UnityEngine;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. using UnityEditor.Callbacks;
  8. #endif
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Collections.ObjectModel;
  12. using System.Linq;
  13. /// <summary>
  14. /// The SDK Setup describes a list of SDKs and game objects to use.
  15. /// </summary>
  16. public sealed class VRTK_SDKSetup : MonoBehaviour
  17. {
  18. public delegate void LoadEventHandler(VRTK_SDKManager sender, VRTK_SDKSetup setup);
  19. [Tooltip("Determines whether the SDK object references are automatically set to the objects of the selected SDKs. If this is true populating is done whenever the selected SDKs change.")]
  20. public bool autoPopulateObjectReferences = true;
  21. [Tooltip("A reference to the GameObject that is the user's boundary or play area, most likely provided by the SDK's Camera Rig.")]
  22. public GameObject actualBoundaries;
  23. [Tooltip("A reference to the GameObject that contains the VR camera, most likely provided by the SDK's Camera Rig Headset.")]
  24. public GameObject actualHeadset;
  25. [Tooltip("A reference to the GameObject that contains the SDK Left Hand Controller.")]
  26. public GameObject actualLeftController;
  27. [Tooltip("A reference to the GameObject that contains the SDK Right Hand Controller.")]
  28. public GameObject actualRightController;
  29. [Tooltip("A reference to the GameObject that models for the Left Hand Controller.")]
  30. public GameObject modelAliasLeftController;
  31. [Tooltip("A reference to the GameObject that models for the Right Hand Controller.")]
  32. public GameObject modelAliasRightController;
  33. public event LoadEventHandler Loaded;
  34. public event LoadEventHandler Unloaded;
  35. /// <summary>
  36. /// The info of the SDK to use to deal with all system actions. By setting this to `null` the fallback SDK will be used.
  37. /// </summary>
  38. public VRTK_SDKInfo systemSDKInfo
  39. {
  40. get
  41. {
  42. return cachedSystemSDKInfo;
  43. }
  44. set
  45. {
  46. value = value ?? VRTK_SDKInfo.Create<SDK_BaseSystem, SDK_FallbackSystem, SDK_FallbackSystem>()[0];
  47. if (cachedSystemSDKInfo == value)
  48. {
  49. return;
  50. }
  51. #if UNITY_EDITOR
  52. DestroyImmediate(cachedSystemSDK);
  53. #else
  54. Destroy(cachedSystemSDK);
  55. #endif
  56. cachedSystemSDK = null;
  57. cachedSystemSDKInfo = new VRTK_SDKInfo(value);
  58. PopulateObjectReferences(false);
  59. }
  60. }
  61. /// <summary>
  62. /// The info of the SDK to use to utilize room scale boundaries. By setting this to `null` the fallback SDK will be used.
  63. /// </summary>
  64. public VRTK_SDKInfo boundariesSDKInfo
  65. {
  66. get
  67. {
  68. return cachedBoundariesSDKInfo;
  69. }
  70. set
  71. {
  72. value = value ?? VRTK_SDKInfo.Create<SDK_BaseBoundaries, SDK_FallbackBoundaries, SDK_FallbackBoundaries>()[0];
  73. if (cachedBoundariesSDKInfo == value)
  74. {
  75. return;
  76. }
  77. #if UNITY_EDITOR
  78. DestroyImmediate(cachedBoundariesSDK);
  79. #else
  80. Destroy(cachedBoundariesSDK);
  81. #endif
  82. cachedBoundariesSDK = null;
  83. cachedBoundariesSDKInfo = new VRTK_SDKInfo(value);
  84. PopulateObjectReferences(false);
  85. }
  86. }
  87. /// <summary>
  88. /// The info of the SDK to use to utilize the VR headset. By setting this to `null` the fallback SDK will be used.
  89. /// </summary>
  90. public VRTK_SDKInfo headsetSDKInfo
  91. {
  92. get
  93. {
  94. return cachedHeadsetSDKInfo;
  95. }
  96. set
  97. {
  98. value = value ?? VRTK_SDKInfo.Create<SDK_BaseHeadset, SDK_FallbackHeadset, SDK_FallbackHeadset>()[0];
  99. if (cachedHeadsetSDKInfo == value)
  100. {
  101. return;
  102. }
  103. #if UNITY_EDITOR
  104. DestroyImmediate(cachedHeadsetSDK);
  105. #else
  106. Destroy(cachedHeadsetSDK);
  107. #endif
  108. cachedHeadsetSDK = null;
  109. cachedHeadsetSDKInfo = new VRTK_SDKInfo(value);
  110. PopulateObjectReferences(false);
  111. }
  112. }
  113. /// <summary>
  114. /// The info of the SDK to use to utilize the input devices. By setting this to `null` the fallback SDK will be used.
  115. /// </summary>
  116. public VRTK_SDKInfo controllerSDKInfo
  117. {
  118. get
  119. {
  120. return cachedControllerSDKInfo;
  121. }
  122. set
  123. {
  124. value = value ?? VRTK_SDKInfo.Create<SDK_BaseController, SDK_FallbackController, SDK_FallbackController>()[0];
  125. if (cachedControllerSDKInfo == value)
  126. {
  127. return;
  128. }
  129. #if UNITY_EDITOR
  130. DestroyImmediate(cachedControllerSDK);
  131. #else
  132. Destroy(cachedControllerSDK);
  133. #endif
  134. cachedControllerSDK = null;
  135. cachedControllerSDKInfo = new VRTK_SDKInfo(value);
  136. PopulateObjectReferences(false);
  137. }
  138. }
  139. /// <summary>
  140. /// The selected system SDK.
  141. /// </summary>
  142. /// <returns>The currently selected system SDK.</returns>
  143. public SDK_BaseSystem systemSDK
  144. {
  145. get
  146. {
  147. if (cachedSystemSDK == null)
  148. {
  149. HandleSDKGetter<SDK_BaseSystem>("System", systemSDKInfo, VRTK_SDKManager.InstalledSystemSDKInfos);
  150. cachedSystemSDK = (SDK_BaseSystem)ScriptableObject.CreateInstance(systemSDKInfo.type);
  151. }
  152. return cachedSystemSDK;
  153. }
  154. }
  155. /// <summary>
  156. /// The selected boundaries SDK.
  157. /// </summary>
  158. /// <returns>The currently selected boundaries SDK.</returns>
  159. public SDK_BaseBoundaries boundariesSDK
  160. {
  161. get
  162. {
  163. if (cachedBoundariesSDK == null)
  164. {
  165. HandleSDKGetter<SDK_BaseBoundaries>("Boundaries", boundariesSDKInfo, VRTK_SDKManager.InstalledBoundariesSDKInfos);
  166. cachedBoundariesSDK = (SDK_BaseBoundaries)ScriptableObject.CreateInstance(boundariesSDKInfo.type);
  167. }
  168. return cachedBoundariesSDK;
  169. }
  170. }
  171. /// <summary>
  172. /// The selected headset SDK.
  173. /// </summary>
  174. /// <returns>The currently selected headset SDK.</returns>
  175. public SDK_BaseHeadset headsetSDK
  176. {
  177. get
  178. {
  179. if (cachedHeadsetSDK == null)
  180. {
  181. HandleSDKGetter<SDK_BaseHeadset>("Headset", headsetSDKInfo, VRTK_SDKManager.InstalledHeadsetSDKInfos);
  182. cachedHeadsetSDK = (SDK_BaseHeadset)ScriptableObject.CreateInstance(headsetSDKInfo.type);
  183. }
  184. return cachedHeadsetSDK;
  185. }
  186. }
  187. /// <summary>
  188. /// The selected controller SDK.
  189. /// </summary>
  190. /// <returns>The currently selected controller SDK.</returns>
  191. public SDK_BaseController controllerSDK
  192. {
  193. get
  194. {
  195. if (cachedControllerSDK == null)
  196. {
  197. HandleSDKGetter<SDK_BaseController>("Controller", controllerSDKInfo, VRTK_SDKManager.InstalledControllerSDKInfos);
  198. cachedControllerSDK = (SDK_BaseController)ScriptableObject.CreateInstance(controllerSDKInfo.type);
  199. }
  200. return cachedControllerSDK;
  201. }
  202. }
  203. /// <summary>
  204. /// The VR device names used by the currently selected SDKs.
  205. /// </summary>
  206. public string[] usedVRDeviceNames
  207. {
  208. get
  209. {
  210. VRTK_SDKInfo[] infos = { systemSDKInfo, boundariesSDKInfo, headsetSDKInfo, controllerSDKInfo };
  211. return infos.Select(info => info.description.vrDeviceName)
  212. .Distinct()
  213. .ToArray();
  214. }
  215. }
  216. /// <summary>
  217. /// Whether it's possible to use the Setup. See `GetSimplifiedErrorDescriptions` for more info.
  218. /// </summary>
  219. public bool isValid
  220. {
  221. get
  222. {
  223. return GetSimplifiedErrorDescriptions().Length == 0;
  224. }
  225. }
  226. [SerializeField]
  227. private VRTK_SDKInfo cachedSystemSDKInfo = VRTK_SDKInfo.Create<SDK_BaseSystem, SDK_FallbackSystem, SDK_FallbackSystem>()[0];
  228. [SerializeField]
  229. private VRTK_SDKInfo cachedBoundariesSDKInfo = VRTK_SDKInfo.Create<SDK_BaseBoundaries, SDK_FallbackBoundaries, SDK_FallbackBoundaries>()[0];
  230. [SerializeField]
  231. private VRTK_SDKInfo cachedHeadsetSDKInfo = VRTK_SDKInfo.Create<SDK_BaseHeadset, SDK_FallbackHeadset, SDK_FallbackHeadset>()[0];
  232. [SerializeField]
  233. private VRTK_SDKInfo cachedControllerSDKInfo = VRTK_SDKInfo.Create<SDK_BaseController, SDK_FallbackController, SDK_FallbackController>()[0];
  234. private SDK_BaseSystem cachedSystemSDK;
  235. private SDK_BaseBoundaries cachedBoundariesSDK;
  236. private SDK_BaseHeadset cachedHeadsetSDK;
  237. private SDK_BaseController cachedControllerSDK;
  238. /// <summary>
  239. /// The PopulateObjectReferences method populates the object references by using the currently set SDKs.
  240. /// </summary>
  241. /// <param name="force">Whether to ignore `autoPopulateObjectReferences` while deciding to populate.</param>
  242. public void PopulateObjectReferences(bool force)
  243. {
  244. if (!(force || autoPopulateObjectReferences))
  245. {
  246. return;
  247. }
  248. #if UNITY_EDITOR
  249. if (!EditorApplication.isPlaying && VRTK_SDKManager.ValidInstance())
  250. {
  251. VRTK_SDKManager.instance.SetLoadedSDKSetupToPopulateObjectReferences(this);
  252. }
  253. #endif
  254. VRTK_SDK_Bridge.InvalidateCaches();
  255. #if UNITY_EDITOR
  256. Undo.RecordObject(this, "Populate Object References");
  257. #endif
  258. actualBoundaries = null;
  259. actualHeadset = null;
  260. actualLeftController = null;
  261. actualRightController = null;
  262. modelAliasLeftController = null;
  263. modelAliasRightController = null;
  264. Transform playAreaTransform = boundariesSDK.GetPlayArea();
  265. Transform headsetTransform = headsetSDK.GetHeadset();
  266. actualBoundaries = playAreaTransform == null ? null : playAreaTransform.gameObject;
  267. actualHeadset = headsetTransform == null ? null : headsetTransform.gameObject;
  268. actualLeftController = controllerSDK.GetControllerLeftHand(true);
  269. actualRightController = controllerSDK.GetControllerRightHand(true);
  270. modelAliasLeftController = controllerSDK.GetControllerModel(SDK_BaseController.ControllerHand.Left);
  271. modelAliasRightController = controllerSDK.GetControllerModel(SDK_BaseController.ControllerHand.Right);
  272. }
  273. /// <summary>
  274. /// The GetSimplifiedErrorDescriptions method checks the setup for errors and creates an array of error descriptions.
  275. /// </summary>
  276. /// <remarks>
  277. /// The returned error descriptions handle the following cases for the current SDK infos:
  278. /// <list type="bullet">
  279. /// <item> <description>Its type doesn't exist anymore.</description> </item>
  280. /// <item> <description>It's a fallback SDK.</description> </item>
  281. /// <item> <description>It doesn't have its scripting define symbols added.</description> </item>
  282. /// <item> <description>It's missing its vendor SDK.</description> </item>
  283. /// </list>
  284. /// Additionally the current SDK infos are checked whether they use multiple VR Devices.
  285. /// </remarks>
  286. /// <returns>An array of all the error descriptions. Returns an empty array if no errors are found.</returns>
  287. public string[] GetSimplifiedErrorDescriptions()
  288. {
  289. List<string> sdkErrorDescriptions = new List<string>();
  290. ReadOnlyCollection<VRTK_SDKInfo>[] installedSDKInfosList = {
  291. VRTK_SDKManager.InstalledSystemSDKInfos,
  292. VRTK_SDKManager.InstalledBoundariesSDKInfos,
  293. VRTK_SDKManager.InstalledHeadsetSDKInfos,
  294. VRTK_SDKManager.InstalledControllerSDKInfos
  295. };
  296. VRTK_SDKInfo[] currentSDKInfos = { systemSDKInfo, boundariesSDKInfo, headsetSDKInfo, controllerSDKInfo };
  297. for (int index = 0; index < installedSDKInfosList.Length; index++)
  298. {
  299. ReadOnlyCollection<VRTK_SDKInfo> installedSDKInfos = installedSDKInfosList[index];
  300. VRTK_SDKInfo currentSDKInfo = currentSDKInfos[index];
  301. Type baseType = VRTK_SharedMethods.GetBaseType(currentSDKInfo.type);
  302. if (baseType == null)
  303. {
  304. continue;
  305. }
  306. if (currentSDKInfo.originalTypeNameWhenFallbackIsUsed != null)
  307. {
  308. sdkErrorDescriptions.Add(string.Format("The SDK '{0}' doesn't exist anymore.", currentSDKInfo.originalTypeNameWhenFallbackIsUsed));
  309. }
  310. else if (currentSDKInfo.description.describesFallbackSDK)
  311. {
  312. sdkErrorDescriptions.Add("A fallback SDK is used. Make sure to set a real SDK.");
  313. }
  314. else if (!installedSDKInfos.Contains(currentSDKInfo))
  315. {
  316. sdkErrorDescriptions.Add(string.Format("The vendor SDK for '{0}' is not installed.", currentSDKInfo.description.prettyName));
  317. }
  318. }
  319. if (usedVRDeviceNames.Except(new[] { "None" }).Count() > 1)
  320. {
  321. sdkErrorDescriptions.Add("The current SDK selection uses multiple VR Devices. It's not possible to use more than one VR Device at the same time.");
  322. }
  323. return sdkErrorDescriptions.Distinct().ToArray();
  324. }
  325. /// <summary>
  326. /// The OnLoaded method determines when an SDK Setup has been loaded.
  327. /// </summary>
  328. /// <param name="sender">The SDK Manager that has loaded the SDK Setup.</param>
  329. public void OnLoaded(VRTK_SDKManager sender)
  330. {
  331. List<SDK_Base> sdkBases = new SDK_Base[] { systemSDK, boundariesSDK, headsetSDK, controllerSDK }.ToList();
  332. sdkBases.ForEach(sdkBase => sdkBase.OnBeforeSetupLoad(this));
  333. gameObject.SetActive(true);
  334. VRTK_SDK_Bridge.InvalidateCaches();
  335. SetupHeadset();
  336. SetupControllers();
  337. boundariesSDK.InitBoundaries();
  338. sdkBases.ForEach(sdkBase => sdkBase.OnAfterSetupLoad(this));
  339. LoadEventHandler handler = Loaded;
  340. if (handler != null)
  341. {
  342. handler(sender, this);
  343. }
  344. }
  345. /// <summary>
  346. /// The OnUnloaded method determines when an SDK Setup has been unloaded.
  347. /// </summary>
  348. /// <param name="sender">The SDK Manager that has unloaded the SDK Setup.</param>
  349. public void OnUnloaded(VRTK_SDKManager sender)
  350. {
  351. List<SDK_Base> sdkBases = new SDK_Base[] { systemSDK, boundariesSDK, headsetSDK, controllerSDK }.ToList();
  352. sdkBases.ForEach(sdkBase => sdkBase.OnBeforeSetupUnload(this));
  353. gameObject.SetActive(false);
  354. sdkBases.ForEach(sdkBase => sdkBase.OnAfterSetupUnload(this));
  355. LoadEventHandler handler = Unloaded;
  356. if (handler != null)
  357. {
  358. handler(sender, this);
  359. }
  360. }
  361. private void OnEnable()
  362. {
  363. #pragma warning disable 618
  364. if (VRTK_SDKManager.ValidInstance() && !VRTK_SDKManager.instance.persistOnLoad)
  365. #pragma warning restore 618
  366. {
  367. PopulateObjectReferences(false);
  368. }
  369. }
  370. #if UNITY_EDITOR
  371. static VRTK_SDKSetup()
  372. {
  373. //call AutoPopulateObjectReferences when the currently active scene changes
  374. #if UNITY_2018_1_OR_NEWER
  375. EditorApplication.hierarchyChanged += AutoPopulateObjectReferences;
  376. #else
  377. EditorApplication.hierarchyWindowChanged += AutoPopulateObjectReferences;
  378. #endif
  379. }
  380. [DidReloadScripts(2)]
  381. private static void AutoPopulateObjectReferences()
  382. {
  383. if (EditorApplication.isPlayingOrWillChangePlaymode)
  384. {
  385. return;
  386. }
  387. foreach (VRTK_SDKSetup setup in VRTK_SharedMethods.FindEvenInactiveComponents<VRTK_SDKSetup>(true))
  388. {
  389. setup.PopulateObjectReferences(false);
  390. }
  391. }
  392. #endif
  393. /// <summary>
  394. /// Handles the various SDK getters by logging potential errors.
  395. /// </summary>
  396. /// <typeparam name="BaseType">The SDK base type of which to handle the getter for. Must be a subclass of SDK_Base.</typeparam>
  397. /// <param name="prettyName">The pretty name of the base SDK to use when logging errors.</param>
  398. /// <param name="info">The SDK info of which the SDK getter was called.</param>
  399. /// <param name="installedInfos">The installed SDK infos of which the SDK getter was called.</param>
  400. private static void HandleSDKGetter<BaseType>(string prettyName, VRTK_SDKInfo info, IEnumerable<VRTK_SDKInfo> installedInfos) where BaseType : SDK_Base
  401. {
  402. if (VRTK_SharedMethods.IsEditTime())
  403. {
  404. return;
  405. }
  406. string sdkErrorDescription = GetSDKErrorDescription<BaseType>(prettyName, info, installedInfos);
  407. if (!string.IsNullOrEmpty(sdkErrorDescription))
  408. {
  409. VRTK_Logger.Error(sdkErrorDescription);
  410. }
  411. }
  412. /// <summary>
  413. /// Returns an error description in case any of these are true for the current SDK info:
  414. /// <list type="bullet">
  415. /// <item> <description>Its type doesn't exist anymore.</description> </item>
  416. /// <item> <description>It's a fallback SDK.</description> </item>
  417. /// <item> <description>It doesn't have its scripting define symbols added.</description> </item>
  418. /// <item> <description>It's missing its vendor SDK.</description> </item>
  419. /// </list>
  420. /// </summary>
  421. /// <typeparam name="BaseType">The SDK base type of which to return the error description for. Must be a subclass of SDK_Base.</typeparam>
  422. /// <param name="prettyName">The pretty name of the base SDK to use when returning error descriptions.</param>
  423. /// <param name="info">The SDK info of which to return the error description for.</param>
  424. /// <param name="installedInfos">The installed SDK infos.</param>
  425. /// <returns>An error description if there is one, else `null`.</returns>
  426. private static string GetSDKErrorDescription<BaseType>(string prettyName, VRTK_SDKInfo info, IEnumerable<VRTK_SDKInfo> installedInfos) where BaseType : SDK_Base
  427. {
  428. Type selectedType = info.type;
  429. Type baseType = typeof(BaseType);
  430. Type fallbackType = VRTK_SDKManager.SDKFallbackTypesByBaseType[baseType];
  431. if (selectedType == fallbackType)
  432. {
  433. return string.Format("The fallback {0} SDK is being used because there is no other {0} SDK set in the SDK Setup.", prettyName);
  434. }
  435. if (!VRTK_SharedMethods.IsTypeAssignableFrom(baseType, selectedType) || VRTK_SharedMethods.IsTypeAssignableFrom(fallbackType, selectedType))
  436. {
  437. string description = string.Format("The fallback {0} SDK is being used despite being set to '{1}'.", prettyName, selectedType.Name);
  438. if (installedInfos.Select(installedInfo => installedInfo.type).Contains(selectedType))
  439. {
  440. return description + " Its needed scripting define symbols are not added. You can click the GameObject with the `VRTK_SDKManager` script attached to it in Edit Mode and choose to automatically let the manager handle the scripting define symbols." + VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.SCRIPTING_DEFINE_SYMBOLS_NOT_FOUND);
  441. }
  442. return description + " The needed vendor SDK isn't installed.";
  443. }
  444. return null;
  445. }
  446. private void SetupHeadset()
  447. {
  448. if (actualHeadset != null && !actualHeadset.GetComponent<VRTK_TrackedHeadset>())
  449. {
  450. actualHeadset.AddComponent<VRTK_TrackedHeadset>();
  451. }
  452. }
  453. private void SetupControllers()
  454. {
  455. Action<GameObject, GameObject> setParent = (scriptAliasGameObject, actualGameObject) =>
  456. {
  457. if (scriptAliasGameObject == null)
  458. {
  459. return;
  460. }
  461. Transform scriptAliasTransform = scriptAliasGameObject.transform;
  462. Transform actualTransform = actualGameObject.transform;
  463. if (scriptAliasTransform.parent != actualTransform)
  464. {
  465. Vector3 previousScale = scriptAliasTransform.localScale;
  466. scriptAliasTransform.SetParent(actualTransform);
  467. scriptAliasTransform.localScale = previousScale;
  468. }
  469. scriptAliasTransform.localPosition = Vector3.zero;
  470. scriptAliasTransform.localRotation = Quaternion.identity;
  471. };
  472. if (actualLeftController != null && VRTK_SDKManager.ValidInstance())
  473. {
  474. setParent(VRTK_SDKManager.instance.scriptAliasLeftController, actualLeftController);
  475. if (actualLeftController.GetComponent<VRTK_TrackedController>() == null)
  476. {
  477. actualLeftController.AddComponent<VRTK_TrackedController>();
  478. }
  479. }
  480. if (actualRightController != null && VRTK_SDKManager.ValidInstance())
  481. {
  482. setParent(VRTK_SDKManager.instance.scriptAliasRightController, actualRightController);
  483. if (actualRightController.GetComponent<VRTK_TrackedController>() == null)
  484. {
  485. actualRightController.AddComponent<VRTK_TrackedController>();
  486. }
  487. }
  488. }
  489. }
  490. }