Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arf/world-analysis-api-helpers/unity-world-analysis-arfoundation-wrapper-package
1 result
Show changes
Commits on Source (9)
Showing with 629 additions and 26 deletions
...@@ -49,5 +49,46 @@ The name of the Trackable in the World Storage must correspond to the name of th ...@@ -49,5 +49,46 @@ The name of the Trackable in the World Storage must correspond to the name of th
More information about ARFoundation Mesh Tracking can be found here: https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/manual/features/object-tracking.html More information about ARFoundation Mesh Tracking can be found here: https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/manual/features/object-tracking.html
### Map Trackables ###
#### iOS: ARWorldMap ####
On iOS/ARKit Map Trackables are supported with ARWorlMap feature. Only one Map Trackable per World Graph is supported.
In ARKit, an ARWorldMap includes ARKit's awareness of the physical space in which the user moves the device that can be serialized into a file to be reloaded later on the same or on another device:
https://developer.apple.com/documentation/arkit/arworldmap
The .map file can be placed in the Unity persistent data path of the application on the user device with the following name: "ARkitWorlMap.map". Alternatively, the variable keyvalueTags of the Trackable can also contain a parameter with the "url" key providing a link to download the map file.
By default, the origin of the Map Trackable is the point (0, 0 ,0). If the Map includes an Anchor, you can use it as the origin by setting the name of the Trackable in the World Storage with the TrackableId of the ARAnchor stored in the map. https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/api/UnityEngine.XR.ARSubsystems.TrackableId.html
Samples for creating and serializing an ARWorldMap with ARFoundation can be found here:
https://github.com/Unity-Technologies/arfoundation-samples/blob/main/Assets/Scripts/Runtime/ARWorldMapController.cs
#### Android ARCore Cloud Anchors ####
On Android/ARcore Map Trackables are supported with Cloud Anchors.
Cloud Anchors are anchors that are hosted on the ARCore API cloud endpoint. This hosting enables users to share experiences in the same app: see https://developers.google.com/ar/develop/cloud-anchors
For using ARCore cloud anchors you need to:
* Add the the arcore unity extensions package as a dependency of the Unity project https://github.com/google-ar/arcore-unity-extensions
* Add to the runtime asmdef of this package a reference to Google.XR.ARCoreExtensions.asmdef
* Add a new Script Define Symbol in the Unity project settings: "ETSIARF_ARCORE_EXTENSIONS"
* Then, follow the instructions detailed here https://developers.google.com/ar/develop/unity-arf/cloud-anchors/developer-guide and here https://developers.google.com/ar/develop/authorization?platform=unity-arf to configure the project and your google cloud account
To use a Map Trackable that corresponds to a Google Cloud Anchor, the name of the Trackable in the World Storage must correspond to the name of Google UUID of the anchor.
### Geo Trackables ###
#### iOS: perspectives ####
On iOS, Geo Trackables are not yet supported with this package.
A first possibility for adding this support is to adapt the Google GeoSpatial code and then run your iOS application with the ARCore AR backend.
A second possibility is to add the support for ARKit GeoTracking: https://developer.apple.com/documentation/arkit/arkit_in_ios/content_anchors/tracking_geographic_locations_in_ar
This feature is not implemented yet due to the small number of places supported (see https://developer.apple.com/documentation/arkit/argeotrackingconfiguration)
#### Android: Google GeoSpatial ####
To come
\ No newline at end of file
...@@ -14,5 +14,5 @@ MonoBehaviour: ...@@ -14,5 +14,5 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
CloudAnchorMode: 1 CloudAnchorMode: 1
SemanticMode: 0 SemanticMode: 0
GeospatialMode: 0 GeospatialMode: 2
StreetscapeGeometryMode: 0 StreetscapeGeometryMode: 0
%YAML 1.1 %YAML 1.1
%TAG !u! tag:unity3d.com,2011: %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 --- !u!1 &6911754431728604848
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
...@@ -33,6 +138,7 @@ Transform: ...@@ -33,6 +138,7 @@ Transform:
- {fileID: 6911754433267149346} - {fileID: 6911754433267149346}
- {fileID: 6911754433204531549} - {fileID: 6911754433204531549}
- {fileID: 5348077246929615354} - {fileID: 5348077246929615354}
- {fileID: 5663275178603938767}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6911754432812775762 --- !u!1 &6911754432812775762
......
...@@ -8,11 +8,11 @@ using System.Collections; ...@@ -8,11 +8,11 @@ using System.Collections;
using System.Linq; using System.Linq;
using ETSI.ARF.OpenAPI.WorldStorage; using ETSI.ARF.OpenAPI.WorldStorage;
using ETSI.ARF.WorldStorage.REST; using ETSI.ARF.WorldStorage.REST;
using ; using Unity.XR.CoreUtils;
//Implementation of the WorldAnalysis interface //Implementation of the WorldAnalysis interface
public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
{ {
/// <summary> /// <summary>
/// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable) /// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable)
/// </summary> /// </summary>
...@@ -51,6 +51,11 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -51,6 +51,11 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
/// </summary> /// </summary>
private bool isChekingAvailabilityRunning = false; private bool isChekingAvailabilityRunning = false;
/// <summary>
/// Anchor manager
/// </summary>
private ARAnchorManager m_anchorManager;
/// <summary> /// <summary>
/// Informations regarding a subscription to a pose /// Informations regarding a subscription to a pose
/// </summary> /// </summary>
...@@ -74,13 +79,18 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -74,13 +79,18 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
m_relocalizationInformations = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldStorage.RelocalizationInformation>(); m_relocalizationInformations = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldStorage.RelocalizationInformation>();
m_computedPoses = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldAnalysis.Pose>(); m_computedPoses = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldAnalysis.Pose>();
m_subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>(); m_subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>();
m_trackableModules = new List<WorldAnalysisARFoundationModule>(); m_trackableModules = new List<WorldAnalysisARFoundationModule>();
WorldAnalysisARFoundationModuleImage imageModule = new WorldAnalysisARFoundationModuleImage(); WorldAnalysisARFoundationModuleImage imageModule = new WorldAnalysisARFoundationModuleImage();
m_trackableModules.Add(imageModule); m_trackableModules.Add(imageModule);
WorldAnalysisARFoundationModuleGeospatial geospatialModule = new WorldAnalysisARFoundationModuleGeospatial();
m_trackableModules.Add(geospatialModule);
#if UNITY_IOS #if UNITY_IOS
WorldAnalysisARFoundationModuleMesh meshModule = new WorldAnalysisARFoundationModuleMesh(); WorldAnalysisARFoundationModuleMesh meshModule = new WorldAnalysisARFoundationModuleMesh();
m_trackableModules.Add(meshModule); m_trackableModules.Add(meshModule);
WorldAnalysisARFoundationModuleARKitWorldMap worldMapModule = new WorldAnalysisARFoundationModuleARKitWorldMap();
m_trackableModules.Add(worldMapModule);
#endif #endif
#if ETSIARF_ARCORE_EXTENSIONS #if ETSIARF_ARCORE_EXTENSIONS
...@@ -159,7 +169,9 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -159,7 +169,9 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
List<RelocObjects> notTrackedCandidates = new List<RelocObjects>(); List<RelocObjects> notTrackedCandidates = new List<RelocObjects>();
//Hierarchy of types //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. //We fill in the confidence level lists.
foreach (KeyValuePair<Guid, RelocObjects> relocObject in temp) foreach (KeyValuePair<Guid, RelocObjects> relocObject in temp)
...@@ -179,9 +191,18 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -179,9 +191,18 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
{ {
notTrackedCandidates.Add(relocObject.Value); 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 //uses the types[] array indexes as key, and UUIDs as values
List<KeyValuePair<string, Guid>> typesSortedList = new List<KeyValuePair<string, Guid>>(); List<KeyValuePair<string, Guid>> typesSortedList = new List<KeyValuePair<string, Guid>>();
foreach (var relocObject in temp) foreach (var relocObject in temp)
...@@ -366,7 +387,10 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -366,7 +387,10 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
List<ETSI.ARF.OpenAPI.WorldStorage.Capability> capabilities = new List<ETSI.ARF.OpenAPI.WorldStorage.Capability>(); List<ETSI.ARF.OpenAPI.WorldStorage.Capability> capabilities = new List<ETSI.ARF.OpenAPI.WorldStorage.Capability>();
foreach (ETSI.ARF.OpenAPI.WorldAnalysis.Capability capability in currentCapabilities) foreach (ETSI.ARF.OpenAPI.WorldAnalysis.Capability capability in currentCapabilities)
{ {
capabilities.Add(WorldAnalysisUnityHelper.ConvertWorldAnalysisCapability(capability)); if(capability != null)
{
capabilities.Add(WorldAnalysisUnityHelper.ConvertWorldAnalysisCapability(capability));
}
} }
/// Collect relocalization information /// Collect relocalization information
...@@ -545,7 +569,7 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -545,7 +569,7 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
/// <returns></returns> /// <returns></returns>
private Guid SelectTrackableWithTypeAndDistance(List<RelocObjects> candidates, List<KeyValuePair<string, Guid>> typesSortedList, UnityEngine.Vector3 cameraTransformPos) 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; Guid selectedTrackableUUID = Guid.Empty;
//if only one trackable is candidate //if only one trackable is candidate
...@@ -603,7 +627,6 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface ...@@ -603,7 +627,6 @@ public class WorldAnalysisARFoundation : MonoBehaviour, WorldAnalysisInterface
} }
} }
} }
return selectedTrackableUUID; return selectedTrackableUUID;
} }
......
...@@ -8,11 +8,6 @@ public class WorldAnalysisARFoundationCoroutineHelper : MonoBehaviour ...@@ -8,11 +8,6 @@ public class WorldAnalysisARFoundationCoroutineHelper : MonoBehaviour
private void Awake() private void Awake()
{ {
// If there is an instance, and it's not me, delete myself.
Debug.Log("HERE SINGLETON AWAKE");
if (Instance != null && Instance != this) if (Instance != null && Instance != this)
{ {
Destroy(this); Destroy(this);
...@@ -25,7 +20,6 @@ public class WorldAnalysisARFoundationCoroutineHelper : MonoBehaviour ...@@ -25,7 +20,6 @@ public class WorldAnalysisARFoundationCoroutineHelper : MonoBehaviour
public void StartACoroutine(IEnumerator coroutine) public void StartACoroutine(IEnumerator coroutine)
{ {
Debug.Log("HERE SINGLETON");
StartCoroutine(coroutine); StartCoroutine(coroutine);
} }
} }
\ No newline at end of file
...@@ -30,17 +30,21 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda ...@@ -30,17 +30,21 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda
public void Initialize() public void Initialize()
{ {
XROrigin origin = UnityEngine.Object.FindAnyObjectByType<XROrigin>(); XROrigin origin = UnityEngine.Object.FindAnyObjectByType<XROrigin>();
m_anchorManager = origin.gameObject.AddComponent<ARAnchorManager>(); m_anchorManager = origin.gameObject.GetComponent<ARAnchorManager>();
GameObject anchorPrefab = (GameObject)Resources.Load("ARFAnchorTrackingPrefab"); if (m_anchorManager == null )
m_anchorManager.anchorPrefab = anchorPrefab; {
m_anchorManager = origin.gameObject.AddComponent<ARAnchorManager>();
GameObject anchorPrefab = (GameObject)Resources.Load("ARFAnchorTrackingPrefab");
m_anchorManager.anchorPrefab = anchorPrefab;
}
m_anchorManager.anchorsChanged += OnTrackedCloudAnchorChanged; m_anchorManager.anchorsChanged += OnTrackedCloudAnchorChanged;
} }
/// <summary> /// <summary>
/// /// Get the pose of the trackable from its uuid
/// </summary> /// </summary>
/// <param name="uuid"></param> /// <param name="uuid">id of the trackable</param>
/// <returns></returns> /// <returns>null or trackableInfo with last updated values</returns>
public TrackableInfo GetPoseTrackable(Guid uuid) public TrackableInfo GetPoseTrackable(Guid uuid)
{ {
if (m_trackedCloudAnchors.ContainsKey(uuid.ToString())) if (m_trackedCloudAnchors.ContainsKey(uuid.ToString()))
...@@ -54,10 +58,10 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda ...@@ -54,10 +58,10 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda
} }
/// <summary> /// <summary>
/// /// Need to be a map
/// </summary> /// </summary>
/// <param name="trackable"></param> /// <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) public bool AddTrackable(ETSI.ARF.OpenAPI.WorldStorage.Trackable trackable)
{ {
/// Here : we don't check if the trackable is allready added, AddImageToLibrary does it /// Here : we don't check if the trackable is allready added, AddImageToLibrary does it
...@@ -130,7 +134,7 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda ...@@ -130,7 +134,7 @@ public class WorldAnalysisARFoundationModuleARCoreAnchor : WorldAnalysisARFounda
info.timeStamp = unchecked((int)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); info.timeStamp = unchecked((int)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
info.position = position; info.position = position;
info.rotation = rotation; 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; m_trackedCloudAnchors[info.name] = info;
} }
} }
......
#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)
{
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 ;
}
}
}
/// <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)
{
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 ;
}
/// <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)
{
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<ARSession>() ;
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<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
fileFormatVersion: 2
guid: 0eb5e51c9f2ae4e8db0c43c245bf7b41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
#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
...@@ -212,7 +212,7 @@ public class WorldAnalysisARFoundationModuleImage : WorldAnalysisARFoundationMod ...@@ -212,7 +212,7 @@ public class WorldAnalysisARFoundationModuleImage : WorldAnalysisARFoundationMod
/// </param name="url"> url to the image to download</param> /// </param name="url"> url to the image to download</param>
/// <param name="fileName">file name</param> /// <param name="fileName">file name</param>
/// <param name="imageWidthInMeters">image width in meters</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); Debug.Log("Download image from url "+ url);
m_allDownloadedImages.Add(url); m_allDownloadedImages.Add(url);
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
"GUID:a9420e37d7990b54abdef6688edbe313", "GUID:a9420e37d7990b54abdef6688edbe313",
"GUID:92703082f92b41ba80f0d6912de66115", "GUID:92703082f92b41ba80f0d6912de66115",
"GUID:dc960734dc080426fa6612f1c5fe95f3", "GUID:dc960734dc080426fa6612f1c5fe95f3",
"GUID:b35d8248e1a04478bb2d70fe76652ddf" "GUID:b35d8248e1a04478bb2d70fe76652ddf",
"GUID:dfd0ce20189d48e5b22bb4134b558f5a"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
......