diff --git a/Editor/BuildResources/ARCoreiOSCloudAnchorDependencies.template b/Editor/BuildResources/ARCoreiOSCloudAnchorDependencies.template index 88c31a8..75b3453 100644 --- a/Editor/BuildResources/ARCoreiOSCloudAnchorDependencies.template +++ b/Editor/BuildResources/ARCoreiOSCloudAnchorDependencies.template @@ -1,6 +1,6 @@ - + diff --git a/Editor/BuildResources/ARCoreiOSDependencies.template b/Editor/BuildResources/ARCoreiOSDependencies.template index a5650e0..fa1d394 100644 --- a/Editor/BuildResources/ARCoreiOSDependencies.template +++ b/Editor/BuildResources/ARCoreiOSDependencies.template @@ -1,6 +1,6 @@ - + diff --git a/Editor/BuildResources/ARCoreiOSGeospatialDependencies.template b/Editor/BuildResources/ARCoreiOSGeospatialDependencies.template index c897a00..1805bbf 100644 --- a/Editor/BuildResources/ARCoreiOSGeospatialDependencies.template +++ b/Editor/BuildResources/ARCoreiOSGeospatialDependencies.template @@ -1,6 +1,6 @@ - + diff --git a/Editor/BuildResources/ARCoreiOSSemanticsDependencies.template b/Editor/BuildResources/ARCoreiOSSemanticsDependencies.template index 2377ba8..7f4a0f7 100644 --- a/Editor/BuildResources/ARCoreiOSSemanticsDependencies.template +++ b/Editor/BuildResources/ARCoreiOSSemanticsDependencies.template @@ -1,6 +1,6 @@ - + diff --git a/Editor/GeospatialCreator/Scripts/GeospatialCreatorCesiumAdapter.cs b/Editor/GeospatialCreator/Scripts/GeospatialCreatorCesiumAdapter.cs index 75ecbfc..9b45f7b 100644 --- a/Editor/GeospatialCreator/Scripts/GeospatialCreatorCesiumAdapter.cs +++ b/Editor/GeospatialCreator/Scripts/GeospatialCreatorCesiumAdapter.cs @@ -144,6 +144,122 @@ internal static bool SetMapTileApiKeyForCesium3DTileset( EditorUtility.SetDirty(tileset); return true; } + + internal class Origin3DTilesetCesiumAdapter : Origin3DTilesetAdapter + { + // The parent ARGeospatialCreatorOrigin object that owns this instance of the Adapter + private readonly ARGeospatialCreatorOrigin _origin; + + internal Origin3DTilesetCesiumAdapter(ARGeospatialCreatorOrigin origin) + { + _origin = origin; + } + + /// Gets the parent ARGeospatialCreatorOrigin object. + public override ARGeospatialCreatorOrigin Origin + { + get + { + return _origin; + } + } + + /// Sets the Cesium tileset's "Create Physics Meshes" setting. + /// Specifies whether to enable the setting or not. + public override void SetPhysicsMeshesEnabled(bool enable) + { + Cesium3DTileset tileset = GetTilesetComponent(Origin); + if (tileset == null) + { + Debug.LogError( + "No Cesium3DTileset component associated with Origin " + Origin.name); + return; + } + + if (tileset.createPhysicsMeshes != enable) + { + tileset.createPhysicsMeshes = enable; + } + } + + /// Gets the Cesium tileset's "Create Physics Meshes" setting. + public override bool GetPhysicsMeshesEnabled() + { + Cesium3DTileset tileset = GetTilesetComponent(Origin); + if (tileset == null) + { + Debug.LogError( + "No Cesium3DTileset component associated with Origin " + Origin.name); + return false; + } + + return tileset.createPhysicsMeshes; + } + + /// Retrieves percentage of tiles loaded for current view. + /// Percentage (0.0 to 100.0) of tiles loaded for current view. + public override float ComputeTilesetLoadProgress() + { + Cesium3DTileset tileset = GetTilesetComponent(Origin); + if (tileset == null) + { + Debug.LogError( + "No Cesium3DTileset component associated with Origin " + Origin.name); + return 0; + } + return tileset.ComputeLoadProgress(); + } + + /// Returns true if the collider belongs to a tile in this tileset. + /// The collider to be checked. + public override bool IsTileCollider(Collider collider) + { + if ((collider != null) && + (collider.gameObject.GetComponent() != null)) + { + return true; + } + return false; + } + } + + internal class OriginComponentCesiumAdapter : IOriginComponentAdapter + { + // The parent ARGeospatialCreatorOrigin object that owns this instance of the Adapter + private readonly ARGeospatialCreatorOrigin _origin; + + public OriginComponentCesiumAdapter(ARGeospatialCreatorOrigin origin) + { + _origin = origin; + } + + public GeoCoordinate GetOriginFromComponent() + { + CesiumForUnity.CesiumGeoreference geoRef = GetCesiumGeoreference(); + return (geoRef == null) ? null : + new GeoCoordinate(geoRef.latitude, geoRef.longitude, geoRef.height); + } + + public void SetComponentOrigin(GeoCoordinate newOrigin) + { + CesiumForUnity.CesiumGeoreference geoRef = GetCesiumGeoreference(); + if (geoRef == null) + { + Debug.LogWarning("Origin location updated for " + _origin.gameObject.name + + ", but there is no Cesium Georeference subcomponent."); + return; + } + + geoRef.latitude = newOrigin.Latitude; + geoRef.longitude = newOrigin.Longitude; + geoRef.height = newOrigin.Altitude; + } + + private CesiumForUnity.CesiumGeoreference GetCesiumGeoreference() + { + return _origin.gameObject.GetComponent(); + } + } } } #endif // ARCORE_INTERNAL_USE_CESIUM diff --git a/Editor/GeospatialCreator/Scripts/Internal/ARGeospatialCreatorAnchorEditor.cs b/Editor/GeospatialCreator/Scripts/Internal/ARGeospatialCreatorAnchorEditor.cs index dfc0f78..46c715d 100644 --- a/Editor/GeospatialCreator/Scripts/Internal/ARGeospatialCreatorAnchorEditor.cs +++ b/Editor/GeospatialCreator/Scripts/Internal/ARGeospatialCreatorAnchorEditor.cs @@ -89,6 +89,52 @@ public override void OnInspectorGUI() } } + private void GUIForSnapToTile() + { + string snapToTileTooltip = "Visually position the anchor within the editor so it " + + "aligns with the top of the tile. This override in the editor does not impact " + + "the position at runtime."; + + if (GUILayout.Button(new GUIContent("Snap to Tile", snapToTileTooltip))) + { + ARGeospatialCreatorOrigin origin = + _origin.objectReferenceValue as ARGeospatialCreatorOrigin; + if (origin == null) + { + Debug.LogError("An ARGeospatialCreatorOrigin object must be present in the " + + "scene and assigned to the anchor to use the Snap to Tile feature."); + return; + } + + var tileset = origin._origin3DTilesetAdapter; + if (tileset.GetPhysicsMeshesEnabled() == false) + { +#if ARCORE_INTERNAL_USE_CESIUM + Debug.LogError("Physics Meshes must be enabled on the tileset to use " + + "the Snap to Tile feature. Please enable the \"Create Physics Meshes\" " + + "setting in the Cesium3DTileset component owned by the Origin.", + GeospatialCreatorCesiumAdapter.GetTilesetComponent(origin)); +#else + Debug.LogError("Physics Meshes must be enabled on the tileset to use " + + "the Snap to Tile feature."); +#endif + return; + } + + (bool success, double tileAltitude) = + tileset.CalcTileAltitudeWGS84(_latitude.doubleValue, _longitude.doubleValue); + if (success) + { + _editorAltitudeOverride.doubleValue = tileAltitude; + } + else + { + Debug.LogWarning("Could not Snap to Tile at Latitude: " + + _latitude.doubleValue + " and Longitude: " + _longitude.doubleValue); + } + } + } + private void GUIForAltitude(AnchorAltitudeType altitudeType) { using (new EditorGUI.IndentLevelScope()) @@ -112,14 +158,16 @@ private void GUIForAltitude(AnchorAltitudeType altitudeType) GUILayout.BeginHorizontal(); + string overrideAltitudeLabel = "Override altitude in Editor Scene View"; _useEditorAltitudeOverride.boolValue = EditorGUILayout.Toggle( - "Override altitude in Editor Scene View", + overrideAltitudeLabel, _useEditorAltitudeOverride.boolValue); // Allow the override value to be edited only if the flag is enabled. EditorGUI.BeginDisabledGroup(!_useEditorAltitudeOverride.boolValue); _editorAltitudeOverride.doubleValue = EditorGUILayout.DoubleField( _editorAltitudeOverride.doubleValue); + GUIForSnapToTile(); EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); @@ -127,7 +175,7 @@ private void GUIForAltitude(AnchorAltitudeType altitudeType) if (_useEditorAltitudeOverride.boolValue) { EditorGUILayout.HelpBox( - "The Editor-only altitude override sets the altitude used in the Scene " + + "\"" + overrideAltitudeLabel + "\" sets the altitude used in the Scene " + "View to position the anchor, in meters according to WGS84. This is " + "useful to vizualize the anchor relative to the scene geometry in cases " + "where the scene geometry altitude is not fully aligned with the real " + @@ -136,7 +184,6 @@ private void GUIForAltitude(AnchorAltitudeType altitudeType) MessageType.Info, wide: true); } - } } diff --git a/Editor/GeospatialCreator/Scripts/Internal/GeospatialAnchorUpdater.cs b/Editor/GeospatialCreator/Scripts/Internal/GeospatialAnchorUpdater.cs index ae02ff7..692da2e 100644 --- a/Editor/GeospatialCreator/Scripts/Internal/GeospatialAnchorUpdater.cs +++ b/Editor/GeospatialCreator/Scripts/Internal/GeospatialAnchorUpdater.cs @@ -23,6 +23,7 @@ namespace Google.XR.ARCoreExtensions.GeospatialCreator.Editor.Internal { using System; + using Google.XR.ARCoreExtensions.Editor.Internal; using Google.XR.ARCoreExtensions.GeospatialCreator; using Google.XR.ARCoreExtensions.GeospatialCreator.Internal; #if ARCORE_INTERNAL_USE_UNITY_MATH @@ -62,6 +63,8 @@ static GeospatialAnchorUpdater() var tracker = new GeospatialObjectTracker(actionFactory); tracker.StartTracking(); + GeospatialCreatorHelper.CountGeospatialCreatorAnchorsDelegate = + tracker.GetTrackedObjectsCount; } public GeospatialAnchorUpdater(ARGeospatialCreatorAnchor anchor) @@ -139,6 +142,7 @@ private bool HasGeodeticPositionChanged() { return true; } + return false; } @@ -211,7 +215,6 @@ private void UpdateUnityPosition() // EditorAltitudeOverride value, if it is used. double alt = _anchor.UseEditorAltitudeOverride ? _anchor.EditorAltitudeOverride : _anchor.Altitude; - GeoCoordinate coor = new GeoCoordinate( _anchor.Latitude, _anchor.Longitude, diff --git a/Editor/GeospatialCreator/Scripts/Internal/GeospatialObjectTracker.cs b/Editor/GeospatialCreator/Scripts/Internal/GeospatialObjectTracker.cs index a5fe57e..f7367a6 100644 --- a/Editor/GeospatialCreator/Scripts/Internal/GeospatialObjectTracker.cs +++ b/Editor/GeospatialCreator/Scripts/Internal/GeospatialObjectTracker.cs @@ -92,6 +92,11 @@ public void StopTracking() _trackedObjects.Clear(); } + public uint GetTrackedObjectsCount() + { + return Convert.ToUInt32(_trackedObjects.Count); + } + /// /// Handles EditorApplication.Update() events to check if GameObjects of type T have been /// added to or removed from the scene. For new objects, a a new update Action is created diff --git a/Editor/GeospatialCreator/Scripts/Internal/GeospatialOriginUpdater.cs b/Editor/GeospatialCreator/Scripts/Internal/GeospatialOriginUpdater.cs index 3140d41..260c7a3 100644 --- a/Editor/GeospatialCreator/Scripts/Internal/GeospatialOriginUpdater.cs +++ b/Editor/GeospatialCreator/Scripts/Internal/GeospatialOriginUpdater.cs @@ -56,8 +56,11 @@ public GeospatialOriginUpdater(ARGeospatialCreatorOrigin origin) { _origin = origin; #if ARCORE_INTERNAL_USE_CESIUM - _origin._originComponentAdapter = new CesiumOriginAdapter(origin); + _origin._originComponentAdapter = + new GeospatialCreatorCesiumAdapter.OriginComponentCesiumAdapter(origin); _origin.UpdateOriginFromComponent(); + _origin._origin3DTilesetAdapter = + new GeospatialCreatorCesiumAdapter.Origin3DTilesetCesiumAdapter(origin); #endif } @@ -66,44 +69,6 @@ private void EditorUpdate() _origin.UpdateOriginFromComponent(); } -#if ARCORE_INTERNAL_USE_CESIUM - private class CesiumOriginAdapter : IOriginComponentAdapter - { - private readonly ARGeospatialCreatorOrigin _origin; - - public CesiumOriginAdapter(ARGeospatialCreatorOrigin origin) - { - _origin = origin; - } - - public GeoCoordinate GetOriginFromComponent() - { - CesiumForUnity.CesiumGeoreference geoRef = GetCesiumGeoreference(); - return (geoRef == null) ? null : - new GeoCoordinate(geoRef.latitude, geoRef.longitude, geoRef.height); - } - - public void SetComponentOrigin(GeoCoordinate newOrigin) - { - CesiumForUnity.CesiumGeoreference geoRef = GetCesiumGeoreference(); - if (geoRef == null) - { - Debug.LogWarning("Origin location updated for " + _origin.gameObject.name + - ", but there is no Cesium Georeference subcomponent."); - return; - } - - geoRef.latitude = newOrigin.Latitude; - geoRef.longitude = newOrigin.Longitude; - geoRef.height = newOrigin.Altitude; - } - - private CesiumForUnity.CesiumGeoreference GetCesiumGeoreference() - { - return _origin.gameObject.GetComponent(); - } - } -#endif //ARCORE_INTERNAL_USE_CESIUM } } #endif // ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED diff --git a/Editor/GeospatialCreator/Scripts/Internal/PlaceSearchHelper.cs b/Editor/GeospatialCreator/Scripts/Internal/PlaceSearchHelper.cs index 675c4bc..d719d8b 100644 --- a/Editor/GeospatialCreator/Scripts/Internal/PlaceSearchHelper.cs +++ b/Editor/GeospatialCreator/Scripts/Internal/PlaceSearchHelper.cs @@ -22,6 +22,7 @@ namespace Google.XR.ARCoreExtensions.GeospatialCreator.Editor.Internal using System; using System.Diagnostics.CodeAnalysis; using System.Text; + using System.Threading.Tasks; using Google.XR.ARCoreExtensions.GeospatialCreator.Internal; #if ARCORE_INTERNAL_USE_UNITY_MATH using Unity.Mathematics; @@ -35,6 +36,10 @@ internal class PlaceSearchHelper private const string _searchFieldDefaultText = "Type the name of a place and press \"Enter\""; + // Desired relative height (meters) from SceneView camera to target lat/lng's highest point + // (ie. Rooftop, or Terrain if no building) + private const double _sceneViewHeightAboveTile = 200; + private static readonly string[] _waitAnimation = new string[] { "/", "-", "\\", "|" }; private int _selectedPlaceIndex = 0; @@ -115,8 +120,7 @@ public void GUIForSearch(UnityEngine.Object targetObject, string targetTypeName, PlaceSearchResponse.Location loc = _response.GetLocation(_selectedPlaceIndex); if (loc != null) { - SetSceneViewPreview(loc.lat, loc.lng, _request.CameraAltitude, - _request.CameraAltitude - 3500.0f, animate: true, setPreviewPinFunc); + SetSceneViewPreview(loc.lat, loc.lng, animate: true, setPreviewPinFunc); } } @@ -136,8 +140,7 @@ public void GUIForSearch(UnityEngine.Object targetObject, string targetTypeName, PlaceSearchResponse.Location loc = _response.GetLocation(_selectedPlaceIndex); if (loc != null) { - SetSceneViewPreview(loc.lat, loc.lng, _request.CameraAltitude, - _request.CameraAltitude - 3500.0f, animate: true, setPreviewPinFunc); + SetSceneViewPreview(loc.lat, loc.lng, animate: true, setPreviewPinFunc); } } @@ -150,8 +153,7 @@ public void GUIForSearch(UnityEngine.Object targetObject, string targetTypeName, if (loc != null) { setLatLongAltFunc(loc.lat, loc.lng, 0.0); - SetSceneViewPreview(loc.lat, loc.lng, _request.CameraAltitude, - _request.CameraAltitude - 3500.0f, animate: false, setPreviewPinFunc); + SetSceneViewPreview(loc.lat, loc.lng, animate: false, setPreviewPinFunc); // remove the preview pin since the object is there now setPreviewPinFunc(null); @@ -162,6 +164,60 @@ public void GUIForSearch(UnityEngine.Object targetObject, string targetTypeName, EditorGUILayout.EndVertical(); } + // Move the SceneView camera to a given lat/lng/alt and have it face the tiles. + internal static void MoveSceneViewCamera( + double lat, + double lng, + double cameraAltitude, + bool animate) + { + ARGeospatialCreatorOrigin origin = ARGeospatialCreatorOrigin.FindDefaultOrigin(); + GeoCoordinate originPoint = origin?._originPoint; + Vector3 originUnityCoord = + origin ? origin.gameObject.transform.position : Vector3.zero; + if (originPoint == null) + { + return; + } + + // Make a high point where the camera will be + GeoCoordinate cameraGeoCoord = new GeoCoordinate(lat, lng, cameraAltitude); + Vector3 cameraViewPosition = GeoMath.GeoCoordinateToUnityWorld( + cameraGeoCoord, originPoint, originUnityCoord); + + // Make a low point at a low altitude where the camera will pivot around + // This altitude should be lower than any possible terrain since the camera + // will not be able to easily zoom down to terrain below the pivot point + GeoCoordinate targetGeoCoord = + new GeoCoordinate(lat, lng, GeoMath.LowestElevationOnEarth); + Vector3 targetTerrainPosition = GeoMath.GeoCoordinateToUnityWorld( + targetGeoCoord, originPoint, originUnityCoord); + + // Get the direction vector + Vector3 viewDirection = Vector3.Normalize(targetTerrainPosition - cameraViewPosition); + + SceneView sceneView = SceneView.lastActiveSceneView; + + // Compute the size parameter based on the desired distance from the camera. + float desiredDistanceCameraToTarget = + Vector3.Distance(cameraViewPosition, targetTerrainPosition); + float size = desiredDistanceCameraToTarget * + Mathf.Sin(sceneView.camera.fieldOfView * 0.5f * Mathf.Deg2Rad); + + if (animate) + { + sceneView.LookAt(targetTerrainPosition, + Quaternion.LookRotation(viewDirection), size); + } + else + { + sceneView.LookAtDirect(targetTerrainPosition, + Quaternion.LookRotation(viewDirection), size); + } + + sceneView.Repaint(); + } + private void StartNewRequest(string searchString, GeoCoordinate originPoint, string apiKey) { // cancel any in-progress request @@ -184,63 +240,48 @@ private void StartNewRequest(string searchString, GeoCoordinate originPoint, str _selectedPlaceIndex = 0; } - private void SetSceneViewPreview(double lat, double lng, double cameraAlt, - double objectAlt, bool animate, SetPreviewPinLocationDelegate setPreviewPinFunc) + private void SetSceneViewPreview( + double lat, double lng, bool animate, SetPreviewPinLocationDelegate setPreviewPinFunc) { - // hack the alt so it is above the point we place other things - Vector3 originUnityCoords = Vector3.zero; #if ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED ARGeospatialCreatorOrigin origin = ARGeospatialCreatorOrigin.FindDefaultOrigin(); - GeoCoordinate originPoint = origin?._originPoint; - originUnityCoords = origin ? origin.gameObject.transform.position: Vector3.zero; -#else // ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED - // just quick out if we creator is not enabled so can't get lat lng. - GeoCoordinate originPoint = null; -#endif // ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED - - if (originPoint == null) + if (origin == null) { - // An error message was already printed (if needed) in FindDefaultOrigin() return; } - // Make a high point where the camera will be - GeoCoordinate cameraCoord = new GeoCoordinate(lat, lng, cameraAlt); - Vector3 EUNHigh = GeoMath.GeoCoordinateToUnityWorld( - cameraCoord, - originPoint, - originUnityCoords); - - // Make a low point where the object is - GeoCoordinate objectCoord = new GeoCoordinate(lat, lng, objectAlt); - Vector3 EUNLow = GeoMath.GeoCoordinateToUnityWorld( - objectCoord, - originPoint, - originUnityCoords); - - // make a vector from that points from high to low. - // So the vector that points down to earth - Vector3 earthDownVec = Vector3.Normalize(EUNLow - EUNHigh); - if (animate) + MoveSceneViewCameraAboveTerrainAsync( + origin._origin3DTilesetAdapter, lat, lng, animate); +#endif // ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED + + // The altitude value can be zero, because we're using the location for a 2D icon that + // will be drawn over the scene view. + setPreviewPinFunc(new GeoCoordinate(lat, lng, 0.0)); + } + + // Calculates the terrain altitude at the given lat/lng and moves the SceneView camera + // at a reasonable height above it. + private async void MoveSceneViewCameraAboveTerrainAsync( + Origin3DTilesetAdapter tileset, double lat, double lng, bool animate) + { + // Move the camera high above the target location to begin loading the tiles + MoveSceneViewCamera(lat, lng, GeoMath.HighestElevationOnEarth, animate); + + var (success, terrainAltitude) = await tileset.CalcTileAltitudeWGS84Async(lat, lng); + double cameraAltitude; + if (success) { - // position the camera at EUNHigh - // Rotate from forward to earth down - SceneView.lastActiveSceneView.LookAt(EUNLow, - Quaternion.FromToRotation(Vector3.forward, earthDownVec)); - SceneView.lastActiveSceneView.pivot = EUNHigh; + cameraAltitude = terrainAltitude + _sceneViewHeightAboveTile; } else { - // position the camera at EUNHigh - // Rotate from forward to earth down - SceneView.lastActiveSceneView.LookAtDirect(EUNLow, - Quaternion.FromToRotation(Vector3.forward, earthDownVec)); - SceneView.lastActiveSceneView.pivot = EUNHigh; + // If terrain sampling failed then set the camera to an altitude above all terrain. + Debug.Log("Could not get Terrain height. Setting camera at an altitude of " + + GeoMath.HighestElevationOnEarth + " meters."); + return; } - // The altitude value can be zero, because we're using the location for a 2D icon that - // will be drawn over the scene view. - setPreviewPinFunc(new GeoCoordinate(lat, lng, 0.0)); + MoveSceneViewCamera(lat, lng, cameraAltitude, animate); } } @@ -250,7 +291,6 @@ internal class PlaceSearchRequest public bool LocationBias = false; public double Latitude; public double Longitude; - public float CameraAltitude = 3500.0f; public int Radius = 50000; public string NextPageToken = string.Empty; public string SearchText = string.Empty; diff --git a/Editor/Scripts/Internal/ARCoreAnalytics.cs b/Editor/Scripts/Internal/ARCoreAnalytics.cs index 8427378..7c80850 100644 --- a/Editor/Scripts/Internal/ARCoreAnalytics.cs +++ b/Editor/Scripts/Internal/ARCoreAnalytics.cs @@ -43,7 +43,7 @@ public class ARCoreAnalytics private UnityWebRequest _webRequest; /// - /// Static constructor permits a once-on-load analytics collection event. + /// Static constructor permits a once-after-editor-load analytics collection event. /// static ARCoreAnalytics() { @@ -51,8 +51,10 @@ static ARCoreAnalytics() Instance = new ARCoreAnalytics(); Instance.Load(); - // Send analytics immediately. - Instance.SendAnalytics(_googleAnalyticsHost, LogRequestUtils.BuildLogRequest()); + // Schedule the first analytics data to be sent after Editor Inspectors have updated. + // Otherwise, we might query the scene for analytics data before it is available. + Instance._lastUpdateTicks = DateTime.Now.Ticks; + EditorApplication.delayCall += OnEditorInspectorsUpdatedOnce; // Use the Editor Update callback to monitor the communication to the server. EditorApplication.update += @@ -140,5 +142,14 @@ public void OnAnalyticsUpdate() _googleAnalyticsHost, LogRequestUtils.BuildLogRequest()); } } + + /// + /// Callback function to send analytics data after Editor Inspectors are ready. + /// + private static void OnEditorInspectorsUpdatedOnce() + { + Instance.SendAnalytics( + _googleAnalyticsHost, LogRequestUtils.BuildLogRequest()); + } } } diff --git a/Editor/Scripts/Internal/Analytics/ArcoreSdkLog.cs b/Editor/Scripts/Internal/Analytics/ArcoreSdkLog.cs index 42da364..e242ac8 100644 --- a/Editor/Scripts/Internal/Analytics/ArcoreSdkLog.cs +++ b/Editor/Scripts/Internal/Analytics/ArcoreSdkLog.cs @@ -1,7 +1,7 @@ //----------------------------------------------------------------------- // // -// Copyright 2019 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -44,24 +44,29 @@ public static partial class ArcoreSdkLogReflection { static ArcoreSdkLogReflection() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( - "ChRhcmNvcmVfc2RrX2xvZy5wcm90bxIRY29tLmdvb2dsZS5hcmNvcmUi/QMK", + "ChRhcmNvcmVfc2RrX2xvZy5wcm90bxIRY29tLmdvb2dsZS5hcmNvcmUinQUK", "DEFyQ29yZVNka0xvZxIXCg9zZGtfaW5zdGFuY2VfaWQYASABKAkSFgoOc2Rr", "X3Nlc3Npb25faWQYBSABKAkSGgoSYXJjb3JlX3Nka192ZXJzaW9uGAIgASgJ", "EjkKCHNka190eXBlGAYgASgOMicuY29tLmdvb2dsZS5hcmNvcmUuQXJDb3Jl", "U2RrTG9nLlNES1R5cGUSEgoKb3NfdmVyc2lvbhgDIAEoCRI8CgV1bml0eRgE", "IAEoCzIrLmNvbS5nb29nbGUuYXJjb3JlLkFyQ29yZVNka0xvZy5Vbml0eUVu", - "Z2luZUgAGrYBCgtVbml0eUVuZ2luZRIPCgd2ZXJzaW9uGAEgASgJEk0KDGVk", - "aXRpb25fdHlwZRgCIAEoDjI3LmNvbS5nb29nbGUuYXJjb3JlLkFyQ29yZVNk", - "a0xvZy5Vbml0eUVuZ2luZS5FZGl0aW9uVHlwZSJHCgtFZGl0aW9uVHlwZRIY", - "ChRVTktOT1dOX0VESVRJT05fVFlQRRAAEgwKCFBFUlNPTkFMEAESEAoMUFJP", - "RkVTU0lPTkFMEAIiUAoHU0RLVHlwZRIUChBVTktOT1dOX1NES19UWVBFEAAS", - "DgoKQVJDT1JFX1NESxABEh8KG0FSRk9VTkRBVElPTl9FWFRFTlNJT05TX1NE", - "SxACQggKBmVuZ2luZUIzqgIwR29vZ2xlLlhSLkFSQ29yZUV4dGVuc2lvbnMu", - "RWRpdG9yLkludGVybmFsLlByb3RvYgZwcm90bzM=")); + "Z2luZUgAEk0KEmdlb3NwYXRpYWxfY3JlYXRvchgHIAEoCzIxLmNvbS5nb29n", + "bGUuYXJjb3JlLkFyQ29yZVNka0xvZy5HZW9zcGF0aWFsQ3JlYXRvchq2AQoL", + "VW5pdHlFbmdpbmUSDwoHdmVyc2lvbhgBIAEoCRJNCgxlZGl0aW9uX3R5cGUY", + "AiABKA4yNy5jb20uZ29vZ2xlLmFyY29yZS5BckNvcmVTZGtMb2cuVW5pdHlF", + "bmdpbmUuRWRpdGlvblR5cGUiRwoLRWRpdGlvblR5cGUSGAoUVU5LTk9XTl9F", + "RElUSU9OX1RZUEUQABIMCghQRVJTT05BTBABEhAKDFBST0ZFU1NJT05BTBAC", + "Gk8KEUdlb3NwYXRpYWxDcmVhdG9yEiUKHWlzX2dlb3NwYXRpYWxfY3JlYXRv", + "cl9lbmFibGVkGAEgASgIEhMKC251bV9hbmNob3JzGAIgASgNIlAKB1NES1R5", + "cGUSFAoQVU5LTk9XTl9TREtfVFlQRRAAEg4KCkFSQ09SRV9TREsQARIfChtB", + "UkZPVU5EQVRJT05fRVhURU5TSU9OU19TREsQAkIICgZlbmdpbmVCM6oCMEdv", + "b2dsZS5YUi5BUkNvcmVFeHRlbnNpb25zLkVkaXRvci5JbnRlcm5hbC5Qcm90", + "b2IGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog), global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Parser, new[]{ "SdkInstanceId", "SdkSessionId", "ArcoreSdkVersion", "SdkType", "OsVersion", "Unity" }, new[]{ "Engine" }, new[]{ typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.SDKType) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine), global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine.Parser, new[]{ "Version", "EditionType" }, null, new[]{ typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine.Types.EditionType) }, null)}) + new pbr::GeneratedClrTypeInfo(typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog), global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Parser, new[]{ "SdkInstanceId", "SdkSessionId", "ArcoreSdkVersion", "SdkType", "OsVersion", "Unity", "GeospatialCreator" }, new[]{ "Engine" }, new[]{ typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.SDKType) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine), global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine.Parser, new[]{ "Version", "EditionType" }, null, new[]{ typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine.Types.EditionType) }, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.GeospatialCreator), global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.GeospatialCreator.Parser, new[]{ "IsGeospatialCreatorEnabled", "NumAnchors" }, null, null, null)}) })); } #endregion @@ -72,7 +77,7 @@ static ArcoreSdkLogReflection() { /// Message with log extension for ARCore SDK within development engines, like /// Unity and Unreal. /// - /// Next ID: 7 + /// Next ID: 8 /// public sealed partial class ArCoreSdkLog : pb::IMessage { private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ArCoreSdkLog()); @@ -104,6 +109,7 @@ public ArCoreSdkLog(ArCoreSdkLog other) : this() { arcoreSdkVersion_ = other.arcoreSdkVersion_; sdkType_ = other.sdkType_; osVersion_ = other.osVersion_; + geospatialCreator_ = other.geospatialCreator_ != null ? other.geospatialCreator_.Clone() : null; switch (other.EngineCase) { case EngineOneofCase.Unity: Unity = other.Unity.Clone(); @@ -205,6 +211,20 @@ public string OsVersion { } } + /// Field number for the "geospatial_creator" field. + public const int GeospatialCreatorFieldNumber = 7; + private global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.GeospatialCreator geospatialCreator_; + /// + /// Data logged about the Geospatial Creator plugin usage in the project. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.GeospatialCreator GeospatialCreator { + get { return geospatialCreator_; } + set { + geospatialCreator_ = value; + } + } + private object engine_; /// Enum of possible cases for the "engine" oneof. public enum EngineOneofCase { @@ -242,6 +262,7 @@ public bool Equals(ArCoreSdkLog other) { if (SdkType != other.SdkType) return false; if (OsVersion != other.OsVersion) return false; if (!object.Equals(Unity, other.Unity)) return false; + if (!object.Equals(GeospatialCreator, other.GeospatialCreator)) return false; if (EngineCase != other.EngineCase) return false; return Equals(_unknownFields, other._unknownFields); } @@ -255,6 +276,7 @@ public override int GetHashCode() { if (SdkType != 0) hash ^= SdkType.GetHashCode(); if (OsVersion.Length != 0) hash ^= OsVersion.GetHashCode(); if (engineCase_ == EngineOneofCase.Unity) hash ^= Unity.GetHashCode(); + if (geospatialCreator_ != null) hash ^= GeospatialCreator.GetHashCode(); hash ^= (int) engineCase_; if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); @@ -293,6 +315,10 @@ public void WriteTo(pb::CodedOutputStream output) { output.WriteRawTag(48); output.WriteEnum((int) SdkType); } + if (geospatialCreator_ != null) { + output.WriteRawTag(58); + output.WriteMessage(GeospatialCreator); + } if (_unknownFields != null) { _unknownFields.WriteTo(output); } @@ -319,6 +345,9 @@ public int CalculateSize() { if (engineCase_ == EngineOneofCase.Unity) { size += 1 + pb::CodedOutputStream.ComputeMessageSize(Unity); } + if (geospatialCreator_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(GeospatialCreator); + } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } @@ -345,6 +374,12 @@ public void MergeFrom(ArCoreSdkLog other) { if (other.OsVersion.Length != 0) { OsVersion = other.OsVersion; } + if (other.geospatialCreator_ != null) { + if (geospatialCreator_ == null) { + GeospatialCreator = new global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.GeospatialCreator(); + } + GeospatialCreator.MergeFrom(other.GeospatialCreator); + } switch (other.EngineCase) { case EngineOneofCase.Unity: if (Unity == null) { @@ -391,7 +426,14 @@ public void MergeFrom(pb::CodedInputStream input) { break; } case 48: { - sdkType_ = (global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.SDKType) input.ReadEnum(); + SdkType = (global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.SDKType) input.ReadEnum(); + break; + } + case 58: { + if (geospatialCreator_ == null) { + GeospatialCreator = new global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.GeospatialCreator(); + } + input.ReadMessage(GeospatialCreator); break; } } @@ -572,7 +614,7 @@ public void MergeFrom(pb::CodedInputStream input) { break; } case 16: { - editionType_ = (global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine.Types.EditionType) input.ReadEnum(); + EditionType = (global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Types.UnityEngine.Types.EditionType) input.ReadEnum(); break; } } @@ -598,6 +640,174 @@ public enum EditionType { } + /// + /// Message with data logged about Geospatial Creator usage. + /// + /// Next ID: 3 + /// + public sealed partial class GeospatialCreator : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new GeospatialCreator()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Google.XR.ARCoreExtensions.Editor.Internal.Proto.ArCoreSdkLog.Descriptor.NestedTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GeospatialCreator() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GeospatialCreator(GeospatialCreator other) : this() { + isGeospatialCreatorEnabled_ = other.isGeospatialCreatorEnabled_; + numAnchors_ = other.numAnchors_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GeospatialCreator Clone() { + return new GeospatialCreator(this); + } + + /// Field number for the "is_geospatial_creator_enabled" field. + public const int IsGeospatialCreatorEnabledFieldNumber = 1; + private bool isGeospatialCreatorEnabled_; + /// + /// Whether Geospatial Creator is enabled in the project settings. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool IsGeospatialCreatorEnabled { + get { return isGeospatialCreatorEnabled_; } + set { + isGeospatialCreatorEnabled_ = value; + } + } + + /// Field number for the "num_anchors" field. + public const int NumAnchorsFieldNumber = 2; + private uint numAnchors_; + /// + /// The number of Geosptial Creator Anchors in the project's active scene. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public uint NumAnchors { + get { return numAnchors_; } + set { + numAnchors_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as GeospatialCreator); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(GeospatialCreator other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (IsGeospatialCreatorEnabled != other.IsGeospatialCreatorEnabled) return false; + if (NumAnchors != other.NumAnchors) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (IsGeospatialCreatorEnabled != false) hash ^= IsGeospatialCreatorEnabled.GetHashCode(); + if (NumAnchors != 0) hash ^= NumAnchors.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (IsGeospatialCreatorEnabled != false) { + output.WriteRawTag(8); + output.WriteBool(IsGeospatialCreatorEnabled); + } + if (NumAnchors != 0) { + output.WriteRawTag(16); + output.WriteUInt32(NumAnchors); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (IsGeospatialCreatorEnabled != false) { + size += 1 + 1; + } + if (NumAnchors != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(NumAnchors); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(GeospatialCreator other) { + if (other == null) { + return; + } + if (other.IsGeospatialCreatorEnabled != false) { + IsGeospatialCreatorEnabled = other.IsGeospatialCreatorEnabled; + } + if (other.NumAnchors != 0) { + NumAnchors = other.NumAnchors; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + IsGeospatialCreatorEnabled = input.ReadBool(); + break; + } + case 16: { + NumAnchors = input.ReadUInt32(); + break; + } + } + } + } + + } + } #endregion diff --git a/Editor/Scripts/Internal/GeospatialCreatorHelper.cs b/Editor/Scripts/Internal/GeospatialCreatorHelper.cs index a298b83..b072560 100644 --- a/Editor/Scripts/Internal/GeospatialCreatorHelper.cs +++ b/Editor/Scripts/Internal/GeospatialCreatorHelper.cs @@ -48,6 +48,10 @@ public static class GeospatialCreatorHelper public static readonly string CreatorEnabledSymbol = "ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED"; + /// Single cast delegate used to bind function that returns the number of + /// Geospatial Creator Anchors in a scene. + public static Func CountGeospatialCreatorAnchorsDelegate; + private static readonly BuildTargetGroup[] _buildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android, @@ -147,6 +151,20 @@ public static void RemoveScriptingSymbols() } } + /// Counts the number of Geospatial Creator Anchors in the scene. + /// The anchor count returned by CountGeospatialCreatorAnchorsDelegate() if + /// it was set, otherwise 0. + public static uint CountGeospatialCreatorAnchors() + { + uint geospatialCreatorAnchorCount = 0; + if (CountGeospatialCreatorAnchorsDelegate != null) + { + geospatialCreatorAnchorCount = CountGeospatialCreatorAnchorsDelegate(); + } + + return geospatialCreatorAnchorCount; + } + private static void AddSymbol(string symbol, BuildTargetGroup target) { HashSet symbolSet = GetCurrentSymbolSet(target); diff --git a/Editor/Scripts/Internal/LogRequestUtils.cs b/Editor/Scripts/Internal/LogRequestUtils.cs index 86ca0a9..aeb9a59 100644 --- a/Editor/Scripts/Internal/LogRequestUtils.cs +++ b/Editor/Scripts/Internal/LogRequestUtils.cs @@ -25,8 +25,8 @@ namespace Google.XR.ARCoreExtensions.Editor.Internal using System.Security.Cryptography; using System.Text; using Google.Protobuf; - using Google.XR.ARCoreExtensions; using Google.XR.ARCoreExtensions.Editor.Internal.Proto; + using Google.XR.ARCoreExtensions.Internal; using UnityEditor; using UnityEngine; @@ -57,6 +57,15 @@ ArCoreSdkLog.Types.UnityEngine.Types.EditionType editionType EditionType = editionType, }; + // Determine Geospatial Creator information. + ArCoreSdkLog.Types.GeospatialCreator geospatialCreatorMessage = + new ArCoreSdkLog.Types.GeospatialCreator() + { + IsGeospatialCreatorEnabled = + ARCoreExtensionsProjectSettings.Instance.GeospatialEditorEnabled, + NumAnchors = GeospatialCreatorHelper.CountGeospatialCreatorAnchors(), + }; + // Collect the set of information to be sent to Google. ArCoreSdkLog logSDK = new ArCoreSdkLog() { @@ -66,6 +75,7 @@ ArCoreSdkLog.Types.UnityEngine.Types.EditionType editionType SdkType = ArCoreSdkLog.Types.SDKType.ArfoundationExtensionsSdk, Unity = engine, // Unity engine version. SdkSessionId = SessionId(), + GeospatialCreator = geospatialCreatorMessage, }; // Assemble the Clearcut log event data. diff --git a/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorAnchor.cs b/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorAnchor.cs index 193c76c..c82fe36 100644 --- a/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorAnchor.cs +++ b/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorAnchor.cs @@ -36,7 +36,7 @@ namespace Google.XR.ARCoreExtensions.GeospatialCreator /// /// A representation of a Geospatial Anchor that was created using the Geospatial Creator tool. - /// This object is responsible for creating a proper ARGeospatialAnchor at runtime at the + /// This object is responsible for creating a proper ARGeospatialAnchor at runtime at the /// latitude, longitude, and altitude specified. /// [AddComponentMenu("XR/AR Geospatial Creator Anchor")] @@ -57,13 +57,14 @@ public class ARGeospatialCreatorAnchor : ARGeospatialCreatorObject internal const string _noAnchorManagersMessage = "No ARAnchorManagers were found in the scene."; - // TODO (b/298042491) This can be private & editor-only when the GEOSPATIAL_CREATOR_API - // flag is permanently enabled. It cannot be removed entirely because we need to be able to +#if UNITY_EDITOR + // This field is deprecated, but cannot be removed entirely because we need to be able to // migrate the value for customers upgrading from older versions of ARCore Extensions. [SerializeField] [Obsolete("Superseded by EditorAltitudeOverride. See MigrateAltitudeOffset() comments.", false)] internal double _altitudeOffset; +#endif // UNITY_EDITOR #if !UNITY_EDITOR #pragma warning disable CS0649 @@ -112,26 +113,40 @@ private enum AnchorResolutionState } #endif - /// - /// Gets and sets the ARAnchorManager used for resolving this anchor at runtime. The - /// property is read-only at runtime. - /// - /// If null, this property will be given a default value during the Awake() + /// Gets and sets the used for resolving this anchor + /// at runtime. The property is read-only at runtime. + /// + /// If null, this property will be given a default value during the + /// Awake() message execution, as follows: + /// + /// #if ARCORE_USE_ARF_5 // use ARF 5 - /// message execution, as follows: If the XROrigin has an AnchorManager + /// If the has an #elif ARCORE_USE_ARF_4 // use ARF 4 - /// message execution, as follows: If the ARSessionOrigin has an AnchorManager + /// If the has an #else // ARF error #error error must define ARCORE_USE_ARF_5 or ARCORE_USE_ARF_4 #endif - /// subcomponent, that AnchorManager will be used; otherwise the first active and enabled - /// ARAnchorManager object returned by Resources.FindObjectsOfTypeAll will be used. If - /// there are no active and enabled ARAnchorManager components in the scene, the first - /// inactive / disabled ARAnchorManager is used. If there are no ARAnchorManagers in the - /// scene, an error will be logged and this property will remain null. - /// GeospatialCreatorAnchors will not be resolved at runtime if the property remains null. - /// + /// subcomponent, that will be used; + /// + /// + /// otherwise the first active and enabled object + /// returned by will be used. + /// + /// + /// If there are no + /// active and enabled components in the scene, the + /// first inactive / disabled is used. + /// + /// + /// If there are no objects in the scene, an error will + /// be logged and this property will remain null. + /// + /// + /// objects will not be resolved at runtime + /// if the property remains null. + /// public ARAnchorManager AnchorManager { get => _anchorManager; @@ -144,23 +159,33 @@ public ARAnchorManager AnchorManager /// /// Gets or sets the Geospatial Creator Origin used to resolve the location of this anchor. /// This property only exists in Editor mode. + /// + /// This property will be given a default value in the Editor's Awake() message + /// execution, as follows: + /// + /// + /// If there are no objects of type in the + /// scene, it will remain null and a warning will be logged. + /// + /// + /// If there is exactly one object of type + /// in the scene, that origin will be assigned to this property. + /// + /// + /// If more than one object of type are in + /// the scene, a warning is logged and the property will remain null. A default + /// origin in the scene will be used to resolve the Anchor's location. + /// + /// /// - /// This property will be given a default value in the Editor's Awake() message - /// execution, as follows:
- /// If there no object of type ARGeospatialCreatorOrigin in the scene, it will remain null - /// and a warning will be logged.
- /// If there is exactly one object of type ARGeospatialCreatorOrigin in the scene, that - /// origin will be assigned to this property.
- /// If more than one objects of type ARGeospatialCreatorOrigin are in the scene, a warning - /// is logged and the property will remain null. A default origin in the scene will be used - /// to resolve the Anchor's location. - ///
public ARGeospatialCreatorOrigin Origin; /// /// Indicates if the anchor should be rendered in the Editor's Scene view at the default - /// Altitude, or at the altitude specified by EditorAltitudeOverride. If false, - /// EditorAltitudeOverride is ignored. UseEditorAltitudeOverride is not available runtime. + /// , or at the altitude specified by + /// . If false, + /// is ignored. + /// UseEditorAltitudeOverride is not available at runtime. /// public bool UseEditorAltitudeOverride { @@ -177,10 +202,13 @@ public bool UseEditorAltitudeOverride /// /// Gets and sets the altitude (in WGS84 meters) at which the Anchor should be rendered in - /// the Editor's scene view. This value is ignored when UseEditorAltitudeOverride is true. - /// EditorAltitudeOverride is useful if the default altitude rooftop or terrain anchors is - /// inaccurate, or if using WGS84 altitude and the scene geometry does not line up exactly - /// with the real world. EditorAltitudeOverride is not used at runtime. + /// the Editor's scene view. This value is ignored when + /// is true. + /// + /// is useful if the default altitude rooftop or + /// terrain anchor is inaccurate, or if using WGS84 altitude and the scene geometry does not + /// line up exactly with the real world. is not + /// used at runtime. /// public double EditorAltitudeOverride { @@ -233,11 +261,15 @@ public double Longitude } /// - /// Gets or sets the altitude. When AltitudeType is WSG84, this value is the altitude of - /// the anchor, in meters according to WGS84. When AltitudeType is Terrain or Rooftop, this - /// value is ONLY used in Editor mode, to determine the altitude at which to render the - /// anchor in the Editor's Scene View. If AltitudeType is Terrain or Rooftop, this value is - /// ignored in the Player. + /// Gets or sets the altitude. When is + /// , this value is the altitude of + /// the anchor, in meters according to WGS84. + /// + /// When is or + /// , this value is ONLY used in Editor mode, to + /// determine the altitude at which to render the anchor in the Editor's Scene View, and it + /// is ignored in the Player. + /// public double Altitude { get => this._altitude; @@ -253,7 +285,10 @@ public double Altitude #endif } - /// Gets or sets the AltitudeType used for resolution of this anchor. + /// + /// Gets or sets the used for resolution of this + /// anchor. + /// public AnchorAltitudeType AltitudeType { get => _altitudeType; @@ -423,7 +458,7 @@ internal bool MigrateAltitudeOffset() } #pragma warning restore CS0618 -#endif // UINITY_EDITOR +#endif // UNITY_EDITOR #if !UNITY_EDITOR private void Update() @@ -470,13 +505,10 @@ private void AddGeoAnchorAtRuntime() return; } - if (_anchorManager == null) { - string errorReason = - "The AnchorManager property for " + name + " is null"; - Debug.LogError("Unable to place ARGeospatialCreatorAnchor " + name + ": " + - errorReason); + Debug.LogError("Unable to place ARGeospatialCreatorAnchor " + name + ": The " + + "AnchorManager property is null"); _anchorResolution = AnchorResolutionState.Complete; return; } @@ -525,6 +557,8 @@ private void ResolveWGS84AltitudeAnchor() { ARGeospatialAnchor anchor = _anchorManager.AddAnchor( Latitude, Longitude, Altitude, transform.rotation); + + _anchorManager.ReportCreateGeospatialCreatorAnchor(ApiGeospatialAnchorType.WGS84); FinishAnchor(anchor); } @@ -533,11 +567,10 @@ private void ResolveWGS84AltitudeAnchor() // creating geospatial anchors. private IEnumerator ResolveTerrainAnchor() { - double altitudeAboveTerrain = Altitude; ARGeospatialAnchor anchor = null; ResolveAnchorOnTerrainPromise promise = _anchorManager.ResolveAnchorOnTerrainAsync( - Latitude, Longitude, altitudeAboveTerrain, transform.rotation); + Latitude, Longitude, Altitude, transform.rotation); yield return promise; var result = promise.Result; @@ -546,6 +579,7 @@ private IEnumerator ResolveTerrainAnchor() anchor = result.Anchor; } + _anchorManager.ReportCreateGeospatialCreatorAnchor(ApiGeospatialAnchorType.Terrain); FinishAnchor(anchor); yield break; } @@ -561,11 +595,10 @@ private IEnumerator ResolveTerrainAnchor() // creating geospatial anchors. private IEnumerator ResolveRooftopAnchor() { - double altitudeAboveRooftop = Altitude; ARGeospatialAnchor anchor = null; ResolveAnchorOnRooftopPromise promise = _anchorManager.ResolveAnchorOnRooftopAsync( - Latitude, Longitude, altitudeAboveRooftop, transform.rotation); + Latitude, Longitude, Altitude, transform.rotation); yield return promise; var result = promise.Result; @@ -574,6 +607,7 @@ private IEnumerator ResolveRooftopAnchor() anchor = result.Anchor; } + _anchorManager.ReportCreateGeospatialCreatorAnchor(ApiGeospatialAnchorType.Rooftop); FinishAnchor(anchor); yield break; } diff --git a/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorOrigin.cs b/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorOrigin.cs index 951d796..5dd4d6a 100644 --- a/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorOrigin.cs +++ b/Runtime/GeospatialCreatorRuntime/Scripts/ARGeospatialCreatorOrigin.cs @@ -27,8 +27,8 @@ namespace Google.XR.ARCoreExtensions.GeospatialCreator using UnityEngine; /// - /// Provides a Geospatial Creator Origin that has both a lat/lon and gamespace coordinates. This is - /// the reference point used by AR Anchors made in the Geospatial Creator to resolve their + /// Provides a Geospatial Creator Origin that has both a lat/lon and gamespace coordinates. This + /// is the reference point used by AR Anchors made in the Geospatial Creator to resolve their /// location in gamespace. /// #if ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED @@ -52,8 +52,8 @@ public class ARGeospatialCreatorOrigin : ARGeospatialCreatorObject new GeoCoordinate(37.422098, -122.08286, 11.5); /// - /// Gets the latitude of this origin, in decimal degrees. Will return Double.NaN if the - /// origin point has not been initialized. + /// Gets the latitude of this origin, in decimal degrees. Will return + /// if the origin point has not been initialized. /// public double Latitude { @@ -61,8 +61,8 @@ public double Latitude } /// - /// Gets the longitude of this origin, in decimal degrees. Will return Double.NaN if the - /// origin point has not been initialized. + /// Gets the longitude of this origin, in decimal degrees. Will return + /// if the origin point has not been initialized. /// public double Longitude { @@ -70,8 +70,8 @@ public double Longitude } /// - /// Gets the altiude of this origin, in meters according to WGS84. Will return Double.NaN - /// if the origin point has not been initialized. + /// Gets the altiude of this origin, in meters according to WGS84. Will return + /// if the origin point has not been initialized. /// public double Altitude { @@ -101,6 +101,12 @@ internal GeoCoordinate _originPoint // comments in IOriginComponentAdapter for more details. internal IOriginComponentAdapter _originComponentAdapter; + // An adapter to access the 3DTileset maintained by this GameObject's subcomponent. + // This property is set in the Editor assembly from the GeospatialOriginUpdater + // when the updater for this Origin is initialized. See the comments in + // Origin3DTilesetAdapter for more details. + internal Origin3DTilesetAdapter _origin3DTilesetAdapter; + /// /// Sets the geospatial location for this origin, including updating the location for any /// dependent subcomponents. This method is only available in the Unity Editor; moving the diff --git a/Runtime/GeospatialCreatorRuntime/Scripts/AnchorAltitudeType.cs b/Runtime/GeospatialCreatorRuntime/Scripts/AnchorAltitudeType.cs index 6f30c4f..5f8b4f6 100644 --- a/Runtime/GeospatialCreatorRuntime/Scripts/AnchorAltitudeType.cs +++ b/Runtime/GeospatialCreatorRuntime/Scripts/AnchorAltitudeType.cs @@ -21,24 +21,58 @@ namespace Google.XR.ARCoreExtensions.GeospatialCreator { /// - /// Specifies how the ARGeospatialCreatorAnchor's Altitude and AlttudeOffset fields will be + /// Specifies how the 's + /// and + /// properties will be /// interpreted. public enum AnchorAltitudeType { /// - /// Altitude specifies the altitude of the anchor in meters for WGS84. AltitudeOffset is - /// not used. + /// The anchor represents a WGS84 + /// anchor. + /// The anchor's specifies the + /// altitude of the anchor in meters for WGS84. + /// + /// is not used. + /// WGS84, /// - /// Altitude specifies the relative altitude above/below the terrain, in meters. If the - /// anchor does not appear to render at the correct height in the Editor, adjust the - /// anchor's EditorAltitude property. + /// The anchor represents a Terrain + /// anchor. + /// The anchor's specifies the + /// relative altitude above or below the terrain, in meters. + /// + /// If the anchor does not appear to render at the correct height in the Editor, set the + /// anchor's + /// property to + /// true and use + /// + /// to adjust the anchor's visual altitude in the Editor. At runtime, the anchor will ignore + /// these values and use an altitude relative to the terrain at that anchor's horizontal + /// location. + /// Terrain, - /// Altitude specifies the relative altitude above/below the rooftop, in meters. - /// If the anchor does not appear to render at the correct height in the Editor, adjust the - /// anchor's EditorAltitude property. + /// + /// The anchor represents a Rooftop + /// anchor. + /// The anchor's specifies the + /// relative altitude above or below to a rooftop at that anchor's horizontal location, in + /// meters. + /// + /// If the anchor does not appear to render at the correct height in the Editor, set the + /// anchor's + /// property to + /// true and use + /// + /// to adjust the anchor's visual altitude in the Editor. At runtime, the anchor will ignore + /// these values and use an altitude relative to a rooftop at that anchor's horizontal + /// location. + /// Rooftop } } diff --git a/Runtime/GeospatialCreatorRuntime/Scripts/Internal/GeoMath.cs b/Runtime/GeospatialCreatorRuntime/Scripts/Internal/GeoMath.cs index a5a120c..62af70f 100644 --- a/Runtime/GeospatialCreatorRuntime/Scripts/Internal/GeoMath.cs +++ b/Runtime/GeospatialCreatorRuntime/Scripts/Internal/GeoMath.cs @@ -30,6 +30,12 @@ namespace Google.XR.ARCoreExtensions.GeospatialCreator.Internal // :TODO: b/277365140 Automated testing internal static class GeoMath { + // This value is higher than any terrain altitude on Earth (ie. Mt. Everest at 8848m) + public const double HighestElevationOnEarth = 9000; + + // This value is lower than any terrain altitude on Earth (ie. Dead Sea shore at -420m) + public const double LowestElevationOnEarth = -420; + // Equatorial radius in meters private const double _wgs84EllipsoidSemiMajorAxis = 6378137.0; diff --git a/Runtime/GeospatialCreatorRuntime/Scripts/Internal/Origin3DTilesetAdapter.cs b/Runtime/GeospatialCreatorRuntime/Scripts/Internal/Origin3DTilesetAdapter.cs new file mode 100644 index 0000000..5128c4c --- /dev/null +++ b/Runtime/GeospatialCreatorRuntime/Scripts/Internal/Origin3DTilesetAdapter.cs @@ -0,0 +1,185 @@ +//----------------------------------------------------------------------- +// +// +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//----------------------------------------------------------------------- + +namespace Google.XR.ARCoreExtensions.GeospatialCreator.Internal +{ + using System; + using System.Threading.Tasks; + using UnityEngine; +#if UNITY_EDITOR + using UnityEditor; +#endif // UNITY_EDITOR + + /// + /// Used when an ARGeospatialCreatorOrigin's 3D Tileset is determined by or maintained by + /// a child object. For example, the default way to configure an origin is to add a + /// Cesium3D Tileset as a child object. The adapter pattern allows us to avoid + /// exposing the implementation details of the Cesium3D Tileset to the + /// ARGeospatialCreatorOrigin class. + /// + internal abstract class Origin3DTilesetAdapter + { + // How many milliseconds to wait for the tiles to load before attempting a hit test + private const int _tileLoadingWaitInterval = 100; + + // How many seconds to try loading tiles before timing out + private readonly TimeSpan _tileLoadingWaitTimeout = TimeSpan.FromSeconds(5); + + /// Gets the parent ARGeospatialCreatorOrigin object. + public abstract ARGeospatialCreatorOrigin Origin { get; } + + /// + /// Whether or not physics meshes are enabled for the tileset to allow collision testing. + /// + /// True if physics meshes are enabled for the tileset. + public abstract bool GetPhysicsMeshesEnabled(); + + /// + /// Used to enable or disable physics meshes on the tileset. + /// + /// If true, tileset meshes will allow collision testing. + public abstract void SetPhysicsMeshesEnabled(bool enable); + + /// + /// Returns what percentage of Tiles have loaded in the current view. + /// + /// Percentage (0.0 to 100.0) of tiles loaded in the current view. + public abstract float ComputeTilesetLoadProgress(); + + /// + /// Verifies whether a collider belongs to a tile in the tileset. + /// + /// The collider to be checked. + /// True if the collider belongs to a tile. + public abstract bool IsTileCollider(Collider collider); + +#if UNITY_EDITOR + /// + /// Asynchronously gets the terrain or rooftop altitude at the given lat and lng. + /// This method ensures that physics are enabled on the tileset and waits for the + /// tiles to load before computing the altitude. + /// + /// The latitude at which to sample the altitude. + /// The longitude at which to sample the altitude. + /// + /// Tuple with "success" and "terrainAltitude" named elements. + /// "success": True if the altitude was successfully calculated. + /// "terrainAltitude": Altitude of the terrain at lat/lng or 0.0 if calculations failed. + /// + /// + /// If disabled, this method will enable physics meshes on the tiles during its execution, + /// and it will revert the setting back to its previous value before returning. + /// The tileset will reload all its tiles each time this setting is changed. + /// + public virtual async Task<(bool success, double terrainAltitude)> + CalcTileAltitudeWGS84Async(double lat, double lng) + { + if (Origin?._originPoint == null) + { + return (false, 0.0); + } + + // Enable physics meshes on the tileset to allow the raycast to hit + bool previousPhysicsSetting = GetPhysicsMeshesEnabled(); + SetPhysicsMeshesEnabled(true); + + // Wait an initial interval of at least one frame before computing tile load progress + // Otherwise, you will get 100% loaded because previous tiles haven't unloaded + await Task.Delay(_tileLoadingWaitInterval); + + // Wait until all tiles have loaded or we've hit a timeout + DateTime waitStartTime = DateTime.UtcNow; + while ((ComputeTilesetLoadProgress() < 99.9f) + && (DateTime.UtcNow - waitStartTime < _tileLoadingWaitTimeout)) + { + await Task.Delay(_tileLoadingWaitInterval); + } + + // TODO: (b/319861940) improve accuracy of sampled altitude. + (bool hitSucceeded, double hitAltitude) = CalcTileAltitudeWGS84(lat, lng); + + // Revert the physics meshes on the tileset to the previous setting + SetPhysicsMeshesEnabled(previousPhysicsSetting); + + return (hitSucceeded, hitAltitude); + } + + /// Gets the terrain or rooftop altitude at the given lat and lng. + /// The latitude at which to sample the altitude. + /// The longitude at which to sample the altitude. + /// + /// Tuple with "success" and "terrainAltitude" named elements. + /// "success": True if the altitude was successfully calculated. + /// "terrainAltitude": Altitude of the terrain at lat/lng or 0.0 if calculations failed. + /// + /// + /// Calculation can fail if the tiles haven't had time to load, if the tiles don't have + /// physics meshes enabled, or if the target tile is out of the camera's view while + /// Frustum Culling is enabled. Sampled altitude can be inaccurate if high precision + /// LODs haven't loaded for the tiles (ex: because the tile is far from the camera). + /// For accurate results, have the desired anchor position well within the camera view. + /// + public virtual (bool success, double terrainAltitude) + CalcTileAltitudeWGS84(double lat, double lng) + { + GeoCoordinate originPoint = Origin?._originPoint; + Vector3 originUnityCoord = Origin ? Origin.gameObject.transform.position: Vector3.zero; + if (originPoint == null) + { + return (false, 0.0); + } + + // Make a high point where the ray will originate (above any possible terrain) + GeoCoordinate highGeoCoord = + new GeoCoordinate(lat, lng, GeoMath.HighestElevationOnEarth); + Vector3 rayOrigin = GeoMath.GeoCoordinateToUnityWorld( + highGeoCoord, originPoint, originUnityCoord); + + // Make a low point at zero altitude where the ray will be cast towards + GeoCoordinate lowGeoCoord = new GeoCoordinate(lat, lng, 0.0); + Vector3 rayTarget = GeoMath.GeoCoordinateToUnityWorld( + lowGeoCoord, originPoint, originUnityCoord); + + // Make a vector that points from the high point to the low point + Vector3 rayDirection = Vector3.Normalize(rayTarget - rayOrigin); + + // Retrieve the highest altitude tile hit + bool hitSucceeded = false; + double hitAltitude = double.MinValue; + RaycastHit[] hits = Physics.RaycastAll(rayOrigin, rayDirection); + foreach (RaycastHit hit in hits) + { + if (IsTileCollider(hit.collider)) + { + GeoCoordinate hitPoint = GeoMath.UnityWorldToGeoCoordinate( + hit.point, originPoint, originUnityCoord); + hitSucceeded = true; + hitAltitude = Math.Max(hitPoint.Altitude, hitAltitude); + } + } + + // If there was no hit, return an altitude of zero + hitAltitude = hitSucceeded ? hitAltitude : 0.0; + + return (hitSucceeded, hitAltitude); + } +#endif // UNITY_EDITOR + } +} diff --git a/Runtime/GeospatialCreatorRuntime/Scripts/Internal/Origin3DTilesetAdapter.cs.meta b/Runtime/GeospatialCreatorRuntime/Scripts/Internal/Origin3DTilesetAdapter.cs.meta new file mode 100644 index 0000000..c61eff6 --- /dev/null +++ b/Runtime/GeospatialCreatorRuntime/Scripts/Internal/Origin3DTilesetAdapter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4184a3ae46144475a98ea52885c2ec37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/ARPresto.aar b/Runtime/Plugins/ARPresto.aar index f490545..fed7f66 100644 Binary files a/Runtime/Plugins/ARPresto.aar and b/Runtime/Plugins/ARPresto.aar differ diff --git a/Runtime/Plugins/arcore_client.aar b/Runtime/Plugins/arcore_client.aar index e4c5c50..f7e32a5 100644 Binary files a/Runtime/Plugins/arcore_client.aar and b/Runtime/Plugins/arcore_client.aar differ diff --git a/Runtime/Scripts/ARAnchorManagerExtensions.cs b/Runtime/Scripts/ARAnchorManagerExtensions.cs index 49f080b..9b23854 100644 --- a/Runtime/Scripts/ARAnchorManagerExtensions.cs +++ b/Runtime/Scripts/ARAnchorManagerExtensions.cs @@ -776,5 +776,19 @@ public static ResolveAnchorOnTerrainPromise ResolveAnchorOnTerrainAsync( return new ResolveAnchorOnTerrainPromise(future); } + + /// + /// Logs that a Geospatial Creator Anchor has been resolved into a Geospatial Anchor. + /// This call only serves to log the anchor creation for analytics and debugging purposes. + /// It does not run any logic related to creating the anchor. + /// + /// The instance. + /// The type of anchor that was created. + internal static void ReportCreateGeospatialCreatorAnchor( + this ARAnchorManager anchorManager, ApiGeospatialAnchorType anchorType) + { + EarthApi.ReportCreateGeospatialCreatorAnchor( + ARCoreExtensions._instance.currentARCoreSessionHandle, anchorType); + } } } diff --git a/Runtime/Scripts/ARCoreExtensions.cs b/Runtime/Scripts/ARCoreExtensions.cs index d3e2a23..0cd4645 100644 --- a/Runtime/Scripts/ARCoreExtensions.cs +++ b/Runtime/Scripts/ARCoreExtensions.cs @@ -27,6 +27,7 @@ namespace Google.XR.ARCoreExtensions { using System; + using System.Collections; using System.Collections.Generic; using System.Linq; using Google.XR.ARCoreExtensions.Internal; @@ -123,6 +124,11 @@ public delegate int OnChooseXRCameraConfigurationEvent( List supportedConfigurations); #if UNITY_ANDROID + // The max number of frames we will wait for a valid session handle to report analytics. + internal const int _frameTimeoutForSessionHandle = 60; + + private bool _geospatialCreatorPlatformLoggedOnce = false; + internal const int _androidSSDKVersion = 31; private static AndroidJavaClass _versionInfo; @@ -227,9 +233,16 @@ public void OnEnable() { _arCoreCameraSubsystem.beforeGetCameraConfiguration += BeforeGetCameraConfiguration; } -#endif // UNITY_ANDROID + // TODO: (b/308463770) - Support Geospatial Creator Platform analytics on iOS. + if (_geospatialCreatorPlatformLoggedOnce == false) + { + StartCoroutine(ReportGeospatialCreatorPlatformWithDelay()); + _geospatialCreatorPlatformLoggedOnce = true; + } +#endif // UNITY_ANDROID #if !UNITY_IOS || ARCORE_EXTENSIONS_IOS_SUPPORT + // TODO: (b/308163258) - EngineType isn't set bc session handle is usually invalid here if (Session != null && currentARCoreSessionHandle != IntPtr.Zero && !_engineTypeLogged) { SessionApi.ReportEngineType(currentARCoreSessionHandle); @@ -540,6 +553,32 @@ private void SelectCameraConfig() CameraManager.currentConfiguration = configurations[configIndex]; } } + + private IEnumerator ReportGeospatialCreatorPlatformWithDelay() + { + // TODO: (b/308463770) - Support Geospatial Creator Platform analytics on iOS. +#if !UNITY_IOS || GEOSPATIAL_IOS_SUPPORT + // Wait a few frames until the session handle is valid + int frameCount = 0; + while (currentARCoreSessionHandle == IntPtr.Zero && + frameCount < _frameTimeoutForSessionHandle) + { + yield return null; + frameCount++; + } + + ApiGeospatialCreatorPlatform geospatialCreatorPlatform = +#if ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED + ApiGeospatialCreatorPlatform.ArCoreExtensionsUnity; +#else + ApiGeospatialCreatorPlatform.Disabled; +#endif // ARCORE_INTERNAL_GEOSPATIAL_CREATOR_ENABLED + + EarthApi.ReportGeospatialCreatorPlatform(currentARCoreSessionHandle, + geospatialCreatorPlatform); +#endif // !UNITY_IOS || GEOSPATIAL_IOS_SUPPORT + yield return null; + } #endif // UNITY_ANDROID } } diff --git a/Runtime/Scripts/AREarthManager.cs b/Runtime/Scripts/AREarthManager.cs index 02c1f61..e967ea5 100644 --- a/Runtime/Scripts/AREarthManager.cs +++ b/Runtime/Scripts/AREarthManager.cs @@ -157,9 +157,6 @@ public static VpsAvailabilityPromise CheckVpsAvailabilityAsync(double latitude, (sessionHandle == IntPtr.Zero) ? IntPtr.Zero : FutureApi.CheckVpsAvailabilityAsync(sessionHandle, latitude, longitude); -#if UNITY_ANDROID - ARPrestoApi.SetSessionRequired(false); -#endif return new VpsAvailabilityPromise(future); } diff --git a/Runtime/Scripts/GeospatialPose.cs b/Runtime/Scripts/GeospatialPose.cs index 33c8a37..b496562 100644 --- a/Runtime/Scripts/GeospatialPose.cs +++ b/Runtime/Scripts/GeospatialPose.cs @@ -142,7 +142,7 @@ public struct GeospatialPose public double HorizontalAccuracy; /// - /// Estimated horizontal accuracy in meters. + /// Estimated vertical accuracy in meters. /// /// We define vertical accuracy as the radius of the 68th percentile confidence level around /// the estimated altitude. In other words, there is a 68% probability that the true diff --git a/Runtime/Scripts/Internal/Types/ApiGeospatialAnchorType.cs b/Runtime/Scripts/Internal/Types/ApiGeospatialAnchorType.cs new file mode 100644 index 0000000..3e35a31 --- /dev/null +++ b/Runtime/Scripts/Internal/Types/ApiGeospatialAnchorType.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------- +// +// +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//----------------------------------------------------------------------- + +namespace Google.XR.ARCoreExtensions.Internal +{ + internal enum ApiGeospatialAnchorType + { + Unknown = 0, + WGS84 = 1, + Terrain = 2, + Rooftop = 3, + } +} diff --git a/Runtime/Scripts/Internal/Types/ApiGeospatialAnchorType.cs.meta b/Runtime/Scripts/Internal/Types/ApiGeospatialAnchorType.cs.meta new file mode 100644 index 0000000..14d8622 --- /dev/null +++ b/Runtime/Scripts/Internal/Types/ApiGeospatialAnchorType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0bb7bfa0b8e714af88f6ca2495764f21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Internal/Types/ApiGeospatialCreatorPlatform.cs b/Runtime/Scripts/Internal/Types/ApiGeospatialCreatorPlatform.cs new file mode 100644 index 0000000..29bac6a --- /dev/null +++ b/Runtime/Scripts/Internal/Types/ApiGeospatialCreatorPlatform.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------- +// +// +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//----------------------------------------------------------------------- + +namespace Google.XR.ARCoreExtensions.Internal +{ + internal enum ApiGeospatialCreatorPlatform + { + // 0, 2, and 4 are not supported values for Unity. + Disabled = 1, + ArCoreExtensionsUnity = 3, + } +} diff --git a/Runtime/Scripts/Internal/Types/ApiGeospatialCreatorPlatform.cs.meta b/Runtime/Scripts/Internal/Types/ApiGeospatialCreatorPlatform.cs.meta new file mode 100644 index 0000000..2fd664e --- /dev/null +++ b/Runtime/Scripts/Internal/Types/ApiGeospatialCreatorPlatform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 31db4cb898b7e41ccb66ea866a525763 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Internal/Wrappers/EarthApi.cs b/Runtime/Scripts/Internal/Wrappers/EarthApi.cs index 1140c31..cacbcd8 100644 --- a/Runtime/Scripts/Internal/Wrappers/EarthApi.cs +++ b/Runtime/Scripts/Internal/Wrappers/EarthApi.cs @@ -102,6 +102,40 @@ public static IntPtr AddAnchor(IntPtr sessionHandle, IntPtr earthHandle, return anchorHandle; } + public static void ReportCreateGeospatialCreatorAnchor( + IntPtr session, ApiGeospatialAnchorType anchorType) + { + // TODO: (b/308463770) - Support anchor creation analytics on iOS. +#if UNITY_ANDROID +#if !UNITY_IOS || GEOSPATIAL_IOS_SUPPORT + ApiArStatus status = + ExternApi.ArEarth_reportCreateGeospatialCreatorAnchor(session, anchorType); + if (status != ApiArStatus.Success) + { + Debug.LogErrorFormat( + "Failed to log Geospatial Creator Anchor Creation, status '{0}'", status); + } +#endif +#endif + } + + public static void ReportGeospatialCreatorPlatform( + IntPtr session, ApiGeospatialCreatorPlatform platform) + { + // TODO: (b/308463770) - Support Geospatial Creator Platform analytics on iOS. +#if UNITY_ANDROID +#if !UNITY_IOS || GEOSPATIAL_IOS_SUPPORT + ApiArStatus status = + ExternApi.ArEarth_reportGeospatialCreatorPlatform(session, platform); + if (status != ApiArStatus.Success) + { + Debug.LogErrorFormat( + "Failed to log Geospatial Creator Platform, status '{0}'", status); + } +#endif +#endif + } + public static IntPtr ResolveAnchorOnTerrain(IntPtr sessionHandle, IntPtr earthHandle, double latitude, double longitude, double altitudeAboveTerrain, Quaternion eunRotation) { @@ -235,6 +269,17 @@ public static extern ApiArStatus ArEarth_acquireNewAnchor( IntPtr session, IntPtr earth, double latitude, double longitude, double altitude, ref ApiQuaternion eus_quaternion_4, ref IntPtr out_anchor); + // TODO: (b/308463770) - Support anchor creation analytics on iOS. +#if UNITY_ANDROID + [EarthImport(ApiConstants.ARCoreNativeApi)] + public static extern ApiArStatus ArEarth_reportCreateGeospatialCreatorAnchor( + IntPtr session, ApiGeospatialAnchorType anchorType); + + [EarthImport(ApiConstants.ARCoreNativeApi)] + public static extern ApiArStatus ArEarth_reportGeospatialCreatorPlatform( + IntPtr session, ApiGeospatialCreatorPlatform platform); +#endif // UNITY_ANDROID + [EarthImport(ApiConstants.ARCoreNativeApi)] public static extern ApiArStatus ArEarth_resolveAndAcquireNewAnchorOnTerrain( IntPtr session, IntPtr earth, double latitude, double longitude, diff --git a/Runtime/Scripts/VersionInfo.cs b/Runtime/Scripts/VersionInfo.cs index b90cc0e..2d0c66d 100644 --- a/Runtime/Scripts/VersionInfo.cs +++ b/Runtime/Scripts/VersionInfo.cs @@ -28,6 +28,6 @@ public class VersionInfo /// /// The current ARCore Extensions package version. /// - public static readonly string Version = "1.41.0"; + public static readonly string Version = "1.42.0"; } } diff --git a/Samples~/Geospatial/Scenes/GeospatialArf5.unity b/Samples~/Geospatial/Scenes/GeospatialArf5.unity index d9bface..c0936c1 100644 --- a/Samples~/Geospatial/Scenes/GeospatialArf5.unity +++ b/Samples~/Geospatial/Scenes/GeospatialArf5.unity @@ -2602,7 +2602,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} m_Name: m_EditorClassIdentifier: - m_SendPointerHoverToParent: 1 m_MoveRepeatDelay: 0.5 m_MoveRepeatRate: 0.1 m_XRTrackingOrigin: {fileID: 596133978} diff --git a/Samples~/Geospatial/Scripts/GeospatialController.cs b/Samples~/Geospatial/Scripts/GeospatialController.cs index 6e7a969..e9a087c 100644 --- a/Samples~/Geospatial/Scripts/GeospatialController.cs +++ b/Samples~/Geospatial/Scripts/GeospatialController.cs @@ -25,6 +25,22 @@ #warning For AR Foundation 5.X compatibility, define the ARCORE_USE_ARF_5 symbol #endif +#if !ENABLE_LEGACY_INPUT_MANAGER +// Input.location will not work at runtime with out the old input system. +// Given that sample has not been ported to support new input +// Check that Project Settings > Player > Other Settings > Active Input Handling +// is set to Both or Input Manager (Old) +#error Input.location API requires Active Input Handling to be set to Input Manager (Old) or Both +#endif + +#if !ENABLE_INPUT_SYSTEM && ARCORE_USE_ARF_5 +// The camera's pose driver in ARF5 needs Input System (New) but given we need Input Manager +// (Old) for Input.location (see above) ARF5 needs both. +// Check that Project Settings > Player > Other Settings > Active Input Handling +// is set to Both +#error The camera's pose driver needs Input System (New) so set Active Input Handling to Both +#endif + namespace Google.XR.ARCoreExtensions.Samples.Geospatial { using System; diff --git a/Samples~/PersistentCloudAnchors/Scenes/PersistentCloudAnchorsArf5.unity b/Samples~/PersistentCloudAnchors/Scenes/PersistentCloudAnchorsArf5.unity index 39df4bd..6a7ad11 100644 --- a/Samples~/PersistentCloudAnchors/Scenes/PersistentCloudAnchorsArf5.unity +++ b/Samples~/PersistentCloudAnchors/Scenes/PersistentCloudAnchorsArf5.unity @@ -7349,7 +7349,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} m_Name: m_EditorClassIdentifier: - m_SendPointerHoverToParent: 1 m_MoveRepeatDelay: 0.5 m_MoveRepeatRate: 0.1 m_XRTrackingOrigin: {fileID: 1520494398} diff --git a/Samples~/PersistentCloudAnchors/Scripts/PersistentCloudAnchorsController.cs b/Samples~/PersistentCloudAnchors/Scripts/PersistentCloudAnchorsController.cs index 3372db4..78ce8fa 100644 --- a/Samples~/PersistentCloudAnchors/Scripts/PersistentCloudAnchorsController.cs +++ b/Samples~/PersistentCloudAnchors/Scripts/PersistentCloudAnchorsController.cs @@ -27,6 +27,13 @@ #warning For AR Foundation 5.X compatibility, define the ARCORE_USE_ARF_5 symbol #endif +#if !(ENABLE_INPUT_SYSTEM && ENABLE_LEGACY_INPUT_MANAGER) && ARCORE_USE_ARF_5 +// The camera's pose driver in ARF5 needs Input System (New) but sample has not been ported to +// support new input so make sure Settings > Player > Other Settings > Active Input Handling +// is set to Both +#error The cloud anchores sample needs Active Input Handling set to Both +#endif + namespace Google.XR.ARCoreExtensions.Samples.PersistentCloudAnchors { using System; diff --git a/package.json b/package.json index a8026a5..febd61d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.google.ar.core.arfoundation.extensions", "displayName": "ARCore Extensions", - "version": "1.41.0", + "version": "1.42.0", "unity": "2019.4", "description": "Google ARCore Extensions for AR Foundation. This package provides access to ARCore features not covered by AR Foundation's cross platform API.", "author":