AutoCalibrationManager.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /// <summary>
  5. /// Handles automatic calibration mode in the ZED MR calibration scene.
  6. /// When set up, it places clickable balls in front of the ZED in positions listed in spherePositions.
  7. /// When you click on the balls and re-align the controllers as prompted, it'll call a sl.ZEDCamera function
  8. /// to calculate a new camera position.
  9. /// </summary>
  10. public class AutoCalibrationManager : MonoBehaviour
  11. {
  12. /// <summary>
  13. /// Prefab of the balls you click on to add a reference point. Should be Prefabs/AutoCalibration Ball.
  14. /// </summary>
  15. [Tooltip("Prefab of the balls you click on to add a reference point. Should be Prefabs/AutoCalibration Ball.")]
  16. public GameObject autoCalibBallPrefab;
  17. /// <summary>
  18. /// CameraAnchor in the scene that's holding the ZED camera.
  19. /// </summary>
  20. [Tooltip("CameraAnchor in the scene that's holding the ZED camera. ")]
  21. public CameraAnchor camAnchor;
  22. private List<AutoCalibBall> balls = new List<AutoCalibBall>();
  23. /// <summary>
  24. /// The positions used to calibrate the ZED. Should all be visible from the ZED,
  25. /// and vary on all three axes to make it easy to calibrate.
  26. /// </summary>
  27. [Tooltip("The positions used to calibrate the ZED. Should all be visible from the ZED, " +
  28. "and vary on all three axes to make it easy to calibrate. ")]
  29. public Vector3[] spherePositions = { new Vector3(0.0f, -0.4f, -0.5f),
  30. new Vector3(0.4f, -0.1f, -0.5f),
  31. new Vector3(-0.4f, -0.4f, -0.3f),
  32. new Vector3(-0.4f, -0.1f, -0.1f),
  33. new Vector3(0.4f, -0.2f, -0.1f)};
  34. /// <summary>
  35. /// The scene's ZEDManager. Will be assigned to ZEDManager of camera index 1 if not set.
  36. /// </summary>
  37. [Tooltip("The scene's ZEDManager. Will be assigned to ZEDManager of camera index 1 if not set. ")]
  38. [Space(5)]
  39. public ZEDManager zedManager;
  40. private Vector3[] virtualPositions;
  41. private Vector3[] realPositions;
  42. private const float angleMAX = 35;
  43. private bool isSetup = false;
  44. private void Awake()
  45. {
  46. if (!zedManager)
  47. {
  48. zedManager = ZEDManager.GetInstance(sl.ZED_CAMERA_ID.CAMERA_ID_01);
  49. }
  50. if(!camAnchor)
  51. {
  52. camAnchor = FindObjectOfType<CameraAnchor>();
  53. }
  54. SetUpBalls(false);
  55. }
  56. /// <summary>
  57. /// Creates the ball objects and other minor setup. "ForceReset" will clear existing balls if
  58. /// they were aleady set up, otherwise nothing will happen.
  59. /// </summary>
  60. public void SetUpBalls(bool forcereset = true)
  61. {
  62. if(isSetup)
  63. {
  64. if(forcereset) //If we're resetting, clear all existing balls and positions.
  65. {
  66. CleanUpBalls();
  67. }
  68. else //Already set up but we don't want to reset. This is likely unintentional.
  69. {
  70. Debug.LogError("Called AutomatedCalibration.Setup when it was already set up, but without requesting a reset.");
  71. return;
  72. }
  73. }
  74. Transform zedtrans = zedManager.transform; //Shorthand
  75. //Create the balls.
  76. for (int i = 0; i < spherePositions.Length; i++)
  77. {
  78. virtualPositions = new Vector3[spherePositions.Length];
  79. realPositions = new Vector3[spherePositions.Length];
  80. GameObject newballgo = Instantiate(autoCalibBallPrefab, zedtrans, false);
  81. newballgo.transform.localPosition = spherePositions[i];
  82. AutoCalibBall newball = newballgo.GetComponentInChildren<AutoCalibBall>();
  83. if (!newball)
  84. {
  85. throw new System.Exception("No AutoCalibBall script on autoCalibBallPrefab.");
  86. }
  87. newball.Setup(this, i);
  88. balls.Add(newball);
  89. }
  90. isSetup = true;
  91. //Update all message displays with instructions.
  92. MessageDisplay.DisplayMessageAll("AUTOMATIC MODE\r\nPut your controller inside a ball and click.\r\n" +
  93. "If the virtual ZED isn't facing you, use Manual Mode to get the image roughly aligned.");
  94. }
  95. /// <summary>
  96. /// Destroys all existing balls. Use when switching out of Automatic mode.
  97. /// </summary>
  98. public void CleanUpBalls()
  99. {
  100. for (int i = 0; i < balls.Count; i++)
  101. {
  102. Destroy(balls[i].gameObject);
  103. }
  104. balls.Clear();
  105. }
  106. /// <summary>
  107. /// Registers a new combination of real and virtual positions to be used to calculate the camera position.
  108. /// This is called after a single ball has been used and set.
  109. /// </summary>
  110. /// <param name="index">Index of the ball within this class, used in both virtualPositions and realPositions.</param>
  111. /// <param name="virtualpos">Position of the controller when the ball was first activated.</param>
  112. /// <param name="realpos">Position of the controller after the user has aligned it with the real world.</param>
  113. public void AddNewPositions(int index, Vector3 virtualpos, Vector3 realpos)
  114. {
  115. if (virtualPositions == null || virtualPositions.Length == 0) virtualPositions = new Vector3[spherePositions.Length];
  116. if (realPositions == null || realPositions.Length == 0) realPositions = new Vector3[spherePositions.Length];
  117. if (index >= spherePositions.Length)
  118. {
  119. throw new System.Exception("Invalid index passed to AutomatedCalibration.AddNewPositions. Passed " +
  120. index + ", max is " + (spherePositions.Length - 1).ToString() + ".");
  121. }
  122. virtualPositions[index] = virtualpos;
  123. realPositions[index] = realpos;
  124. UpdateZEDPosition();
  125. }
  126. /// <summary>
  127. /// Prepare the current virtual and real positions (provided by AddNewPositions after using the balls) into
  128. /// data to be used by ZEDCamera.ComputeOffset, and call it. This will update the camera's position based on
  129. /// the inputs the user provided.
  130. /// </summary>
  131. private void UpdateZEDPosition()
  132. {
  133. List<Vector3> validvirtposes = new List<Vector3>();
  134. List<Vector3> validrealposes = new List<Vector3>();
  135. for (int i = 0; i < virtualPositions.Length; i++)
  136. {
  137. if (virtualPositions[i] != null && realPositions[i] != null) //Don't add either unless both are valid.
  138. {
  139. validvirtposes.Add(virtualPositions[i]);
  140. validrealposes.Add(realPositions[i]);
  141. }
  142. }
  143. if (validvirtposes.Count == 0) return;
  144. int posecount = validvirtposes.Count; //Shorthand.
  145. //Make array of floats used to call ZEDCamera.ComputeOffset. We use arrays because it's turned into a matrix internally.
  146. float[] inputA = new float[posecount * 4];
  147. float[] inputB = new float[posecount * 4];
  148. for (int i = 0; i < posecount; i++)
  149. {
  150. inputA[i * 4 + 0] = validvirtposes[i].x;
  151. inputA[i * 4 + 1] = validvirtposes[i].y;
  152. inputA[i * 4 + 2] = validvirtposes[i].z;
  153. inputA[i * 4 + 3] = 1; //Will be W in the matrix.
  154. inputB[i * 4 + 0] = validrealposes[i].x;
  155. inputB[i * 4 + 1] = validrealposes[i].y;
  156. inputB[i * 4 + 2] = validrealposes[i].z;
  157. inputB[i * 4 + 3] = 1; //Will be W in the matrix.
  158. }
  159. Vector3 newtranslation = new Vector3();
  160. Quaternion newrotation = Quaternion.identity;
  161. sl.ZEDCamera.ComputeOffset(inputA, inputB, posecount, ref newrotation, ref newtranslation);
  162. if (IsInsideRangeAngle(newrotation))
  163. {
  164. /* //This was in original calibration app but I suspect it caused issues.
  165. for (int j = 0; j < (calibrationStage + 1) * 0.5; j++)
  166. {
  167. realPositions[j] = Quaternion.Inverse(rotation) * (realPositions[j] - translation);
  168. }*/ //Okay nevermind let's try it.
  169. for(int i = 0; i < realPositions.Length; i++)
  170. {
  171. if(realPositions[i] != null && realPositions[i] != Vector3.zero) //May not need that second half. We'll see.
  172. {
  173. realPositions[i] = Quaternion.Inverse(newrotation) * (realPositions[i] - newtranslation);
  174. }
  175. }
  176. //zedManager.transform.position -= Quaternion.Inverse(newrotation) * newtranslation;
  177. //zedManager.transform.rotation = Quaternion.Inverse(newrotation) * zedManager.transform.rotation;
  178. camAnchor.MoveZEDPose(-(Quaternion.Inverse(newrotation) * newtranslation), Quaternion.Inverse(newrotation), false);
  179. }
  180. else
  181. {
  182. print("Calibration is not yet precise. Continue adding points and consider redoing existing points.");
  183. }
  184. }
  185. private void OnDisable()
  186. {
  187. CleanUpBalls();
  188. }
  189. /// <summary>
  190. /// Checks whether or not the current alignment is reasonably accurate.
  191. /// </summary>
  192. /// <param name="rotation"></param>
  193. /// <returns></returns>
  194. private bool IsInsideRangeAngle(Quaternion rotation)
  195. {
  196. Vector3 angle = rotation.eulerAngles;
  197. return (angle.x < angleMAX || angle.x > 360 - angleMAX)
  198. && (angle.y < angleMAX || angle.y > 360 - angleMAX)
  199. && (angle.z < angleMAX || angle.z > 360 - angleMAX);
  200. }
  201. }