AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
BoundingBoxRig.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 System.Collections.Generic;
6 using UnityEngine;
7 
8 namespace HoloToolkit.Unity.UX
9 {
13  public class BoundingBoxRig : MonoBehaviour
14  {
15  [Header("Flattening")]
16  [SerializeField]
17  [Tooltip("Choose this option if Rig is to be applied to a 2D object.")]
18  private BoundingBox.FlattenModeEnum flattenedAxis = default(BoundingBox.FlattenModeEnum);
19 
20  [Header("Customization Settings")]
21  [SerializeField]
22  private Material scaleHandleMaterial;
23 
24  [SerializeField]
25  private Material rotateHandleMaterial;
26 
27  [SerializeField]
28  private Material interactingMaterial;
29 
30  [Header("Behavior")]
31  [SerializeField]
32  private float scaleRate = 1.0f;
33 
34  [SerializeField]
35  private float appBarHoverOffsetZ = 0.05f;
36 
37  [SerializeField]
38  [Tooltip("This is the maximum scale that one grab can accomplish.")]
39  private float maxScale = 2.0f;
40 
41  [SerializeField]
42  private BoundingBoxGizmoHandleRotationType rotationType = BoundingBoxGizmoHandleRotationType.objectCoordinates;
43 
44  [SerializeField]
45  private BoundingBoxGizmoHandleHandMotionType handMotionToRotate = BoundingBoxGizmoHandleHandMotionType.handRotatesToRotateObject;
46 
47  [SerializeField]
48  private bool rotateAroundPivot = false;
49 
50  [Header("Preset Components")]
51  [SerializeField]
52  [Tooltip("To visualize the object bounding box, drop the MixedRealityToolkit/UX/Prefabs/BoundingBoxes/BoundingBoxBasic.prefab here.")]
53  private BoundingBox boundingBoxPrefab;
54 
55  [SerializeField]
56  [Tooltip("AppBar prefab.")]
57  private AppBar appBarPrefab = null;
58 
59  private BoundingBox boxInstance;
60 
61  private GameObject objectToBound;
62 
63  private AppBar appBarInstance;
64 
65  private GameObject[] rotateHandles;
66 
67  private GameObject[] cornerHandles;
68 
69  private List<Vector3> handleCentroids;
70 
71  private GameObject transformRig;
72 
73  private BoundingBoxGizmoHandle[] rigScaleGizmoHandles;
74 
75  private BoundingBoxGizmoHandle[] rigRotateGizmoHandles;
76 
77  private bool showRig = false;
78 
79  private Vector3 scaleHandleSize = new Vector3(0.04f, 0.04f, 0.04f);
80 
81  private Vector3 rotateHandleSize = new Vector3(0.04f, 0.04f, 0.04f);
82 
83  private bool destroying = false;
84 
85  public BoundingBox BoundingBoxPrefab
86  {
87  get
88  {
89  return boundingBoxPrefab;
90  }
91 
92  set
93  {
94  boundingBoxPrefab = value;
95  }
96  }
97 
98  public Material ScaleHandleMaterial
99  {
100  get
101  {
102  return scaleHandleMaterial;
103  }
104 
105  set
106  {
107  scaleHandleMaterial = value;
108  }
109  }
110 
111  public Material RotateHandleMaterial
112  {
113  get
114  {
115  return rotateHandleMaterial;
116  }
117 
118  set
119  {
120  rotateHandleMaterial = value;
121  }
122  }
123 
124  public Material InteractingMaterial
125  {
126  get
127  {
128  return interactingMaterial;
129  }
130 
131  set
132  {
133  interactingMaterial = value;
134  }
135  }
136 
137  public void Activate()
138  {
139  InputManager.Instance.RaiseBoundingBoxRigActivated(gameObject);
140  ShowRig = true;
141  }
142 
143  public void Deactivate()
144  {
145  InputManager.Instance.RaiseBoundingBoxRigDeactivated(gameObject);
146  ShowRig = false;
147  }
148 
149  public void FocusOnHandle(GameObject handle)
150  {
151  if (handle != null)
152  {
153  for (int i = 0; i < rotateHandles.Length; ++i)
154  {
155  rotateHandles[i].SetActive(rotateHandles[i].gameObject == handle);
156  }
157  for (int i = 0; i < cornerHandles.Length; ++i)
158  {
159  cornerHandles[i].SetActive(cornerHandles[i].gameObject == handle);
160  }
161  }
162  else
163  {
164  for (int i = 0; i < rotateHandles.Length; ++i)
165  {
166  rotateHandles[i].SetActive(true);
167  }
168  for (int i = 0; i < cornerHandles.Length; ++i)
169  {
170  cornerHandles[i].SetActive(true);
171  }
172  }
173  }
174 
175 
176  private void Start()
177  {
178  objectToBound = this.gameObject;
179 
180  boxInstance = Instantiate(BoundingBoxPrefab) as BoundingBox;
181  boxInstance.Target = objectToBound;
182  boxInstance.FlattenPreference = flattenedAxis;
183 
184  BuildRig();
185 
186  appBarInstance = Instantiate(appBarPrefab) as AppBar;
187  appBarInstance.BoundingBox = boxInstance;
188  appBarInstance.HoverOffsetZ = appBarHoverOffsetZ;
189 
190  boxInstance.IsVisible = false;
191  }
192 
193  private void Update()
194  {
195  if (destroying == false && ShowRig)
196  {
197  UpdateBoundsPoints();
198  UpdateHandles();
199  }
200  }
201 
202  private void OnDestroy()
203  {
204  destroying = true;
205  ShowRig = false;
206  ClearHandles();
207  }
208 
209  private void UpdateBoundsPoints()
210  {
211  handleCentroids = GetBounds();
212  }
213 
214  private void CreateHandles()
215  {
216  ClearHandles();
217  UpdateCornerHandles();
218  UpdateRotateHandles();
219  ParentHandles();
220  UpdateHandles();
221  }
222 
223  private void UpdateCornerHandles()
224  {
225  if (handleCentroids != null)
226  {
227  GetBounds();
228  }
229 
230  if (cornerHandles == null)
231  {
232  cornerHandles = new GameObject[handleCentroids.Count];
233  rigScaleGizmoHandles = new BoundingBoxGizmoHandle[handleCentroids.Count];
234  for (int i = 0; i < cornerHandles.Length; ++i)
235  {
236  cornerHandles[i] = GameObject.CreatePrimitive(PrimitiveType.Cube);
237  cornerHandles[i].GetComponent<Renderer>().material = scaleHandleMaterial;
238  cornerHandles[i].transform.localScale = scaleHandleSize;
239  cornerHandles[i].name = "Corner " + i.ToString();
240  rigScaleGizmoHandles[i] = cornerHandles[i].AddComponent<BoundingBoxGizmoHandle>();
241  rigScaleGizmoHandles[i].Rig = this;
242  rigScaleGizmoHandles[i].ScaleRate = scaleRate;
243  rigScaleGizmoHandles[i].MaxScale = maxScale;
244  rigScaleGizmoHandles[i].TransformToAffect = objectToBound.transform;
245  rigScaleGizmoHandles[i].Axis = BoundingBoxGizmoHandleAxisToAffect.Y;
246  rigScaleGizmoHandles[i].AffineType = BoundingBoxGizmoHandleTransformType.Scale;
247  }
248  }
249 
250  for (int i = 0; i < cornerHandles.Length; ++i)
251  {
252  cornerHandles[i].transform.position = handleCentroids[i];
253  cornerHandles[i].transform.localRotation = objectToBound.transform.rotation;
254  }
255  }
256 
257  private void UpdateRotateHandles()
258  {
259  if (handleCentroids != null)
260  {
261  GetBounds();
262  }
263 
264  if (rotateHandles == null)
265  {
266  rotateHandles = new GameObject[12];
267  rigRotateGizmoHandles = new BoundingBoxGizmoHandle[12];
268  for (int i = 0; i < rotateHandles.Length; ++i)
269  {
270  rotateHandles[i] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
271  rotateHandles[i].GetComponent<Renderer>().material = rotateHandleMaterial;
272  rotateHandles[i].transform.localScale = rotateHandleSize;
273  rotateHandles[i].name = "Middle " + i.ToString();
274  rigRotateGizmoHandles[i] = rotateHandles[i].AddComponent<BoundingBoxGizmoHandle>();
275  rigRotateGizmoHandles[i].RotateAroundPivot = rotateAroundPivot;
276  rigRotateGizmoHandles[i].Rig = this;
277  rigRotateGizmoHandles[i].HandMotionForRotation = handMotionToRotate;
278  rigRotateGizmoHandles[i].RotationCoordinateSystem = rotationType;
279  rigRotateGizmoHandles[i].TransformToAffect = objectToBound.transform;
280  rigRotateGizmoHandles[i].AffineType = BoundingBoxGizmoHandleTransformType.Rotation;
281 
282  }
283 
284  //set axis to affect
285  rigRotateGizmoHandles[0].Axis = BoundingBoxGizmoHandleAxisToAffect.Y;
286  rigRotateGizmoHandles[1].Axis = BoundingBoxGizmoHandleAxisToAffect.Y;
287  rigRotateGizmoHandles[2].Axis = BoundingBoxGizmoHandleAxisToAffect.Y;
288  rigRotateGizmoHandles[3].Axis = BoundingBoxGizmoHandleAxisToAffect.Y;
289 
290  rigRotateGizmoHandles[4].Axis = BoundingBoxGizmoHandleAxisToAffect.Z;
291  rigRotateGizmoHandles[5].Axis = BoundingBoxGizmoHandleAxisToAffect.Z;
292  rigRotateGizmoHandles[6].Axis = BoundingBoxGizmoHandleAxisToAffect.Z;
293  rigRotateGizmoHandles[7].Axis = BoundingBoxGizmoHandleAxisToAffect.Z;
294 
295  rigRotateGizmoHandles[8].Axis = BoundingBoxGizmoHandleAxisToAffect.X;
296  rigRotateGizmoHandles[9].Axis = BoundingBoxGizmoHandleAxisToAffect.X;
297  rigRotateGizmoHandles[10].Axis = BoundingBoxGizmoHandleAxisToAffect.X;
298  rigRotateGizmoHandles[11].Axis = BoundingBoxGizmoHandleAxisToAffect.X;
299 
300  //set lefthandedness
301  rigRotateGizmoHandles[0].IsLeftHandedRotation = false;
302  rigRotateGizmoHandles[1].IsLeftHandedRotation = false;
303  rigRotateGizmoHandles[2].IsLeftHandedRotation = false;
304  rigRotateGizmoHandles[3].IsLeftHandedRotation = false;
305 
306  rigRotateGizmoHandles[4].IsLeftHandedRotation = false;
307  rigRotateGizmoHandles[5].IsLeftHandedRotation = false;
308  rigRotateGizmoHandles[6].IsLeftHandedRotation = true;
309  rigRotateGizmoHandles[7].IsLeftHandedRotation = true;
310 
311  rigRotateGizmoHandles[8].IsLeftHandedRotation = false;
312  rigRotateGizmoHandles[9].IsLeftHandedRotation = true;
313  rigRotateGizmoHandles[10].IsLeftHandedRotation = false;
314  rigRotateGizmoHandles[11].IsLeftHandedRotation = true;
315  }
316 
317  rotateHandles[0].transform.localPosition = (handleCentroids[2] + handleCentroids[0]) * 0.5f;
318  rotateHandles[1].transform.localPosition = (handleCentroids[3] + handleCentroids[1]) * 0.5f;
319  rotateHandles[2].transform.localPosition = (handleCentroids[6] + handleCentroids[4]) * 0.5f;
320  rotateHandles[3].transform.localPosition = (handleCentroids[7] + handleCentroids[5]) * 0.5f;
321  rotateHandles[4].transform.localPosition = (handleCentroids[0] + handleCentroids[1]) * 0.5f;
322  rotateHandles[5].transform.localPosition = (handleCentroids[2] + handleCentroids[3]) * 0.5f;
323  rotateHandles[6].transform.localPosition = (handleCentroids[4] + handleCentroids[5]) * 0.5f;
324  rotateHandles[7].transform.localPosition = (handleCentroids[6] + handleCentroids[7]) * 0.5f;
325  rotateHandles[8].transform.localPosition = (handleCentroids[0] + handleCentroids[4]) * 0.5f;
326  rotateHandles[9].transform.localPosition = (handleCentroids[1] + handleCentroids[5]) * 0.5f;
327  rotateHandles[10].transform.localPosition = (handleCentroids[2] + handleCentroids[6]) * 0.5f;
328  rotateHandles[11].transform.localPosition = (handleCentroids[3] + handleCentroids[7]) * 0.5f;
329  }
330 
331  private void ParentHandles()
332  {
333  transformRig.transform.position = boxInstance.transform.position;
334  transformRig.transform.rotation = boxInstance.transform.rotation;
335 
336  Vector3 invScale = objectToBound.transform.localScale;
337 
338  transformRig.transform.localScale = new Vector3(0.5f / invScale.x, 0.5f / invScale.y, 0.5f / invScale.z);
339  transformRig.transform.parent = objectToBound.transform;
340  }
341 
342  private void UpdateHandles()
343  {
344  UpdateCornerHandles();
345  UpdateRotateHandles();
346  }
347 
348  private void ClearCornerHandles()
349  {
350  if (cornerHandles != null)
351  {
352  for (int i = 0; i < cornerHandles.Length; ++i)
353  {
354  GameObject.Destroy(cornerHandles[i]);
355  }
356  cornerHandles = null;
357  handleCentroids = null;
358  }
359 
360  cornerHandles = null;
361  handleCentroids = null;
362  }
363 
364  private void ClearRotateHandles()
365  {
366  if (rotateHandles != null && rotateHandles.Length > 0 && rotateHandles[0] != null)
367  {
368  for (int i = 0; i < rotateHandles.Length; ++i)
369  {
370  if (rotateHandles[i] != null)
371  {
372  Destroy(rotateHandles[i]);
373  rotateHandles[i] = null;
374  }
375  }
376  }
377 
378  rotateHandles = null;
379  }
380 
381  private void ClearHandles()
382  {
383  ClearCornerHandles();
384  ClearRotateHandles();
385  }
386 
387  private GameObject BuildRig()
388  {
389  Vector3 scale = objectToBound.transform.localScale;
390 
391  GameObject rig = new GameObject();
392  rig.name = "center";
393  rig.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
394  rig.transform.localScale = new Vector3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z);
395 
396  GameObject upperLeftFront = new GameObject();
397  upperLeftFront.name = "upperleftfront";
398  upperLeftFront.transform.SetPositionAndRotation(new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity);
399  upperLeftFront.transform.localScale = new Vector3(1, 1, 1);
400  upperLeftFront.transform.parent = rig.transform;
401 
402  GameObject upperLeftBack = new GameObject();
403  upperLeftBack.name = "upperleftback";
404  upperLeftBack.transform.SetPositionAndRotation(new Vector3(0.5f, 0.5f, -0.5f), Quaternion.identity);
405  upperLeftBack.transform.localScale = new Vector3(1, 1, 1);
406  upperLeftBack.transform.parent = rig.transform;
407 
408  GameObject lowerLeftFront = new GameObject();
409  lowerLeftFront.name = "lowerleftfront";
410  lowerLeftFront.transform.SetPositionAndRotation(new Vector3(0.5f, -0.5f, 0.5f), Quaternion.identity);
411  lowerLeftFront.transform.localScale = new Vector3(1, 1, 1);
412  lowerLeftFront.transform.parent = rig.transform;
413 
414  GameObject lowerLeftBack = new GameObject();
415  lowerLeftBack.name = "lowerleftback";
416  lowerLeftBack.transform.SetPositionAndRotation(new Vector3(0.5f, -0.5f, -0.5f), Quaternion.identity);
417  lowerLeftBack.transform.localScale = new Vector3(1, 1, 1);
418  lowerLeftBack.transform.parent = rig.transform;
419 
420  GameObject upperRightFront = new GameObject();
421  upperRightFront.name = "upperrightfront";
422  upperRightFront.transform.SetPositionAndRotation(new Vector3(-0.5f, 0.5f, 0.5f), Quaternion.identity);
423  upperRightFront.transform.localScale = new Vector3(1, 1, 1);
424  upperRightFront.transform.parent = rig.transform;
425 
426  GameObject upperRightBack = new GameObject();
427  upperRightBack.name = "upperrightback";
428  upperRightBack.transform.SetPositionAndRotation(new Vector3(-0.5f, 0.5f, -0.5f), Quaternion.identity);
429  upperRightBack.transform.localScale = new Vector3(1, 1, 1);
430  upperRightBack.transform.parent = rig.transform;
431 
432  GameObject lowerRightFront = new GameObject();
433  lowerRightFront.name = "lowerrightfront";
434  lowerRightFront.transform.SetPositionAndRotation(new Vector3(-0.5f, -0.5f, 0.5f), Quaternion.identity);
435  lowerRightFront.transform.localScale = new Vector3(1, 1, 1);
436  lowerRightFront.transform.parent = rig.transform;
437 
438  GameObject lowerRightBack = new GameObject();
439  lowerRightBack.name = "lowerrightback";
440  lowerRightBack.transform.SetPositionAndRotation(new Vector3(-0.5f, -0.5f, -0.5f), Quaternion.identity);
441  lowerRightBack.transform.localScale = new Vector3(1, 1, 1);
442  lowerRightBack.transform.parent = rig.transform;
443 
444  transformRig = rig;
445 
446  return rig;
447  }
448 
449  private bool ShowRig
450  {
451  get
452  {
453  return showRig;
454  }
455  set
456  {
457  if (destroying == false)
458  {
459  if (value == true)
460  {
461  UpdateBoundsPoints();
462  UpdateHandles();
463  }
464 
465  if (boxInstance != null)
466  {
467  boxInstance.IsVisible = value;
468  }
469 
470  if (cornerHandles != null && rotateHandles != null)
471  {
472  foreach (GameObject handle in cornerHandles)
473  {
474  handle.SetActive(value);
475  }
476  foreach (GameObject handle in rotateHandles)
477  {
478  handle.SetActive(value);
479  }
480  }
481 
482  showRig = value;
483  }
484  }
485  }
486 
487  public List<Vector3> GetBounds()
488  {
489  if (objectToBound != null)
490  {
491  List<Vector3> bounds = new List<Vector3>();
492  LayerMask mask = new LayerMask();
493 
494  GameObject clone = GameObject.Instantiate(boxInstance.gameObject);
495  clone.transform.localRotation = Quaternion.identity;
496  clone.transform.position = Vector3.zero;
497  BoundingBox.GetMeshFilterBoundsPoints(clone, bounds, mask);
498  Vector3 centroid = boxInstance.TargetBoundsCenter;
499  GameObject.Destroy(clone);
500 #if UNITY_2017_1_OR_NEWER
501  Matrix4x4 m = Matrix4x4.Rotate(objectToBound.transform.rotation);
502  for (int i = 0; i < bounds.Count; ++i)
503  {
504  bounds[i] = m.MultiplyPoint(bounds[i]);
505  bounds[i] += boxInstance.TargetBoundsCenter;
506  }
507 #endif // UNITY_2017_1_OR_NEWER
508  return bounds;
509  }
510 
511  return null;
512  }
513 
514  private BoundingBox.FlattenModeEnum GetBestAxisToFlatten()
515  {
516  int index = handleCentroids.Count - 8;
517  float width = (handleCentroids[index + 0] - handleCentroids[index + 4]).magnitude;
518  float height = (handleCentroids[index + 0] - handleCentroids[index + 2]).magnitude;
519  float depth = (handleCentroids[index + 0] - handleCentroids[index + 1]).magnitude;
520 
521  if (width < height && width < depth)
522  {
523  return BoundingBox.FlattenModeEnum.FlattenX;
524  }
525  else if (height < width && height < depth)
526  {
527  return BoundingBox.FlattenModeEnum.FlattenY;
528  }
529  else if (depth < height && depth < width)
530  {
531  return BoundingBox.FlattenModeEnum.FlattenZ;
532  }
533 
534  return BoundingBox.FlattenModeEnum.DoNotFlatten;
535  }
536  }
537 }
BoundingBoxGizmoHandleHandMotionType HandMotionForRotation
Constructs the scale and rotate gizmo handles for the Bounding Box
float HoverOffsetZ
Pushes the app bar away from the object
Definition: AppBar.cs:36
virtual GameObject Target
The target object being manipulated
Definition: BoundingBox.cs:147
Input Manager is responsible for managing input sources and dispatching relevant events to the approp...
Definition: InputManager.cs:19
bool IsVisible
Sets the bounding box invisible while not interrupting the computation of bounds points.
Definition: BoundingBox.cs:214
BoundingBoxGizmoHandleTransformType AffineType
Vector3 TargetBoundsCenter
The world-space center of the target object&#39;s bounds
Definition: BoundingBox.cs:179
BoundingBoxGizmoHandleAxisToAffect Axis
Base class for bounding box objects
Definition: BoundingBox.cs:14
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
BoundingBoxGizmoHandleRotationType RotationCoordinateSystem
static void GetMeshFilterBoundsPoints(GameObject target, List< Vector3 > boundsPoints, LayerMask ignoreLayers)
GetMeshFilterBoundsPoints - gets boundingbox points using MeshFilter method.
Definition: BoundingBox.cs:594
Logic for the App Bar. Generates buttons, manages states.
Definition: AppBar.cs:17
BoundingBox BoundingBox
Definition: AppBar.cs:110
virtual FlattenModeEnum FlattenPreference
Public property describing axis intended to be regarded as flat.
Definition: BoundingBox.cs:60
Logic for the gizmo handles in Bounding Box
void FocusOnHandle(GameObject handle)