/// <summary>Gets a leaf node from a given point.</summary> /// <param name="pt">the point to retrieve the parent node from</param> /// <returns>the node which contains the given point; null if the point is invalid</returns> internal ZoneSpacePartitionNode GetLeafFromPoint(ref Vector3 pt) { if (IsLeaf) { if (m_bounds.Contains(ref pt)) { return(this); } return(null); } for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Contains(ref pt)) { return(child.GetLeafFromPoint(ref pt)); } } } return(null); }
/// <summary>Iterates over all objects in this Node.</summary> /// <param name="predicate">Returns whether to continue iteration.</param> /// <returns>Whether Iteration was not cancelled (usually indicating that we did not find what we were looking for).</returns> internal bool Iterate(ref BoundingSphere sphere, Func <WorldObject, bool> predicate, uint phase) { if (IsLeaf) { if (HasObjects) { foreach (WorldObject worldObject in m_objects.Values) { Vector3 position = worldObject.Position; if (sphere.Contains(ref position) && worldObject.IsInPhase(phase) && !predicate(worldObject)) { return(false); } } } } else { for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Intersects(ref sphere).HasAnyFlag(IntersectionType.Touches) && !child.Iterate(ref sphere, predicate, phase)) { return(false); } } } } return(true); }
/// <summary>Adds an object to the node.</summary> /// <param name="obj">the object to add</param> /// <returns>whether or not the object was added successfully</returns> internal bool AddObject(WorldObject obj) { if (IsLeaf) { if (m_objects.ContainsKey(obj.EntityId)) { throw new ArgumentException(string.Format( "Tried to add Object \"{0}\" with duplicate EntityId {1} to Map.", obj, obj.EntityId)); } m_objects.Add(obj.EntityId, obj); obj.Node = this; return(true); } Vector3 position = obj.Position; for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Contains(ref position)) { return(child.AddObject(obj)); } } } return(false); }
/// <summary> /// Partitions the node, dividing it into subnodes until the desired depth is reached. /// </summary> /// <param name="maxLevels">the maximum depth to partition</param> /// <param name="startingDepth">the depth to start partitioning from</param> internal void PartitionSpace(ZoneSpacePartitionNode parent, int maxLevels, int startingDepth) { float num1 = Length / 2f; float num2 = Width / 2f; if (startingDepth < maxLevels && num1 > (double)MinNodeLength && num2 > (double)MinNodeLength) { m_children = new ZoneSpacePartitionNode[2, 2]; m_children[1, 0] = new ZoneSpacePartitionNode(new BoundingBox( new Vector3(m_bounds.Min.X, m_bounds.Min.Y, m_bounds.Min.Z), new Vector3(m_bounds.Min.X + num1, m_bounds.Min.Y + num2, m_bounds.Max.Z))); m_children[0, 0] = new ZoneSpacePartitionNode(new BoundingBox( new Vector3(m_bounds.Min.X, m_bounds.Min.Y + num2, m_bounds.Min.Z), new Vector3(m_bounds.Min.X + num1, m_bounds.Max.Y, m_bounds.Max.Z))); m_children[1, 1] = new ZoneSpacePartitionNode(new BoundingBox( new Vector3(m_bounds.Min.X + num1, m_bounds.Min.Y, m_bounds.Min.Z), new Vector3(m_bounds.Max.X, m_bounds.Min.Y + num2, m_bounds.Max.Z))); m_children[0, 1] = new ZoneSpacePartitionNode(new BoundingBox( new Vector3(m_bounds.Min.X + num1, m_bounds.Min.Y + num2, m_bounds.Min.Z), new Vector3(m_bounds.Max.X, m_bounds.Max.Y, m_bounds.Max.Z))); ++startingDepth; for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { m_children[index1, index2].PartitionSpace(this, maxLevels, startingDepth); } } } else { m_objects = new Dictionary <EntityId, WorldObject>(0); } }
/// <summary> /// Partitions the node, dividing it into subnodes until the desired depth is reached. /// </summary> /// <param name="maxLevels">the maximum depth to partition</param> /// <param name="startingDepth">the depth to start partitioning from</param> internal void PartitionSpace(ZoneSpacePartitionNode parent, int maxLevels, int startingDepth) { var width = Length / Two; var height = Width / Two; if (startingDepth < maxLevels && width > MinNodeLength && height > MinNodeLength) { m_children = new ZoneSpacePartitionNode[Two, Two]; // Bottom left m_children[SOUTH, WEST] = new ZoneSpacePartitionNode( new BoundingBox(new Vector3(m_bounds.Min.X, m_bounds.Min.Y, m_bounds.Min.Z), new Vector3(m_bounds.Min.X + width, m_bounds.Min.Y + height, m_bounds.Max.Z))); // Top left m_children[NORTH, WEST] = new ZoneSpacePartitionNode( new BoundingBox(new Vector3(m_bounds.Min.X, m_bounds.Min.Y + height, m_bounds.Min.Z), new Vector3(m_bounds.Min.X + width, m_bounds.Max.Y, m_bounds.Max.Z))); // Bottom right m_children[SOUTH, EAST] = new ZoneSpacePartitionNode( new BoundingBox(new Vector3(m_bounds.Min.X + width, m_bounds.Min.Y, m_bounds.Min.Z), new Vector3(m_bounds.Max.X, m_bounds.Min.Y + height, m_bounds.Max.Z))); // Top right m_children[NORTH, EAST] = new ZoneSpacePartitionNode( new BoundingBox(new Vector3(m_bounds.Min.X + width, m_bounds.Min.Y + height, m_bounds.Min.Z), new Vector3(m_bounds.Max.X, m_bounds.Max.Y, m_bounds.Max.Z))); // Set the next level's depth startingDepth++; // Partition any further depths for (var i0 = 0; i0 < Two; i0++) { for (var i1 = 0; i1 < Two; i1++) { var node = m_children[i0, i1]; node.PartitionSpace(this, maxLevels, startingDepth); } } } else { m_objects = new Dictionary <EntityId, WorldObject>(0); } }
//private static Dictionary<int, ZoneSpacePartitionNode[,]> m_raster = new Dictionary<int, ZoneSpacePartitionNode[,]>(); // private static Dictionary<int, ZoneSpacePartitionNode[,]> m_raster = new Dictionary<int, ZoneSpacePartitionNode[,]>(); ///// <summary> ///// Partitions the node, dividing it into subnodes until the desired depth is reached. ///// </summary> ///// <param name="maxLevels">the maximum depth to partition</param> ///// <param name="startingDepth">the depth to start partitioning from</param> //internal void PartitionSpace(int maxLevels, int startingDepth, int nodeY, int nodeX, int nodesCountAtCurrentDepth) //{ // float incX = Length / 2; // float incY = Width / 2; // if (!m_raster.ContainsKey(startingDepth)) // { // m_raster.Add(startingDepth, new ZoneSpacePartitionNode[nodesCountAtCurrentDepth, nodesCountAtCurrentDepth]); // } // if (m_raster[startingDepth][nodeY, nodeX] != null) // { // throw new Exception(); // } // m_raster[startingDepth][nodeY, nodeX] = this; // if (startingDepth < maxLevels) // { // m_children = new ZoneSpacePartitionNode[2, 2]; // // Bottom left // m_children[SOUTH, WEST] = // new ZoneSpacePartitionNode( // new BoundingBox(new Vector3(m_bounds.Min.X, m_bounds.Min.Y, m_bounds.Min.Z), // new Vector3(m_bounds.Min.X + incX, m_bounds.Min.Y + incY, m_bounds.Max.Z))); // // Top left // m_children[NORTH, WEST] = // new ZoneSpacePartitionNode( // new BoundingBox(new Vector3(m_bounds.Min.X, m_bounds.Min.Y + incY, m_bounds.Min.Z), // new Vector3(m_bounds.Min.X + incX, m_bounds.Max.Y, m_bounds.Max.Z))); // // Bottom right // m_children[SOUTH, EAST] = // new ZoneSpacePartitionNode( // new BoundingBox(new Vector3(m_bounds.Min.X + incX, m_bounds.Min.Y, m_bounds.Min.Z), // new Vector3(m_bounds.Max.X, m_bounds.Min.Y + incY, m_bounds.Max.Z))); // // Top right // m_children[NORTH, EAST] = // new ZoneSpacePartitionNode( // new BoundingBox(new Vector3(m_bounds.Min.X + incX, m_bounds.Min.Y + incY, m_bounds.Min.Z), // new Vector3(m_bounds.Max.X, m_bounds.Max.Y, m_bounds.Max.Z))); // // Set the next level's depth // startingDepth++; // int newNodesCount = nodesCountAtCurrentDepth * 2; // m_children[SOUTH, WEST].PartitionSpace(maxLevels, startingDepth, nodeY * 2 + SOUTH, nodeX * 2 + WEST, newNodesCount); // m_children[NORTH, WEST].PartitionSpace(maxLevels, startingDepth, nodeY * 2 + NORTH, nodeX * 2 + WEST, newNodesCount); // m_children[SOUTH, EAST].PartitionSpace(maxLevels, startingDepth, nodeY * 2 + SOUTH, nodeX * 2 + EAST, newNodesCount); // m_children[NORTH, EAST].PartitionSpace(maxLevels, startingDepth, nodeY * 2 + NORTH, nodeX * 2 + EAST, newNodesCount); // //// Partition any further depths // //foreach (var node in m_children) // //{ // // node.PartitionSpace(maxLevels, startingDepth, nodesCountAtCurrentDepth * 2); // //} // //FindNeighbors(parent, 0, 0); // } // else // { // m_objects = new Dictionary<EntityId, WorldObject>(); // } //} ///// <summary> ///// Partitions the space of the zone. ///// </summary> //public void PartitionSpace() //{ // // Start partitioning the map space // m_root.PartitionSpace(ZoneSpacePartitionNode.DefaultPartitionThreshold, 0, 0, 0, 1); //} /// <summary> /// TODO: Find all intermediate neighbors /// </summary> /// <param name="parent"></param> /// <param name="vertical"></param> /// <param name="horizontal"></param> internal void FindNeighbors(ZoneSpacePartitionNode parent, int vertical, int horizontal) { //m_neighbors = new ZoneSpacePartitionNode[3, 3]; //if (parent != null) //{ // if (vertical == NORTH) // { // if (horizontal == EAST) // { // m_neighbors[VER_CENTER, HOR_WEST] = parent.m_children[NORTH, WEST]; // m_neighbors[VER_CENTER, HOR_WEST] = parent.m_children[NORTH, WEST]; // } // } //} }
/// <summary>Gets all objects within a specified radius.</summary> /// <typeparam name="T">the minimum type of the objects to retrieve</typeparam> /// <param name="sphere">the area to search</param> /// <param name="entities">the list to append retrieved objects to</param> internal void GetEntitiesInArea <T>(ref BoundingSphere sphere, List <T> entities, Func <T, bool> filter, uint phase, ref int limitCounter) where T : WorldObject { if (IsLeaf) { if (!HasObjects) { return; } foreach (WorldObject worldObject in m_objects.Values) { Vector3 position = worldObject.Position; if (sphere.Contains(ref position) && worldObject.IsInPhase(phase) && worldObject is T) { T obj = worldObject as T; if (filter(obj)) { entities.Add(obj); if (--limitCounter == 0) { break; } } } } } else { for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Intersects(ref sphere).HasAnyFlag(IntersectionType.Touches)) { child.GetEntitiesInArea(ref sphere, entities, filter, phase, ref limitCounter); } } } } }
/// <summary>Removes an object from the node.</summary> /// <param name="obj">the object to remove</param> /// <returns>whether or not the object was removed successfully</returns> internal bool RemoveObject(WorldObject obj) { if (IsLeaf) { return(m_objects.Remove(obj.EntityId)); } Vector3 position = obj.Position; for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Contains(ref position)) { return(child.RemoveObject(obj)); } } } return(false); }
/// <summary>Gets all objects within a specified radius.</summary> /// <param name="box">the area to search</param> /// <param name="entities">the list to append retrieved objects to</param> /// <param name="filter">the type (in respect to the WoW client) that the object must be</param> internal void GetEntitiesInArea(ref BoundingBox box, List <WorldObject> entities, ObjectTypes filter, uint phase, ref int limitCounter) { if (IsLeaf) { if (!HasObjects) { return; } foreach (WorldObject worldObject in m_objects.Values) { Vector3 position = worldObject.Position; if (box.Contains(ref position) && worldObject.IsInPhase(phase) && worldObject.Type.HasAnyFlag(filter)) { entities.Add(worldObject); if (--limitCounter == 0) { break; } } } } else { for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Intersects(ref box).HasAnyFlag(IntersectionType.Touches)) { child.GetEntitiesInArea(ref box, entities, filter, phase, ref limitCounter); } } } } }
/// <summary>Gets all objects within a specified radius.</summary> /// <typeparam name="T">the specific type of the objects to retrieve</typeparam> /// <param name="entities">the list to append retrieved objects to</param> internal void GetObjectsOfSpecificType <T>(ref BoundingBox box, List <T> entities, uint phase, ref int limitCounter) where T : WorldObject { if (IsLeaf) { if (!HasObjects) { return; } foreach (WorldObject worldObject in m_objects.Values) { Vector3 position = worldObject.Position; if (box.Contains(ref position) && worldObject.IsInPhase(phase) && worldObject.GetType() == typeof(T)) { entities.Add(worldObject as T); if (--limitCounter == 0) { break; } } } } else { for (int index1 = 0; index1 < 2; ++index1) { for (int index2 = 0; index2 < 2; ++index2) { ZoneSpacePartitionNode child = m_children[index1, index2]; if (child.Bounds.Intersects(ref box).HasAnyFlag(IntersectionType.Touches)) { child.GetObjectsOfSpecificType(ref box, entities, phase, ref limitCounter); } } } } }
/// <summary>TODO: Find all intermediate neighbors</summary> /// <param name="parent"></param> /// <param name="vertical"></param> /// <param name="horizontal"></param> internal void FindNeighbors(ZoneSpacePartitionNode parent, int vertical, int horizontal) { }