#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
{
///
/// Anchor manager
///
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 ;
///
/// Initialize image tracking module
///
public void Initialize()
{
XROrigin origin = UnityEngine.Object.FindAnyObjectByType();
m_anchorManager = origin.gameObject.AddComponent();
GameObject anchorPrefab = (GameObject)Resources.Load("ARFAnchorTrackingPrefab");
m_anchorManager.anchorPrefab = anchorPrefab;
m_anchorManager.anchorsChanged += OnTrackedAnchorChanged;
m_trackedPose = null ;
m_hasAddedMap= false ;
}
///
/// found
///
/// name of the mesh trackable
public TrackableInfo GetPoseTrackable(Guid uuid)
{
if (m_trackedPose != null)
{
if (m_trackedUUID == uuid)
{
return m_trackedPose ;
}
}
return null;
}
///
/// Add a trackable : need to be a map
///
/// Image trackable
/// Supported or not
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;
}
///
/// Initialize capability object with Mesh tracking
///
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 ;
}
///
/// Callback when Unity anchors are updated
///
/// update values
private void OnTrackedAnchorChanged(ARAnchorsChangedEventArgs eventArgs)
{
foreach (var trackedAnchor in eventArgs.updated)
{
Debug.Log("Anchor found " +trackedAnchor.trackableId.ToString());
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 ;
}
}
}
///
/// Add a map to arkitsubsystem : check local file exist or download from given url
///
/// url of a map to download
/// found or not
protected bool AddWorldMapToARKit(string url)
{
if (url.Length > 0)
{
Debug.Log("Load AR Map from URL");
LoadWorldMapFromURL(url);
// don't check if url is valid
return true ;
}
else
{
Debug.Log("Load AR Map locally");
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 ;
}
///
/// Update pose of the map trackable
///
/// evaluated position of trackable/param>
/// evaluated rotation of the trackable
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;
}
///
/// Load a world map from a url
///
/// url to the worlmap to download
private async void LoadWorldMapFromURL(string url)
{
Debug.Log("Download WorlMap from url "+ url);
KeyValuePair downloaded = await WorldAnalysisARFoundationHelper.DownloadFileHTTP(url);
var coroutine = CoroutineLoadWorldMap(downloaded.Key);
WorldAnalysisARFoundationCoroutineHelper.Instance.StartACoroutine(coroutine);
}
///
/// Load current map from path
///
/// path of the map
/// coroutine
public IEnumerator CoroutineLoadWorldMap(string mapPath)
{
while (ARSession.state == ARSessionState.CheckingAvailability || ARSession.state == ARSessionState.None ||ARSession.state == ARSessionState.SessionInitializing)
{
// wait for ar session to be ready
yield return null ;
}
ARSession session = Component.FindAnyObjectByType() ;
ARKitSessionSubsystem sessionSubsystem = (ARKitSessionSubsystem)session.subsystem;
if (sessionSubsystem == null)
{
Debug.Log("Cannot load map: no ARKitSessionSubsystem");
}
else
{
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();
while (bytesRemaining > 0)
{
var bytes = binaryReader.ReadBytes(bytesPerFrame);
allBytes.AddRange(bytes);
bytesRemaining -= bytesPerFrame;
yield return null;
}
var data = new NativeArray(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