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.

494 lines
16 KiB

4 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine.XR.ARSubsystems;
  4. #if UNITY_2019_3_OR_NEWER
  5. using LegacyMeshId = UnityEngine.XR.MeshId;
  6. #else
  7. using LegacyMeshId = UnityEngine.Experimental.XR.TrackableId;
  8. using MeshInfo = UnityEngine.Experimental.XR.MeshInfo;
  9. using XRMeshSubsystem = UnityEngine.Experimental.XR.XRMeshSubsystem;
  10. using XRMeshSubsystemDescriptor = UnityEngine.Experimental.XR.XRMeshSubsystemDescriptor;
  11. using MeshChangeState = UnityEngine.Experimental.XR.MeshChangeState;
  12. using MeshVertexAttributes = UnityEngine.Experimental.XR.MeshVertexAttributes;
  13. using MeshGenerationStatus = UnityEngine.Experimental.XR.MeshGenerationStatus;
  14. using MeshGenerationResult = UnityEngine.Experimental.XR.MeshGenerationResult;
  15. #endif
  16. #if !UNITY_2019_2_OR_NEWER
  17. using SubsystemManager = UnityEngine.Experimental.SubsystemManager;
  18. #endif
  19. namespace UnityEngine.XR.ARFoundation
  20. {
  21. /// <summary>
  22. /// A manager for triangle meshes generated by an AR device.
  23. /// Creates, updates, and removes <c>GameObject</c>s in response to
  24. /// the environment. For each mesh, a <see cref="meshPrefab"/> is
  25. /// instantiated which must contain at least a <c>MeshFilter</c>.
  26. /// If the <see cref="meshPrefab"/>'s <c>GameObject</c> also has a
  27. /// <c>MeshCollider</c>, then a physics mesh is generated asynchronously,
  28. /// without blocking the main thread.
  29. /// </summary>
  30. [DefaultExecutionOrder(ARUpdateOrder.k_MeshManager)]
  31. [HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.0/api/UnityEngine.XR.ARFoundation.ARMeshManager.html")]
  32. [DisallowMultipleComponent]
  33. public class ARMeshManager : MonoBehaviour
  34. {
  35. [SerializeField]
  36. [Tooltip("The prefab to be instantiated for each generated mesh. MeshColliders are processed asynchronously and do not block the main thread.")]
  37. MeshFilter m_MeshPrefab;
  38. /// <summary>
  39. /// A prefab to be instantiated for each generated mesh. The prefab must have at least a
  40. /// <c>MeshFilter</c> component on it. If it also has a <c>MeshCollider</c> component, the
  41. /// physics bounding volume data will be generated asynchronously. This does not block the
  42. /// main thread, but may take longer to process.
  43. /// </summary>
  44. public MeshFilter meshPrefab
  45. {
  46. get { return m_MeshPrefab; }
  47. set { m_MeshPrefab = value; }
  48. }
  49. [SerializeField]
  50. [Tooltip("The density of the generated mesh [0..1]. 1 will be highly tesselated while 0 will be very low.")]
  51. [Range(0, 1)]
  52. float m_Density = 0.5f;
  53. /// <summary>
  54. /// The density of the generated mesh [0..1]. 1 will be densely tesselated,
  55. /// while 0 will have the lowest supported tesselation.
  56. /// </summary>
  57. public float density
  58. {
  59. get { return m_Density; }
  60. set
  61. {
  62. if (value < 0f || value > 1f)
  63. throw new ArgumentOutOfRangeException(nameof(value), value, "Mesh density must be between 0 and 1, inclusive.");
  64. if (m_Density == value)
  65. return;
  66. m_Density = value;
  67. if (m_Subsystem != null)
  68. m_Subsystem.meshDensity = m_Density;
  69. }
  70. }
  71. [SerializeField]
  72. [Tooltip("If enabled, a normal is requested for each vertex.")]
  73. bool m_Normals = true;
  74. /// <summary>
  75. /// If <c>True</c>, requests a normal for each vertex in generated meshes.
  76. /// </summary>
  77. public bool normals
  78. {
  79. get { return m_Normals; }
  80. set { m_Normals = value; }
  81. }
  82. [SerializeField]
  83. [Tooltip("If enabled, a tangent is requested for each vertex.")]
  84. bool m_Tangents;
  85. /// <summary>
  86. /// If <c>True</c>, requests a tangent for each vertex in generated meshes.
  87. /// </summary>
  88. public bool tangents
  89. {
  90. get { return m_Tangents; }
  91. set { m_Tangents = value; }
  92. }
  93. [SerializeField]
  94. [Tooltip("If enabled, a UV texture coordinate is requested for each vertex.")]
  95. bool m_TextureCoordinates;
  96. /// <summary>
  97. /// If <c>True</c>, requests a texture coordinate for each vertex in generated meshes.
  98. /// </summary>
  99. public bool textureCoordinates
  100. {
  101. get { return m_TextureCoordinates; }
  102. set { m_TextureCoordinates = value; }
  103. }
  104. [SerializeField]
  105. [Tooltip("If enabled, a color value is requested for each vertex.")]
  106. bool m_Colors;
  107. /// <summary>
  108. /// If <c>True</c>, requests a color value for each vertex in generated meshes.
  109. /// </summary>
  110. public bool colors
  111. {
  112. get { return m_Colors; }
  113. set { m_Colors = value; }
  114. }
  115. [SerializeField]
  116. [Tooltip("The number of meshes to process concurrently. Higher values require more CPU time.")]
  117. int m_ConcurrentQueueSize = 4;
  118. /// <summary>
  119. /// The number of meshes to process concurrently. Meshes are processed on a background
  120. /// thread. Higher numbers will require additional CPU time.
  121. /// </summary>
  122. public int concurrentQueueSize
  123. {
  124. get { return m_ConcurrentQueueSize; }
  125. set { m_ConcurrentQueueSize = value; }
  126. }
  127. /// <summary>
  128. /// Invoked whenever meshes have changed (been added, updated, or removed).
  129. /// </summary>
  130. public event Action<ARMeshesChangedEventArgs> meshesChanged;
  131. /// <summary>
  132. /// The <c>XRMeshSubsystem</c> used by this component to generate meshes.
  133. /// </summary>
  134. public XRMeshSubsystem subsystem
  135. {
  136. get { return m_Subsystem; }
  137. }
  138. /// <summary>
  139. /// Returns a collection of <c>MeshFilter</c>s representing meshes generated by this component.
  140. /// </summary>
  141. public IList<MeshFilter> meshes
  142. {
  143. get { return m_Meshes.Values; }
  144. }
  145. /// <summary>
  146. /// Destroys all generated meshes and ignores any pending meshes.
  147. /// </summary>
  148. public void DestroyAllMeshes()
  149. {
  150. m_Pending.Clear();
  151. m_Generating.Clear();
  152. foreach (var meshFilter in meshes)
  153. {
  154. if (meshFilter != null)
  155. Destroy(meshFilter.gameObject);
  156. }
  157. m_Meshes.Clear();
  158. }
  159. ARSessionOrigin GetSessionOrigin()
  160. {
  161. if (transform.parent == null)
  162. return null;
  163. return transform.parent.GetComponent<ARSessionOrigin>();
  164. }
  165. #if UNITY_EDITOR
  166. void Reset()
  167. {
  168. if (GetSessionOrigin() != null)
  169. transform.localScale = Vector3.one * 10f;
  170. }
  171. void OnValidate()
  172. {
  173. if (GetSessionOrigin() == null)
  174. {
  175. UnityEditor.EditorUtility.DisplayDialog(
  176. "Hierarchy not allowed",
  177. $"An {nameof(ARMeshManager)} must be a child of an {nameof(ARSessionOrigin)}.",
  178. "Remove Component");
  179. UnityEditor.EditorApplication.delayCall += ()=>
  180. {
  181. DestroyImmediate(this);
  182. };
  183. }
  184. }
  185. #endif
  186. void SetBoundingVolume()
  187. {
  188. m_Subsystem.SetBoundingVolume(transform.localPosition, transform.localScale);
  189. transform.hasChanged = false;
  190. }
  191. void OnEnable()
  192. {
  193. if (GetSessionOrigin() == null)
  194. {
  195. enabled = false;
  196. throw new InvalidOperationException($"An {nameof(ARMeshManager)} must be a child of an {nameof(ARSessionOrigin)}.");
  197. }
  198. if (m_Subsystem == null)
  199. m_Subsystem = CreateSubsystem();
  200. if (m_Subsystem != null)
  201. {
  202. m_Subsystem.meshDensity = m_Density;
  203. SetBoundingVolume();
  204. m_Subsystem.Start();
  205. }
  206. else
  207. {
  208. enabled = false;
  209. }
  210. }
  211. void OnDrawGizmosSelected()
  212. {
  213. Gizmos.color = new Color(0, .5f, 0, .35f);
  214. Gizmos.matrix = transform.localToWorldMatrix;
  215. Gizmos.DrawCube(Vector3.zero, Vector3.one);
  216. }
  217. void Update()
  218. {
  219. if (m_Subsystem != null)
  220. {
  221. if (transform.hasChanged)
  222. SetBoundingVolume();
  223. UpdateMeshInfos();
  224. if (m_MeshPrefab != null)
  225. Generate();
  226. }
  227. // Invoke user callbacks
  228. try
  229. {
  230. if (meshesChanged != null && (m_Added.Count + m_Updated.Count + m_Removed.Count > 0))
  231. {
  232. meshesChanged(new ARMeshesChangedEventArgs(m_Added, m_Updated, m_Removed));
  233. }
  234. }
  235. finally
  236. {
  237. // Make sure we clear the internal lists if user code throws an exception
  238. m_Added.Clear();
  239. m_Updated.Clear();
  240. foreach (var meshFilter in m_Removed)
  241. {
  242. if (meshFilter != null)
  243. Destroy(meshFilter.gameObject);
  244. }
  245. m_Removed.Clear();
  246. }
  247. }
  248. void Generate()
  249. {
  250. var vertexAttributes = MeshVertexAttributes.None;
  251. if (m_Normals)
  252. vertexAttributes |= MeshVertexAttributes.Normals;
  253. if (m_Tangents)
  254. vertexAttributes |= MeshVertexAttributes.Tangents;
  255. if (m_TextureCoordinates)
  256. vertexAttributes |= MeshVertexAttributes.UVs;
  257. if (m_Colors)
  258. vertexAttributes |= MeshVertexAttributes.Colors;
  259. while ((m_Generating.Count < m_ConcurrentQueueSize) &&
  260. m_Pending.TryDequeue(m_Generating, out MeshInfo meshInfo))
  261. {
  262. var meshId = meshInfo.MeshId;
  263. var meshFilter = GetOrCreateMeshFilter(GetTrackableId(meshId));
  264. var meshCollider = meshFilter.GetComponent<MeshCollider>();
  265. var mesh = (meshFilter.sharedMesh != null) ? meshFilter.sharedMesh : meshFilter.mesh;
  266. m_Generating.Add(meshId, meshInfo);
  267. m_Subsystem.GenerateMeshAsync(
  268. meshInfo.MeshId,
  269. mesh,
  270. meshCollider,
  271. vertexAttributes,
  272. m_OnMeshGeneratedDelegate);
  273. }
  274. }
  275. void OnMeshGenerated(MeshGenerationResult result)
  276. {
  277. if (!m_Generating.TryGetValue(result.MeshId, out MeshInfo meshInfo))
  278. return;
  279. m_Generating.Remove(result.MeshId);
  280. if (result.Status != MeshGenerationStatus.Success)
  281. return;
  282. if (!m_Meshes.TryGetValue(GetTrackableId(result.MeshId), out MeshFilter meshFilter) || (meshFilter == null))
  283. return;
  284. meshFilter.gameObject.SetActive(true);
  285. switch (meshInfo.ChangeState)
  286. {
  287. case MeshChangeState.Added:
  288. m_Added.Add(meshFilter);
  289. break;
  290. case MeshChangeState.Updated:
  291. m_Updated.Add(meshFilter);
  292. break;
  293. // Removed/unchanged meshes don't get generated.
  294. default:
  295. break;
  296. }
  297. }
  298. void UpdateMeshInfos()
  299. {
  300. if (m_Subsystem.TryGetMeshInfos(s_MeshInfos))
  301. {
  302. foreach (var meshInfo in s_MeshInfos)
  303. {
  304. switch (meshInfo.ChangeState)
  305. {
  306. case MeshChangeState.Added:
  307. case MeshChangeState.Updated:
  308. m_Pending.EnqueueUnique(meshInfo);
  309. break;
  310. case MeshChangeState.Removed:
  311. // Remove from processing queues
  312. m_Pending.Remove(meshInfo.MeshId);
  313. m_Generating.Remove(meshInfo.MeshId);
  314. // Add to list of removed meshes
  315. var trackableId = GetTrackableId(meshInfo.MeshId);
  316. if (m_Meshes.TryGetValue(trackableId, out MeshFilter meshFilter))
  317. {
  318. m_Meshes.Remove(trackableId);
  319. if (meshFilter != null)
  320. m_Removed.Add(meshFilter);
  321. }
  322. break;
  323. default:
  324. break;
  325. }
  326. }
  327. }
  328. }
  329. void OnDisable()
  330. {
  331. if (m_Subsystem != null)
  332. m_Subsystem.Stop();
  333. }
  334. void OnDestroy()
  335. {
  336. if (m_Subsystem != null)
  337. m_Subsystem.Destroy();
  338. m_Subsystem = null;
  339. }
  340. MeshFilter GetOrCreateMeshFilter(TrackableId trackableId)
  341. {
  342. // If the mesh filter is Destroyed by user code, then meshFilter will compare
  343. // equal with null. In that case, we want to recreate it.
  344. if (m_Meshes.TryGetValue(trackableId, out MeshFilter meshFilter) && (meshFilter != null))
  345. return meshFilter;
  346. var sessionOrigin = GetSessionOrigin();
  347. meshFilter = (sessionOrigin == null) ?
  348. Instantiate(m_MeshPrefab) :
  349. Instantiate(m_MeshPrefab, sessionOrigin.trackablesParent);
  350. meshFilter.gameObject.name = $"Mesh {trackableId.ToString()}";
  351. // The GameObject should start life inactive until we've populated it
  352. meshFilter.gameObject.SetActive(false);
  353. m_Meshes[trackableId] = meshFilter;
  354. return meshFilter;
  355. }
  356. internal static unsafe TrackableId GetTrackableId(LegacyMeshId trackableId)
  357. {
  358. return *(TrackableId*)&trackableId;
  359. }
  360. internal static unsafe LegacyMeshId GetLegacyMeshId(TrackableId trackableId)
  361. {
  362. return *(LegacyMeshId*)&trackableId;
  363. }
  364. void Awake()
  365. {
  366. m_Added = new List<MeshFilter>();
  367. m_Updated = new List<MeshFilter>();
  368. m_Removed = new List<MeshFilter>();
  369. m_Pending = new MeshQueue();
  370. m_Generating = new Dictionary<LegacyMeshId, MeshInfo>();
  371. m_Meshes = new SortedList<TrackableId, MeshFilter>(s_TrackableIdComparer);
  372. m_OnMeshGeneratedDelegate = new Action<MeshGenerationResult>(OnMeshGenerated);
  373. }
  374. XRMeshSubsystem CreateSubsystem()
  375. {
  376. SubsystemManager.GetSubsystemDescriptors(s_SubsystemDescriptors);
  377. if (s_SubsystemDescriptors.Count > 0)
  378. {
  379. var descriptor = s_SubsystemDescriptors[0];
  380. if (s_SubsystemDescriptors.Count > 1)
  381. Debug.LogWarning($"Multiple {nameof(XRMeshSubsystem)} found. Using {descriptor.id}");
  382. return descriptor.Create();
  383. }
  384. else
  385. {
  386. return null;
  387. }
  388. }
  389. class TrackableIdComparer : IComparer<TrackableId>
  390. {
  391. public int Compare(TrackableId trackableIdA, TrackableId trackableIdB)
  392. {
  393. if (trackableIdA.subId1 == trackableIdB.subId1)
  394. {
  395. return trackableIdA.subId2.CompareTo(trackableIdB.subId2);
  396. }
  397. else
  398. {
  399. return trackableIdA.subId1.CompareTo(trackableIdB.subId1);
  400. }
  401. }
  402. }
  403. List<MeshFilter> m_Added;
  404. List<MeshFilter> m_Updated;
  405. List<MeshFilter> m_Removed;
  406. MeshQueue m_Pending;
  407. Dictionary<LegacyMeshId, MeshInfo> m_Generating;
  408. SortedList<TrackableId, MeshFilter> m_Meshes;
  409. Action<MeshGenerationResult> m_OnMeshGeneratedDelegate;
  410. XRMeshSubsystem m_Subsystem;
  411. static TrackableIdComparer s_TrackableIdComparer = new TrackableIdComparer();
  412. static List<MeshInfo> s_MeshInfos = new List<MeshInfo>();
  413. static List<XRMeshSubsystemDescriptor> s_SubsystemDescriptors =
  414. new List<XRMeshSubsystemDescriptor>();
  415. }
  416. }