// Copyright (C) 2018 Daniel Fortes // All rights reserved. // // See LICENSE.TXT for more info using System; using ARLocation.Session; using UnityEngine; using UnityEngine.Events; #if !ARGPS_USE_VUFORIA using UnityEngine.XR.ARFoundation; #endif namespace ARLocation { using Utils; /// /// This Component manages all positioned GameObjects, synchronizing their world position in the scene /// with their geographical coordinates. This is done by calculating their position relative to the device's position. /// /// Should be placed in a GameObject called "ARLocationRoot", whose parent is the "AR Session Origin". /// [RequireComponent(typeof(ARLocationOrientation))] [DisallowMultipleComponent] [AddComponentMenu("AR+GPS/AR Location Manager")] [HelpURL("https://http://docs.unity-ar-gps-location.com/guide/#arlocationmanager")] public class ARLocationManager : Singleton { [Tooltip("The AR Camera that will be used for rendering the AR content. If this is not set, the camera tagger as 'MainCamera' will be used." + " Make sure either this is set, or a camera is tagged as 'MainCamera', or an error will be thrown.")] public Camera Camera; [Tooltip("If true, wait until the AR Tracking starts to start with location and orientation updates and object placement.")] public bool WaitForARTrackingToStart = true; [Tooltip("If true, every time the AR tracking is lost and regained, the AR+GPS system is restarted, repositioning all the objects.")] public bool RestartWhenARTrackingIsRestored; [Tooltip("If true, the manager will set 'Application.targetFrameRate' to 60.")] public bool SetTargetFrameRateTo60Mhz = true; [Header("Debug")] [Tooltip("When debug mode is enabled, this component will print relevant messages to the console. Filter by 'ARLocationManager' in the log output to see the messages.")] public bool DebugMode; [Header("AR Session Events")] public UnityEvent OnTrackingStarted = new UnityEvent(); public UnityEvent OnTrackingLost = new UnityEvent(); public UnityEvent OnTrackingRestored = new UnityEvent(); /// /// The instance of the 'IARSessionManager'. Handles the interface with the underlying AR session (i.e., Vuforia or AR Foundation). /// public IARSessionManager SessionManager { get; private set; } /// /// The 'MainCamera' that is being used for rendering the AR content. /// public Camera MainCamera { get; private set; } /// /// Returns the Y world-coordinate of the detected plane which is nearest to the user/camera. /// public float CurrentGroundY => groundHeight ? groundHeight.CurrentGroundY : -ARLocation.Config.InitialGroundHeightGuess; private ARLocationOrientation arLocationOrientation; private ARLocationProvider arLocationProvider; private Action onARTrackingStartedAction; private GameObject groundHeightDummy; private GroundHeight groundHeight; private Vector3 cameraPositionAtLastUpdate; public override void Awake() { base.Awake(); if (SetTargetFrameRateTo60Mhz) { Logger.LogFromMethod("ARLocationManager", "Awake", "Setting 'Application.targetFrameRate' to 60", DebugMode); Application.targetFrameRate = 60; } MainCamera = Camera ? Camera : Camera.main; if (MainCamera == null) { throw new NullReferenceException("[AR+GPS][ARLocationManager#Start]: Missing Camera. " + "Either set the 'Camera' property to the AR Camera, or tag it as a 'MainCamera'."); } transform.localPosition = new Vector3(0, 0, 0); } private void Start() { arLocationOrientation = GetComponent(); arLocationProvider = ARLocationProvider.Instance; if (WaitForARTrackingToStart) { arLocationProvider.Mute(); } #if !ARGPS_USE_VUFORIA var arSession = FindObjectOfType(); if (!arSession) { throw new NullReferenceException("[AR+GPS][ARLocationManager#Start]: No ARSession found in the scene!"); } SessionManager = new ARFoundationSessionManager(arSession); #else SessionManager = new VuforiaSessionManager(); #endif SessionManager.DebugMode = DebugMode; SessionManager.OnARTrackingStarted(ARTrackingStartedCallback); SessionManager.OnARTrackingRestored(ARTrackingRestoredCallback); SessionManager.OnARTrackingLost(ARTrackingLostCallback); groundHeightDummy = new GameObject("ARLocationGroundHeight"); groundHeightDummy.transform.SetParent(MainCamera.transform); groundHeightDummy.transform.localPosition = new Vector3(); groundHeight = groundHeightDummy.AddComponent(); arLocationProvider.OnLocationUpdatedEvent(onLocationUpdated); } private void onLocationUpdated(LocationReading currentLocation, LocationReading lastLocation) { cameraPositionAtLastUpdate = MainCamera.transform.position; } private void ARTrackingLostCallback() { Logger.LogFromMethod("ARLocationManager", "ARTrackingLostCallback", "'ARTrackingLost' event received.", DebugMode); OnTrackingLost?.Invoke(); } private void ARTrackingRestoredCallback() { Logger.LogFromMethod("ARLocationManager", "ARTrackingRestoredCallback", "'ARTrackingRestore' event received.", DebugMode); if (RestartWhenARTrackingIsRestored) { Logger.LogFromMethod("ARLocationManager", "ARTrackingRestoredCallback", "'RestartWhenARTrackingIsRestored' is enabled; restarting.", DebugMode); Restart(); } OnTrackingRestored?.Invoke(); } private void ARTrackingStartedCallback() { Logger.LogFromMethod("ARLocationManager", "ARTrackingStartedCallback", "'OnARTrackingStarted' event received.", DebugMode); if (WaitForARTrackingToStart) { arLocationProvider.Unmute(); } OnTrackingStarted?.Invoke(); onARTrackingStartedAction?.Invoke(); } /// /// This will reset the AR Session and the AR+GPS system, repositioning all objects. /// /// Optional callback, called when the system has restarted. public void ResetARSession(Action cb = null) { Logger.LogFromMethod("ARLocationManager", "ResetARSession", "Resetting the AR Session...", DebugMode); SessionManager?.Reset(() => { Logger.LogFromMethod("ARLocationManager", "ResetARSession", "ARSession restarted. Resetting AR+GPS location...", DebugMode); Restart(); cb?.Invoke(); }); } /// /// This will restart the AR+GPS system, repositioning all the objects. /// public void Restart() { Logger.LogFromMethod("ARLocationManager", "Restart", "Resetting AR+GPS location...", DebugMode); arLocationOrientation.Restart(); arLocationProvider.Restart(); Logger.LogFromMethod("ARLocationManager", "Restart", "Done.", DebugMode); } /// /// Returns a string describing the current AR session tracking status /// /// public string GetARSessionInfoString() { return SessionManager != null ? SessionManager.GetSessionInfoString() : "None"; } /// /// Returns a string describing the current AR Session provider, e.g., AR Foundation or Vuforia. /// /// public string GetARSessionProviderString() { return SessionManager != null ? SessionManager.GetProviderString() : "None"; } /// /// Add a event listener for when the AR Tracking starts. /// /// public void OnARTrackingStarted(Action o) { if (SessionManager == null) { onARTrackingStartedAction += o; } else { SessionManager.OnARTrackingStarted(o); } } /// /// Add a event listener for when the AR Tracking regained after it was lost. /// /// public void OnARTrackingRestored(Action callback) { SessionManager?.OnARTrackingRestored(callback); } /// /// Add a event listener for when the AR Tracking is lost. /// /// public void OnARTrackingLost(Action callback) { SessionManager?.OnARTrackingLost(callback); } /// /// Given a world position vector, return the corresponding geographical Location. /// /// /// public Location GetLocationForWorldPosition(Vector3 position) { return Location.GetLocationForWorldPosition(gameObject.transform, cameraPositionAtLastUpdate, arLocationProvider.CurrentLocation.ToLocation(), position); } } }