ZEDControllerTracker.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. using UnityEngine;
  2. using UnityEngine.XR;
  3. using System.Collections.Generic;
  4. #if ZED_STEAM_VR
  5. using Valve.VR;
  6. #endif
  7. #if UNITY_EDITOR
  8. using UnityEditor;
  9. #endif
  10. /// <summary>
  11. /// Causes the GameObject it's attached to to position itself where a tracked VR object is, such as
  12. /// a Touch controller or Vive Tracker, but compensates for the ZED's latency. This way, virtual
  13. /// controllers don't move ahead of its real-world image.
  14. /// This is done by logging position data from the VR SDK in use (Oculus or OpenVR/SteamVR) each frame, but only
  15. /// applying that position data to this transform after the delay in the latencyCompensation field.
  16. /// Used in the ZED GreenScreen, Drone Shooter, Movie Screen, Planetarium and VR Plane Detection example scenes.
  17. /// </summary>
  18. public class ZEDControllerTracker : MonoBehaviour
  19. {
  20. /// <summary>
  21. /// Type of VR SDK loaded. 'Oculus', 'OpenVR' or empty.
  22. /// </summary>
  23. private string loadeddevice = "";
  24. #if ZED_STEAM_VR //Only enabled if the SteamVR Unity plugin is detected.
  25. /// <summary>
  26. /// OpenVR System class that lets us get inputs straight from the API, bypassing SteamVR.
  27. /// This is necessary because different versions of SteamVR use completely different input systems.
  28. /// </summary>
  29. protected CVRSystem openvrsystem = OpenVR.System;
  30. /// <summary>
  31. /// State of the controller's buttons and axes. Used to check inputs.
  32. /// </summary>
  33. protected VRControllerState_t controllerstate;
  34. /// <summary>
  35. /// State of the controller's buttons and axes last frame. Used to check if buttons just went down or up.
  36. /// </summary>
  37. protected VRControllerState_t lastcontrollerstate;
  38. /// <summary>
  39. /// Size of VRControllerState_t class in bytes. Used to call GetControllerState from the OpenVR API.
  40. /// </summary>
  41. protected const uint controllerstatesize = 64;
  42. /// <summary>
  43. /// Enumerated version of the uint index SteamVR assigns to each device.
  44. /// Converted from OpenVR.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole).
  45. /// </summary>
  46. public enum EIndex
  47. {
  48. None = -1,
  49. Hmd = (int)OpenVR.k_unTrackedDeviceIndex_Hmd,
  50. Device1,
  51. Device2,
  52. Device3,
  53. Device4,
  54. Device5,
  55. Device6,
  56. Device7,
  57. Device8,
  58. Device9,
  59. Device10,
  60. Device11,
  61. Device12,
  62. Device13,
  63. Device14,
  64. Device15
  65. }
  66. [HideInInspector]
  67. public EIndex index = EIndex.None;
  68. /// <summary>
  69. /// How long since we've last checked OpenVR for the specified device.
  70. /// Incremented by Time.deltaTime each frame and reset when it reached timerMaxSteamVR.
  71. /// </summary>
  72. private float timerSteamVR = 0.0f;
  73. /// <summary>
  74. /// How many seconds to wait between checking if the specified device is present in OpenVR.
  75. /// The check is performed when timerSteamVR reaches this number, unless we've already retrieved the device index.
  76. /// </summary>
  77. private float timerMaxSteamVR = 0.25f;
  78. private Devices oldDevice;
  79. /// <summary>
  80. /// If true, will use a direct API check to OpenVR's API to check if a button is down.
  81. /// Does not work if using the SteamVR plugin 2.0 or higher as well as the action system it includes.
  82. /// </summary>
  83. [Tooltip("If true, will use a direct API check to OpenVR's API to check if a button is down.\r\n" +
  84. "Does not work if using the SteamVR plugin 2.0 or higher as well as the action system it includes. ")]
  85. public bool useLegacySteamVRInput = false;
  86. #endif
  87. /// <summary>
  88. /// Per each tracked object ID, contains a list of their recent positions.
  89. /// Used to look up where OpenVR says a tracked object was in the past, for latency compensation.
  90. /// </summary>
  91. public Dictionary<int, List<TimedPoseData>> poseData = new Dictionary<int, List<TimedPoseData>>();
  92. /// <summary>
  93. /// Types of tracked devices.
  94. /// </summary>
  95. public enum Devices
  96. {
  97. RightController,
  98. LeftController,
  99. ViveTracker,
  100. Hmd,
  101. };
  102. /// <summary>
  103. /// Type of trackable device that should be tracked.
  104. /// </summary>
  105. [Tooltip("Type of trackable device that should be tracked.")]
  106. public Devices deviceToTrack;
  107. /// <summary>
  108. /// Latency in milliseconds to be applied on the movement of this tracked object, so that virtual controllers don't
  109. /// move ahead of their real-world image.
  110. /// </summary>
  111. [Tooltip("Latency in milliseconds to be applied on the movement of this tracked object, so that virtual controllers don't" +
  112. " move ahead of their real-world image.")]
  113. [Range(0, 200)]
  114. public int latencyCompensation = 78;
  115. /// <summary>
  116. /// If true, and this is a controller, will offset controller's position by the difference between
  117. /// the VR headset and the ZED's tracked position. This keeps controller's position relative to the ZED.
  118. /// </summary>
  119. [Tooltip("If true, and this is a controller, will offset controller's position by the difference between " +
  120. "the VR headset and the ZED's tracked position. This keeps controller's position relative to the ZED. ")]
  121. [LabelOverride("Enable Controller Drift Fix")]
  122. public bool correctControllerDrift = true;
  123. /// <summary>
  124. /// The Serial number of the controller/tracker to be tracked.
  125. /// If specified, it will override the device returned using the 'Device to Track' selection.
  126. /// Useful for forcing a specific device to be tracked, instead of the first left/right/Tracker object found.
  127. /// If Null, then there's no calibration to be applied to this script.
  128. /// If NONE, the ZEDControllerOffset failed to find any calibration file.
  129. /// If S/N is present, then this script will calibrate itself to track the correct device, if that's not the case already.
  130. /// Note that ZEDOffsetController will load this number from a GreenScreen calibration file, if present.
  131. /// </summary>
  132. [Tooltip("The Serial number of the controller/tracker to be tracked." +
  133. " If specified, overrides the 'Device to Track' selection.")]
  134. public string SNHolder = "";
  135. /// <summary>
  136. /// Cached transform that represents the ZED's head, retrieved from ZEDManager.GetZedRootTransform().
  137. /// Used to find the offset between the HMD and tracked transform to compensate for drift.
  138. /// </summary>
  139. protected Transform zedRigRoot;
  140. /// <summary>
  141. /// Reference to the scene's ZEDManager component. Used for compensating for headset drift when this is on a controller.
  142. /// </summary>
  143. [Tooltip("Reference to the scene's ZEDManager component. Used for compensating for headset drift when this is on a controller. " +
  144. "If left blank, will be set to the first available ZEDManager.")]
  145. public ZEDManager zedManager = null;
  146. /// <summary>
  147. /// Sets up the timed pose dictionary and identifies the VR SDK being used.
  148. /// </summary>
  149. protected virtual void Awake()
  150. {
  151. #if ZED_STEAM_VR
  152. openvrsystem = OpenVR.System;
  153. #endif
  154. poseData.Clear(); //Reset the dictionary.
  155. poseData.Add(1, new List<TimedPoseData>()); //Create the list within the dictionary with its key and value.
  156. //Looking for the loaded device
  157. loadeddevice = XRSettings.loadedDeviceName;
  158. if (!zedManager)
  159. {
  160. zedManager = FindObjectOfType<ZEDManager>();
  161. //If there are multiple cameras in a scene, this arbitrary assignment could be bad. Warn the user.
  162. if (ZEDManager.GetInstances().Count > 1)
  163. {
  164. //Using Log instead of LogWarning because most users don't enable warnings but this is actually important.
  165. Debug.Log("Warning: ZEDController automatically set itself to first available ZED (" + zedManager.cameraID + ") because zedManager " +
  166. "value wasn't set, but there are multiple ZEDManagers in the scene. Assign a reference directly to ensure no unexpected behavior.");
  167. }
  168. }
  169. if (zedManager) zedRigRoot = zedManager.GetZedRootTansform();
  170. }
  171. /// <summary>
  172. /// Update is called every frame.
  173. /// For SteamVR plugin this is where the device Index is set up.
  174. /// For Oculus plugin this is where the tracking is done.
  175. /// </summary>
  176. protected virtual void Update()
  177. {
  178. #if ZED_OCULUS //Used only if the Oculus Integration plugin is detected.
  179. //Check if the VR headset is connected.
  180. if (OVRManager.isHmdPresent && loadeddevice.ToString().ToLower().Contains("oculus"))
  181. {
  182. if (OVRInput.GetConnectedControllers().ToString().ToLower().Contains("touch"))
  183. {
  184. //Depending on which tracked device we are looking for, start tracking it.
  185. if (deviceToTrack == Devices.LeftController) //Track the Left Oculus Controller.
  186. RegisterPosition(1, OVRInput.GetLocalControllerPosition(OVRInput.Controller.LTouch), OVRInput.GetLocalControllerRotation(OVRInput.Controller.LTouch));
  187. if (deviceToTrack == Devices.RightController) //Track the Right Oculus Controller.
  188. RegisterPosition(1, OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch), OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch));
  189. if (deviceToTrack == Devices.Hmd) //Track the Oculus Hmd.
  190. {
  191. #if UNITY_2019_3_OR_NEWER
  192. InputDevice head = InputDevices.GetDeviceAtXRNode(XRNode.CenterEye);
  193. head.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 headPosition);
  194. head.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion headRotation);
  195. RegisterPosition(1, headPosition, headRotation);
  196. #else
  197. RegisterPosition(1, InputTracking.GetLocalPosition(XRNode.CenterEye), InputTracking.GetLocalRotation(XRNode.CenterEye));
  198. #endif
  199. }
  200. //Use our saved positions to apply a delay before assigning it to this object's Transform.
  201. if (poseData.Count > 0)
  202. {
  203. sl.Pose p;
  204. //Delay the saved values inside GetValuePosition() by a factor of latencyCompensation in milliseconds.
  205. p = GetValuePosition(1, (float)(latencyCompensation / 1000.0f));
  206. transform.position = p.translation; //Assign new delayed Position
  207. transform.rotation = p.rotation; //Assign new delayed Rotation.
  208. }
  209. }
  210. }
  211. //Enable updating the internal state of OVRInput.
  212. OVRInput.Update();
  213. #endif
  214. #if ZED_STEAM_VR
  215. UpdateControllerState(); //Get the button states so we can check if buttons are down or not.
  216. timerSteamVR += Time.deltaTime; //Increment timer for checking on devices
  217. if (timerSteamVR <= timerMaxSteamVR)
  218. return;
  219. timerSteamVR = 0f;
  220. //Checks if a device has been assigned
  221. if (index == EIndex.None && loadeddevice.Contains("OpenVR"))
  222. {
  223. //We look for any device that has "tracker" in its 3D model mesh name.
  224. //We're doing this since the device ID changes based on how many devices are connected to SteamVR.
  225. //This way, if there's no controllers or just one, it's going to get the right ID for the Tracker.
  226. if (deviceToTrack == Devices.ViveTracker)
  227. {
  228. var error = ETrackedPropertyError.TrackedProp_Success;
  229. for (uint i = 0; i < 16; i++)
  230. {
  231. var result = new System.Text.StringBuilder((int)64);
  232. OpenVR.System.GetStringTrackedDeviceProperty(i, ETrackedDeviceProperty.Prop_RenderModelName_String, result, 64, ref error);
  233. if (result.ToString().Contains("tracker"))
  234. {
  235. index = (EIndex)i;
  236. break; //We break out of the loop, but we can use this to set up multiple Vive Trackers if we want to.
  237. }
  238. }
  239. }
  240. //Looks for a device with the role of a Right Hand.
  241. if (deviceToTrack == Devices.RightController)
  242. {
  243. index = (EIndex)OpenVR.System.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand);
  244. }
  245. //Looks for a device with the role of a Left Hand.
  246. if (deviceToTrack == Devices.LeftController)
  247. {
  248. index = (EIndex)OpenVR.System.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand);
  249. }
  250. //Assigns the HMD.
  251. if (deviceToTrack == Devices.Hmd)
  252. {
  253. index = EIndex.Hmd;
  254. }
  255. //Display a warning if there was supposed to be a calibration file, and none was found.
  256. if (SNHolder.Equals("NONE"))
  257. {
  258. Debug.LogWarning(ZEDLogMessage.Error2Str(ZEDLogMessage.ERROR.PAD_CAMERA_CALIBRATION_NOT_FOUND));
  259. }
  260. else if (SNHolder != null && index != EIndex.None) //
  261. {
  262. //If the Serial number of the Calibrated device isn't the same as the current tracked device by this script...
  263. var snerror = ETrackedPropertyError.TrackedProp_Success;
  264. var snresult = new System.Text.StringBuilder((int)64);
  265. OpenVR.System.GetStringTrackedDeviceProperty((uint)index, ETrackedDeviceProperty.Prop_SerialNumber_String, snresult, 64, ref snerror);
  266. if (!snresult.ToString().Contains(SNHolder))
  267. {
  268. Debug.LogWarning(ZEDLogMessage.Error2Str(ZEDLogMessage.ERROR.PAD_CAMERA_CALIBRATION_MISMATCH) + " Serial Number: " + SNHolder);
  269. //... then look for that device through all the connected devices.
  270. for (int i = 0; i < 16; i++)
  271. {
  272. //If a device with the same Serial Number is found, then change the device to track of this script.
  273. var chsnresult = new System.Text.StringBuilder((int)64);
  274. OpenVR.System.GetStringTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_RenderModelName_String, snresult, 64, ref snerror);
  275. if (snresult.ToString().Contains("tracker"))
  276. {
  277. index = (EIndex)i;
  278. OpenVR.System.GetStringTrackedDeviceProperty((uint)i, ETrackedDeviceProperty.Prop_SerialNumber_String, snresult, 64, ref snerror);
  279. }
  280. if (snresult.ToString().Contains(SNHolder))
  281. {
  282. index = (EIndex)i;
  283. string deviceRole = OpenVR.System.GetControllerRoleForTrackedDeviceIndex((uint)index).ToString();
  284. if (deviceRole.Equals("RightHand"))
  285. deviceToTrack = Devices.RightController;
  286. else if (deviceRole.Equals("LeftHand"))
  287. deviceToTrack = Devices.LeftController;
  288. else if (deviceRole.Equals("Invalid"))
  289. {
  290. var error = ETrackedPropertyError.TrackedProp_Success;
  291. var result = new System.Text.StringBuilder((int)64);
  292. OpenVR.System.GetStringTrackedDeviceProperty((uint)index, ETrackedDeviceProperty.Prop_RenderModelName_String, result, 64, ref error);
  293. if (result.ToString().Contains("tracker"))
  294. deviceToTrack = Devices.ViveTracker;
  295. }
  296. Debug.Log("A connected device with the correct Serial Number was found, and assigned to " + this + " the correct device to track.");
  297. break;
  298. }
  299. }
  300. }
  301. }
  302. oldDevice = deviceToTrack;
  303. }
  304. if (deviceToTrack != oldDevice)
  305. index = EIndex.None;
  306. #endif
  307. }
  308. #if ZED_STEAM_VR
  309. /// <summary>
  310. /// Whether a given set of poses is currently valid - contains at least one pose and attached to an actual device.
  311. /// </summary>
  312. public bool isValid { get; private set; }
  313. /// <summary>
  314. /// Track the devices for SteamVR and applying a delay.
  315. /// <summary>
  316. protected void OnNewPoses(TrackedDevicePose_t newpose)
  317. {
  318. if (index == EIndex.None)
  319. return;
  320. var i = (int)index;
  321. isValid = false;
  322. if (!newpose.bDeviceIsConnected)
  323. return;
  324. if (!newpose.bPoseIsValid)
  325. return;
  326. isValid = true;
  327. //Get the position and rotation of our tracked device.
  328. var pose = new SteamVR_Utils.RigidTransform(newpose.mDeviceToAbsoluteTracking);
  329. //Saving those values.
  330. RegisterPosition(1, pose.pos, pose.rot);
  331. //Delay the saved values inside GetValuePosition() by a factor of latencyCompensation in milliseconds.
  332. sl.Pose p = GetValuePosition(1, (float)(latencyCompensation / 1000.0f));
  333. transform.localPosition = p.translation;
  334. transform.localRotation = p.rotation;
  335. }
  336. protected void OnEnable()
  337. {
  338. if (openvrsystem == null)
  339. {
  340. enabled = false;
  341. return;
  342. }
  343. }
  344. protected void OnDisable()
  345. {
  346. isValid = false;
  347. }
  348. protected virtual void UpdateControllerState()
  349. {
  350. lastcontrollerstate = controllerstate;
  351. //Update position.
  352. if (index > EIndex.Hmd)
  353. {
  354. if (OpenVR.Compositor != null){
  355. ETrackingUniverseOrigin tracktype = OpenVR.Compositor.GetTrackingSpace();
  356. TrackedDevicePose_t[] absoluteposes = new TrackedDevicePose_t[16];
  357. openvrsystem.GetDeviceToAbsoluteTrackingPose(tracktype, 0, absoluteposes);
  358. TrackedDevicePose_t newposes = absoluteposes[(int)index];
  359. OnNewPoses(newposes);
  360. }
  361. }
  362. //We need to check for this always in case the user uses the deprecated GetVRButton methods.
  363. if (useLegacySteamVRInput)
  364. {
  365. openvrsystem.GetControllerState((uint)index, ref controllerstate, controllerstatesize);
  366. }
  367. }
  368. /// <summary>
  369. /// Returns if the VR controller button with the given ID was pressed for the first time this frame.
  370. /// </summary>
  371. /// <param name="buttonid">EVR ID of the button as listed in OpenVR.</param>
  372. [System.ObsoleteAttribute("ZEDControllerTracker's GetVRButton methods are deprecated.\r\n " +
  373. "Use ZEDControllerTracker_DemoInputs.GetVRButtonDown_Legacy instead., false")]
  374. public bool GetVRButtonDown(EVRButtonId buttonid)
  375. {
  376. if (openvrsystem == null) return false; //If VR isn't running, we can't check.
  377. bool washeldlastupdate = (lastcontrollerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L;
  378. if (washeldlastupdate == true) return false; //If the key was held last check, it can't be pressed for the first time now.
  379. bool isheld = (controllerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L;
  380. return isheld; //If we got here, we know it was not down last frame.
  381. }
  382. /// <summary>
  383. /// Returns if the VR controller button with the given ID is currently held.
  384. /// </summary>
  385. /// <param name="buttonid">EVR ID of the button as listed in OpenVR.</param>
  386. [System.ObsoleteAttribute("ZEDControllerTracker's GetVRButton methods are deprecated.\r\n " +
  387. "Use ZEDControllerTracker_DemoInputs.GetVRButtonHeld_Legacy instead.", false)]
  388. public bool GetVRButtonHeld(EVRButtonId buttonid)
  389. {
  390. if (openvrsystem == null) return false; //If VR isn't running, we can't check.
  391. bool isheld = (controllerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L;
  392. return isheld;
  393. }
  394. /// <summary>
  395. /// Returns if the VR controller button with the given ID was held last frame, but released this frame.
  396. /// </summary>
  397. /// <param name="buttonid">EVR ID of the button as listed in OpenVR.</param>
  398. [System.ObsoleteAttribute("ZEDControllerTracker's GetVRButton methods are deprecated.\r\n " +
  399. "Use ZEDControllerTracker_DemoInputs.GetVRButtonReleased_Legacy instead.", false)]
  400. public bool GetVRButtonReleased(EVRButtonId buttonid)
  401. {
  402. if (openvrsystem == null) return false; //If VR isn't running, we can't check.
  403. bool washeldlastupdate = (lastcontrollerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L;
  404. if (washeldlastupdate == false) return false; //If the key was held last check, it can't be released now.
  405. bool isheld = (controllerstate.ulButtonPressed & (1UL << (int)buttonid)) > 0L;
  406. return !isheld; //If we got here, we know it was not up last frame.
  407. }
  408. /// <summary>
  409. /// Returns the value of an axis with the provided ID.
  410. /// Note that for single-value axes, the relevant value will be the X in the returned Vector2 (the Y is unused).
  411. /// </summary>
  412. /// <param name="buttonid"></param>
  413. [System.ObsoleteAttribute("ZEDControllerTracker.GetAxis is deprecated.\r\n " +
  414. "Use ZEDControllerTracker_DemoInputs.GetVRAxis_Legacy instead.", false)]
  415. public Vector2 GetAxis(EVRButtonId buttonid)
  416. {
  417. //Convert the EVRButtonID enum to the axis number and check if it's not an axis.
  418. uint axis = (uint)buttonid - (uint)EVRButtonId.k_EButton_Axis0;
  419. if (axis < 0 || axis > 4)
  420. {
  421. Debug.LogError("Called GetAxis with " + buttonid + ", which is not an axis.");
  422. return Vector2.zero;
  423. }
  424. switch (axis)
  425. {
  426. case 0: return new Vector2(controllerstate.rAxis0.x, controllerstate.rAxis0.y);
  427. case 1: return new Vector2(controllerstate.rAxis1.x, controllerstate.rAxis1.y);
  428. case 2: return new Vector2(controllerstate.rAxis2.x, controllerstate.rAxis2.y);
  429. case 3: return new Vector2(controllerstate.rAxis3.x, controllerstate.rAxis3.y);
  430. case 4: return new Vector2(controllerstate.rAxis4.x, controllerstate.rAxis4.y);
  431. default: return Vector2.zero;
  432. }
  433. }
  434. #endif
  435. /// <summary>
  436. /// Compute the delayed position and rotation from the history stored in the poseData dictionary.
  437. /// </summary>
  438. /// <param name="keyindex"></param>
  439. /// <param name="timeDelay"></param>
  440. /// <returns></returns>
  441. private sl.Pose GetValuePosition(int keyindex, float timeDelay)
  442. {
  443. sl.Pose p = new sl.Pose();
  444. if (poseData.ContainsKey(keyindex))
  445. {
  446. //Get the saved position & rotation.
  447. p.translation = poseData[keyindex][poseData[keyindex].Count - 1].position;
  448. p.rotation = poseData[keyindex][poseData[keyindex].Count - 1].rotation;
  449. float idealTS = (Time.time - timeDelay);
  450. for (int i = 0; i < poseData[keyindex].Count; ++i)
  451. {
  452. if (poseData[keyindex][i].timestamp > idealTS)
  453. {
  454. int currentIndex = i;
  455. if (currentIndex > 0)
  456. {
  457. //Calculate the time between the pose and the delayed pose.
  458. float timeBetween = poseData[keyindex][currentIndex].timestamp - poseData[keyindex][currentIndex - 1].timestamp;
  459. float alpha = ((Time.time - poseData[keyindex][currentIndex - 1].timestamp) - timeDelay) / timeBetween;
  460. //Lerp to the next position based on the time determined above.
  461. Vector3 pos = Vector3.Lerp(poseData[keyindex][currentIndex - 1].position, poseData[keyindex][currentIndex].position, alpha);
  462. Quaternion rot = Quaternion.Lerp(poseData[keyindex][currentIndex - 1].rotation, poseData[keyindex][currentIndex].rotation, alpha);
  463. //Apply new values.
  464. p = new sl.Pose();
  465. p.translation = pos;
  466. p.rotation = rot;
  467. //Add drift correction, but only if the user hasn't disabled it, it's on an actual controller, and the zedRigRoot position won't be affected.
  468. if (correctControllerDrift == true &&
  469. (deviceToTrack == Devices.LeftController || deviceToTrack == Devices.RightController || deviceToTrack == Devices.ViveTracker) &&
  470. (zedManager != null && zedManager.IsStereoRig == true && !zedManager.transform.IsChildOf(transform)))
  471. {
  472. //Compensate for positional drift by measuring the distance between HMD and ZED rig root (the head's center).
  473. #if UNITY_2019_3_OR_NEWER
  474. InputDevice head = InputDevices.GetDeviceAtXRNode(XRNode.Head);
  475. head.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 headPosition);
  476. Vector3 zedhmdposoffset = zedRigRoot.position - headPosition;
  477. #else
  478. Vector3 zedhmdposoffset = zedRigRoot.position - InputTracking.GetLocalPosition(XRNode.Head);
  479. #endif
  480. p.translation += zedhmdposoffset;
  481. }
  482. //Removes used elements from the dictionary.
  483. poseData[keyindex].RemoveRange(0, currentIndex - 1);
  484. }
  485. return p;
  486. }
  487. }
  488. }
  489. return p;
  490. }
  491. /// <summary>
  492. /// Set the current tracking to a container (TimedPoseData) to be stored in poseData and retrieved/applied after the latency period.
  493. /// </summary>
  494. /// <param name="index">Key value in the dictionary.</param>
  495. /// <param name="position">Tracked object's position from the VR SDK.</param>
  496. /// <param name="rot">Tracked object's rotation from the VR SDK.</param>
  497. private void RegisterPosition(int keyindex, Vector3 position, Quaternion rot)
  498. {
  499. TimedPoseData currentPoseData = new TimedPoseData();
  500. currentPoseData.timestamp = Time.time;
  501. currentPoseData.rotation = rot;
  502. currentPoseData.position = position;
  503. poseData[keyindex].Add(currentPoseData);
  504. }
  505. /// <summary>
  506. /// Structure used to hold the pose of a controller at a given timestamp.
  507. /// This is stored in poseData with RegisterPosition() each time the VR SDK makes poses available.
  508. /// It's retrieved with GetValuePosition() in Update() each frame.
  509. /// </summary>
  510. public struct TimedPoseData
  511. {
  512. /// <summary>
  513. /// Value from Time.time when the pose was collected.
  514. /// </summary>
  515. public float timestamp;
  516. /// <summary>
  517. /// Rotation of the tracked object as provided by the VR SDK.
  518. /// </summary>
  519. public Quaternion rotation;
  520. /// <summary>
  521. /// Position of the tracked object as provided by the VR SDK.
  522. /// </summary>
  523. public Vector3 position;
  524. }
  525. }
  526. #if UNITY_EDITOR
  527. /// <summary>
  528. /// Custom editor for ZEDControllerTracker.
  529. /// If no VR Unity plugin (Oculus Integration or SteamVR plugin) has been loaded by the ZED plugin but one is found,
  530. /// presents a button to create project defines that tell ZED scripts that this plugin is loaded.
  531. /// These defines (ZED_STEAM_VR and ZED_OCULUS) are used to allow compiling parts of ZED scripts that depend on scripts in these VR plugins.
  532. /// Note that this detection will also be attempted any time an asset has been imported. See nested class AssetPostProcessZEDVR.
  533. /// </summary>
  534. [CustomEditor(typeof(ZEDControllerTracker)), CanEditMultipleObjects]
  535. public class ZEDVRDependencies : Editor
  536. {
  537. [SerializeField]
  538. static string defineName;
  539. static string packageName;
  540. public override void OnInspectorGUI() //Called when the Inspector is visible.
  541. {
  542. //if (CheckPackageExists("OpenVR"))
  543. if (CheckPackageExists("SteamVR_Camera.cs"))
  544. {
  545. defineName = "ZED_STEAM_VR";
  546. packageName = "SteamVR";
  547. }
  548. //else if (CheckPackageExists("Oculus") || CheckPackageExists("OVR"))
  549. else if (CheckPackageExists("OVRManager"))
  550. {
  551. defineName = "ZED_OCULUS";
  552. packageName = "Oculus";
  553. }
  554. if (EditorPrefs.GetBool(packageName)) //Has it been set?
  555. {
  556. DrawDefaultInspector();
  557. }
  558. else //No package loaded, but one has been detected. Present a button to load it.
  559. {
  560. GUILayout.Space(20);
  561. if (GUILayout.Button("Load " + packageName + " data"))
  562. {
  563. if (CheckPackageExists(packageName))
  564. {
  565. ActivateDefine();
  566. }
  567. }
  568. if (packageName == "SteamVR")
  569. EditorGUILayout.HelpBox(ZEDLogMessage.Error2Str(ZEDLogMessage.ERROR.STEAMVR_NOT_INSTALLED), MessageType.Warning);
  570. else if (packageName == "Oculus")
  571. EditorGUILayout.HelpBox(ZEDLogMessage.Error2Str(ZEDLogMessage.ERROR.OVR_NOT_INSTALLED), MessageType.Warning);
  572. }
  573. }
  574. /// <summary>
  575. /// Finds if a folder in the project exists with the specified name.
  576. /// Used to check if a plugin has been imported, as the relevant plugins are placed
  577. /// in a folder named after the package. Example: "Assets/Oculus".
  578. /// </summary>
  579. /// <param name="name">Package name.</param>
  580. /// <returns></returns>
  581. public static bool CheckPackageExists(string name)
  582. {
  583. string[] packages = AssetDatabase.FindAssets(name);
  584. return packages.Length != 0;
  585. }
  586. /// <summary>
  587. /// Activates a define tag in the project. Used to enable compiling sections of scripts with that tag enabled.
  588. /// For instance, parts of this script under a #if ZED_STEAM_VR statement will be ignored by the compiler unless ZED_STEAM_VR is enabled.
  589. /// </summary>
  590. public static void ActivateDefine()
  591. {
  592. EditorPrefs.SetBool(packageName, true);
  593. string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
  594. if (defines.Length != 0)
  595. {
  596. if (!defines.Contains(defineName))
  597. {
  598. defines += ";" + defineName;
  599. }
  600. }
  601. else
  602. {
  603. if (!defines.Contains(defineName))
  604. {
  605. defines += defineName;
  606. }
  607. }
  608. PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, defines);
  609. }
  610. /// <summary>
  611. /// Removes a define tag from the project.
  612. /// Called whenever a package is checked for but not found.
  613. /// Removing the define tags will prevent compilation of code marked with that tag, like #if ZED_OCULUS.
  614. /// </summary>
  615. public static void DeactivateDefine(string packagename)
  616. {
  617. EditorPrefs.SetBool(packagename, false);
  618. string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
  619. if (defines.Length != 0)
  620. {
  621. if (defineName != null && defines.Contains(defineName))
  622. {
  623. defines = defines.Remove(defines.IndexOf(defineName), defineName.Length);
  624. if (defines.LastIndexOf(";") == defines.Length - 1 && defines.Length != 0)
  625. {
  626. defines.Remove(defines.LastIndexOf(";"), 1);
  627. }
  628. }
  629. }
  630. PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, defines);
  631. }
  632. }
  633. #endif