AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
DirectionIndicator.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 
4 using UnityEngine;
5 
6 namespace HoloToolkit.Unity
7 {
12  public class DirectionIndicator : MonoBehaviour
13  {
14  [Tooltip("The Cursor object the direction indicator will be positioned around.")]
15  public GameObject Cursor;
16 
17  [Tooltip("Model to display the direction to the object this script is attached to.")]
18  public GameObject DirectionIndicatorObject;
19 
20  [Tooltip("Color to shade the direction indicator.")]
21  public Color DirectionIndicatorColor = Color.blue;
22 
23  [Tooltip("Allowable percentage inside the holographic frame to continue to show a directional indicator.")]
24  [Range(-0.3f, 0.3f)]
25  public float VisibilitySafeFactor = 0.1f;
26 
27  [Tooltip("Multiplier to decrease the distance from the cursor center an object is rendered to keep it in view.")]
28  [Range(0.1f, 1.0f)]
29  public float MetersFromCursor = 0.3f;
30 
31  // The default rotation of the cursor direction indicator.
32  private Quaternion directionIndicatorDefaultRotation = Quaternion.identity;
33 
34  // Cache the MeshRenderer for the on-cursor indicator since it will be enabled and disabled frequently.
35  private Renderer directionIndicatorRenderer;
36 
37  // Cache the Material to prevent material leak.
38  private Material indicatorMaterial;
39 
40  // Check if the cursor direction indicator is visible.
41  private bool isDirectionIndicatorVisible;
42 
43  public void Awake()
44  {
45  if (Cursor == null)
46  {
47  Debug.LogError("Please include a GameObject for the cursor.");
48  }
49 
50  if (DirectionIndicatorObject == null)
51  {
52  Debug.LogError("Please include a GameObject for the Direction Indicator.");
53  }
54 
55  // Instantiate the direction indicator.
56  DirectionIndicatorObject = InstantiateDirectionIndicator(DirectionIndicatorObject);
57 
58  if (DirectionIndicatorObject == null)
59  {
60  Debug.LogError("Direction Indicator failed to instantiate.");
61  }
62  }
63 
64  public void OnDestroy()
65  {
66  DestroyImmediate(indicatorMaterial);
67  Destroy(DirectionIndicatorObject);
68  }
69 
70  private GameObject InstantiateDirectionIndicator(GameObject directionIndicator)
71  {
72  if (directionIndicator == null)
73  {
74  return null;
75  }
76 
77  GameObject indicator = Instantiate(directionIndicator);
78 
79  // Set local variables for the indicator.
80  directionIndicatorDefaultRotation = indicator.transform.rotation;
81  directionIndicatorRenderer = indicator.GetComponent<Renderer>();
82 
83  // Start with the indicator disabled.
84  directionIndicatorRenderer.enabled = false;
85 
86  // Remove any colliders and rigidbodies so the indicators do not interfere with Unity's physics system.
87  foreach (Collider indicatorCollider in indicator.GetComponents<Collider>())
88  {
89  Destroy(indicatorCollider);
90  }
91 
92  foreach (Rigidbody rigidBody in indicator.GetComponents<Rigidbody>())
93  {
94  Destroy(rigidBody);
95  }
96 
97  indicatorMaterial = directionIndicatorRenderer.material;
98  indicatorMaterial.color = DirectionIndicatorColor;
99  indicatorMaterial.SetColor("_TintColor", DirectionIndicatorColor);
100 
101  return indicator;
102  }
103 
104  public void Update()
105  {
106  if (DirectionIndicatorObject == null)
107  {
108  return;
109  }
110  Camera mainCamera = CameraCache.Main;
111  // Direction from the Main Camera to this script's parent gameObject.
112  Vector3 camToObjectDirection = gameObject.transform.position - mainCamera.transform.position;
113  camToObjectDirection.Normalize();
114 
115  // The cursor indicator should only be visible if the target is not visible.
116  isDirectionIndicatorVisible = !IsTargetVisible(mainCamera);
117  directionIndicatorRenderer.enabled = isDirectionIndicatorVisible;
118 
119  if (isDirectionIndicatorVisible)
120  {
121  Vector3 position;
122  Quaternion rotation;
123  GetDirectionIndicatorPositionAndRotation(
124  camToObjectDirection,
125  mainCamera.transform,
126  out position,
127  out rotation);
128 
129  DirectionIndicatorObject.transform.position = position;
130  DirectionIndicatorObject.transform.rotation = rotation;
131  }
132  }
133 
134  private bool IsTargetVisible(Camera mainCamera)
135  {
136  // This will return true if the target's mesh is within the Main Camera's view frustums.
137  Vector3 targetViewportPosition = mainCamera.WorldToViewportPoint(gameObject.transform.position);
138  return (targetViewportPosition.x > VisibilitySafeFactor && targetViewportPosition.x < 1 - VisibilitySafeFactor &&
139  targetViewportPosition.y > VisibilitySafeFactor && targetViewportPosition.y < 1 - VisibilitySafeFactor &&
140  targetViewportPosition.z > 0);
141  }
142 
143  private void GetDirectionIndicatorPositionAndRotation(Vector3 camToObjectDirection, Transform cameraTransform, out Vector3 position, out Quaternion rotation)
144  {
145  // Find position:
146  // Save the cursor transform position in a variable.
147  Vector3 origin = Cursor.transform.position;
148  // Project the camera to target direction onto the screen plane.
149  Vector3 cursorIndicatorDirection = Vector3.ProjectOnPlane(camToObjectDirection, -1 * cameraTransform.forward);
150  cursorIndicatorDirection.Normalize();
151 
152  // If the direction is 0, set the direction to the right.
153  // This will only happen if the camera is facing directly away from the target.
154  if (cursorIndicatorDirection == Vector3.zero)
155  {
156  cursorIndicatorDirection = cameraTransform.right;
157  }
158 
159  // The final position is translated from the center of the screen along this direction vector.
160  position = origin + cursorIndicatorDirection * MetersFromCursor;
161 
162  // Find the rotation from the facing direction to the target object.
163  rotation = Quaternion.LookRotation(cameraTransform.forward, cursorIndicatorDirection) * directionIndicatorDefaultRotation;
164  }
165  }
166 }
DirectionIndicator creates an indicator around the cursor showing what direction to turn to find this...
The purpose of this class is to provide a cached reference to the main camera. Calling Camera...
Definition: CameraCache.cs:12
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