SW 중심대학 OSS GIT 서버 박건태, 이승준, 고기완, 이준호 새로운 배포
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
9.4 KiB

4 years ago
  1. using System;
  2. using UnityEngine;
  3. #if !ARGPS_USE_VUFORIA
  4. using UnityEngine.XR.ARFoundation;
  5. using UnityEngine.XR.ARSubsystems;
  6. #endif
  7. // ReSharper disable once RedundantUsingDirective
  8. using Logger = ARLocation.Utils.Logger;
  9. #if ARGPS_USE_VUFORIA
  10. using Vuforia;
  11. #endif
  12. namespace ARLocation
  13. {
  14. /// <summary>
  15. /// This component will change the Y component of the GameObject's position,
  16. /// so that it is set to the level of the nearest detected ground plane.
  17. /// </summary>
  18. [DisallowMultipleComponent]
  19. public class GroundHeight : MonoBehaviour
  20. {
  21. [Serializable]
  22. public class SettingsData
  23. {
  24. [Range(0, 10)]
  25. public float InitialGroundHeightGuess = 1.4f;
  26. [Range(0, 10)]
  27. public float MinGroundHeight = 0.4f;
  28. [Range(0, 10)]
  29. public float MaxGroundHeight = 3.0f;
  30. [Range(0, 1)]
  31. public float Smoothing = 0.05f;
  32. public float Altitude;
  33. public bool DisableUpdate;
  34. [Range(0, 0.1f)]
  35. public float Precision = 0.005f;
  36. public bool UseArLocationConfigSettings = true;
  37. #if ARGPS_USE_VUFORIA
  38. public float MinHitDistance = 0.5f;
  39. #endif
  40. }
  41. [Serializable]
  42. public class StateData
  43. {
  44. public float CurrentGroundY;
  45. public float CurrentPlaneDistance = -1.0f;
  46. public Vector3 CurrentPlaneCenter;
  47. public bool NeedsUpdate = true;
  48. }
  49. public float CurrentGroundY => state.CurrentGroundY;
  50. public SettingsData Settings = new SettingsData();
  51. private readonly StateData state = new StateData();
  52. private Camera mainCamera;
  53. #if !ARGPS_USE_VUFORIA
  54. private ARPlaneManager arPlaneManager;
  55. private float targetY;
  56. // Start is called before the first frame update
  57. void Start()
  58. {
  59. arPlaneManager = FindObjectOfType<ARPlaneManager>();
  60. var arSessionOrigin = FindObjectOfType<ARSessionOrigin>();
  61. mainCamera = ARLocationManager.Instance.MainCamera;
  62. if (arPlaneManager == null)
  63. {
  64. if (arSessionOrigin == null)
  65. {
  66. Debug.LogWarning("[AR+GPS][GroundHeight#Start]: ARSessionOrigin not present in the scene!");
  67. return;
  68. }
  69. arPlaneManager = arSessionOrigin.gameObject.AddComponent<ARPlaneManager>();
  70. arPlaneManager.detectionMode = PlaneDetectionMode.Horizontal;
  71. }
  72. if (Settings.UseArLocationConfigSettings)
  73. {
  74. Settings.MaxGroundHeight = ARLocation.Config.MaxGroundHeight;
  75. Settings.MinGroundHeight = ARLocation.Config.MinGroundHeight;
  76. Settings.InitialGroundHeightGuess = ARLocation.Config.InitialGroundHeightGuess;
  77. Settings.Smoothing = ARLocation.Config.GroundHeightSmoothingFactor;
  78. }
  79. state.CurrentGroundY = -Settings.InitialGroundHeightGuess;
  80. arPlaneManager.planesChanged += ArPlaneManagerOnPlanesChanged;
  81. UpdateObjectHeight();
  82. }
  83. private void ArPlaneManagerOnPlanesChanged(ARPlanesChangedEventArgs eventArgs)
  84. {
  85. var addedPlanes = eventArgs.added;
  86. var updatedPlanes = eventArgs.updated;
  87. if (addedPlanes.Count <= 0 && updatedPlanes.Count <= 0)
  88. {
  89. // Debug.Log("[AR+GPS][GroundHeight#ArPlaneManagerOnPlanesChanged]: No added or modified planes!");
  90. return;
  91. }
  92. foreach (ARPlane plane in addedPlanes)
  93. {
  94. ProcessPlane(plane);
  95. }
  96. foreach (ARPlane plane in updatedPlanes)
  97. {
  98. ProcessPlane(plane);
  99. }
  100. UpdateObjectHeight();
  101. }
  102. private void ProcessPlane(ARPlane plane)
  103. {
  104. // Debug.Log("[AR+GPS][GroundHeight#ProcessPlane]: Processing plane " + plane.trackableId.subId1 + ", " + plane.trackableId.subId2);
  105. if (plane.alignment != PlaneAlignment.HorizontalDown && plane.alignment != PlaneAlignment.HorizontalUp)
  106. {
  107. // Debug.LogWarning("[AR+GPS][GroundHeight#ProcessPlane]: Wrong plane alignment!");
  108. return;
  109. }
  110. if (!IsValidHeightForGround(plane.center.y))
  111. {
  112. // Debug.LogWarning("[AR+GPS][GroundHeight#ProcessPlane]: Invalid plane height!");
  113. return;
  114. }
  115. var distance = MathUtils.HorizontalDistance(transform.position, plane.center);
  116. if (!(state.CurrentPlaneDistance < 0) && (distance >= state.CurrentPlaneDistance))
  117. {
  118. // Debug.LogWarning("[AR+GPS][GroundHeight#ProcessPlane]: Plane too far!");
  119. return;
  120. }
  121. // Debug.Log("[AR+GPS][GroundHeight#ProcessPlane]: New plane Y: " + plane.center.y);
  122. state.CurrentPlaneDistance = distance;
  123. state.CurrentGroundY = plane.center.y;
  124. state.CurrentPlaneCenter = plane.center;
  125. state.NeedsUpdate = true;
  126. }
  127. #else
  128. private PlaneFinderBehaviour planeFinderBehaviour;
  129. private void Start()
  130. {
  131. planeFinderBehaviour = FindObjectOfType<PlaneFinderBehaviour>();
  132. mainCamera = ARLocationManager.Instance.MainCamera;
  133. if (planeFinderBehaviour == null)
  134. {
  135. Logger.WarnFromMethod("VuforiaGroundHeight", "Start", "No planeFinderBehaviour!");
  136. }
  137. if (Settings.UseArLocationConfigSettings)
  138. {
  139. Settings.MaxGroundHeight = ARLocation.Config.MaxGroundHeight;
  140. Settings.MinGroundHeight = ARLocation.Config.MinGroundHeight;
  141. Settings.InitialGroundHeightGuess = ARLocation.Config.InitialGroundHeightGuess;
  142. Settings.MinHitDistance = ARLocation.Config.VuforiaGroundHitTestDistance;
  143. Settings.Smoothing = ARLocation.Config.GroundHeightSmoothingFactor;
  144. }
  145. state.CurrentGroundY = -Settings.InitialGroundHeightGuess;
  146. planeFinderBehaviour.Height = Settings.InitialGroundHeightGuess;
  147. planeFinderBehaviour.HitTestMode = HitTestMode.AUTOMATIC;
  148. planeFinderBehaviour.OnAutomaticHitTest.AddListener(HitTestHandler);
  149. planeFinderBehaviour.OnInteractiveHitTest.AddListener(HitTestHandler);
  150. UpdateObjectHeight();
  151. }
  152. private void HitTestHandler(HitTestResult result)
  153. {
  154. //Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"result.Position = {result.Position}");
  155. // If the ground height is not in range, reject
  156. // var height = -1.0f * result.Position.y;
  157. if (!IsValidHeightForGround(result.Position.y))
  158. {
  159. //Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"Not in range: {result.Position.y} {height}) > {Settings.MinGroundHeight}");
  160. return;
  161. }
  162. var distanceToObject = MathUtils.HorizontalDistance(transform.position, result.Position);
  163. // If hit to close to previous hit do nothing
  164. if (state.CurrentPlaneDistance >= 0 && distanceToObject <= Settings.MinHitDistance)
  165. {
  166. //Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"Too close :{distanceToObject}");
  167. return;
  168. }
  169. // If there is no previous hit, or if the new hit is closes to the object, apply new
  170. // hit point.
  171. if (state.CurrentPlaneDistance < 0 || distanceToObject < state.CurrentPlaneDistance)
  172. {
  173. state.CurrentPlaneDistance = distanceToObject;
  174. state.CurrentGroundY = result.Position.y;
  175. state.NeedsUpdate = true;
  176. UpdateObjectHeight();
  177. // Logger.LogFromMethod("VuforiaGroundHeight", "HitTestHandler", $"New ground Y = {state.CurrentGroundY}");
  178. }
  179. }
  180. #endif
  181. private void UpdateObjectHeight()
  182. {
  183. if (!state.NeedsUpdate) return;
  184. // Debug.Log("[AR+GPS][GroundHeight#UpdateObjectHeight]: Setting Y to " + state.CurrentGroundY);
  185. if (Settings.Smoothing <= 0)
  186. {
  187. transform.position = MathUtils.SetY(transform.position, state.CurrentGroundY + Settings.Altitude);
  188. }
  189. state.NeedsUpdate = false;
  190. }
  191. private bool IsValidHeightForGround(float y)
  192. {
  193. var diff = (mainCamera.transform.position.y - y);
  194. return (diff >= Settings.MinGroundHeight) && (diff <= Settings.MaxGroundHeight);
  195. }
  196. public void Update()
  197. {
  198. if (Settings.Smoothing <= 0 || Settings.DisableUpdate) return;
  199. if (Mathf.Abs(transform.position.y - (state.CurrentGroundY + Settings.Altitude)) <= Settings.Precision)
  200. {
  201. transform.position = MathUtils.SetY(transform.position, (state.CurrentGroundY + Settings.Altitude));
  202. return;
  203. }
  204. var t = 1.0f - Mathf.Pow(Settings.Smoothing, Time.deltaTime);
  205. var position = transform.position;
  206. var value = Mathf.Lerp(position.y, (state.CurrentGroundY + Settings.Altitude), t);
  207. position = MathUtils.SetY(position, value);
  208. transform.position = position;
  209. }
  210. }
  211. }