From 900e2250f8d8b9eaa2514fac3818d7d41213f84d Mon Sep 17 00:00:00 2001 From: jlacoche <jeremy.lacoche@orange.com> Date: Sat, 6 Jul 2024 00:35:24 +0200 Subject: [PATCH] Add module for arkit world map --- Runtime/Scripts/WorldAnalysisARFoundation.cs | 2 + ...dAnalysisARFoundationModuleARCoreAnchor.cs | 12 +- ...AnalysisARFoundationModuleARKitWorldMap.cs | 221 ++++++++++++++++++ ...sisARFoundationModuleARKitWorldMap.cs.meta | 11 + .../WorldAnalysisARFoundationModuleImage.cs | 2 +- ...i.isg.arf.worldanalysisarfoundation.asmdef | 3 +- 6 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs create mode 100644 Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs.meta diff --git a/Runtime/Scripts/WorldAnalysisARFoundation.cs b/Runtime/Scripts/WorldAnalysisARFoundation.cs index 02484a7..055a72d 100644 --- a/Runtime/Scripts/WorldAnalysisARFoundation.cs +++ b/Runtime/Scripts/WorldAnalysisARFoundation.cs @@ -80,6 +80,8 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface #if UNITY_IOS WorldAnalysisARFoundationModuleMesh meshModule = new WorldAnalysisARFoundationModuleMesh(); m_trackableModules.Add(meshModule); + WorldAnalysisARFoundationModuleARKitWorldMap worldMapModule = new WorldAnalysisARFoundationModuleARKitWorldMap(); + m_trackableModules.Add(worldMapModule); #endif #if ETSIARF_ARCORE_EXTENSIONS diff --git a/Runtime/Scripts/WorldAnalysisARFoundationModuleARCoreAnchor.cs b/Runtime/Scripts/WorldAnalysisARFoundationModuleARCoreAnchor.cs index 9bd2d3e..f593e41 100644 --- a/Runtime/Scripts/WorldAnalysisARFoundationModuleARCoreAnchor.cs +++ b/Runtime/Scripts/WorldAnalysisARFoundationModuleARCoreAnchor.cs @@ -37,10 +37,10 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda } /// <summary> - /// + /// Get the pose of the trackable from its uuid /// </summary> - /// <param name="uuid"></param> - /// <returns></returns> + /// <param name="uuid">id of the trackable</param> + /// <returns>null or trackableInfo with last updated values</returns> public TrackableInfo GetPoseTrackable(Guid uuid) { if (m_trackedCloudAnchors.ContainsKey(uuid.ToString())) @@ -54,10 +54,10 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda } /// <summary> - /// + /// Need to be a map /// </summary> /// <param name="trackable"></param> - /// <returns></returns> + /// <returns>map or not (does not check is solved)</returns> public bool AddTrackable(ETSI.ARF.OpenAPI.WorldStorage.Trackable trackable) { /// Here : we don't check if the trackable is allready added, AddImageToLibrary does it @@ -130,7 +130,7 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda info.timeStamp = unchecked((int)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); info.position = position; info.rotation = rotation; - info.trackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.IMAGE_MARKER; + info.trackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.MAP; m_trackedCloudAnchors[info.name] = info; } } diff --git a/Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs b/Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs new file mode 100644 index 0000000..5bb1ea4 --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs @@ -0,0 +1,221 @@ + +#if UNITY_IOS +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.XR.ARFoundation; +using UnityEngine.XR.ARSubsystems; +using UnityEngine.XR.ARKit; +using System.IO; +using Unity.XR.CoreUtils; +using static WorldAnalysisARFoundationModule; +using ETSI.ARF.OpenAPI.WorldStorage; +using Unity.Collections; + + +public class WorldAnalysisARFoundationModuleARKitWorldMap : WorldAnalysisARFoundationModule +{ + /// <summary> + /// Anchor manager + /// </summary> + private ARAnchorManager m_anchorManager; + ///Has loaded a worl map + private bool m_hasAddedMap ; + /// Possible trackable id (arfoundation) of an anchor contained in the world map. Take the pose of this anchor if it exists or zero + private string m_arfoundationAnchorTrackableId ; + /// uuId of the map trackabled + private Guid m_trackedUUID ; + /// computed and updated pose + private TrackableInfo m_trackedPose ; + + /// <summary> + /// Initialize image tracking module + /// </summary> + public void Initialize() + { + XROrigin origin = UnityEngine.Object.FindAnyObjectByType<XROrigin>(); + m_anchorManager = origin.gameObject.AddComponent<ARAnchorManager>(); + GameObject anchorPrefab = (GameObject)Resources.Load("ARFAnchorTrackingPrefab"); + m_anchorManager.anchorPrefab = anchorPrefab; + m_anchorManager.anchorsChanged += OnTrackedAnchorChanged; + m_trackedPose = null ; + m_hasAddedMap= false ; + } + + /// <summary> + /// found + /// </summary> + /// <param name="uuid">name of the mesh trackable</param> + public TrackableInfo GetPoseTrackable(Guid uuid) + { + if (m_trackedPose != null) + { + if (m_trackedUUID == uuid) + { + return m_trackedPose ; + } + } + return null; + } + + /// <summary> + /// Add a trackable : need to be a map + /// </summary> + /// <param name="trackable">Image trackable</param> + /// <returns>Supported or not</returns> + public bool AddTrackable(Trackable trackable) + { + if (trackable.TrackableType != ETSI.ARF.OpenAPI.WorldStorage.TrackableType.MAP) + { + return false; + } + if (m_hasAddedMap) + { + Debug.Log("Only one ARKit map can be loaded"); + return false ; + } + + // Check if a map url is provided + string url = "" ; + if (trackable.KeyvalueTags.ContainsKey("url")) + { + foreach(string s in trackable.KeyvalueTags["url"]) + { + // first one + url = s ; + break ; + } + } + bool resul = AddWorldMapToARKit(url); + m_trackedUUID = trackable.UUID ; + m_arfoundationAnchorTrackableId = trackable.Name ; + m_hasAddedMap = resul ; + return resul; + } + + /// <summary> + /// Initialize capability object with Mesh tracking + /// </summary> + public ETSI.ARF.OpenAPI.WorldAnalysis.Capability GetSupportedCapability() + { + ETSI.ARF.OpenAPI.WorldAnalysis.Capability capabilityMesh = new ETSI.ARF.OpenAPI.WorldAnalysis.Capability(); + capabilityMesh.TrackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.MAP; + ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructure encodingInformation = new ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructure(); + encodingInformation.DataFormat = ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructureDataFormat.ARKIT; + encodingInformation.Version = "1.01"; + capabilityMesh.EncodingInformation = encodingInformation; + capabilityMesh.Framerate = 30; // Not particularly specified on ARKit + capabilityMesh.Latency = 0; // Not particularly specified on ARKit + capabilityMesh.Accuracy = 1; // Not particularly specified on ARKit + return capabilityMesh ; + } + + /// <summary> + /// Callback when Unity anchors are updated + /// </summary> + /// <param name="eventArgs">update values</param> + private void OnTrackedAnchorChanged(ARAnchorsChangedEventArgs eventArgs) + { + foreach (var trackedAnchor in eventArgs.updated) + { + if (trackedAnchor.trackableId.ToString() == m_arfoundationAnchorTrackableId) + { + /// look for an anchor with the trackable Id correspond to the ETSI ARF trackable name + UpdateTrackableInfoWithPose(trackedAnchor.transform.position,trackedAnchor.transform.rotation); + break ; + } + } + } + + /// <summary> + /// Add a map to arkitsubsystem : check local file exist or download from given url + /// </summary> + /// <param name="url">url of a map to download</param> + /// <returns>found or not</returns> + protected bool AddWorldMapToARKit(string url) + { + if (url.Length > 0) + { + LoadWorldMapFromURL(url); + // don't check if url is valid + return true ; + } + else + { + string localMap = Application.persistentDataPath + "/ARkitWorlMap.map"; + if (File.Exists(localMap)) + { + var coroutine = CoroutineLoadWorldMap(localMap); + WorldAnalysisARFoundationCoroutineHelper.Instance.StartACoroutine(coroutine); + return true ; + } + } + // no url and no local map + return false ; + } + + /// <summary> + /// Update pose of the map trackable + /// </summary> + /// </param name="position"> evaluated position of trackable/param> + /// </param name="rotation"> evaluated rotation of the trackable</param> + private void UpdateTrackableInfoWithPose(Vector3 position, Quaternion rotation) + { + m_trackedPose = new TrackableInfo(); + m_trackedPose.name = m_arfoundationAnchorTrackableId; + m_trackedPose.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.OK; // here : could check the state of the ARKit slam + m_trackedPose.confidence = 100; + m_trackedPose.timeStamp = unchecked((int)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); + m_trackedPose.position = position; + m_trackedPose.rotation = rotation; + m_trackedPose.trackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.MAP; + } + + /// <summary> + /// Load a world map from a url + /// </summary> + /// </param name="url"> url to the worlmap to download</param> + private async void LoadWorldMapFromURL(string url) + { + Debug.Log("Download WorlMap from url "+ url); + KeyValuePair<string , string> downloaded = await WorldAnalysisARFoundationHelper.DownloadFileHTTP(url); + var coroutine = CoroutineLoadWorldMap(downloaded.Key); + WorldAnalysisARFoundationCoroutineHelper.Instance.StartACoroutine(coroutine); + } + + /// <summary> + /// Load current map from path + /// </summary> + /// <param name="mapPath">path of the map</param> + /// <returns>coroutine</returns> + public IEnumerator CoroutineLoadWorldMap(string mapPath) + { + ARSession session = Component.FindAnyObjectByType<ARSession>() ; + ARKitSessionSubsystem sessionSubsystem = (ARKitSessionSubsystem)session.subsystem; + FileStream file; + file = File.Open(mapPath, FileMode.Open); + const int bytesPerFrame = 1024 * 10; + var bytesRemaining = file.Length; + var binaryReader = new BinaryReader(file); + var allBytes = new List<byte>(); + while (bytesRemaining > 0) + { + var bytes = binaryReader.ReadBytes(bytesPerFrame); + allBytes.AddRange(bytes); + bytesRemaining -= bytesPerFrame; + yield return null; + } + + var data = new NativeArray<byte>(allBytes.Count, Allocator.Temp); + data.CopyFrom(allBytes.ToArray()); + ARWorldMap worldMap; + if (ARWorldMap.TryDeserialize(data, out worldMap)) + { + data.Dispose(); + } + sessionSubsystem.ApplyWorldMap(worldMap); + UpdateTrackableInfoWithPose(Vector3.zero, Quaternion.identity); // before trying to find an anchor: default pause is origin of the map + } +} +#endif \ No newline at end of file diff --git a/Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs.meta b/Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs.meta new file mode 100644 index 0000000..ac4deff --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisARFoundationModuleARKitWorldMap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0eb5e51c9f2ae4e8db0c43c245bf7b41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/WorldAnalysisARFoundationModuleImage.cs b/Runtime/Scripts/WorldAnalysisARFoundationModuleImage.cs index bba58d8..ab15c6e 100644 --- a/Runtime/Scripts/WorldAnalysisARFoundationModuleImage.cs +++ b/Runtime/Scripts/WorldAnalysisARFoundationModuleImage.cs @@ -212,7 +212,7 @@ public class WorldAnalysisARFoundationModuleImage : WorldAnalysisARFoundationMod /// </param name="url"> url to the image to download</param> /// <param name="fileName">file name</param> /// <param name="imageWidthInMeters">image width in meters</param> - public async void LoadTextureFromURL(string url, string fileName, float imageWidthInMeters) + private async void LoadTextureFromURL(string url, string fileName, float imageWidthInMeters) { Debug.Log("Download image from url "+ url); m_allDownloadedImages.Add(url); diff --git a/Runtime/etsi.isg.arf.worldanalysisarfoundation.asmdef b/Runtime/etsi.isg.arf.worldanalysisarfoundation.asmdef index 8a0ef7e..6ce3bc4 100644 --- a/Runtime/etsi.isg.arf.worldanalysisarfoundation.asmdef +++ b/Runtime/etsi.isg.arf.worldanalysisarfoundation.asmdef @@ -7,7 +7,8 @@ "GUID:a9420e37d7990b54abdef6688edbe313", "GUID:92703082f92b41ba80f0d6912de66115", "GUID:dc960734dc080426fa6612f1c5fe95f3", - "GUID:b35d8248e1a04478bb2d70fe76652ddf" + "GUID:b35d8248e1a04478bb2d70fe76652ddf", + "GUID:dfd0ce20189d48e5b22bb4134b558f5a" ], "includePlatforms": [], "excludePlatforms": [], -- GitLab