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.

336 lines
14 KiB

4 years ago
  1. using System.Collections.Generic;
  2. using Unity.Collections;
  3. using UnityEngine.XR.ARSubsystems;
  4. #if !UNITY_2019_2_OR_NEWER
  5. using UnityEngine.Experimental;
  6. #endif
  7. namespace UnityEngine.XR.ARFoundation
  8. {
  9. /// <summary>
  10. /// A generic manager for components generated by features detected in the physical environment.
  11. /// </summary>
  12. /// <remarks>
  13. /// When the manager is informed that a trackable has been added, a new <c>GameObject</c>
  14. /// is created with an <c>ARTrackable</c> component on it. If
  15. /// <see cref="ARTrackableManager{TSubsystem, TSubsystemDescriptor, TSessionRelativeData, TTrackable}.prefab"/>
  16. /// is not null, then that prefab will be instantiated.
  17. /// </remarks>
  18. /// <typeparam name="TSubsystem">The <c>Subsystem</c> which provides this manager data.</typeparam>
  19. /// <typeparam name="TSubsystemDescriptor">The <c>SubsystemDescriptor</c> required to create the Subsystem.</typeparam>
  20. /// <typeparam name="TSessionRelativeData">A concrete struct used to hold data provided by the Subsystem.</typeparam>
  21. /// <typeparam name="TTrackable">The type of component this component will manage (i.e., create, update, and destroy).</typeparam>
  22. [RequireComponent(typeof(ARSessionOrigin))]
  23. [DisallowMultipleComponent]
  24. public abstract class ARTrackableManager<TSubsystem, TSubsystemDescriptor, TSessionRelativeData, TTrackable>
  25. : SubsystemLifecycleManager<TSubsystem, TSubsystemDescriptor>
  26. where TSubsystem : TrackingSubsystem<TSessionRelativeData, TSubsystemDescriptor>
  27. where TSubsystemDescriptor : SubsystemDescriptor<TSubsystem>
  28. where TSessionRelativeData : struct, ITrackable
  29. where TTrackable : ARTrackable<TSessionRelativeData, TTrackable>
  30. {
  31. /// <summary>
  32. /// A collection of all trackables managed by this component.
  33. /// </summary>
  34. public TrackableCollection<TTrackable> trackables
  35. {
  36. get
  37. {
  38. return new TrackableCollection<TTrackable>(m_Trackables);
  39. }
  40. }
  41. /// <summary>
  42. /// Iterates over every instantiated <see cref="ARTrackable"/> and
  43. /// activates or deactivates its <c>GameObject</c> based on the value of
  44. /// <paramref name="active"/>.
  45. /// This calls
  46. /// <a href="https://docs.unity3d.com/ScriptReference/GameObject.SetActive.html">GameObject.SetActive</a>
  47. /// on each trackable's <c>GameObject</c>.
  48. /// </summary>
  49. /// <param name="active">If <c>true</c> each trackable's <c>GameObject</c> is activated.
  50. /// Otherwise, it is deactivated.</param>
  51. public void SetTrackablesActive(bool active)
  52. {
  53. foreach (var trackable in trackables)
  54. {
  55. trackable.gameObject.SetActive(active);
  56. }
  57. }
  58. /// <summary>
  59. /// The <c>ARSessionOrigin</c> which will be used to instantiate detected trackables.
  60. /// </summary>
  61. protected ARSessionOrigin sessionOrigin { get; private set; }
  62. /// <summary>
  63. /// The name prefix that should be used when instantiating new <c>GameObject</c>s.
  64. /// </summary>
  65. protected abstract string gameObjectName { get; }
  66. /// <summary>
  67. /// The prefab that should be instantiated when adding a trackable.
  68. /// </summary>
  69. protected virtual GameObject GetPrefab()
  70. {
  71. return null;
  72. }
  73. /// <summary>
  74. /// A dictionary of all trackables, keyed by <c>TrackableId</c>.
  75. /// </summary>
  76. protected Dictionary<TrackableId, TTrackable> m_Trackables = new Dictionary<TrackableId, TTrackable>();
  77. /// <summary>
  78. /// A dictionary of trackables added via <see cref="CreateTrackableImmediate(TSessionRelativeData)"/> but not yet reported as added.
  79. /// </summary>
  80. protected Dictionary<TrackableId, TTrackable> m_PendingAdds = new Dictionary<TrackableId, TTrackable>();
  81. /// <summary>
  82. /// Invoked by Unity once when this component wakes up.
  83. /// </summary>
  84. protected virtual void Awake()
  85. {
  86. sessionOrigin = GetComponent<ARSessionOrigin>();
  87. }
  88. /// <summary>
  89. /// Update is called once per frame. This component's internal state
  90. /// is first updated, and then the <see cref="trackablesChanged"/> event is invoked.
  91. /// </summary>
  92. protected virtual void Update()
  93. {
  94. if (subsystem == null)
  95. return;
  96. using (var changes = subsystem.GetChanges(Allocator.Temp))
  97. {
  98. ClearAndSetCapacity(s_Added, changes.added.Length);
  99. foreach (var added in changes.added)
  100. s_Added.Add(CreateOrUpdateTrackable(added));
  101. ClearAndSetCapacity(s_Updated, changes.updated.Length);
  102. foreach (var updated in changes.updated)
  103. s_Updated.Add(CreateOrUpdateTrackable(updated));
  104. ClearAndSetCapacity(s_Removed, changes.removed.Length);
  105. foreach (var trackableId in changes.removed)
  106. {
  107. TTrackable trackable;
  108. if (m_Trackables.TryGetValue(trackableId, out trackable))
  109. {
  110. m_Trackables.Remove(trackableId);
  111. s_Removed.Add(trackable);
  112. }
  113. }
  114. }
  115. try
  116. {
  117. // User events
  118. if ((s_Added.Count) > 0 ||
  119. (s_Updated.Count) > 0 ||
  120. (s_Removed.Count) > 0)
  121. {
  122. OnTrackablesChanged(s_Added, s_Updated, s_Removed);
  123. }
  124. }
  125. finally
  126. {
  127. // Make sure destroy happens even if a user callback throws an exception
  128. foreach (var removed in s_Removed)
  129. DestroyTrackable(removed);
  130. }
  131. }
  132. /// <summary>
  133. /// Invoked when trackables have changed, i.e., added, updated, or removed.
  134. /// Use this to perform additional logic, or to invoke public events
  135. /// related to your trackables.
  136. /// </summary>
  137. /// <param name="added">A list of trackables added this frame.</param>
  138. /// <param name="updated">A list of trackables updated this frame.</param>
  139. /// <param name="removed">A list of trackables removed this frame.
  140. /// The trackable components are not destroyed until after this method returns.</param>
  141. protected virtual void OnTrackablesChanged(
  142. List<TTrackable> added,
  143. List<TTrackable> updated,
  144. List<TTrackable> removed)
  145. { }
  146. /// <summary>
  147. /// Invoked after creating the trackable. The trackable's <c>sessionRelativeData</c> property will already be set.
  148. /// </summary>
  149. /// <param name="trackable">The newly created trackable.</param>
  150. protected virtual void OnCreateTrackable(TTrackable trackable)
  151. { }
  152. /// <summary>
  153. /// Invoked just after session relative data has been set on a trackable.
  154. /// </summary>
  155. /// <param name="trackable">The trackable that has just been updated.</param>
  156. /// <param name="sessionRelativeData">The session relative data used to update the trackable.</param>
  157. protected virtual void OnAfterSetSessionRelativeData(
  158. TTrackable trackable,
  159. TSessionRelativeData sessionRelativeData)
  160. { }
  161. /// <summary>
  162. /// Creates a <see cref="TTrackable"/> immediately, leaving it in a "pending" state.
  163. /// </summary>
  164. /// <remarks>
  165. /// <para>
  166. /// Trackables are usually created, updated, or destroyed during <see cref="Update()"/>.
  167. /// This method creates a trackable immediately, and marks it as "pending"
  168. /// until it is reported as added by the subsystem. This is useful for subsystems that deal
  169. /// with trackables that can be both detected and manually created.
  170. /// </para></<para>
  171. /// This method does not invoke <see cref="OnTrackablesChanged(List{TTrackable}, List{TTrackable}, List{TTrackable})"/>,
  172. /// so no "added" notifications will occur until the next call to <see cref="Update"/>.
  173. /// However, this method does invoke <see cref="ARTrackable{TSessionRelativeData, TTrackable}.updated"/>
  174. /// on the new trackable.
  175. /// </para><para>
  176. /// The trackable will appear in the <see cref="trackables"/> collection immediately.
  177. /// </para>
  178. /// </remarks>
  179. /// <returns>A new <c>TTrackable</c></returns>
  180. protected TTrackable CreateTrackableImmediate(TSessionRelativeData sessionRelativeData)
  181. {
  182. var trackable = CreateOrUpdateTrackable(sessionRelativeData);
  183. trackable.pending = true;
  184. m_PendingAdds.Add(trackable.trackableId, trackable);
  185. return trackable;
  186. }
  187. /// <summary>
  188. /// If in a "pending" state, the trackable with <paramref name="trackableId"/>'s
  189. /// <see cref="ARTrackable{TSessionRelativeData, TTrackable}.removed"/>
  190. /// event is invoked, and the trackable's <c>GameObject</c> destroyed if
  191. /// <see cref="ARTrackable{TSessionRelativeData, TTrackable}.destroyOnRemoval"/> is true.
  192. /// Otherwise, this method has no effect.
  193. /// </summary>
  194. /// <remarks>
  195. /// <para>
  196. /// Trackables are usually removed only when the subsystem reports they
  197. /// have been removed during <see cref="Update"/>
  198. /// </para><para>
  199. /// This method will immediately remove a trackable only if it was created by
  200. /// <see cref="CreateTrackableImmediate(TSessionRelativeData)"/>
  201. /// and has not yet been reported as added by the
  202. /// <see cref="SubsystemLifecycleManager{TSubsystem, TSubsystemDescriptor}.subsystem"/>.
  203. /// </para><para>
  204. /// This can happen if the trackable is created and removed within the same frame, as the subsystem may never
  205. /// have a chance to report its existence. Derived classes should use this
  206. /// if they support the concept of manual addition and removal of trackables, as there may not
  207. /// be a removal event if the trackable is added and removed quickly.
  208. /// </para><para>
  209. /// If the trackable is not in a pending state, i.e., it has already been reported as "added",
  210. /// then this method does nothing.
  211. /// </para>
  212. /// <para>
  213. /// This method does not invoke <see cref="OnTrackablesChanged(List{TTrackable}, List{TTrackable}, List{TTrackable})"/>,
  214. /// so no "removed" notifications will occur until the next call to <see cref="Update"/> (and only if it was
  215. /// previously reported as "added").
  216. /// However, this method does invoke <see cref="ARTrackable{TSessionRelativeData, TTrackable}.removed"/>
  217. /// on the trackable.
  218. /// </para>
  219. /// </remarks>
  220. /// <returns><c>True</c> if the trackable is "pending" (i.e., not yet reported as "added").</returns>
  221. protected bool DestroyPendingTrackable(TrackableId trackableId)
  222. {
  223. TTrackable trackable;
  224. if (m_PendingAdds.TryGetValue(trackableId, out trackable))
  225. {
  226. m_PendingAdds.Remove(trackableId);
  227. m_Trackables.Remove(trackableId);
  228. DestroyTrackable(trackable);
  229. return true;
  230. }
  231. return false;
  232. }
  233. static void ClearAndSetCapacity(List<TTrackable> list, int capacity)
  234. {
  235. list.Clear();
  236. if (list.Capacity < capacity)
  237. list.Capacity = capacity;
  238. }
  239. string GetTrackableName(TrackableId trackableId)
  240. {
  241. return gameObjectName + " " + trackableId.ToString();
  242. }
  243. GameObject CreateGameObject()
  244. {
  245. var prefab = GetPrefab();
  246. if (prefab == null)
  247. {
  248. var go = new GameObject();
  249. go.transform.parent = sessionOrigin.trackablesParent;
  250. return go;
  251. }
  252. return Instantiate(prefab, sessionOrigin.trackablesParent);
  253. }
  254. GameObject CreateGameObject(string name)
  255. {
  256. var go = CreateGameObject();
  257. go.name = name;
  258. return go;
  259. }
  260. GameObject CreateGameObject(TrackableId trackableId)
  261. {
  262. return CreateGameObject(GetTrackableName(trackableId));
  263. }
  264. TTrackable CreateTrackable(TrackableId trackableId)
  265. {
  266. var go = CreateGameObject(trackableId);
  267. var trackable = go.GetComponent<TTrackable>();
  268. if (trackable == null)
  269. trackable = go.AddComponent<TTrackable>();
  270. return trackable;
  271. }
  272. TTrackable CreateOrUpdateTrackable(TSessionRelativeData sessionRelativeData)
  273. {
  274. var trackableId = sessionRelativeData.trackableId;
  275. TTrackable trackable;
  276. if (m_Trackables.TryGetValue(trackableId, out trackable))
  277. {
  278. m_PendingAdds.Remove(trackableId);
  279. trackable.pending = false;
  280. trackable.SetSessionRelativeData(sessionRelativeData);
  281. }
  282. else
  283. {
  284. trackable = CreateTrackable(trackableId);
  285. m_Trackables.Add(trackableId, trackable);
  286. trackable.SetSessionRelativeData(sessionRelativeData);
  287. OnCreateTrackable(trackable);
  288. }
  289. OnAfterSetSessionRelativeData(trackable, sessionRelativeData);
  290. trackable.OnAfterSetSessionRelativeData();
  291. return trackable;
  292. }
  293. void DestroyTrackable(TTrackable trackable)
  294. {
  295. if (trackable.destroyOnRemoval)
  296. Destroy(trackable.gameObject);
  297. }
  298. static List<TTrackable> s_Added = new List<TTrackable>();
  299. static List<TTrackable> s_Updated = new List<TTrackable>();
  300. static List<TTrackable> s_Removed = new List<TTrackable>();
  301. }
  302. }