123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- using System;
- using UnityEngine;
- using System.Collections.Generic;
- namespace Pathfinding {
- /// <summary>
- /// Axis Aligned Bounding Box Tree.
- /// Holds a bounding box tree of RecastMeshObj components.
- /// Note that it assumes that once an object has been added, it stays at the same
- /// world position. If it is moved, then it might not be able to be found.
- /// </summary>
- public class RecastBBTree {
- RecastBBTreeBox root;
- /// <summary>Queries the tree for all RecastMeshObjs inside the specified bounds.</summary>
- /// <param name="bounds">World space bounds to search within</param>
- /// <param name="buffer">The results will be added to the buffer</param>
- public void QueryInBounds (Rect bounds, List<RecastMeshObj> buffer) {
- if (root == null) return;
- QueryBoxInBounds(root, bounds, buffer);
- }
- void QueryBoxInBounds (RecastBBTreeBox box, Rect bounds, List<RecastMeshObj> boxes) {
- if (box.mesh != null) {
- // Leaf node
- if (RectIntersectsRect(box.rect, bounds)) {
- // Found a RecastMeshObj, add it to the result
- boxes.Add(box.mesh);
- }
- } else {
- // Search children
- if (RectIntersectsRect(box.c1.rect, bounds)) {
- QueryBoxInBounds(box.c1, bounds, boxes);
- }
- if (RectIntersectsRect(box.c2.rect, bounds)) {
- QueryBoxInBounds(box.c2, bounds, boxes);
- }
- }
- }
- /// <summary>
- /// Removes the specified mesh from the tree.
- /// Assumes that it has the correct bounds information.
- ///
- /// Returns: True if the mesh was removed from the tree, false otherwise.
- /// </summary>
- public bool Remove (RecastMeshObj mesh) {
- if (mesh == null) throw new ArgumentNullException("mesh");
- if (root == null) {
- return false;
- }
- bool found = false;
- Bounds b = mesh.GetBounds();
- //Convert to top down rect
- Rect r = Rect.MinMaxRect(b.min.x, b.min.z, b.max.x, b.max.z);
- root = RemoveBox(root, mesh, r, ref found);
- return found;
- }
- RecastBBTreeBox RemoveBox (RecastBBTreeBox c, RecastMeshObj mesh, Rect bounds, ref bool found) {
- if (!RectIntersectsRect(c.rect, bounds)) {
- return c;
- }
- if (c.mesh == mesh) {
- found = true;
- return null;
- }
- if (c.mesh == null && !found) {
- c.c1 = RemoveBox(c.c1, mesh, bounds, ref found);
- if (c.c1 == null) {
- return c.c2;
- }
- if (!found) {
- c.c2 = RemoveBox(c.c2, mesh, bounds, ref found);
- if (c.c2 == null) {
- return c.c1;
- }
- }
- // TODO: Will this ever be called?
- if (found) {
- c.rect = ExpandToContain(c.c1.rect, c.c2.rect);
- }
- }
- return c;
- }
- /// <summary>Inserts a RecastMeshObj in the tree at its current position</summary>
- public void Insert (RecastMeshObj mesh) {
- var box = new RecastBBTreeBox(mesh);
- if (root == null) {
- root = box;
- return;
- }
- RecastBBTreeBox c = root;
- while (true) {
- c.rect = ExpandToContain(c.rect, box.rect);
- if (c.mesh != null) {
- //Is Leaf
- c.c1 = box;
- var box2 = new RecastBBTreeBox(c.mesh);
- c.c2 = box2;
- c.mesh = null;
- return;
- } else {
- float e1 = ExpansionRequired(c.c1.rect, box.rect);
- float e2 = ExpansionRequired(c.c2.rect, box.rect);
- // Choose the rect requiring the least expansion to contain box.rect
- if (e1 < e2) {
- c = c.c1;
- } else if (e2 < e1) {
- c = c.c2;
- } else {
- // Equal, Choose the one with the smallest area
- c = RectArea(c.c1.rect) < RectArea(c.c2.rect) ? c.c1 : c.c2;
- }
- }
- }
- }
- static bool RectIntersectsRect (Rect r, Rect r2) {
- return (r.xMax > r2.xMin && r.yMax > r2.yMin && r2.xMax > r.xMin && r2.yMax > r.yMin);
- }
- /// <summary>Returns the difference in area between r and r expanded to contain r2</summary>
- static float ExpansionRequired (Rect r, Rect r2) {
- float xMin = Mathf.Min(r.xMin, r2.xMin);
- float xMax = Mathf.Max(r.xMax, r2.xMax);
- float yMin = Mathf.Min(r.yMin, r2.yMin);
- float yMax = Mathf.Max(r.yMax, r2.yMax);
- return (xMax-xMin)*(yMax-yMin)-RectArea(r);
- }
- /// <summary>Returns a new rect which contains both r and r2</summary>
- static Rect ExpandToContain (Rect r, Rect r2) {
- float xMin = Mathf.Min(r.xMin, r2.xMin);
- float xMax = Mathf.Max(r.xMax, r2.xMax);
- float yMin = Mathf.Min(r.yMin, r2.yMin);
- float yMax = Mathf.Max(r.yMax, r2.yMax);
- return Rect.MinMaxRect(xMin, yMin, xMax, yMax);
- }
- /// <summary>Returns the area of a rect</summary>
- static float RectArea (Rect r) {
- return r.width*r.height;
- }
- }
- public class RecastBBTreeBox {
- public Rect rect;
- public RecastMeshObj mesh;
- public RecastBBTreeBox c1;
- public RecastBBTreeBox c2;
- public RecastBBTreeBox (RecastMeshObj mesh) {
- this.mesh = mesh;
- Vector3 min = mesh.bounds.min;
- Vector3 max = mesh.bounds.max;
- rect = Rect.MinMaxRect(min.x, min.z, max.x, max.z);
- }
- public bool Contains (Vector3 p) {
- return rect.Contains(p);
- }
- }
- }
|