public void AddOctreeItem(OctreeItem <T> octreeItem) { if (_cachedItems.Count < MaxCachedItemCount) { octreeItem.Item = default(T); octreeItem.Container = null; _cachedItems.Push(octreeItem); } }
public OctreeNode <T> AddItem(ref BoundingBox itemBounds, T item, out OctreeItem <T> octreeItem) { if (Parent != null) { throw new InvalidOperationException("Can only add items to the root Octree node."); } octreeItem = _nodeCache.GetOctreeItem(ref itemBounds, item); return(CoreAddRootItem(octreeItem)); }
private OctreeNode <T> CoreAddRootItem(OctreeItem <T> octreeItem) { OctreeNode <T> root = this; bool result = CoreAddItem(octreeItem); if (!result) { root = ResizeAndAdd(octreeItem); } return(root); }
private OctreeNode <T> ResizeAndAdd(OctreeItem <T> octreeItem) { OctreeNode <T> oldRoot = this; Vector3 oldRootCenter = Bounds.GetCenter(); Vector3 oldRootHalfExtents = Bounds.GetDimensions() * 0.5f; Vector3 expandDirection = Vector3.Normalize(octreeItem.Bounds.GetCenter() - oldRootCenter); Vector3 newCenter = oldRootCenter; if (expandDirection.X >= 0) // oldRoot = Left { newCenter.X += oldRootHalfExtents.X; } else { newCenter.X -= oldRootHalfExtents.X; } if (expandDirection.Y >= 0) // oldRoot = Bottom { newCenter.Y += oldRootHalfExtents.Y; } else { newCenter.Y -= oldRootHalfExtents.Y; } if (expandDirection.Z >= 0) // oldRoot = Far { newCenter.Z += oldRootHalfExtents.Z; } else { newCenter.Z -= oldRootHalfExtents.Z; } BoundingBox newRootBounds = new BoundingBox(newCenter - oldRootHalfExtents * 2f, newCenter + oldRootHalfExtents * 2f); OctreeNode <T> newRoot = _nodeCache.GetNode(ref newRootBounds); OctreeNode <T> fittingNode = newRoot.SplitChildren(ref octreeItem.Bounds, oldRoot); if (fittingNode != null) { bool succeeded = fittingNode.CoreAddItem(octreeItem); Debug.Assert(succeeded, "Octree node returned from SplitChildren must fit the item given to it."); return(newRoot); } else { return(newRoot.CoreAddRootItem(octreeItem)); } }
/// <summary> /// Move a contained OctreeItem. If the root OctreeNode needs to be resized, the new root node is returned. /// </summary> public OctreeNode <T> MoveContainedItem(OctreeItem <T> item, BoundingBox newBounds) { OctreeNode <T> newRoot = null; var container = item.Container; if (!container._items.Contains(item)) { throw new InvalidOperationException("Can't move item " + item + ", its container does not contain it."); } item.Bounds = newBounds; if (container.Bounds.Contains(ref item.Bounds) == ContainmentType.Contains) { // Item did not leave the node. newRoot = null; // It may have moved into the bounds of a child node. foreach (var child in Children) { if (child.CoreAddItem(item)) { _items.Remove(item); break; } } } else { container._items.Remove(item); item.Container = null; var node = container; while (node.Parent != null && !node.CoreAddItem(item)) { node = node.Parent; } if (item.Container == null) { // This should only occur if the item has moved beyond the root node's bounds. // We need to resize the root tree. Debug.Assert(node == GetRootNode()); newRoot = node.CoreAddRootItem(item); } container.Parent.ConsiderConsolidation(); } return(newRoot); }
/// <summary> /// Mark an item as having moved, but do not alter the octree structure. Call <see cref="Octree{T}.ApplyPendingMoves"/> to update the octree structure. /// </summary> public void MarkItemAsMoved(OctreeItem <T> octreeItem, BoundingBox newBounds) { if (!_items.Contains(octreeItem)) { throw new InvalidOperationException("Cannot mark item as moved which doesn't belong to this OctreeNode."); } if (newBounds.ContainsNaN()) { throw new InvalidOperationException("Invalid bounds: " + newBounds); } octreeItem.HasPendingMove = true; octreeItem.Bounds = newBounds; }
public void MoveItem(OctreeItem <T> octreeItem, BoundingBox newBounds) { if (newBounds.ContainsNaN()) { throw new VeldridException("Invalid bounds: " + newBounds); } var newRoot = octreeItem.Container.MoveContainedItem(octreeItem, newBounds); if (newRoot != null) { _currentRoot = newRoot; } _currentRoot = _currentRoot.TryTrimChildren(); }
public void RemoveItem(OctreeItem <T> octreeItem) { var container = octreeItem.Container; if (!container._items.Remove(octreeItem)) { throw new InvalidOperationException("Item isn't contained in its container."); } if (container.Parent != null) { container.Parent.ConsiderConsolidation(); } _nodeCache.AddOctreeItem(octreeItem); }
private bool CoreAddItem(OctreeItem <T> item) { if (Bounds.Contains(ref item.Bounds) != ContainmentType.Contains) { return(false); } if (_items.Count >= MaxChildren && Children.Length == 0) { OctreeNode <T> newNode = SplitChildren(ref item.Bounds, null); if (newNode != null) { bool succeeded = newNode.CoreAddItem(item); Debug.Assert(succeeded, "Octree node returned from SplitChildren must fit the item given to it."); return(true); } } else if (Children.Length > 0) { foreach (var child in Children) { if (child.CoreAddItem(item)) { return(true); } } } // Couldn't fit in any children. #if DEBUG foreach (var child in Children) { Debug.Assert(child.Bounds.Contains(ref item.Bounds) != ContainmentType.Contains); } #endif _items.Add(item); item.Container = this; return(true); }
/// <summary> /// Attempts to find an OctreeNode for the given item, in this OctreeNode and its children. /// </summary> /// <param name="item">The item to find.</param> /// <param name="octreeItem">The contained OctreeItem.</param> /// <returns>true if the item was contained in the Octree; false otherwise.</returns> internal bool TryGetContainedOctreeItem(T item, out OctreeItem <T> octreeItem) { foreach (var containedItem in _items) { if (containedItem.Item.Equals(item)) { octreeItem = containedItem; return(true); } } foreach (var child in Children) { Debug.Assert(child != null, "node child cannot be null."); if (child.TryGetContainedOctreeItem(item, out octreeItem)) { return(true); } } octreeItem = null; return(false); }
public void RemoveItem(OctreeItem <T> octreeItem) { octreeItem.Container.RemoveItem(octreeItem); _currentRoot = _currentRoot.TryTrimChildren(); }
public OctreeNode <T> AddItem(BoundingBox itemBounds, T item, out OctreeItem <T> itemContainer) { return(AddItem(ref itemBounds, item, out itemContainer)); }