SW 중심대학 OSS GIT 서버 박건태, 이승준, 고기완, 이준호 새로운 배포
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.

289 lines
14 KiB

4 years ago
  1. using System;
  2. #if USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER
  3. using UnityEngine.SpatialTracking;
  4. #endif
  5. namespace UnityEngine.XR.ARFoundation
  6. {
  7. /// <summary>
  8. /// An <c>ARSessionOrigin</c> is the parent for an AR setup. It contains a <c>Camera</c> and
  9. /// any <c>GameObject</c>s created from detected features, such as planes or point clouds.
  10. /// </summary>
  11. /// <remarks>
  12. /// Session space vs Unity space
  13. ///
  14. /// Since an AR device will be used to drive the <c>Camera</c>'s position and rotation,
  15. /// you cannot directly place the <c>Camera</c> at an arbitrary position in the Unity scene.
  16. /// Instead, you should position the <c>ARSessionOrigin</c>. This will make the <c>Camera</c>
  17. /// (and any detected features) relative to that as a result.
  18. ///
  19. /// It is important to keep the <c>Camera</c> and detected features in the same space relative to
  20. /// each other (otherwise, detected features like planes won't appear in the correct place relative
  21. /// to the <c>Camera</c>). We call the space relative to the AR device's starting position
  22. /// "session space" or "device space". For example, when the AR session begins, the device may
  23. /// report its position as (0, 0, 0). Detected features, such as planes, will be reported relative
  24. /// to this starting position. The purpose of the <c>ARSessionOrigin</c> is to convert the session space
  25. /// to Unity world space.
  26. ///
  27. /// To facilitate this, the <c>ARSessionOrigin</c> creates a new <c>GameObject</c> called "Trackables"
  28. /// as a sibling of its <c>Camera</c>. This should be the parent <c>GameObject</c> for all
  29. /// detected features.
  30. ///
  31. /// At runtime, a typical scene graph might look like this:
  32. /// - AR Session Origin
  33. /// - Camera
  34. /// - Trackables
  35. /// - Detected plane 1
  36. /// - Detected plane 2
  37. /// - Point cloud
  38. /// - etc...
  39. ///
  40. /// You can access the "trackables" <c>GameObject</c> with <see cref="trackablesParent"/>.
  41. ///
  42. /// Note that the <c>localPosition</c> and <c>localRotation</c> of detected trackables
  43. /// remain in real-world meters relative to the AR device's starting position and rotation.
  44. ///
  45. /// Scale
  46. ///
  47. /// If you want to scale the content rendered by the <c>ARSessionOrigin</c> you should apply
  48. /// the scale to the <c>ARSessionOrigin</c>'s transform. This is preferrable to scaling
  49. /// the content directly as that can have undesirable side-effects. Physics and NavMeshes,
  50. /// for example, do not perform well when scaled very small.
  51. /// </remarks>
  52. [DisallowMultipleComponent]
  53. [HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.0/api/UnityEngine.XR.ARFoundation.ARSessionOrigin.html")]
  54. public class ARSessionOrigin : MonoBehaviour
  55. {
  56. [SerializeField]
  57. [Tooltip("The Camera to associate with the AR device.")]
  58. Camera m_Camera;
  59. /// <summary>
  60. /// The <c>Camera</c> to associate with the AR device. It must be a child of this <c>ARSessionOrigin</c>.
  61. /// </summary>
  62. /// <remarks>
  63. /// The <c>Camera</c> should update its position and rotation according to the AR device.
  64. /// This is typically accomplished by adding a <c>TrackedPoseDriver</c> component to the
  65. /// <c>Camera</c>.
  66. /// </remarks>
  67. #if UNITY_EDITOR
  68. public new Camera camera
  69. #else
  70. public Camera camera
  71. #endif
  72. {
  73. get { return m_Camera; }
  74. set { m_Camera = value; }
  75. }
  76. /// <summary>
  77. /// The parent <c>Transform</c> for all "trackables", e.g., planes and feature points.
  78. /// </summary>
  79. public Transform trackablesParent { get; private set; }
  80. GameObject m_ContentOffsetGameObject;
  81. Transform contentOffsetTransform
  82. {
  83. get
  84. {
  85. if (m_ContentOffsetGameObject == null)
  86. {
  87. // Insert a GameObject directly below the rig
  88. m_ContentOffsetGameObject = new GameObject("Content Placement Offset");
  89. m_ContentOffsetGameObject.transform.SetParent(transform, false);
  90. // Re-parent any children of the ARSessionOrigin
  91. for (var i = 0; i < transform.childCount; ++i)
  92. {
  93. var child = transform.GetChild(i);
  94. if (child != m_ContentOffsetGameObject.transform)
  95. {
  96. child.SetParent(m_ContentOffsetGameObject.transform, true);
  97. --i; // Decrement because childCount is also one less.
  98. }
  99. }
  100. }
  101. return m_ContentOffsetGameObject.transform;
  102. }
  103. }
  104. /// <summary>
  105. /// Makes <paramref name="content"/> appear to be placed at <paramref name="position"/> with orientation <paramref name="rotation"/>.
  106. /// </summary>
  107. /// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
  108. /// <param name="position">The position you wish the content to appear at. This could be
  109. /// a position on a detected plane, for example.</param>
  110. /// <param name="rotation">The rotation the content should appear to be in, relative
  111. /// to the <c>Camera</c>.</param>
  112. /// <remarks>
  113. /// This method does not actually change the <c>Transform</c> of content; instead,
  114. /// it updates the <c>ARSessionOrigin</c>'s <c>Transform</c> such that it appears the content
  115. /// is now at the given position and rotation. This is useful for placing AR
  116. /// content onto surfaces when the content itself cannot be moved at runtime.
  117. /// For example, if your content includes terrain or a nav mesh, then it cannot
  118. /// be moved or rotated dynamically.
  119. /// </remarks>
  120. public void MakeContentAppearAt(Transform content, Vector3 position, Quaternion rotation)
  121. {
  122. MakeContentAppearAt(content, position);
  123. MakeContentAppearAt(content, rotation);
  124. }
  125. /// <summary>
  126. /// Makes <paramref name="content"/> appear to be placed at <paramref name="position"/>.
  127. /// </summary>
  128. /// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
  129. /// <param name="position">The position you wish the content to appear at. This could be
  130. /// a position on a detected plane, for example.</param>
  131. /// <remarks>
  132. /// This method does not actually change the <c>Transform</c> of content; instead,
  133. /// it updates the <c>ARSessionOrigin</c>'s <c>Transform</c> such that it appears the content
  134. /// is now at the given position.
  135. /// </remarks>
  136. public void MakeContentAppearAt(Transform content, Vector3 position)
  137. {
  138. if (content == null)
  139. throw new ArgumentNullException("content");
  140. // Adjust the "point of interest" transform to account
  141. // for the actual position we want the content to appear at.
  142. contentOffsetTransform.position += transform.position - position;
  143. // The ARSessionOrigin's position needs to match the content's pivot. This is so
  144. // the entire ARSessionOrigin rotates around the content (so the impression is that
  145. // the content is rotating, not the rig).
  146. transform.position = content.position;
  147. }
  148. /// <summary>
  149. /// Makes <paramref name="content"/> appear to have orientation <paramref name="rotation"/> relative to the <c>Camera</c>.
  150. /// </summary>
  151. /// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
  152. /// <param name="rotation">The rotation the content should appear to be in, relative
  153. /// to the <c>Camera</c>.</param>
  154. /// <remarks>
  155. /// This method does not actually change the <c>Transform</c> of content; instead,
  156. /// it updates the <c>ARSessionOrigin</c>'s <c>Transform</c> such that it appears the content
  157. /// is in the requested orientation.
  158. /// </remarks>
  159. public void MakeContentAppearAt(Transform content, Quaternion rotation)
  160. {
  161. if (content == null)
  162. throw new ArgumentNullException("content");
  163. // Since we aren't rotating the content, we need to perform the inverse
  164. // operation on the ARSessionOrigin. For example, if we want the
  165. // content to appear to be rotated 90 degrees on the Y axis, we should
  166. // rotate our rig -90 degrees on the Y axis.
  167. transform.rotation = Quaternion.Inverse(rotation) * content.rotation;
  168. }
  169. void Awake()
  170. {
  171. // This will be the parent GameObject for any trackables (such as planes) for which
  172. // we want a corresponding GameObject.
  173. trackablesParent = (new GameObject("Trackables")).transform;
  174. trackablesParent.SetParent(transform, false);
  175. trackablesParent.localPosition = Vector3.zero;
  176. trackablesParent.localRotation = Quaternion.identity;
  177. trackablesParent.localScale = Vector3.one;
  178. if (camera)
  179. {
  180. var arPoseDriver = camera.GetComponent<ARPoseDriver>();
  181. #if USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER
  182. var trackedPoseDriver = camera.GetComponent<TrackedPoseDriver>();
  183. // Warn if not using a ARPoseDriver or a TrackedPoseDriver
  184. if (arPoseDriver == null && trackedPoseDriver == null)
  185. {
  186. Debug.LogWarning(
  187. $"Camera \"{camera.name}\" does not use a AR Pose Driver or a Tracked Pose Driver, " +
  188. "so its transform will not be updated by an XR device. In order for this to be " +
  189. "updated, please add either an AR Pose Driver or a Tracked Pose Driver.");
  190. }
  191. // If we are using an TrackedPoseDriver, and the user hasn't chosen "make relative"
  192. // then warn if the camera has a non-identity transform (since it will be overwritten).
  193. else if ((trackedPoseDriver != null && !trackedPoseDriver.UseRelativeTransform))
  194. {
  195. var cameraTransform = camera.transform;
  196. if ((cameraTransform.localPosition != Vector3.zero) || (cameraTransform.localRotation != Quaternion.identity))
  197. {
  198. Debug.LogWarning(
  199. $"Camera \"{camera.name}\" has a non-identity transform " +
  200. $"(position = {cameraTransform.localPosition}, rotation = {cameraTransform.localRotation}). " +
  201. "The camera's local position and rotation will be overwritten by the XR device, " +
  202. "so this starting transform will have no effect. Tick the \"Make Relative\" " +
  203. "checkbox on the camera's Tracked Pose Driver to apply this starting transform.");
  204. }
  205. }
  206. // If using ARPoseDriver then it will get overwritten no matter what
  207. else
  208. {
  209. var cameraTransform = camera.transform;
  210. if ((cameraTransform.localPosition != Vector3.zero) || (cameraTransform.localRotation != Quaternion.identity))
  211. {
  212. Debug.LogWarning(
  213. $"Camera \"{camera.name}\" has a non-identity transform " +
  214. $"(position = {cameraTransform.localPosition}, rotation = {cameraTransform.localRotation}). " +
  215. "The camera's local position and rotation will be overwritten by the XR device.");
  216. }
  217. }
  218. #else // !(USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER)
  219. if (arPoseDriver == null)
  220. {
  221. Debug.LogWarning(
  222. $"Camera \"{camera.name}\" does not use a AR Pose Driver, so its transform will not be updated by " +
  223. "an XR device. In order for this to be updated, please add an AR Pose Driver component.");
  224. }
  225. #endif // USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER
  226. }
  227. }
  228. Pose GetCameraOriginPose()
  229. {
  230. var localOriginPose = Pose.identity;
  231. var parent = camera.transform.parent;
  232. #if USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER
  233. var trackedPoseDriver = camera.GetComponent<TrackedPoseDriver>();
  234. if (trackedPoseDriver)
  235. {
  236. localOriginPose = trackedPoseDriver.originPose;
  237. }
  238. #endif // USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER
  239. return parent?.TransformPose(localOriginPose) ?? localOriginPose;
  240. }
  241. void Update()
  242. {
  243. if (camera)
  244. {
  245. var pose = GetCameraOriginPose();
  246. trackablesParent.position = pose.position;
  247. trackablesParent.rotation = pose.rotation;
  248. }
  249. }
  250. #if UNITY_EDITOR && (USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER)
  251. void OnValidate()
  252. {
  253. if (camera)
  254. {
  255. if ((camera.GetComponent<TrackedPoseDriver>()?.enabled ?? false) &&
  256. (camera.GetComponent<ARPoseDriver>()?.enabled ?? false))
  257. {
  258. Debug.LogWarning(
  259. $"Camera \"{camera.name}\" has an AR Pose Driver and a Tracked Pose Driver and both are enabled. " +
  260. "This configuration will cause the camera transform to be updated from both components in a non-deterministic " +
  261. "way. This is likely an unintended error.");
  262. }
  263. }
  264. }
  265. #endif // UNITY_EDITOR && (USE_LEGACY_INPUT_HELPERS || !UNITY_2019_1_OR_NEWER)
  266. }
  267. }