Neodroid  0.2.0
Machine Learning Environment Prototyping Tool
BoundingBox.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
8 using UnityEditor;
9 using UnityEngine;
10 
11 #if UNITY_EDITOR
12 
13 #endif
14 
15 namespace droid.Runtime.Utilities.GameObjects.BoundingBoxes {
19  [ExecuteInEditMode]
20  public class BoundingBox : MonoBehaviour {
23  Transform _bb_transform = null;
24 
27  protected Bounds _Bounds = new Bounds();
28 
31  protected Vector3 _Bounds_Offset;
32 
35  Collider[] _children_colliders = null;
36 
39  MeshFilter[] _children_meshes = null;
40 
41  GameObject _empty_go = null;
42 
45  Vector3[,] _lines = null;
46 
47  List<Vector3[]> _lines_list = new List<Vector3[]>();
48 
49  Collider _local_collider;
50 
51  MeshFilter _local_mesh;
52 
53  Vector3 _point_bbl;
54  Vector3 _point_bbr;
55  Vector3 _point_bfl;
56  Vector3 _point_bfr;
57  Vector3 _point_tbl;
58  Vector3 _point_tbr;
59  Vector3 _point_tfl;
60  Vector3 _point_tfr;
61 
64  Vector3[] _points = null;
65 
66  #region fields
67 
70  [SerializeField]
71  public bool _use_bb_transform = false;
72 
73  [SerializeField] bool _use_shared_mesh = false;
74 
77  [SearchableEnum]
78  public BasedOn basedOn = BasedOn.Geometry_;
79 
82  [SerializeField]
83  float bb_margin = 0;
84 
87  [SerializeField]
88  BoundingBoxOrientation BbAligning = BoundingBoxOrientation.Axis_aligned_;
89 
92  [SerializeField]
93  bool cacheChildren = true;
94 
97  [SerializeField]
98  Color editorPreviewLineColor = new Color(1f, 0.36f, 0.38f, 0.74f);
99 
102  [SerializeField]
103  IAbstractPrototypingEnvironment environment = null;
104 
107  [SerializeField]
108  bool freezeAfterFirstCalculation = true;
109 
112  [SerializeField]
113  bool includeChildren = false;
114 
117  [SerializeField]
118  bool _only_active_children = true;
119 
122  [SerializeField]
123  bool includeSelf = true;
124 
127  [SerializeField]
128  bool OnAwakeSetup = false;
129 
132  [SerializeField]
133  bool RunInEditModeSetup = false;
134 
135  #endregion
136 
137  #region Properties
138 
141  public Vector3[] BoundingBoxCoordinates {
142  get {
143  return new[] {
144  this._point_tfl,
145  this._point_tfr,
146  this._point_tbl,
147  this._point_tbr,
148  this._point_bfl,
149  this._point_bfr,
150  this._point_bbl,
151  this._point_bbr
152  };
153  }
154  }
155 
158  public Bounds Bounds { get { return this._Bounds; } }
159 
160  public Vector3 Max { get { return this._Bounds.max; } }
161 
162  public Vector3 Min { get { return this._Bounds.min; } }
163 
166  public string BoundingBoxCoordinatesAsString {
167  get {
168  var str_rep = "";
169  str_rep += $"\"_top_front_left\":{this.BoundingBoxCoordinates[0]}, ";
170  str_rep += $"\"_top_front_right\":{this.BoundingBoxCoordinates[1]}, ";
171  str_rep += $"\"_top_back_left\":{this.BoundingBoxCoordinates[2]}, ";
172  str_rep += $"\"_top_back_right\":{this.BoundingBoxCoordinates[3]}, ";
173  str_rep += $"\"_bottom_front_left\":{this.BoundingBoxCoordinates[4]}, ";
174  str_rep += $"\"_bottom_front_right\":{this.BoundingBoxCoordinates[5]}, ";
175  str_rep += $"\"_bottom_back_left\":{this.BoundingBoxCoordinates[6]}, ";
176  str_rep += $"\"_bottom_back_right\":{this.BoundingBoxCoordinates[7]}";
177  return str_rep;
178  }
179  }
180 
183  public string BoundingBoxCoordinatesWorldSpaceAsJson {
184  get {
185  var str_rep = "{";
186  var transform1 = this.transform;
187  if (this._use_bb_transform) {
188  transform1 = this._bb_transform;
189  }
190 
191  var rotation = transform1.rotation;
192  var position = transform1.position;
193  if (this.environment != null) {
194  str_rep +=
195  $"\"top_front_left\":{this.JsonifyVec3(this.environment.TransformPoint(rotation * this._point_tfl + position))}, ";
196  str_rep +=
197  $"\"bottom_back_right\":{this.JsonifyVec3(this.environment.TransformPoint(rotation * this._point_bbr + position))}";
198  }
199 
200  str_rep += "}";
201  return str_rep;
202  }
203  }
204 
207  public Vector3[,] Lines { get { return this._lines; } }
208 
211  public Vector3[] Points { get { return this._points; } }
212 
213  public Color EditorPreviewLineColor {
214  get { return this.editorPreviewLineColor; }
215  set { this.editorPreviewLineColor = value; }
216  }
217 
218  #endregion
219 
225  public Rect ScreenSpaceBoundingRect(Camera a_camera, float margin = 0f) {
226  if (this.basedOn == BasedOn.Collider_) {
227  var a = this._local_collider as MeshCollider;
228  if (a) {
229  return a.sharedMesh.GetCameraMinMaxRect(this.transform, a_camera, this.bb_margin - margin);
230  }
231  }
232 
233  if (this._local_mesh) {
234  if (this._use_shared_mesh || !Application.isPlaying) {
235  var a = this._local_mesh.sharedMesh.GetCameraMinMaxPoints(this.transform, a_camera);
236  if (this.includeChildren) {
237  foreach (var children_mesh in this._children_meshes) {
238  a = children_mesh.sharedMesh.GetCameraMinMaxPoints(children_mesh.transform,
239  a_camera,
240  a[0],
241  a[1]);
242  }
243 
244  return BoundingBoxUtilities.GetMinMaxRect(a[0], a[1], this.bb_margin - margin);
245  }
246  } else {
247  var a = this._local_mesh.mesh.GetCameraMinMaxPoints(this.transform, a_camera);
248  if (this.includeChildren) {
249  foreach (var children_mesh in this._children_meshes) {
250  a = children_mesh.mesh.GetCameraMinMaxPoints(children_mesh.transform, a_camera, a[0], a[1]);
251  }
252 
253  return BoundingBoxUtilities.GetMinMaxRect(a[0], a[1], this.bb_margin - margin);
254  }
255  }
256  } else {
257  if (this._use_shared_mesh || !Application.isPlaying) {
258  if (this._children_meshes != null && this._children_meshes.Length > 0) {
259  var a = this._children_meshes[0].sharedMesh
260  .GetCameraMinMaxPoints(this._children_meshes[0].transform, a_camera);
261  if (this.includeChildren) {
262  for (var index = 1; index < this._children_meshes.Length; index++) {
263  var children_mesh = this._children_meshes[index];
264  a = children_mesh.sharedMesh.GetCameraMinMaxPoints(children_mesh.transform,
265  a_camera,
266  a[0],
267  a[1]);
268  }
269 
270  return BoundingBoxUtilities.GetMinMaxRect(a[0], a[1], this.bb_margin - margin);
271  }
272  }
273  } else {
274  if (this._children_meshes != null && this._children_meshes.Length > 0) {
275  var a = this._children_meshes[0].mesh
276  .GetCameraMinMaxPoints(this._children_meshes[0].transform, a_camera);
277  if (this.includeChildren) {
278  for (var index = 1; index < this._children_meshes.Length; index++) {
279  var children_mesh = this._children_meshes[index];
280  a = children_mesh.mesh.GetCameraMinMaxPoints(children_mesh.transform, a_camera, a[0], a[1]);
281  }
282 
283  return BoundingBoxUtilities.GetMinMaxRect(a[0], a[1], this.bb_margin - margin);
284  }
285  }
286  }
287  }
288 
289  return new Rect();
290  }
291 
296  string JsonifyVec3(Vector3 vec) { return $"[{vec.x},{vec.y},{vec.z}]"; }
297 
300  void Reset() {
301  this.Awake();
302  this.Start();
303  }
304 
307  void Start() {
308  if (!this.OnAwakeSetup) {
309  this.Setup();
310  }
311  }
312 
315  void Awake() {
316  if (!this.enabled) {
317  return;
318  }
319 
320  if (this.environment == null) {
321  this.environment = FindObjectOfType<ActorisedPrototypingEnvironment>();
322  }
323 
324  if (!this._bb_transform) {
325  this._empty_go = new GameObject {hideFlags = HideFlags.HideAndDontSave};
326  this._bb_transform = this._empty_go.transform;
327  }
328 
329  if (this.OnAwakeSetup) {
330  this.Setup();
331  }
332  }
333 
336  void Setup() {
337  if (!this.RunInEditModeSetup && !Application.isPlaying) {
338  return;
339  }
340 
341  if (!this._bb_transform) {
342  this._empty_go = new GameObject {hideFlags = HideFlags.HideAndDontSave};
343  this._bb_transform = this._empty_go.transform;
344  }
345 
346  if (this.includeSelf) {
347  this._local_collider = this.GetComponent<BoxCollider>();
348  this._local_mesh = this.GetComponent<MeshFilter>();
349  }
350 
351  if (this.includeChildren) {
352  this._children_meshes = this.GetComponentsInChildren<MeshFilter>();
353  this._children_colliders = this.GetComponentsInChildren<Collider>();
354  }
355 
356  this.CalculateBoundingBox();
357  }
358 
361  void LateUpdate() {
362  if (this.freezeAfterFirstCalculation) {
363  return;
364  }
365 
366  if (this.includeChildren && !this.cacheChildren) {
367  if (this._children_meshes != this.GetComponentsInChildren<MeshFilter>()) {
368  this.Reset();
369  }
370 
371  if (this._children_colliders != this.GetComponentsInChildren<Collider>()) {
372  this.Reset();
373  }
374  } else {
375  this.CalculateBoundingBox();
376  }
377  }
378 
381  void FitCollidersAabb() {
382  var transform1 = this.transform;
383  this._bb_transform.rotation = transform1.rotation;
384  this._bb_transform.position = transform1.position;
385 
386  var bounds = new Bounds(this._bb_transform.position, Vector3.zero);
387 
388  if (this.includeSelf && this._local_collider) {
389  this._bb_transform.position = this._local_collider.bounds.center;
390  bounds = this._local_collider.bounds;
391  }
392 
393  if (this.includeChildren && this._children_colliders != null) {
394  foreach (var a_collider in this._children_colliders) {
395  if (a_collider && a_collider != this._local_collider) {
396  if (this._only_active_children) {
397  if (a_collider.gameObject.activeInHierarchy
398  && a_collider.gameObject.activeSelf
399  && a_collider.enabled) {
400  if (bounds.size == Vector3.zero) {
401  this._bb_transform.rotation = a_collider.transform.rotation;
402  this._bb_transform.position = a_collider.bounds.center;
403  bounds = a_collider.bounds;
404  } else {
405  bounds.Encapsulate(a_collider.bounds);
406  }
407  }
408  } else {
409  if (bounds.size == Vector3.zero) {
410  this._bb_transform.rotation = a_collider.transform.rotation;
411  this._bb_transform.position = a_collider.bounds.center;
412  bounds = a_collider.bounds;
413  } else {
414  bounds.Encapsulate(a_collider.bounds);
415  }
416  }
417  }
418  }
419  }
420 
421  this._Bounds = bounds;
422  this._Bounds_Offset = this._Bounds.center - this._bb_transform.position;
423  }
424 
427  void FitRenderersAabb() {
428  var transform1 = this.transform;
429  var position = transform1.position;
430  this._bb_transform.position = position;
431  this._bb_transform.rotation = transform1.rotation;
432 
433  var bounds = new Bounds(position, Vector3.zero);
434 
435  if (this.includeSelf && this._local_mesh) {
436  Mesh a_mesh;
437 
438  if (this._use_shared_mesh) {
439  a_mesh = this._local_mesh.sharedMesh;
440  } else {
441  a_mesh = this._local_mesh.mesh;
442  }
443 
444  if (a_mesh.isReadable) {
445  var vc = a_mesh.vertexCount;
446  for (var i = 0; i < vc; i++) {
447  bounds.Encapsulate(this._local_mesh.transform.TransformPoint(a_mesh.vertices[i]));
448  }
449  } else {
450  Debug.LogWarning("Make sure mesh is marked as readable when imported!");
451  }
452  }
453 
454  if (this.includeChildren && this._children_meshes != null) {
455  foreach (var t in this._children_meshes) {
456  if (t) {
457  if (this._only_active_children) {
458  if (t.gameObject.activeInHierarchy && t.gameObject.activeSelf) {
459  if (bounds.size == Vector3.zero) {
460  var transform2 = t.transform;
461  position = transform2.position;
462  this._bb_transform.position = position;
463  this._bb_transform.rotation = transform2.rotation;
464  bounds = new Bounds(position, Vector3.zero);
465  }
466 
467  Mesh a_mesh;
468 
469  if (this._use_shared_mesh) {
470  a_mesh = t.sharedMesh;
471  } else {
472  a_mesh = t.mesh;
473  }
474 
475  if (a_mesh) {
476  if (a_mesh.isReadable) {
477  var vc = a_mesh.vertexCount;
478  for (var j = 0; j < vc; j++) {
479  bounds.Encapsulate(t.transform.TransformPoint(a_mesh.vertices[j]));
480  }
481  } else {
482  Debug.LogWarning("Make sure mesh is marked as readable when imported!");
483  }
484  }
485  }
486  } else {
487  if (bounds.size == Vector3.zero) {
488  bounds = new Bounds(t.transform.position, Vector3.zero);
489  }
490 
491  Mesh a_mesh;
492 
493  if (this._use_shared_mesh) {
494  a_mesh = t.sharedMesh;
495  } else {
496  a_mesh = t.mesh;
497  }
498 
499  if (a_mesh) {
500  var vc = a_mesh.vertexCount;
501  for (var j = 0; j < vc; j++) {
502  bounds.Encapsulate(t.transform.TransformPoint(a_mesh.vertices[j]));
503  }
504  }
505  }
506  }
507  }
508  }
509 
510  this._Bounds = bounds;
511  this._Bounds_Offset = this._Bounds.center - position;
512  }
513 
516  void CalculateBoundingBox() {
517  if (!this.RunInEditModeSetup && !Application.isPlaying || this._bb_transform == null) {
518  return;
519  }
520 
521  if (this.basedOn == BasedOn.Collider_) {
522  switch (this.BbAligning) {
523  case BoundingBoxOrientation.Axis_aligned_:
524  this.FitCollidersAabb();
525  this.RecalculatePoints();
526  this.RecalculateLines();
527  break;
528  case BoundingBoxOrientation.Object_oriented_:
529  this.FitCollidersOobb();
530  break;
531  case BoundingBoxOrientation.Camera_oriented_:
532  this.FitRenderersCabb();
533  break;
534  default: throw new ArgumentOutOfRangeException();
535  }
536  } else {
537  switch (this.BbAligning) {
538  case BoundingBoxOrientation.Axis_aligned_:
539  this.FitRenderersAabb();
540  this.RecalculatePoints();
541  this.RecalculateLines();
542  break;
543  case BoundingBoxOrientation.Object_oriented_:
544  this.FitRenderersOobb();
545  break;
546  case BoundingBoxOrientation.Camera_oriented_:
547  this.FitRenderersCabb();
548 
549  break;
550  default: throw new ArgumentOutOfRangeException();
551  }
552  }
553  }
554 
555  void FitRenderersCabb() {
556  throw new NotImplementedException();
557 
558  /*
559  var transform1 = this.transform;
560  var position = transform1.position;
561  this._bb_transform.position = position;
562  this._bb_transform.rotation = transform1.rotation;
563 
564  var a = this._local_mesh.sharedMesh.GetCameraMinMaxPoints(this._bb_transform,
565  this._camera,
566  this.use_view_port);
567  var min = a[0];
568  var max = a[1];
569  var extent = a[2];
570 
571  if (this.use_view_port) {
572  min = this._camera.ViewportToWorldPoint(min);
573  max = this._camera.ViewportToWorldPoint(max);
574  } else {
575  min = this._camera.ScreenToWorldPoint(min);
576  max = this._camera.ScreenToWorldPoint(max);
577  extent = max - min;
578  }
579 
580  var cobb_extent = extent;
581  var cobb_center =
582  new Vector3(min.x + extent.x / 2.0f, min.y + extent.y / 2.0f, min.z + extent.z / 2.0f);
583 
584  this._point_tfr = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Top_Front_Right);
585  this._point_tfl = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Top_Front_Left);
586  this._point_tbl = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Top_Back_Left);
587  this._point_tbr = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Top_Back_Right);
588  this._point_bfr = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Bottom_Front_Right);
589  this._point_bfl = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Bottom_Front_Left);
590  this._point_bbl = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Bottom_Back_Left);
591  this._point_bbr = cobb_center + Vector3.Scale(cobb_extent, BoundingBoxUtilities._Bottom_Back_Right);
592 
593  this._Bounds.center = cobb_center;
594  this._Bounds.extents = cobb_extent;
595 
596  this._points = new[] {
597  this._point_tfr,
598  this._point_tfl,
599  this._point_tbl,
600  this._point_tbr,
601  this._point_bfr,
602  this._point_bfl,
603  this._point_bbl,
604  this._point_bbr
605  };
606 
607  var rot = Quaternion.identity;
608  var pos = Vector3.zero;
609 
610  //rot = transform1.rotation;
611  //pos = transform1.position;
612 
613  this._lines_list.Clear();
614 
615  for (var i = 0; i < 4; i++) {
616  //width
617  var line = new[] {rot * this.Points[2 * i] + pos, rot * this.Points[2 * i + 1] + pos};
618  this._lines_list.Add(line);
619 
620  //height
621  line = new[] {rot * this.Points[i] + pos, rot * this.Points[i + 4] + pos};
622  this._lines_list.Add(line);
623 
624  //depth
625  line = new[] {rot * this.Points[2 * i] + pos, rot * this.Points[2 * i + 3 - 4 * (i % 2)] + pos};
626  this._lines_list.Add(line);
627  }
628 
629  this._lines = new Vector3[BoundingBoxUtilities._Num_Lines, BoundingBoxUtilities._Num_Points_Per_Line];
630  for (var j = 0; j < BoundingBoxUtilities._Num_Lines; j++) {
631  this.Lines[j, 0] = this._lines_list[j][0];
632  this.Lines[j, 1] = this._lines_list[j][1];
633  }
634  */
635  }
636 
637  void FitRenderersOobb() { throw new NotImplementedException(); }
638 
639  void FitCollidersOobb() { throw new NotImplementedException(); }
640 
643  void RecalculatePoints() {
644  this._point_tfr = this._Bounds_Offset
645  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Top_Front_Right);
646  this._point_tfl = this._Bounds_Offset
647  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Top_Front_Left);
648  this._point_tbl = this._Bounds_Offset
649  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Top_Back_Left);
650  this._point_tbr = this._Bounds_Offset
651  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Top_Back_Right);
652  this._point_bfr = this._Bounds_Offset
653  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Bottom_Front_Right);
654  this._point_bfl = this._Bounds_Offset
655  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Bottom_Front_Left);
656  this._point_bbl = this._Bounds_Offset
657  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Bottom_Back_Left);
658  this._point_bbr = this._Bounds_Offset
659  + Vector3.Scale(this._Bounds.extents, BoundingBoxUtilities._Bottom_Back_Right);
660 
661  this._points = new[] {
662  this._point_tfr,
663  this._point_tfl,
664  this._point_tbl,
665  this._point_tbr,
666  this._point_bfr,
667  this._point_bfl,
668  this._point_bbl,
669  this._point_bbr
670  };
671  }
672 
675  void RecalculateLines() {
676  var transform1 = this.transform;
677  if (this._bb_transform) {
678  transform1 = this._bb_transform;
679  }
680 
681  var rot = Quaternion.identity;
682  var pos = Vector3.zero;
683  if (this._use_bb_transform) {
684  rot = transform1.rotation;
685  }
686 
687  pos = transform1.position;
688 
689  this._lines_list.Clear();
690 
691  for (var i = 0; i < 4; i++) {
692  //width
693  var line = new[] {rot * this.Points[2 * i] + pos, rot * this.Points[2 * i + 1] + pos};
694  this._lines_list.Add(line);
695 
696  //height
697  line = new[] {rot * this.Points[i] + pos, rot * this.Points[i + 4] + pos};
698  this._lines_list.Add(line);
699 
700  //depth
701  line = new[] {rot * this.Points[2 * i] + pos, rot * this.Points[2 * i + 3 - 4 * (i % 2)] + pos};
702  this._lines_list.Add(line);
703  }
704 
705  this._lines = new Vector3[BoundingBoxUtilities._Num_Lines, BoundingBoxUtilities._Num_Points_Per_Line];
706  for (var j = 0; j < BoundingBoxUtilities._Num_Lines; j++) {
707  this.Lines[j, 0] = this._lines_list[j][0];
708  this.Lines[j, 1] = this._lines_list[j][1];
709  }
710  }
711 
714  void OnMouseDown() {
715  //if (_permanent)
716  // return;
717  //this.enabled = !this.enabled;
718  }
719 
720  #if UNITY_EDITOR
721  void OnValidate() {
724  if (!this.enabled) {
725  return;
726  }
727 
728  if (EditorApplication.isPlaying) {
729  return;
730  }
731 
732  this.CalculateBoundingBox();
733  }
734 
737  void OnDrawGizmos() {
738  if (this.enabled) {
739  Gizmos.color = this.editorPreviewLineColor;
740 
741  if (this._use_bb_transform) {
742  if (this.Lines != null) {
743  for (var i = 0; i < this.Lines.GetLength(0); i++) {
744  Gizmos.DrawLine(this.Lines[i, 0], this.Lines[i, 1]);
745  }
746  } else {
747  Gizmos.DrawWireCube(this.Bounds.center, this.Bounds.size);
748  }
749 
750  if (this._bb_transform) {
751  Handles.Label(this._bb_transform.position, this._bb_transform.gameObject.name);
752  } else {
753  Handles.Label(this.transform.position, this.gameObject.name);
754  }
755  }
756  }
757  }
758  #endif
759  }
760 }
Rect ScreenSpaceBoundingRect(Camera a_camera, float margin=0f)
Definition: BoundingBox.cs:225