using System; using System.Collections.Generic; namespace UnityEngine.PathTracing.Core { internal class TextureQuadTree { public class TextureNode { public TextureNode TopLeft; public TextureNode TopRight; public TextureNode BottomLeft; public TextureNode BottomRight; public TextureNode Parent; public int PosX; public int PosY; public int Size; } private readonly int _size; private readonly List _leaves; public TextureQuadTree(int size) { _size = size; var root = new TextureNode() { PosX = 0, PosY = 0, Size = _size }; _leaves = new List() { root }; } private void SubdivideNode(TextureNode node) { node.TopLeft = new TextureNode() { PosX = node.PosX, PosY = node.PosY, Size = node.Size / 2, Parent = node }; node.BottomLeft = new TextureNode() { PosX = node.PosX, PosY = node.PosY + node.Size / 2, Size = node.Size / 2, Parent = node }; node.TopRight = new TextureNode() { PosX = node.PosX + node.Size / 2, PosY = node.PosY, Size = node.Size / 2, Parent = node }; node.BottomRight = new TextureNode() { PosX = node.PosX + node.Size / 2, PosY = node.PosY + node.Size / 2, Size = node.Size / 2, Parent = node }; } public bool AddTexture(int size, out TextureNode node) { int targetSize = Mathf.Min(Mathf.NextPowerOfTwo(size), _size); // Search for a node with the right size int bestSize = int.MaxValue; TextureNode candidate = null; for (int i = _leaves.Count - 1; i >= 0; i--) { // If we have a match, return it int leafSize = _leaves[i].Size; if (targetSize == leafSize) { node = _leaves[i]; _leaves.RemoveAt(i); return true; } // Keep track of the best candidate we find if (targetSize < leafSize && leafSize < bestSize) { candidate = _leaves[i]; bestSize = leafSize; } } // If we have a candidate, subdivide one child until we are at the right size if (candidate != null) { while (candidate.Size != targetSize) { SubdivideNode(candidate); _leaves.Remove(candidate); _leaves.Add(candidate.BottomRight); _leaves.Add(candidate.BottomLeft); _leaves.Add(candidate.TopRight); candidate = candidate.TopLeft; } node = candidate; return true; } node = null; return false; } public void RemoveTexture(TextureNode node) { bool ShouldCollapse(TextureNode node) { return (node.TopLeft == null || _leaves.Contains(node.TopLeft)) && (node.TopRight == null || _leaves.Contains(node.TopRight)) && (node.BottomLeft == null || _leaves.Contains(node.BottomLeft)) && (node.BottomRight == null || _leaves.Contains(node.BottomRight)); } // Add the node to the list of leaves _leaves.Add(node); // Recursively collapse the quad tree if the parent node is empty, to avoid fragmentation TextureNode parent = node.Parent; while (parent != null && ShouldCollapse(parent)) { // Remove the children from the leaves list if (_leaves.Contains(parent.TopLeft)) _leaves.Remove(parent.TopLeft); if (_leaves.Contains(parent.TopRight)) _leaves.Remove(parent.TopRight); if (_leaves.Contains(parent.BottomLeft)) _leaves.Remove(parent.BottomLeft); if (_leaves.Contains(parent.BottomRight)) _leaves.Remove(parent.BottomRight); // Collapse the parent node parent.TopLeft = null; parent.TopRight = null; parent.BottomLeft = null; parent.BottomRight = null; // Add the parent to the leaves list, and check the next parent _leaves.Add(parent); parent = parent.Parent; } } public bool HasSpaceForTexture(int textureSize) { return _leaves.Exists(leaf => leaf.Size >= textureSize); } public bool IsFull => _leaves.Count == 0; } }