Skip to content
Snippets Groups Projects
WorldAnalysisARFoundationModuleGeospatial.cs 7.14 KiB
Newer Older
#if UNITY_ANDROID && ETSIARF_ARCORE_EXTENSIONS
using System;
using System.Collections.Generic;
using Google.XR.ARCoreExtensions;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using static WorldAnalysisARFoundationModule;

public class WorldAnalysisARFoundationModuleGeospatial : WorldAnalysisARFoundationModule 
{
    /// <summary>
    ///  Anchor manager
    /// </summary>
    private ARAnchorManager m_anchorManager;

    private AREarthManager m_arEarthManager;

    private Dictionary<string, TrackableInfo> m_trackedGeospatialAnchors = new Dictionary<string, TrackableInfo>();

    private bool geospatialSupported = true;

    /// Correspondance between local trackable and etsi uuid
    /// </summary>
    private Dictionary<string, string> m_localIdToEtsiId = new Dictionary<string, string>();

    /// <summary>
    /// Initialize ARCore geospatial anchors tracking module
    /// </summary>
    public void Initialize()
    {
        XROrigin origin = UnityEngine.Object.FindAnyObjectByType<XROrigin>();
        m_anchorManager = origin.gameObject.GetComponent<ARAnchorManager>();
        if (m_anchorManager == null)
        {
            m_anchorManager = origin.gameObject.AddComponent<ARAnchorManager>();
            GameObject anchorPrefab = (GameObject)Resources.Load("ARFAnchorTrackingPrefab");
            m_anchorManager.anchorPrefab = anchorPrefab;
        }
        m_arEarthManager = origin.gameObject.AddComponent<AREarthManager>();
        m_anchorManager.anchorsChanged += OnTrackedGeospatialAnchorChanged;   
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="uuid"></param>
    /// <returns></returns>
    public TrackableInfo GetPoseTrackable(Guid uuid)
    {
        if (m_trackedGeospatialAnchors.ContainsKey(uuid.ToString()))
        {
            return m_trackedGeospatialAnchors[uuid.ToString()];
        }
        else
        {
            return null;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="trackable"></param>
    /// <returns></returns>
    public bool AddTrackable(ETSI.ARF.OpenAPI.WorldStorage.Trackable trackable)
    {
        if (!geospatialSupported) return false;

        if (trackable.TrackableType != ETSI.ARF.OpenAPI.WorldStorage.TrackableType.GEOPOSE)
        {
            return false;
        }
        CreateGeosptialAnchor(trackable);
  
        return true;
    }


    public async void CreateGeosptialAnchor(ETSI.ARF.OpenAPI.WorldStorage.Trackable trackable)
    {
        if (geospatialSupported)
        {
            while (ARSession.state == ARSessionState.CheckingAvailability || ARSession.state == ARSessionState.None || ARSession.state == ARSessionState.SessionInitializing || m_arEarthManager.EarthState != EarthState.Enabled || m_arEarthManager.EarthTrackingState != TrackingState.Tracking)
            {
                await System.Threading.Tasks.Task.Delay(100);
            }
            if (m_arEarthManager.IsGeospatialModeSupported(GeospatialMode.Enabled) != FeatureSupported.Supported)
            {
                Debug.Log("Support : " + m_arEarthManager.IsGeospatialModeSupported(GeospatialMode.Enabled));
                geospatialSupported = false;
            }
            else
            {
                double[] values = new double[trackable.TrackablePayload.Length / sizeof(double)];

                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = BitConverter.ToDouble(trackable.TrackablePayload, i * sizeof(double));
                }

                Quaternion rotation = Quaternion.Euler((float)values[3], (float)values[4], (float)values[5]);

                if (m_arEarthManager.EarthTrackingState == TrackingState.Tracking)
                {
                    var anchor = m_anchorManager.AddAnchor(values[0], values[1], values[2], rotation);
                    m_localIdToEtsiId.Add(anchor.trackableId.subId2.ToString("X16"), trackable.UUID.ToString());
                }
            }
        }
    }


    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public ETSI.ARF.OpenAPI.WorldAnalysis.Capability GetSupportedCapability() //s'occuper de a (fonction doc)
    {
        if (!geospatialSupported) return null;

        ETSI.ARF.OpenAPI.WorldAnalysis.Capability capabilityGeospatialAnchor = new ETSI.ARF.OpenAPI.WorldAnalysis.Capability();
        capabilityGeospatialAnchor.TrackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.GEOPOSE;
        ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructure encodingInformation = new ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructure();
        encodingInformation.DataFormat = ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructureDataFormat.ARCORE;
        encodingInformation.Version = "1.01";
        capabilityGeospatialAnchor.EncodingInformation = encodingInformation;
        capabilityGeospatialAnchor.Framerate = 30; // Not particularly specified on ARKit and ARCore
        capabilityGeospatialAnchor.Latency = 0; // Not particularly specified on ARKit and ARCore
        capabilityGeospatialAnchor.Accuracy = 1; // Not particularly specified on ARKit and ARCore
        return capabilityGeospatialAnchor;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="eventArgs"></param>
    private void OnTrackedGeospatialAnchorChanged(ARAnchorsChangedEventArgs eventArgs)
    {
        foreach (var trackedGeospatialAnchor in eventArgs.updated)
        {
            Vector3 position = trackedGeospatialAnchor.transform.position;
            Quaternion rotation = trackedGeospatialAnchor.transform.rotation;

            TrackableInfo info = new TrackableInfo();
            info.name = trackedGeospatialAnchor.name;
            string localId = trackedGeospatialAnchor.trackableId.subId1.ToString("X16");// there must be a better way : does it work every time?

            if (m_localIdToEtsiId.ContainsKey(localId))
            {
                info.name = m_localIdToEtsiId[localId];
            }
            else
            {
                Debug.Log("ARCore Geospatial Anchor No correspondance for Local Anchor " + localId);
                continue;
            }

            if (trackedGeospatialAnchor.trackingState == TrackingState.Tracking)
            {
                info.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.OK;
                info.confidence = 100;
            }
            else if (trackedGeospatialAnchor.trackingState == TrackingState.Limited)
            {
                info.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.OK;
                info.confidence = 50;
            }
            else
            {
                info.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.FAILURE; //ADD NOT_TRACKED ?
                info.confidence = 0;
            }
            info.timeStamp = unchecked((int)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
            info.position = position;
            info.rotation = rotation;
            info.trackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.GEOPOSE;
                       
            m_trackedGeospatialAnchors[info.name] = info;
        }
    }
}

#endif