123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- using System;
- using UnityEngine;
- #if !ARGPS_USE_VUFORIA
- using UnityEngine.XR.ARFoundation;
- using UnityEngine.XR.ARSubsystems;
- #endif
- #pragma warning disable
- using Logger = ARLocation.Utils.Logger;
- #pragma warning enable
- #if ARGPS_USE_VUFORIA
- using Vuforia;
- #endif
- namespace ARLocation
- {
- /// <summary>
- /// This component will change the Y component of the GameObject's position,
- /// so that it is set to the level of the nearest detected ground plane.
- /// </summary>
- [DisallowMultipleComponent]
- public class GroundHeight : MonoBehaviour
- {
- [Serializable]
- public class SettingsData
- {
- [Range(0, 10)]
- public float InitialGroundHeightGuess = 1.4f;
- [Range(0, 10)]
- public float MinGroundHeight = 0.4f;
- [Range(0, 10)]
- public float MaxGroundHeight = 3.0f;
- [Range(0, 1)]
- public float Smoothing = 0.05f;
- public float Altitude;
- public bool DisableUpdate;
- [Range(0, 0.1f)]
- public float Precision = 0.005f;
- public bool UseArLocationConfigSettings = true;
- #if ARGPS_USE_VUFORIA
- public float MinHitDistance = 0.5f;
- #endif
- }
- [Serializable]
- public class StateData
- {
- public float CurrentGroundY;
- public float CurrentPlaneDistance = -1.0f;
- public Vector3 CurrentPlaneCenter;
- public bool NeedsUpdate = true;
- }
- public float CurrentGroundY => state.CurrentGroundY;
- public SettingsData Settings = new SettingsData();
- private readonly StateData state = new StateData();
- private Camera mainCamera;
- #if !ARGPS_USE_VUFORIA
- private ARPlaneManager arPlaneManager;
- private float targetY;
- void Start()
- {
- arPlaneManager = FindObjectOfType<ARPlaneManager>();
- var arSessionOrigin = FindObjectOfType<ARSessionOrigin>();
- mainCamera = ARLocationManager.Instance.MainCamera;
- if (arPlaneManager == null)
- {
- if (arSessionOrigin == null)
- {
- Debug.LogWarning("[AR+GPS][GroundHeight#Start]: ARSessionOrigin not present in the scene!");
- return;
- }
- arPlaneManager = arSessionOrigin.gameObject.AddComponent<ARPlaneManager>();
- Utils.Misc.RequestPlaneDetectionMode(arPlaneManager, PlaneDetectionMode.Horizontal);
- }
- if (Settings.UseArLocationConfigSettings)
- {
- Settings.MaxGroundHeight = ARLocation.Config.MaxGroundHeight;
- Settings.MinGroundHeight = ARLocation.Config.MinGroundHeight;
- Settings.InitialGroundHeightGuess = ARLocation.Config.InitialGroundHeightGuess;
- Settings.Smoothing = ARLocation.Config.GroundHeightSmoothingFactor;
- }
- state.CurrentGroundY = -Settings.InitialGroundHeightGuess;
- arPlaneManager.planesChanged += ArPlaneManagerOnPlanesChanged;
- UpdateObjectHeight();
- }
- void OnEnable()
- {
- if (arPlaneManager)
- {
- arPlaneManager.planesChanged += ArPlaneManagerOnPlanesChanged;
- }
- }
- void OnDisable()
- {
- if (arPlaneManager)
- {
- arPlaneManager.planesChanged -= ArPlaneManagerOnPlanesChanged;
- }
- }
- private void ArPlaneManagerOnPlanesChanged(ARPlanesChangedEventArgs eventArgs)
- {
- var addedPlanes = eventArgs.added;
- var updatedPlanes = eventArgs.updated;
- if (addedPlanes.Count <= 0 && updatedPlanes.Count <= 0)
- {
- // Debug.Log("[AR+GPS][GroundHeight#ArPlaneManagerOnPlanesChanged]: No added or modified planes!");
- return;
- }
- foreach (ARPlane plane in addedPlanes)
- {
- ProcessPlane(plane);
- }
- foreach (ARPlane plane in updatedPlanes)
- {
- ProcessPlane(plane);
- }
- UpdateObjectHeight();
- }
- private void ProcessPlane(ARPlane plane)
- {
- // Debug.Log("[AR+GPS][GroundHeight#ProcessPlane]: Processing plane " + plane.trackableId.subId1 + ", " + plane.trackableId.subId2);
- if (plane.alignment != PlaneAlignment.HorizontalDown && plane.alignment != PlaneAlignment.HorizontalUp)
- {
- // Debug.LogWarning("[AR+GPS][GroundHeight#ProcessPlane]: Wrong plane alignment!");
- return;
- }
- if (!IsValidHeightForGround(plane.center.y))
- {
- // Debug.LogWarning("[AR+GPS][GroundHeight#ProcessPlane]: Invalid plane height!");
- return;
- }
- var distance = MathUtils.HorizontalDistance(transform.position, plane.center);
- if (!(state.CurrentPlaneDistance < 0) && (distance >= state.CurrentPlaneDistance))
- {
- // Debug.LogWarning("[AR+GPS][GroundHeight#ProcessPlane]: Plane too far!");
- return;
- }
- // Debug.Log("[AR+GPS][GroundHeight#ProcessPlane]: New plane Y: " + plane.center.y);
- state.CurrentPlaneDistance = distance;
- state.CurrentGroundY = plane.center.y;
- state.CurrentPlaneCenter = plane.center;
- state.NeedsUpdate = true;
- }
- #else
- private PlaneFinderBehaviour planeFinderBehaviour;
- private void Start()
- {
- planeFinderBehaviour = FindObjectOfType<PlaneFinderBehaviour>();
- mainCamera = ARLocationManager.Instance.MainCamera;
- if (planeFinderBehaviour == null)
- {
- Logger.WarnFromMethod("VuforiaGroundHeight", "Start", "No planeFinderBehaviour!");
- }
- if (Settings.UseArLocationConfigSettings)
- {
- Settings.MaxGroundHeight = ARLocation.Config.MaxGroundHeight;
- Settings.MinGroundHeight = ARLocation.Config.MinGroundHeight;
- Settings.InitialGroundHeightGuess = ARLocation.Config.InitialGroundHeightGuess;
- Settings.MinHitDistance = ARLocation.Config.VuforiaGroundHitTestDistance;
- Settings.Smoothing = ARLocation.Config.GroundHeightSmoothingFactor;
- }
- state.CurrentGroundY = -Settings.InitialGroundHeightGuess;
- planeFinderBehaviour.Height = Settings.InitialGroundHeightGuess;
- planeFinderBehaviour.HitTestMode = HitTestMode.AUTOMATIC;
- planeFinderBehaviour.OnAutomaticHitTest.AddListener(HitTestHandler);
- planeFinderBehaviour.OnInteractiveHitTest.AddListener(HitTestHandler);
- UpdateObjectHeight();
- }
- private void HitTestHandler(HitTestResult result)
- {
- //Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"result.Position = {result.Position}");
- // If the ground height is not in range, reject
- // var height = -1.0f * result.Position.y;
- if (!IsValidHeightForGround(result.Position.y))
- {
- //Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"Not in range: {result.Position.y} {height}) > {Settings.MinGroundHeight}");
- return;
- }
- var distanceToObject = MathUtils.HorizontalDistance(transform.position, result.Position);
- // If hit to close to previous hit do nothing
- if (state.CurrentPlaneDistance >= 0 && distanceToObject <= Settings.MinHitDistance)
- {
- //Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"Too close :{distanceToObject}");
- return;
- }
- // If there is no previous hit, or if the new hit is closes to the object, apply new
- // hit point.
- if (state.CurrentPlaneDistance < 0 || distanceToObject < state.CurrentPlaneDistance)
- {
- state.CurrentPlaneDistance = distanceToObject;
- state.CurrentGroundY = result.Position.y;
- state.NeedsUpdate = true;
- UpdateObjectHeight();
- // Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"New ground Y = {state.CurrentGroundY}");
- }
- }
- #endif
- public void UpdateObjectHeight(bool force = false)
- {
- if (!state.NeedsUpdate && !force) return;
- // Debug.Log("[AR+GPS][GroundHeight#UpdateObjectHeight]: Setting Y to " + state.CurrentGroundY);
- if (Settings.Smoothing <= 0)
- {
- transform.position = MathUtils.SetY(transform.position, state.CurrentGroundY + Settings.Altitude);
- }
- state.NeedsUpdate = false;
- }
- private bool IsValidHeightForGround(float y)
- {
- var diff = (mainCamera.transform.position.y - y);
- return (diff >= Settings.MinGroundHeight) && (diff <= Settings.MaxGroundHeight);
- }
- public void Update()
- {
- if (Settings.Smoothing <= 0 || Settings.DisableUpdate) return;
- if (Mathf.Abs(transform.position.y - (state.CurrentGroundY + Settings.Altitude)) <= Settings.Precision)
- {
- transform.position = MathUtils.SetY(transform.position, (state.CurrentGroundY + Settings.Altitude));
- return;
- }
- var t = 1.0f - Mathf.Pow(Settings.Smoothing, Time.deltaTime);
- var position = transform.position;
- var value = Mathf.Lerp(position.y, (state.CurrentGroundY + Settings.Altitude), t);
- position = MathUtils.SetY(position, value);
- transform.position = position;
- }
- }
- }
|