TrackedPoseDriver.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. #if ENABLE_VR || ENABLE_AR
  5. using UnityEngine.XR;
  6. using UnityEngine.Experimental.XR.Interaction;
  7. #endif
  8. #if ENABLE_AR
  9. using UnityEngine.XR.Tango;
  10. #endif
  11. [assembly: InternalsVisibleTo("UnityEditor.SpatialTracking")]
  12. namespace UnityEngine.SpatialTracking
  13. {
  14. internal class TrackedPoseDriverDataDescription
  15. {
  16. internal struct PoseData
  17. {
  18. public List<string> PoseNames;
  19. public List<TrackedPoseDriver.TrackedPose> Poses;
  20. }
  21. internal static List<PoseData> DeviceData = new List<PoseData>
  22. {
  23. // Generic XR Device
  24. new PoseData
  25. {
  26. PoseNames = new List<string>
  27. {
  28. "Left Eye", "Right Eye", "Center Eye - HMD Reference", "Head", "Color Camera"
  29. },
  30. Poses = new List<TrackedPoseDriver.TrackedPose>
  31. {
  32. TrackedPoseDriver.TrackedPose.LeftEye,
  33. TrackedPoseDriver.TrackedPose.RightEye,
  34. TrackedPoseDriver.TrackedPose.Center,
  35. TrackedPoseDriver.TrackedPose.Head,
  36. TrackedPoseDriver.TrackedPose.ColorCamera
  37. }
  38. },
  39. // generic controller
  40. new PoseData
  41. {
  42. PoseNames = new List<string>
  43. {
  44. "Left Controller", "Right Controller"
  45. },
  46. Poses = new List<TrackedPoseDriver.TrackedPose>
  47. {
  48. TrackedPoseDriver.TrackedPose.LeftPose,
  49. TrackedPoseDriver.TrackedPose.RightPose
  50. }
  51. },
  52. // generic remote
  53. new PoseData
  54. {
  55. PoseNames = new List<string>
  56. {
  57. "Device Pose"
  58. },
  59. Poses = new List<TrackedPoseDriver.TrackedPose>
  60. {
  61. TrackedPoseDriver.TrackedPose.RemotePose,
  62. }
  63. },
  64. };
  65. }
  66. /// <summary>
  67. /// Bitflag enum which represents what data was set on an associated Pose struct
  68. /// </summary>
  69. [Flags]
  70. public enum PoseDataFlags
  71. {
  72. /// <summary>
  73. /// No data was actually set on the pose
  74. /// </summary>
  75. NoData = 0,
  76. /// <summary>
  77. /// If this flag is set, position data was updated on the associated pose struct
  78. /// </summary>
  79. Position = 1 << 0,
  80. /// <summary>
  81. /// If this flag is set, rotation data was updated on the associated pose struct
  82. /// </summary>
  83. Rotation = 1 << 1,
  84. }
  85. /// <summary>
  86. /// The PoseDataSource class acts as a container for the GetDatafromSource method call that should be used by PoseProviders wanting to query data for a particular pose.
  87. /// </summary>
  88. static public class PoseDataSource
  89. {
  90. #if ENABLE_AR || ENABLE_VR
  91. static internal List<XR.XRNodeState> nodeStates = new List<XR.XRNodeState>();
  92. static internal PoseDataFlags GetNodePoseData(XR.XRNode node, out Pose resultPose)
  93. {
  94. PoseDataFlags retData = PoseDataFlags.NoData;
  95. XR.InputTracking.GetNodeStates(nodeStates);
  96. foreach (XR.XRNodeState nodeState in nodeStates)
  97. {
  98. if (nodeState.nodeType == node)
  99. {
  100. if(nodeState.TryGetPosition(out resultPose.position))
  101. {
  102. retData |= PoseDataFlags.Position;
  103. }
  104. if (nodeState.TryGetRotation(out resultPose.rotation))
  105. {
  106. retData |= PoseDataFlags.Rotation;
  107. }
  108. return retData;
  109. }
  110. }
  111. resultPose = Pose.identity;
  112. return retData;
  113. }
  114. #endif
  115. /// <summary>The GetDatafromSource method is used to query data from the XRNode subsystem based on the provided pose source.</summary>
  116. /// <param name = "poseSource" > The pose source to request data for.</param>
  117. /// <param name = "resultPose" > The resulting pose data.</param>
  118. /// <returns>True, if the pose source is valid, otherwise false.</returns>
  119. static public bool TryGetDataFromSource(TrackedPoseDriver.TrackedPose poseSource, out Pose resultPose)
  120. {
  121. return GetDataFromSource(poseSource, out resultPose) == (PoseDataFlags.Position | PoseDataFlags.Rotation);
  122. }
  123. /// <summary>The GetDatafromSource method is used to query data from the XRNode subsystem based on the provided pose source.</summary>
  124. /// <param name = "poseSource" > The pose source to request data for.</param>
  125. /// <param name = "resultPose" > The resulting pose data. This function will return the Center Eye pose if the Color Camera pose is not available. </param>
  126. /// <returns>Retuns a bitflag which represents which data has been retrieved from the provided pose source</returns>
  127. static public PoseDataFlags GetDataFromSource(TrackedPoseDriver.TrackedPose poseSource, out Pose resultPose)
  128. {
  129. #if ENABLE_AR || ENABLE_VR
  130. switch (poseSource)
  131. {
  132. case TrackedPoseDriver.TrackedPose.RemotePose:
  133. {
  134. PoseDataFlags retFlags = GetNodePoseData(XR.XRNode.RightHand, out resultPose);
  135. if (retFlags == PoseDataFlags.NoData)
  136. return GetNodePoseData(XR.XRNode.LeftHand, out resultPose);
  137. return retFlags;
  138. }
  139. case TrackedPoseDriver.TrackedPose.LeftEye:
  140. {
  141. return GetNodePoseData(XR.XRNode.LeftEye, out resultPose);
  142. }
  143. case TrackedPoseDriver.TrackedPose.RightEye:
  144. {
  145. return GetNodePoseData(XR.XRNode.RightEye, out resultPose);
  146. }
  147. case TrackedPoseDriver.TrackedPose.Head:
  148. {
  149. return GetNodePoseData(XR.XRNode.Head, out resultPose);
  150. }
  151. case TrackedPoseDriver.TrackedPose.Center:
  152. {
  153. return GetNodePoseData(XR.XRNode.CenterEye, out resultPose);
  154. }
  155. case TrackedPoseDriver.TrackedPose.LeftPose:
  156. {
  157. return GetNodePoseData(XR.XRNode.LeftHand, out resultPose);
  158. }
  159. case TrackedPoseDriver.TrackedPose.RightPose:
  160. {
  161. return GetNodePoseData(XR.XRNode.RightHand, out resultPose);
  162. }
  163. case TrackedPoseDriver.TrackedPose.ColorCamera:
  164. {
  165. PoseDataFlags retFlags = TryGetTangoPose(out resultPose);
  166. if(retFlags == PoseDataFlags.NoData)
  167. {
  168. // We fall back to CenterEye because we can't currently extend the XRNode structure, nor are we ready to replace it.
  169. return GetNodePoseData(XR.XRNode.CenterEye, out resultPose);
  170. }
  171. return retFlags;
  172. }
  173. default:
  174. {
  175. Debug.LogWarningFormat("Unable to retrieve pose data for poseSource: {0}", poseSource.ToString());
  176. break;
  177. }
  178. }
  179. #endif
  180. resultPose = Pose.identity;
  181. return PoseDataFlags.NoData;
  182. }
  183. static PoseDataFlags TryGetTangoPose(out Pose pose)
  184. {
  185. #if ENABLE_AR
  186. PoseData poseOut;
  187. if (TangoInputTracking.TryGetPoseAtTime(out poseOut) && poseOut.statusCode == PoseStatus.Valid)
  188. {
  189. pose.position = poseOut.position;
  190. pose.rotation = poseOut.rotation;
  191. return PoseDataFlags.Position | PoseDataFlags.Rotation; ;
  192. }
  193. #endif
  194. pose = Pose.identity;
  195. return PoseDataFlags.NoData;
  196. }
  197. }
  198. // The DefaultExecutionOrder is needed because TrackedPoseDriver does some
  199. // of its work in regular Update and FixedUpdate calls, but this needs to
  200. // be done before regular user scripts have their own Update and
  201. // FixedUpdate calls, in order that they correctly get the values for this
  202. // frame and not the previous.
  203. // -32000 is the minimal possible execution order value; -30000 makes it
  204. // is unlikely users chose lower values for their scripts by accident, but
  205. // still makes it possible.
  206. /// <summary>
  207. /// The TrackedPoseDriver component applies the current Pose value of a tracked device to the transform of the GameObject.
  208. /// TrackedPoseDriver can track multiple types of devices including XR HMDs, controllers, and remotes.
  209. /// </summary>
  210. [DefaultExecutionOrder(-30000)]
  211. [Serializable]
  212. [AddComponentMenu("XR/Tracked Pose Driver")]
  213. [HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.legacyinputhelpers@2.1/manual/index.html")]
  214. public class TrackedPoseDriver : MonoBehaviour
  215. {
  216. /// <summary>
  217. /// The device being tracked by the tracked pose driver
  218. /// </summary>
  219. public enum DeviceType
  220. {
  221. /// <summary>
  222. /// An XR Controller, use this value for controllers
  223. /// </summary>
  224. GenericXRDevice = 0,
  225. /// <summary>
  226. /// An Generic XR Devices, use this value for HMD and AR Mobile device tracking
  227. /// </summary>
  228. GenericXRController = 1,
  229. /// <summary>
  230. /// An XR Remote, use this value for mobile remotes
  231. /// </summary>
  232. GenericXRRemote = 2
  233. }
  234. /// <summary>
  235. /// The list of endpoints that users can track with the <see cref="TrackedPoseDriver"/>
  236. /// </summary>
  237. public enum TrackedPose
  238. {
  239. /// <summary>
  240. /// The left eye of a HMD style device
  241. /// </summary>
  242. LeftEye = 0,
  243. /// <summary>
  244. /// The right eye of a HMD style device
  245. /// </summary>
  246. RightEye = 1,
  247. /// <summary>
  248. /// The center eye of a HMD style device, this is usually the default for most HMDs
  249. /// </summary>
  250. Center = 2,
  251. /// <summary>
  252. /// The head eye of a HMD style device
  253. /// </summary>
  254. Head = 3,
  255. /// <summary>
  256. /// The left hand controller pose
  257. /// </summary>
  258. LeftPose = 4,
  259. /// <summary>
  260. /// The right hand controller pose
  261. /// </summary>
  262. RightPose = 5,
  263. /// <summary>
  264. /// The color camera of a mobile device
  265. /// </summary>
  266. ColorCamera = 6,
  267. /// <summary>
  268. /// No Longer Used
  269. /// </summary>
  270. DepthCameraDeprecated = 7,
  271. /// <summary>
  272. /// No Longer Used
  273. /// </summary>
  274. FisheyeCameraDeprected = 8,
  275. /// <summary>
  276. /// No Longer Used
  277. /// </summary>
  278. DeviceDeprecated = 9,
  279. /// <summary>
  280. /// The pose of a mobile remote
  281. /// </summary>
  282. RemotePose = 10,
  283. }
  284. [SerializeField]
  285. DeviceType m_Device;
  286. /// <summary>
  287. /// This is used to indicate which pose the TrackedPoseDriver is currently tracking.
  288. /// </summary>
  289. public DeviceType deviceType
  290. {
  291. get { return m_Device; }
  292. internal set { m_Device = value; }
  293. }
  294. [SerializeField]
  295. TrackedPose m_PoseSource = TrackedPoseDriver.TrackedPose.Center;
  296. /// <summary>
  297. /// The pose being tracked by the tracked pose driver
  298. /// </summary>
  299. public TrackedPose poseSource
  300. {
  301. get { return m_PoseSource; }
  302. internal set { m_PoseSource = value; }
  303. }
  304. /// <summary>
  305. /// This method is used to set the device / pose pair for the SpatialTracking.TrackedPoseDriver. setting an invalid combination of these values will return false.
  306. /// </summary>
  307. /// <param name="deviceType">The device type that we wish to track </param>
  308. /// <param name="pose">The pose source that we wish to track</param>
  309. /// <returns>true if the values provided are sensible, otherwise false</returns>
  310. public bool SetPoseSource(DeviceType deviceType, TrackedPose pose)
  311. {
  312. if ((int)deviceType < TrackedPoseDriverDataDescription.DeviceData.Count)
  313. {
  314. TrackedPoseDriverDataDescription.PoseData val = TrackedPoseDriverDataDescription.DeviceData[(int)deviceType];
  315. for (int i = 0; i < val.Poses.Count; ++i)
  316. {
  317. if (val.Poses[i] == pose)
  318. {
  319. this.deviceType = deviceType;
  320. poseSource = pose;
  321. return true;
  322. }
  323. }
  324. }
  325. return false;
  326. }
  327. #if ENABLE_VR || ENABLE_AR
  328. [SerializeField]
  329. BasePoseProvider m_PoseProviderComponent = null;
  330. /// <summary>
  331. /// Optional: This field holds the reference to the PoseProvider instance that, if set, will be used to override the behavior of
  332. /// the TrackedPoseDriver. When this field is empty, the TrackedPoseDriver will operate as per usual, with pose data being
  333. /// retrieved from the device or pose settings of the TrackedPoseDriver. When this field is set, the pose data will be
  334. /// provided by the attached PoseProvider. The device or pose fields will be hidden as they are no longer used to
  335. /// control the parent GameObject Transform.
  336. /// </summary>
  337. public BasePoseProvider poseProviderComponent
  338. {
  339. get { return m_PoseProviderComponent; }
  340. set
  341. {
  342. m_PoseProviderComponent = value;
  343. }
  344. }
  345. #endif
  346. PoseDataFlags GetPoseData(DeviceType device, TrackedPose poseSource, out Pose resultPose)
  347. {
  348. #if ENABLE_VR || ENABLE_AR
  349. if (m_PoseProviderComponent != null)
  350. {
  351. return m_PoseProviderComponent.GetPoseFromProvider(out resultPose);
  352. }
  353. #endif
  354. return PoseDataSource.GetDataFromSource(poseSource, out resultPose);
  355. }
  356. /// <summary>
  357. /// This enum is used to indicate which parts of the pose will be applied to the parent transform
  358. /// </summary>
  359. public enum TrackingType
  360. {
  361. /// <summary>
  362. /// With this setting, both the pose's rotation and position will be applied to the parent transform
  363. /// </summary>
  364. RotationAndPosition,
  365. /// <summary>
  366. /// With this setting, only the pose's rotation will be applied to the parent transform
  367. /// </summary>
  368. RotationOnly,
  369. /// <summary>
  370. /// With this setting, only the pose's position will be applied to the parent transform
  371. /// </summary>
  372. PositionOnly
  373. }
  374. [SerializeField]
  375. TrackingType m_TrackingType;
  376. /// <summary>
  377. /// The tracking type being used by the tracked pose driver
  378. /// </summary>
  379. public TrackingType trackingType
  380. {
  381. get { return m_TrackingType; }
  382. set { m_TrackingType = value; }
  383. }
  384. /// <summary>
  385. /// The update type being used by the tracked pose driver
  386. /// </summary>
  387. public enum UpdateType
  388. {
  389. /// <summary>
  390. /// Sample input at both update, and directly before rendering. For smooth head pose tracking,
  391. /// we recommend using this value as it will provide the lowest input latency for the device.
  392. /// This is the default value for the UpdateType option
  393. /// </summary>
  394. UpdateAndBeforeRender,
  395. /// <summary>
  396. /// Only sample input during the update phase of the frame.
  397. /// </summary>
  398. Update,
  399. /// <summary>
  400. /// Only sample input directly before rendering
  401. /// </summary>
  402. BeforeRender,
  403. }
  404. [SerializeField]
  405. UpdateType m_UpdateType = UpdateType.UpdateAndBeforeRender;
  406. /// <summary>
  407. /// The update type being used by the tracked pose driver
  408. /// </summary>
  409. public UpdateType updateType
  410. {
  411. get { return m_UpdateType; }
  412. set { m_UpdateType = value; }
  413. }
  414. [SerializeField]
  415. bool m_UseRelativeTransform = false;
  416. /// <summary>
  417. /// This is used to indicate whether the TrackedPoseDriver will use the object's original transform as its basis.
  418. /// </summary>
  419. public bool UseRelativeTransform
  420. {
  421. get { return m_UseRelativeTransform; }
  422. set { m_UseRelativeTransform = value; }
  423. }
  424. /// <summary>
  425. /// The origin pose is the offset applied to any tracking data. This is only used when in legacy compatibility mode.
  426. /// </summary>
  427. protected Pose m_OriginPose;
  428. /// <summary>
  429. /// originPose is an offset applied to any tracking data read from this object.
  430. /// Setting this value should be reserved for dealing with edge-cases, such as
  431. /// achieving parity between room-scale (floor centered) and stationary (head centered)
  432. /// tracking - without having to alter the transform hierarchy.
  433. /// For user locomotion and gameplay purposes you are usually better off just
  434. /// moving the parent transform of this object.
  435. /// </summary>
  436. public Pose originPose
  437. {
  438. get { return m_OriginPose; }
  439. set { m_OriginPose = value; }
  440. }
  441. private void CacheLocalPosition()
  442. {
  443. m_OriginPose.position = transform.localPosition;
  444. m_OriginPose.rotation = transform.localRotation;
  445. }
  446. private void ResetToCachedLocalPosition()
  447. {
  448. SetLocalTransform(m_OriginPose.position, m_OriginPose.rotation, PoseDataFlags.Position | PoseDataFlags.Rotation);
  449. }
  450. /// <inheritdoc />
  451. protected virtual void Awake()
  452. {
  453. CacheLocalPosition();
  454. #if UNITY_2019_3_OR_NEWER
  455. // deprecated functionality in 2020.1
  456. #else
  457. if (HasStereoCamera())
  458. {
  459. #if ENABLE_AR || ENABLE_VR
  460. XRDevice.DisableAutoXRCameraTracking(GetComponent<Camera>(), true);
  461. #endif
  462. }
  463. #endif
  464. }
  465. /// <inheritdoc />
  466. protected virtual void OnDestroy()
  467. {
  468. #if UNITY_2019_3_OR_NEWER
  469. // deprecated functionality in 2020.1
  470. #else
  471. if (HasStereoCamera())
  472. {
  473. #if ENABLE_AR || ENABLE_VR
  474. XRDevice.DisableAutoXRCameraTracking(GetComponent<Camera>(), false);
  475. #endif
  476. }
  477. #endif
  478. }
  479. /// <inheritdoc />
  480. protected virtual void OnEnable()
  481. {
  482. Application.onBeforeRender += OnBeforeRender;
  483. }
  484. /// <inheritdoc />
  485. protected virtual void OnDisable()
  486. {
  487. // remove delegate registration
  488. ResetToCachedLocalPosition();
  489. Application.onBeforeRender -= OnBeforeRender;
  490. }
  491. /// <inheritdoc />
  492. protected virtual void FixedUpdate()
  493. {
  494. if (m_UpdateType == UpdateType.Update ||
  495. m_UpdateType == UpdateType.UpdateAndBeforeRender)
  496. {
  497. PerformUpdate();
  498. }
  499. }
  500. /// <inheritdoc />
  501. protected virtual void Update()
  502. {
  503. if (m_UpdateType == UpdateType.Update ||
  504. m_UpdateType == UpdateType.UpdateAndBeforeRender)
  505. {
  506. PerformUpdate();
  507. }
  508. }
  509. /// <inheritdoc />
  510. protected virtual void OnBeforeRender()
  511. {
  512. if (m_UpdateType == UpdateType.BeforeRender ||
  513. m_UpdateType == UpdateType.UpdateAndBeforeRender)
  514. {
  515. PerformUpdate();
  516. }
  517. }
  518. /// <summary>
  519. /// Sets the transform that is being driven by the <see cref="TrackedPoseDriver"/>. will only correct set the rotation or position depending on the <see cref="PoseDataFlags"/>
  520. /// </summary>
  521. /// <param name="newPosition">The position to apply.</param>
  522. /// <param name="newRotation">The rotation to apply.</param>
  523. /// <param name="poseFlags">The flags indiciating which of the position/rotation values are provided by the calling code.</param>
  524. protected virtual void SetLocalTransform(Vector3 newPosition, Quaternion newRotation, PoseDataFlags poseFlags)
  525. {
  526. if ((m_TrackingType == TrackingType.RotationAndPosition ||
  527. m_TrackingType == TrackingType.RotationOnly) &&
  528. (poseFlags & PoseDataFlags.Rotation) > 0)
  529. {
  530. transform.localRotation = newRotation;
  531. }
  532. if ((m_TrackingType == TrackingType.RotationAndPosition ||
  533. m_TrackingType == TrackingType.PositionOnly) &&
  534. (poseFlags & PoseDataFlags.Position) > 0)
  535. {
  536. transform.localPosition = newPosition;
  537. }
  538. }
  539. /// <summary>
  540. /// This is only used when running in legacy mode, and will fake the behavior of the old implicit camera tracking. This will transform by the origin pose if necessary.
  541. /// </summary>
  542. /// <param name="pose">Pose to transform by the origin if in relative transform mode.</param>
  543. /// <returns>The pose, with the applied transform if in Relative Transform mode.</returns>
  544. protected Pose TransformPoseByOriginIfNeeded(Pose pose)
  545. {
  546. if (m_UseRelativeTransform)
  547. {
  548. return pose.GetTransformedBy(m_OriginPose);
  549. }
  550. else
  551. {
  552. return pose;
  553. }
  554. }
  555. private bool HasStereoCamera()
  556. {
  557. Camera camera = GetComponent<Camera>();
  558. return camera != null && camera.stereoEnabled;
  559. }
  560. /// <summary>
  561. /// PerformUpdate queries the data from the selected pose source, and then calls <see cref="SetLocalTransform"/> to apply the pose.
  562. /// </summary>
  563. protected virtual void PerformUpdate()
  564. {
  565. if (!enabled)
  566. return;
  567. Pose currentPose = new Pose();
  568. currentPose = Pose.identity;
  569. PoseDataFlags poseFlags = GetPoseData(m_Device, m_PoseSource, out currentPose);
  570. if(poseFlags != PoseDataFlags.NoData)
  571. {
  572. Pose localPose = TransformPoseByOriginIfNeeded(currentPose);
  573. SetLocalTransform(localPose.position, localPose.rotation, poseFlags);
  574. }
  575. }
  576. }
  577. }