Commit 086784eb authored by Sylvain Buche's avatar Sylvain Buche
Browse files

Implementation of the Geospatial module using ARCore API.

Fix a bug in the trackable selection algorithm.
Change the way ARAnchorManager is used in the ARCoreAnchors module
parent 5ca2a94b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -14,5 +14,5 @@ MonoBehaviour:
  m_EditorClassIdentifier: 
  CloudAnchorMode: 1
  SemanticMode: 0
  GeospatialMode: 0
  GeospatialMode: 2
  StreetscapeGeometryMode: 0
+106 −0
Original line number Diff line number Diff line
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &919512057412407595
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 5663275178603938767}
  - component: {fileID: 2250489381470411502}
  - component: {fileID: 5243785181569724411}
  - component: {fileID: 4959683567476516339}
  m_Layer: 0
  m_Name: PurpleSphere
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &5663275178603938767
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 919512057412407595}
  serializedVersion: 2
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
  m_LocalPosition: {x: 0, y: 0, z: 0}
  m_LocalScale: {x: 20, y: 20, z: 20}
  m_ConstrainProportionsScale: 1
  m_Children: []
  m_Father: {fileID: 6911754431728604849}
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &2250489381470411502
MeshFilter:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 919512057412407595}
  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &5243785181569724411
MeshRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 919512057412407595}
  m_Enabled: 1
  m_CastShadows: 1
  m_ReceiveShadows: 1
  m_DynamicOccludee: 1
  m_StaticShadowCaster: 0
  m_MotionVectors: 1
  m_LightProbeUsage: 1
  m_ReflectionProbeUsage: 1
  m_RayTracingMode: 2
  m_RayTraceProcedural: 0
  m_RenderingLayerMask: 1
  m_RendererPriority: 0
  m_Materials:
  - {fileID: 2100000, guid: a64202207c2bc6042a40ad749c6eabf1, type: 2}
  m_StaticBatchInfo:
    firstSubMesh: 0
    subMeshCount: 0
  m_StaticBatchRoot: {fileID: 0}
  m_ProbeAnchor: {fileID: 0}
  m_LightProbeVolumeOverride: {fileID: 0}
  m_ScaleInLightmap: 1
  m_ReceiveGI: 1
  m_PreserveUVs: 0
  m_IgnoreNormalsForChartDetection: 0
  m_ImportantGI: 0
  m_StitchLightmapSeams: 1
  m_SelectedEditorRenderState: 3
  m_MinimumChartSize: 4
  m_AutoUVMaxDistance: 0.5
  m_AutoUVMaxAngle: 89
  m_LightmapParameters: {fileID: 0}
  m_SortingLayerID: 0
  m_SortingLayer: 0
  m_SortingOrder: 0
  m_AdditionalVertexStreams: {fileID: 0}
--- !u!135 &4959683567476516339
SphereCollider:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 919512057412407595}
  m_Material: {fileID: 0}
  m_IncludeLayers:
    serializedVersion: 2
    m_Bits: 0
  m_ExcludeLayers:
    serializedVersion: 2
    m_Bits: 0
  m_LayerOverridePriority: 0
  m_IsTrigger: 0
  m_ProvidesContacts: 0
  m_Enabled: 1
  serializedVersion: 3
  m_Radius: 0.5
  m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6911754431728604848
GameObject:
  m_ObjectHideFlags: 0
@@ -33,6 +138,7 @@ Transform:
  - {fileID: 6911754433267149346}
  - {fileID: 6911754433204531549}
  - {fileID: 5348077246929615354}
  - {fileID: 5663275178603938767}
  m_Father: {fileID: 0}
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6911754432812775762
+28 −6
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ using System.Collections;
using System.Linq;
using ETSI.ARF.OpenAPI.WorldStorage;
using ETSI.ARF.WorldStorage.REST;
using Unity.XR.CoreUtils;

//Implementation of the WorldAnalysis interface
public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
@@ -50,6 +51,11 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
    /// </summary>
    private bool isChekingAvailabilityRunning = false;

    /// <summary>
    ///  Anchor manager
    /// </summary>
    private ARAnchorManager m_anchorManager;

    /// <summary>
    /// Informations regarding a subscription to a pose
    /// </summary>
@@ -77,6 +83,9 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
        m_trackableModules = new List<WorldAnalysisARFoundationModule>();
        WorldAnalysisARFoundationModuleImage imageModule = new WorldAnalysisARFoundationModuleImage();
        m_trackableModules.Add(imageModule);

        WorldAnalysisARFoundationModuleGeospatial geospatialModule = new WorldAnalysisARFoundationModuleGeospatial();
        m_trackableModules.Add(geospatialModule);
#if UNITY_IOS
        WorldAnalysisARFoundationModuleMesh meshModule = new WorldAnalysisARFoundationModuleMesh();
        m_trackableModules.Add(meshModule);
@@ -158,7 +167,9 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
                List<RelocObjects> notTrackedCandidates = new List<RelocObjects>();

                //Hierarchy of types
                string[] types = { "MESH", "IMAGE_MARKER", "FIDUCIAL_MARKER", "MAP", "OTHER" }; //TODO : GEOPOSE
                string[] types = { "MESH", "IMAGE_MARKER", "FIDUCIAL_MARKER", "MAP", "GEOPOSE", "OTHER" };

                List<Guid> keysToRemove = new List<Guid>();

                //We fill in the confidence level lists.
                foreach (KeyValuePair<Guid, RelocObjects> relocObject in temp)
@@ -178,7 +189,16 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
                        {
                            notTrackedCandidates.Add(relocObject.Value);
                        }
                    }else
                    {
                        keysToRemove.Add(relocObject.Key);
                    }
                }

                // Remove null trackables from temp
                foreach (Guid key in keysToRemove)
                {
                    temp.Remove(key);
                }

                //uses the types[] array indexes as key, and UUIDs as values
@@ -364,9 +384,12 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
            ///Convert into world storage capabilities
            List<ETSI.ARF.OpenAPI.WorldStorage.Capability> capabilities = new List<ETSI.ARF.OpenAPI.WorldStorage.Capability>();
            foreach (ETSI.ARF.OpenAPI.WorldAnalysis.Capability capability in currentCapabilities)
            {
                if(capability != null)
                {
                    capabilities.Add(WorldAnalysisUnityHelper.ConvertWorldAnalysisCapability(capability));
                }
            }

            /// Collect relocalization information
            ETSI.ARF.OpenAPI.WorldStorage.Response response = RelocalizationInformationRequest.GetRelocalizationInformation(m_worldStorageServer, uuids, modes, capabilities);
@@ -544,7 +567,7 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
    /// <returns></returns>
    private Guid SelectTrackableWithTypeAndDistance(List<RelocObjects> candidates, List<KeyValuePair<string, Guid>> typesSortedList, UnityEngine.Vector3 cameraTransformPos)
    {
        float bestDistance = 100.0f; //valeur par d�faut  = grande distance ( changer)
        float bestDistance = 10000000.0f; //default value : long distance (to change ?)
        Guid selectedTrackableUUID = Guid.Empty;

        //if only one trackable is candidate
@@ -602,7 +625,6 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
                }
            }
        }

        return selectedTrackableUUID;
    }

+7 −3
Original line number Diff line number Diff line
@@ -30,9 +30,13 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda
    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_anchorManager.anchorsChanged += OnTrackedCloudAnchorChanged;
    }

+185 −0
Original line number Diff line number Diff line
#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
 No newline at end of file