RecastBBTree.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using System;
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. namespace Pathfinding {
  5. /// <summary>
  6. /// Axis Aligned Bounding Box Tree.
  7. /// Holds a bounding box tree of RecastMeshObj components.
  8. /// Note that it assumes that once an object has been added, it stays at the same
  9. /// world position. If it is moved, then it might not be able to be found.
  10. /// </summary>
  11. public class RecastBBTree {
  12. RecastBBTreeBox root;
  13. /// <summary>Queries the tree for all RecastMeshObjs inside the specified bounds.</summary>
  14. /// <param name="bounds">World space bounds to search within</param>
  15. /// <param name="buffer">The results will be added to the buffer</param>
  16. public void QueryInBounds (Rect bounds, List<RecastMeshObj> buffer) {
  17. if (root == null) return;
  18. QueryBoxInBounds(root, bounds, buffer);
  19. }
  20. void QueryBoxInBounds (RecastBBTreeBox box, Rect bounds, List<RecastMeshObj> boxes) {
  21. if (box.mesh != null) {
  22. // Leaf node
  23. if (RectIntersectsRect(box.rect, bounds)) {
  24. // Found a RecastMeshObj, add it to the result
  25. boxes.Add(box.mesh);
  26. }
  27. } else {
  28. // Search children
  29. if (RectIntersectsRect(box.c1.rect, bounds)) {
  30. QueryBoxInBounds(box.c1, bounds, boxes);
  31. }
  32. if (RectIntersectsRect(box.c2.rect, bounds)) {
  33. QueryBoxInBounds(box.c2, bounds, boxes);
  34. }
  35. }
  36. }
  37. /// <summary>
  38. /// Removes the specified mesh from the tree.
  39. /// Assumes that it has the correct bounds information.
  40. ///
  41. /// Returns: True if the mesh was removed from the tree, false otherwise.
  42. /// </summary>
  43. public bool Remove (RecastMeshObj mesh) {
  44. if (mesh == null) throw new ArgumentNullException("mesh");
  45. if (root == null) {
  46. return false;
  47. }
  48. bool found = false;
  49. Bounds b = mesh.GetBounds();
  50. //Convert to top down rect
  51. Rect r = Rect.MinMaxRect(b.min.x, b.min.z, b.max.x, b.max.z);
  52. root = RemoveBox(root, mesh, r, ref found);
  53. return found;
  54. }
  55. RecastBBTreeBox RemoveBox (RecastBBTreeBox c, RecastMeshObj mesh, Rect bounds, ref bool found) {
  56. if (!RectIntersectsRect(c.rect, bounds)) {
  57. return c;
  58. }
  59. if (c.mesh == mesh) {
  60. found = true;
  61. return null;
  62. }
  63. if (c.mesh == null && !found) {
  64. c.c1 = RemoveBox(c.c1, mesh, bounds, ref found);
  65. if (c.c1 == null) {
  66. return c.c2;
  67. }
  68. if (!found) {
  69. c.c2 = RemoveBox(c.c2, mesh, bounds, ref found);
  70. if (c.c2 == null) {
  71. return c.c1;
  72. }
  73. }
  74. // TODO: Will this ever be called?
  75. if (found) {
  76. c.rect = ExpandToContain(c.c1.rect, c.c2.rect);
  77. }
  78. }
  79. return c;
  80. }
  81. /// <summary>Inserts a RecastMeshObj in the tree at its current position</summary>
  82. public void Insert (RecastMeshObj mesh) {
  83. var box = new RecastBBTreeBox(mesh);
  84. if (root == null) {
  85. root = box;
  86. return;
  87. }
  88. RecastBBTreeBox c = root;
  89. while (true) {
  90. c.rect = ExpandToContain(c.rect, box.rect);
  91. if (c.mesh != null) {
  92. //Is Leaf
  93. c.c1 = box;
  94. var box2 = new RecastBBTreeBox(c.mesh);
  95. c.c2 = box2;
  96. c.mesh = null;
  97. return;
  98. } else {
  99. float e1 = ExpansionRequired(c.c1.rect, box.rect);
  100. float e2 = ExpansionRequired(c.c2.rect, box.rect);
  101. // Choose the rect requiring the least expansion to contain box.rect
  102. if (e1 < e2) {
  103. c = c.c1;
  104. } else if (e2 < e1) {
  105. c = c.c2;
  106. } else {
  107. // Equal, Choose the one with the smallest area
  108. c = RectArea(c.c1.rect) < RectArea(c.c2.rect) ? c.c1 : c.c2;
  109. }
  110. }
  111. }
  112. }
  113. static bool RectIntersectsRect (Rect r, Rect r2) {
  114. return (r.xMax > r2.xMin && r.yMax > r2.yMin && r2.xMax > r.xMin && r2.yMax > r.yMin);
  115. }
  116. /// <summary>Returns the difference in area between r and r expanded to contain r2</summary>
  117. static float ExpansionRequired (Rect r, Rect r2) {
  118. float xMin = Mathf.Min(r.xMin, r2.xMin);
  119. float xMax = Mathf.Max(r.xMax, r2.xMax);
  120. float yMin = Mathf.Min(r.yMin, r2.yMin);
  121. float yMax = Mathf.Max(r.yMax, r2.yMax);
  122. return (xMax-xMin)*(yMax-yMin)-RectArea(r);
  123. }
  124. /// <summary>Returns a new rect which contains both r and r2</summary>
  125. static Rect ExpandToContain (Rect r, Rect r2) {
  126. float xMin = Mathf.Min(r.xMin, r2.xMin);
  127. float xMax = Mathf.Max(r.xMax, r2.xMax);
  128. float yMin = Mathf.Min(r.yMin, r2.yMin);
  129. float yMax = Mathf.Max(r.yMax, r2.yMax);
  130. return Rect.MinMaxRect(xMin, yMin, xMax, yMax);
  131. }
  132. /// <summary>Returns the area of a rect</summary>
  133. static float RectArea (Rect r) {
  134. return r.width*r.height;
  135. }
  136. }
  137. public class RecastBBTreeBox {
  138. public Rect rect;
  139. public RecastMeshObj mesh;
  140. public RecastBBTreeBox c1;
  141. public RecastBBTreeBox c2;
  142. public RecastBBTreeBox (RecastMeshObj mesh) {
  143. this.mesh = mesh;
  144. Vector3 min = mesh.bounds.min;
  145. Vector3 max = mesh.bounds.max;
  146. rect = Rect.MinMaxRect(min.x, min.z, max.x, max.z);
  147. }
  148. public bool Contains (Vector3 p) {
  149. return rect.Contains(p);
  150. }
  151. }
  152. }