SolverHandler.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License. See LICENSE in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using UnityEngine;
  7. using UnityEngine.Serialization;
  8. using Microsoft.MixedReality.Toolkit.Utilities.Solvers;
  9. using XRTool.Util;
  10. namespace Microsoft.MixedReality.Toolkit.Utilities.Solvers
  11. {
  12. /// <summary>
  13. /// This class handles the solver components that are attached to this <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see>
  14. /// </summary>
  15. public class SolverHandler : MonoBehaviour
  16. {
  17. [SerializeField]
  18. [Tooltip("Manual override for when TrackedTargetType is set to CustomOverride")]
  19. private Transform transformOverride;
  20. /// <summary>
  21. /// Manual override for when TrackedTargetType is set to CustomOverride
  22. /// </summary>
  23. public Transform TransformOverride
  24. {
  25. set
  26. {
  27. if (value != null && transformOverride != value)
  28. {
  29. transformOverride = value;
  30. RefreshTrackedObject();
  31. }
  32. }
  33. }
  34. [SerializeField]
  35. [Tooltip("Add an additional offset of the tracked object to base the solver on. Useful for tracking something like a halo position above your head or off the side of a controller.")]
  36. private Vector3 additionalOffset;
  37. /// <summary>
  38. /// Add an additional offset of the tracked object to base the solver on. Useful for tracking something like a halo position above your head or off the side of a controller.
  39. /// </summary>
  40. public Vector3 AdditionalOffset
  41. {
  42. get => additionalOffset;
  43. set
  44. {
  45. if (additionalOffset != value)
  46. {
  47. additionalOffset = value;
  48. RefreshTrackedObject();
  49. }
  50. }
  51. }
  52. [SerializeField]
  53. [Tooltip("Add an additional rotation on top of the tracked object. Useful for tracking what is essentially the up or right/left vectors.")]
  54. private Vector3 additionalRotation;
  55. /// <summary>
  56. /// Add an additional rotation on top of the tracked object. Useful for tracking what is essentially the up or right/left vectors.
  57. /// </summary>
  58. public Vector3 AdditionalRotation
  59. {
  60. get => additionalRotation;
  61. set
  62. {
  63. if (additionalRotation != value)
  64. {
  65. additionalRotation = value;
  66. RefreshTrackedObject();
  67. }
  68. }
  69. }
  70. [SerializeField]
  71. [Tooltip("Whether or not this SolverHandler calls SolverUpdate() every frame. Only one SolverHandler should manage SolverUpdate(). This setting does not affect whether the Target Transform of this SolverHandler gets updated or not.")]
  72. private bool updateSolvers = true;
  73. /// <summary>
  74. /// Whether or not this SolverHandler calls SolverUpdate() every frame. Only one SolverHandler should manage SolverUpdate(). This setting does not affect whether the Target Transform of this SolverHandler gets updated or not.
  75. /// </summary>
  76. public bool UpdateSolvers
  77. {
  78. get => updateSolvers;
  79. set => updateSolvers = value;
  80. }
  81. protected readonly List<Solver> solvers = new List<Solver>();
  82. private bool updateSolversList = false;
  83. /// <summary>
  84. /// List of solvers that this handler will manage and update
  85. /// </summary>
  86. public IReadOnlyCollection<Solver> Solvers
  87. {
  88. get => solvers.AsReadOnly();
  89. set
  90. {
  91. if (value != null)
  92. {
  93. solvers.Clear();
  94. solvers.AddRange(value);
  95. }
  96. }
  97. }
  98. /// <summary>
  99. /// The position the solver is trying to move to.
  100. /// </summary>
  101. public Vector3 GoalPosition { get; set; }
  102. /// <summary>
  103. /// The rotation the solver is trying to rotate to.
  104. /// </summary>
  105. public Quaternion GoalRotation { get; set; }
  106. /// <summary>
  107. /// The scale the solver is trying to scale to.
  108. /// </summary>
  109. public Vector3 GoalScale { get; set; }
  110. /// <summary>
  111. /// Alternate scale.
  112. /// </summary>
  113. public Vector3Smoothed AltScale { get; set; }
  114. /// <summary>
  115. /// The timestamp the solvers will use to calculate with.
  116. /// </summary>
  117. public float DeltaTime { get; set; }
  118. /// <summary>
  119. /// The target transform that the solvers will act upon.
  120. /// </summary>
  121. public Transform TransformTarget
  122. {
  123. get
  124. {
  125. if (IsInvalidTracking())
  126. {
  127. RefreshTrackedObject();
  128. }
  129. return trackingTarget != null ? trackingTarget.transform : null;
  130. }
  131. }
  132. // Hidden GameObject managed by this component and attached as a child to the tracked target type (i.e head, hand etc)
  133. private GameObject trackingTarget;
  134. private float lastUpdateTime;
  135. #region MonoBehaviour Implementation
  136. private void Awake()
  137. {
  138. GoalScale = Vector3.one;
  139. AltScale = new Vector3Smoothed(Vector3.one, 0.1f);
  140. DeltaTime = Time.deltaTime;
  141. lastUpdateTime = Time.realtimeSinceStartup;
  142. }
  143. protected virtual void Start()
  144. {
  145. RefreshTrackedObject();
  146. }
  147. protected virtual void Update()
  148. {
  149. if (IsInvalidTracking())
  150. {
  151. RefreshTrackedObject();
  152. }
  153. DeltaTime = Time.realtimeSinceStartup - lastUpdateTime;
  154. lastUpdateTime = Time.realtimeSinceStartup;
  155. }
  156. private void LateUpdate()
  157. {
  158. if (updateSolversList)
  159. {
  160. IEnumerable<Solver> inspectorOrderedSolvers = GetComponents<Solver>().Intersect(solvers);
  161. //Solvers = inspectorOrderedSolvers.Union(Solvers).ToReadOnlyCollection();
  162. updateSolversList = false;
  163. }
  164. if (UpdateSolvers)
  165. {
  166. // Before calling solvers, update goal to be the transform so that working and transform will match
  167. GoalPosition = transform.position;
  168. GoalRotation = transform.rotation;
  169. GoalScale = transform.localScale;
  170. for (int i = 0; i < solvers.Count; ++i)
  171. {
  172. Solver solver = solvers[i];
  173. if (solver != null && solver.enabled)
  174. {
  175. solver.SolverUpdateEntry();
  176. }
  177. }
  178. }
  179. }
  180. protected void OnDestroy()
  181. {
  182. DetachFromCurrentTrackedObject();
  183. }
  184. #endregion MonoBehaviour Implementation
  185. /// <summary>
  186. /// Clears the transform target and attaches to the current <see cref="TrackedTargetType"/>.
  187. /// </summary>
  188. public void RefreshTrackedObject()
  189. {
  190. DetachFromCurrentTrackedObject();
  191. AttachToNewTrackedObject();
  192. }
  193. /// <summary>
  194. /// Adds <paramref name="solver"/> to the list of <see cref="Solvers"/> guaranteeing inspector ordering.
  195. /// </summary>
  196. public void RegisterSolver(Solver solver)
  197. {
  198. if (!solvers.Contains(solver))
  199. {
  200. solvers.Add(solver);
  201. updateSolversList = true;
  202. }
  203. }
  204. /// <summary>
  205. /// Removes <paramref name="solver"/> from the list of <see cref="Solvers"/>.
  206. /// </summary>
  207. public void UnregisterSolver(Solver solver)
  208. {
  209. solvers.Remove(solver);
  210. }
  211. protected virtual void DetachFromCurrentTrackedObject()
  212. {
  213. if (trackingTarget != null)
  214. {
  215. Destroy(trackingTarget);
  216. trackingTarget = null;
  217. }
  218. }
  219. protected virtual void AttachToNewTrackedObject()
  220. {
  221. if (!transformOverride&& GameSession.Instance&& GameSession.Instance.mainCamera)
  222. {
  223. transformOverride = GameSession.Instance.mainCamera.transform;
  224. }
  225. if (transformOverride)
  226. {
  227. TrackTransform(transformOverride);
  228. }
  229. }
  230. private void TrackTransform(Transform target)
  231. {
  232. if (trackingTarget != null || target == null) return;
  233. string name = string.Format("SolverHandler Target on {0} with offset {1}, {2}", target.gameObject.name, AdditionalOffset, AdditionalRotation);
  234. trackingTarget = new GameObject(name);
  235. trackingTarget.hideFlags = HideFlags.HideInHierarchy;
  236. trackingTarget.transform.parent = target;
  237. trackingTarget.transform.localPosition = Vector3.Scale(AdditionalOffset, trackingTarget.transform.localScale);
  238. trackingTarget.transform.localRotation = Quaternion.Euler(AdditionalRotation);
  239. }
  240. /// <summary>
  241. /// Returns true if the solver handler's transform target is not valid
  242. /// </summary>
  243. /// <returns>true if not tracking valid hands and/or target, false otherwise</returns>
  244. private bool IsInvalidTracking()
  245. {
  246. if (trackingTarget == null)
  247. {
  248. return true;
  249. }
  250. return false;
  251. }
  252. }
  253. }