AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
StabilizationPlaneModifier.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
5 using UnityEngine;
6 
7 #if UNITY_WSA
8 #if UNITY_2017_2_OR_NEWER
9 using UnityEngine.XR.WSA;
10 #else
11 using UnityEngine.VR.WSA;
12 #endif
13 #endif
14 
15 namespace HoloToolkit.Unity
16 {
20  public class StabilizationPlaneModifier : Singleton<StabilizationPlaneModifier>
21  {
22  [Tooltip("Checking enables SetFocusPointForFrame to set the stabilization plane.")]
23  public bool SetStabilizationPlane = true;
24 
25  [Tooltip("When lerping, use unscaled time. This is useful for games that have a pause mechanism or otherwise adjust the game timescale.")]
26  public bool UseUnscaledTime = true;
27 
28  [Tooltip("Lerp speed when moving focus point closer.")]
29  public float LerpStabilizationPlanePowerCloser = 4.0f;
30 
31  [Tooltip("Lerp speed when moving focus point farther away.")]
32  public float LerpStabilizationPlanePowerFarther = 7.0f;
33 
34  [SerializeField, Tooltip("Used to temporarily override the location of the stabilization plane.")]
35  private Transform targetOverride;
36  public Transform TargetOverride
37  {
38  get
39  {
40  return targetOverride;
41  }
42  set
43  {
44  if (targetOverride != value)
45  {
46  targetOverride = value;
47  if (targetOverride)
48  {
49  targetOverridePreviousPosition = targetOverride.position;
50  }
51  }
52  }
53  }
54 
55  [SerializeField, Tooltip("Keeps track of position-based velocity for the target object.")]
56  private bool trackVelocity;
57  public bool TrackVelocity
58  {
59  get
60  {
61  return trackVelocity;
62  }
63  set
64  {
65  trackVelocity = value;
66  if (TargetOverride)
67  {
68  targetOverridePreviousPosition = TargetOverride.position;
69  }
70  }
71  }
72 
73  [Tooltip("Use the GazeManager class to set the plane to the gazed upon hologram. If disabled, the plane will always be at a constant distance.")]
74  public bool UseGazeManager = true;
75 
76  [Tooltip("Default distance to set plane if plane is gaze-locked or if no object is hit.")]
77  public float DefaultPlaneDistance = 2.0f;
78 
79  [Tooltip("Visualize the plane at runtime.")]
80  public bool DrawGizmos;
81 
85  private Vector3 planePosition;
86 
91  private float currentPlaneDistance = 4.0f;
92 
96  private Vector3 targetOverridePreviousPosition;
97 
101  private void LateUpdate()
102  {
103  if (SetStabilizationPlane)
104  {
105  float deltaTime = UseUnscaledTime
106  ? Time.unscaledDeltaTime
107  : Time.deltaTime;
108 
109  if (TargetOverride != null)
110  {
111  ConfigureTransformOverridePlane(deltaTime);
112  }
113  else if (UseGazeManager)
114  {
115  ConfigureGazeManagerPlane(deltaTime);
116  }
117  else
118  {
119  ConfigureFixedDistancePlane(deltaTime);
120  }
121  }
122  }
123 
128  private void OnValidate()
129  {
130  TrackVelocity = trackVelocity;
131  TargetOverride = targetOverride;
132  }
133 
137  private Vector3 GazeOrigin
138  {
139  get
140  {
142  {
143  return GazeManager.Instance.GazeOrigin;
144  }
145  return CameraCache.Main.transform.position;
146  }
147  }
148 
152  private Vector3 GazeNormal
153  {
154  get
155  {
157  {
158  return GazeManager.Instance.GazeNormal;
159  }
160  return CameraCache.Main.transform.forward;
161  }
162  }
163 
169  private bool TryGetGazeHitPosition(out Vector3 hitPosition)
170  {
172  {
173  hitPosition = GazeManager.Instance.HitPosition;
174  return true;
175  }
176  hitPosition = Vector3.zero;
177  return false;
178  }
179 
183  private void ConfigureTransformOverridePlane(float deltaTime)
184  {
185  planePosition = TargetOverride.position;
186 
187  Vector3 velocity = Vector3.zero;
188  if (TrackVelocity)
189  {
190  velocity = UpdateVelocity(deltaTime);
191  }
192 
193 #if UNITY_WSA
194  // Place the plane at the desired depth in front of the user and billboard it to the gaze origin.
195  HolographicSettings.SetFocusPointForFrame(planePosition, -GazeNormal, velocity);
196 #endif
197  }
198 
202  private void ConfigureGazeManagerPlane(float deltaTime)
203  {
204  Vector3 gazeOrigin = GazeOrigin;
205  Vector3 gazeDirection = GazeNormal;
206 
207  // Calculate the delta between gaze origin's position and current hit position. If no object is hit, use default distance.
208  float focusPointDistance;
209  Vector3 gazeHitPosition;
210  if (TryGetGazeHitPosition(out gazeHitPosition))
211  {
212  focusPointDistance = (gazeOrigin - gazeHitPosition).magnitude;
213  }
214  else
215  {
216  focusPointDistance = DefaultPlaneDistance;
217  }
218 
219  float lerpPower = focusPointDistance > currentPlaneDistance ? LerpStabilizationPlanePowerFarther
220  : LerpStabilizationPlanePowerCloser;
221 
222  // Smoothly move the focus point from previous hit position to new position.
223  currentPlaneDistance = Mathf.Lerp(currentPlaneDistance, focusPointDistance, lerpPower * deltaTime);
224 
225  planePosition = gazeOrigin + (gazeDirection * currentPlaneDistance);
226 
227 #if UNITY_WSA
228  HolographicSettings.SetFocusPointForFrame(planePosition, -gazeDirection, Vector3.zero);
229 #endif
230  }
231 
235  private void ConfigureFixedDistancePlane(float deltaTime)
236  {
237  Vector3 gazeOrigin = GazeOrigin;
238  Vector3 gazeNormal = GazeNormal;
239 
240  float lerpPower = DefaultPlaneDistance > currentPlaneDistance ? LerpStabilizationPlanePowerFarther
241  : LerpStabilizationPlanePowerCloser;
242 
243  // Smoothly move the focus point from previous hit position to new position.
244  currentPlaneDistance = Mathf.Lerp(currentPlaneDistance, DefaultPlaneDistance, lerpPower * deltaTime);
245 
246  planePosition = gazeOrigin + (gazeNormal * currentPlaneDistance);
247 #if UNITY_WSA
248  HolographicSettings.SetFocusPointForFrame(planePosition, -gazeNormal, Vector3.zero);
249 #endif
250  }
251 
255  private Vector3 UpdateVelocity(float deltaTime)
256  {
257  // Roughly calculate the velocity based on previous position, current position, and frame time.
258  Vector3 velocity = (TargetOverride.position - targetOverridePreviousPosition) / deltaTime;
259  targetOverridePreviousPosition = TargetOverride.position;
260  return velocity;
261  }
262 
266  private void OnDrawGizmos()
267  {
268  if (Application.isPlaying && DrawGizmos)
269  {
270  Vector3 focalPlaneNormal = -GazeNormal;
271  Vector3 planeUp = Vector3.Cross(Vector3.Cross(focalPlaneNormal, Vector3.up), focalPlaneNormal);
272  Gizmos.matrix = Matrix4x4.TRS(planePosition, Quaternion.LookRotation(focalPlaneNormal, planeUp), new Vector3(4.0f, 3.0f, 0.01f));
273 
274  Color gizmoColor = Color.magenta;
275  gizmoColor.a = 0.5f;
276  Gizmos.color = gizmoColor;
277 
278  Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
279  Gizmos.DrawCube(Vector3.zero, Vector3.one);
280  }
281  }
282  }
283 }
The gaze manager manages everything related to a gaze ray that can interact with other objects...
Definition: GazeManager.cs:13
The purpose of this class is to provide a cached reference to the main camera. Calling Camera...
Definition: CameraCache.cs:12
static T Instance
Returns the Singleton instance of the classes type. If no instance is found, then we search for an in...
Definition: Singleton.cs:26
static Camera Main
Returns a cached reference to the main camera and uses Camera.main if it hasn&#39;t been cached yet...
Definition: CameraCache.cs:20
StabilizationPlaneModifier handles the setting of the stabilization plane in several ways...
static bool IsInitialized
Returns whether the instance has been initialized or not.
Definition: Singleton.cs:58
Singleton behaviour class, used for components that should only have one instance.
Definition: Singleton.cs:14