RVOQuadtree.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. using UnityEngine;
  2. using Pathfinding.RVO.Sampled;
  3. namespace Pathfinding.RVO {
  4. /// <summary>
  5. /// Quadtree for quick nearest neighbour search of rvo agents.
  6. /// See: Pathfinding.RVO.Simulator
  7. /// </summary>
  8. public class RVOQuadtree {
  9. const int LeafSize = 15;
  10. float maxRadius = 0;
  11. /// <summary>
  12. /// Node in a quadtree for storing RVO agents.
  13. /// See: Pathfinding.GraphNode for the node class that is used for pathfinding data.
  14. /// </summary>
  15. struct Node {
  16. public int child00;
  17. public Agent linkedList;
  18. public byte count;
  19. /// <summary>Maximum speed of all agents inside this node</summary>
  20. public float maxSpeed;
  21. public void Add (Agent agent) {
  22. agent.next = linkedList;
  23. linkedList = agent;
  24. }
  25. /// <summary>
  26. /// Distribute the agents in this node among the children.
  27. /// Used after subdividing the node.
  28. /// </summary>
  29. public void Distribute (Node[] nodes, Rect r) {
  30. Vector2 c = r.center;
  31. while (linkedList != null) {
  32. Agent nx = linkedList.next;
  33. var index = child00 + (linkedList.position.x > c.x ? 2 : 0) + (linkedList.position.y > c.y ? 1 : 0);
  34. nodes[index].Add(linkedList);
  35. linkedList = nx;
  36. }
  37. count = 0;
  38. }
  39. public float CalculateMaxSpeed (Node[] nodes, int index) {
  40. if (child00 == index) {
  41. // Leaf node
  42. for (var agent = linkedList; agent != null; agent = agent.next) {
  43. maxSpeed = System.Math.Max(maxSpeed, agent.CalculatedSpeed);
  44. }
  45. } else {
  46. maxSpeed = System.Math.Max(nodes[child00].CalculateMaxSpeed(nodes, child00), nodes[child00+1].CalculateMaxSpeed(nodes, child00+1));
  47. maxSpeed = System.Math.Max(maxSpeed, nodes[child00+2].CalculateMaxSpeed(nodes, child00+2));
  48. maxSpeed = System.Math.Max(maxSpeed, nodes[child00+3].CalculateMaxSpeed(nodes, child00+3));
  49. }
  50. return maxSpeed;
  51. }
  52. }
  53. Node[] nodes = new Node[16];
  54. int filledNodes = 1;
  55. Rect bounds;
  56. /// <summary>Removes all agents from the tree</summary>
  57. public void Clear () {
  58. nodes[0] = new Node();
  59. filledNodes = 1;
  60. maxRadius = 0;
  61. }
  62. public void SetBounds (Rect r) {
  63. bounds = r;
  64. }
  65. int GetNodeIndex () {
  66. if (filledNodes + 4 >= nodes.Length) {
  67. var nds = new Node[nodes.Length*2];
  68. for (int i = 0; i < nodes.Length; i++) nds[i] = nodes[i];
  69. nodes = nds;
  70. }
  71. nodes[filledNodes] = new Node();
  72. nodes[filledNodes].child00 = filledNodes;
  73. filledNodes++;
  74. nodes[filledNodes] = new Node();
  75. nodes[filledNodes].child00 = filledNodes;
  76. filledNodes++;
  77. nodes[filledNodes] = new Node();
  78. nodes[filledNodes].child00 = filledNodes;
  79. filledNodes++;
  80. nodes[filledNodes] = new Node();
  81. nodes[filledNodes].child00 = filledNodes;
  82. filledNodes++;
  83. return filledNodes-4;
  84. }
  85. /// <summary>
  86. /// Add a new agent to the tree.
  87. /// Warning: Agents must not be added multiple times to the same tree
  88. /// </summary>
  89. public void Insert (Agent agent) {
  90. int i = 0;
  91. Rect r = bounds;
  92. Vector2 p = new Vector2(agent.position.x, agent.position.y);
  93. agent.next = null;
  94. maxRadius = System.Math.Max(agent.radius, maxRadius);
  95. int depth = 0;
  96. while (true) {
  97. depth++;
  98. if (nodes[i].child00 == i) {
  99. // Leaf node. Break at depth 10 in case lots of agents ( > LeafSize ) are in the same spot
  100. if (nodes[i].count < LeafSize || depth > 10) {
  101. nodes[i].Add(agent);
  102. nodes[i].count++;
  103. break;
  104. } else {
  105. // Split
  106. nodes[i].child00 = GetNodeIndex();
  107. nodes[i].Distribute(nodes, r);
  108. }
  109. }
  110. // Note, no else
  111. if (nodes[i].child00 != i) {
  112. // Not a leaf node
  113. Vector2 c = r.center;
  114. if (p.x > c.x) {
  115. if (p.y > c.y) {
  116. i = nodes[i].child00+3;
  117. r = Rect.MinMaxRect(c.x, c.y, r.xMax, r.yMax);
  118. } else {
  119. i = nodes[i].child00+2;
  120. r = Rect.MinMaxRect(c.x, r.yMin, r.xMax, c.y);
  121. }
  122. } else {
  123. if (p.y > c.y) {
  124. i = nodes[i].child00+1;
  125. r = Rect.MinMaxRect(r.xMin, c.y, c.x, r.yMax);
  126. } else {
  127. i = nodes[i].child00;
  128. r = Rect.MinMaxRect(r.xMin, r.yMin, c.x, c.y);
  129. }
  130. }
  131. }
  132. }
  133. }
  134. public void CalculateSpeeds () {
  135. nodes[0].CalculateMaxSpeed(nodes, 0);
  136. }
  137. public void Query (Vector2 p, float speed, float timeHorizon, float agentRadius, Agent agent) {
  138. new QuadtreeQuery {
  139. p = p, speed = speed, timeHorizon = timeHorizon, maxRadius = float.PositiveInfinity,
  140. agentRadius = agentRadius, agent = agent, nodes = nodes
  141. }.QueryRec(0, bounds);
  142. }
  143. struct QuadtreeQuery {
  144. public Vector2 p;
  145. public float speed, timeHorizon, agentRadius, maxRadius;
  146. public Agent agent;
  147. public Node[] nodes;
  148. public void QueryRec (int i, Rect r) {
  149. // Determine the radius that we need to search to take all agents into account
  150. // Note: the second agentRadius usage should actually be the radius of the other agents, not this agent
  151. // but for performance reasons and for simplicity we assume that agents have approximately the same radius.
  152. // Thus an agent with a very small radius may in some cases detect an agent with a very large radius too late
  153. // however this effect should be minor.
  154. var radius = System.Math.Min(System.Math.Max((nodes[i].maxSpeed + speed)*timeHorizon, agentRadius) + agentRadius, maxRadius);
  155. if (nodes[i].child00 == i) {
  156. // Leaf node
  157. for (Agent a = nodes[i].linkedList; a != null; a = a.next) {
  158. float v = agent.InsertAgentNeighbour(a, radius*radius);
  159. // Limit the search if the agent has hit the max number of nearby agents threshold
  160. if (v < maxRadius*maxRadius) {
  161. maxRadius = Mathf.Sqrt(v);
  162. }
  163. }
  164. } else {
  165. // Not a leaf node
  166. Vector2 c = r.center;
  167. if (p.x-radius < c.x) {
  168. if (p.y-radius < c.y) {
  169. QueryRec(nodes[i].child00, Rect.MinMaxRect(r.xMin, r.yMin, c.x, c.y));
  170. radius = System.Math.Min(radius, maxRadius);
  171. }
  172. if (p.y+radius > c.y) {
  173. QueryRec(nodes[i].child00+1, Rect.MinMaxRect(r.xMin, c.y, c.x, r.yMax));
  174. radius = System.Math.Min(radius, maxRadius);
  175. }
  176. }
  177. if (p.x+radius > c.x) {
  178. if (p.y-radius < c.y) {
  179. QueryRec(nodes[i].child00+2, Rect.MinMaxRect(c.x, r.yMin, r.xMax, c.y));
  180. radius = System.Math.Min(radius, maxRadius);
  181. }
  182. if (p.y+radius > c.y) {
  183. QueryRec(nodes[i].child00+3, Rect.MinMaxRect(c.x, c.y, r.xMax, r.yMax));
  184. }
  185. }
  186. }
  187. }
  188. }
  189. public void DebugDraw () {
  190. DebugDrawRec(0, bounds);
  191. }
  192. void DebugDrawRec (int i, Rect r) {
  193. Debug.DrawLine(new Vector3(r.xMin, 0, r.yMin), new Vector3(r.xMax, 0, r.yMin), Color.white);
  194. Debug.DrawLine(new Vector3(r.xMax, 0, r.yMin), new Vector3(r.xMax, 0, r.yMax), Color.white);
  195. Debug.DrawLine(new Vector3(r.xMax, 0, r.yMax), new Vector3(r.xMin, 0, r.yMax), Color.white);
  196. Debug.DrawLine(new Vector3(r.xMin, 0, r.yMax), new Vector3(r.xMin, 0, r.yMin), Color.white);
  197. if (nodes[i].child00 != i) {
  198. // Not a leaf node
  199. Vector2 c = r.center;
  200. DebugDrawRec(nodes[i].child00+3, Rect.MinMaxRect(c.x, c.y, r.xMax, r.yMax));
  201. DebugDrawRec(nodes[i].child00+2, Rect.MinMaxRect(c.x, r.yMin, r.xMax, c.y));
  202. DebugDrawRec(nodes[i].child00+1, Rect.MinMaxRect(r.xMin, c.y, c.x, r.yMax));
  203. DebugDrawRec(nodes[i].child00+0, Rect.MinMaxRect(r.xMin, r.yMin, c.x, c.y));
  204. }
  205. for (Agent a = nodes[i].linkedList; a != null; a = a.next) {
  206. var p = nodes[i].linkedList.position;
  207. Debug.DrawLine(new Vector3(p.x, 0, p.y)+Vector3.up, new Vector3(a.position.x, 0, a.position.y)+Vector3.up, new Color(1, 1, 0, 0.5f));
  208. }
  209. }
  210. }
  211. }