public unsafe void GetSelfOverlapsViaQueries <TResultList>(ref TResultList results) where TResultList : IList <Overlap> { var leafQueryResults = new QuickList <int>(BufferPools <int> .Thread); for (int i = 0; i < leafCount; ++i) { var leaf = leaves[i]; BoundingBoxWide leafBoundingBox; BoundingBoxWide.GetBoundingBox(ref Levels[leaf.LevelIndex].Nodes[leaf.NodeIndex].BoundingBoxes, leaf.ChildIndex, out leafBoundingBox); TestRecursive(0, 0, ref leafBoundingBox, ref leafQueryResults); for (int j = 0; j < leafQueryResults.Count; ++j) { //Only include results which which are forward in the list to avoid self tests. if (i < leafQueryResults.Elements[j]) { results.Add(new Overlap { A = i, B = leafQueryResults.Elements[j] }); } } leafQueryResults.Count = 0; } leafQueryResults.Dispose(); //Console.WriteLine("Query-based results:"); //for (int i = 0; i < results.Count; ++i) //{ // Console.WriteLine($"{results[i].A}, {results[i].B}"); //} }
public unsafe void Refit() { //Go through each level, refitting as you go. //Note that the deepest level is skipped. It does not need to be tested; it's all leaves that were already updated. for (int levelIndex = maximumDepth - 1; levelIndex >= 0; --levelIndex) { //consider testing caching Levels[levelIndex]. It may have a minor effect. for (int nodeIndex = 0; nodeIndex < Levels[levelIndex].Count; ++nodeIndex) { for (int childIndex = 0; childIndex < Vector <int> .Count; ++childIndex) { var childNodeIndex = Levels[levelIndex].Nodes[nodeIndex].Children[childIndex]; if (childNodeIndex >= 0) { BoundingBoxWide merged; ComputeBoundingBox(ref Levels[levelIndex + 1].Nodes[childNodeIndex].BoundingBoxes, out merged); BoundingBoxWide.ConditionalSelect(ref singleMasks[childIndex], ref merged, ref Levels[levelIndex].Nodes[nodeIndex].BoundingBoxes, out Levels[levelIndex].Nodes[nodeIndex].BoundingBoxes); } } } } }
//[MethodImpl(MethodImplOptions.AggressiveInlining)] unsafe void TestRecursive <TResultList>(int level, int nodeIndex, ref BoundingBoxWide query, ref TResultList results) where TResultList : IList <int> { Vector <int> intersectionMask; BoundingBoxWide.Intersects(ref Levels[level].Nodes[nodeIndex].BoundingBoxes, ref query, out intersectionMask); //Console.WriteLine($"Intersection mask: {intersectionMask}"); //Console.WriteLine(node.BoundingBoxes); //int INTERSECTEDCOUNT = 0; for (int i = 0; i < Vector <int> .Count; ++i) { if (intersectionMask[i] < 0) { //++INTERSECTEDCOUNT; if (Levels[level].Nodes[nodeIndex].Children[i] >= 0) { TestRecursive(level + 1, Levels[level].Nodes[nodeIndex].Children[i], ref query, ref results); } else if (Levels[level].Nodes[nodeIndex].Children[i] < -1) { results.Add(Encode(Levels[level].Nodes[nodeIndex].Children[i])); } } } //Console.WriteLine($"Level {level}, intersected count: {INTERSECTEDCOUNT}"); }
unsafe void Test <TResultList>(TraversalTarget *stack, ref int count, int stackCapacity, int level, ref BoundingBoxWide query, ref Node node, ref TResultList results) where TResultList : IList <int> { Vector <int> intersectionMask; BoundingBoxWide.Intersects(ref node.BoundingBoxes, ref query, out intersectionMask); //Console.WriteLine($"Intersection mask: {intersectionMask}"); //Console.WriteLine(node.BoundingBoxes); for (int i = 0; i < Vector <int> .Count; ++i) { if (intersectionMask[i] < 0) { if (node.Children[i] >= 0) { Debug.Assert(count < stackCapacity); stack[count++] = new TraversalTarget { Level = level + 1, Node = node.Children[i] }; } else if (node.Children[i] < -1) { results.Add(Encode(node.Children[i])); } } } }
unsafe void GetOverlapsBetweenDifferentNodes <TResultList>(int levelIndex, int aIndex, int bIndex, ref TResultList results) where TResultList : IList <Overlap> { //There are no shared children, so test them all. var a = Levels[levelIndex].Nodes[aIndex]; var b = Levels[levelIndex].Nodes[bIndex]; int nextLevel = levelIndex + 1; for (int i = 0; i < Vector <float> .Count; ++i) { if (a.Children[i] == -1) { continue; } //REALLY want a shuffle here. If we could just push forward, retest, push forward, retest... //Unclear how much of a performance benefit this would actually be, since this is just a series of broadcasts. //... not sure it's actually compiled to a series of broadcasts, though. Vector <int> intersectionMask; BoundingBoxWide aWide; BoundingBoxWide.GetBoundingBox(ref a.BoundingBoxes, i, out aWide); //var aChild = new Vector<int>(a.Children[i]); //var less = Vector.LessThan(aChild, b.Children); //var bChildIsVector.LessThan(b.Children, new Vector<int>(-1)); BoundingBoxWide.Intersects(ref aWide, ref b.BoundingBoxes, out intersectionMask); for (int j = 0; j < Vector <float> .Count; ++j) { //TODO: would it be faster to compute a single combined value via simd and then use a single switch? if (b.Children[j] != -1 && intersectionMask[j] < 0) { //TODO: try removing branches via bool ops if (a.Children[i] >= 0 && b.Children[j] >= 0) { GetOverlapsBetweenDifferentNodes(nextLevel, a.Children[i], b.Children[j], ref results); } else if (a.Children[i] < -1 && b.Children[j] >= 0) { //leaf A versus node B. TestLeafAgainstNode(Encode(a.Children[i]), ref aWide, nextLevel, b.Children[j], ref results); } else if (a.Children[i] >= 0 && b.Children[j] < -1) { //leaf B versus node A. BoundingBoxWide bWide; BoundingBoxWide.GetBoundingBox(ref b.BoundingBoxes, j, out bWide); TestLeafAgainstNode(Encode(b.Children[j]), ref bWide, nextLevel, a.Children[i], ref results); } else if (a.Children[i] < -1 && b.Children[j] < -1) { //Two leaves. var leafA = Encode(a.Children[i]); var leafB = Encode(b.Children[j]); results.Add(new Overlap { A = leafA, B = leafB }); } } } } }
public void Add(ref StreamingLeafGroup parent, int sourceIndex, int destinationIndex, Vector <int>[] masks) { //This is basically: a[i] = b[j] var leavesBroadcast = new Vector <int>(parent.Leaves[sourceIndex]); BoundingBoxWide boundsBroadcast; BoundingBoxWide.GetBoundingBox(ref parent.BoundingBoxes, sourceIndex, out boundsBroadcast); Leaves = Vector.ConditionalSelect(masks[destinationIndex], leavesBroadcast, Leaves); BoundingBoxWide.ConditionalSelect(ref masks[destinationIndex], ref boundsBroadcast, ref BoundingBoxes, out BoundingBoxes); }
public void Add(ref Vector <int> leafIndex, ref BoundingBoxWide boundingBox, Vector <int>[] masks) { if (LastLeavesCount < Vector <float> .Count) { LeafGroups.Elements[LeafGroups.Count - 1].Add(ref boundingBox, ref leafIndex, ref masks[LastLeavesCount]); ++LastLeavesCount; } else { var newLeaves = new StreamingLeafGroup(); newLeaves.Add(ref boundingBox, ref leafIndex, ref masks[0]); LeafGroups.Add(ref newLeaves); LastLeavesCount = 1; } }
public unsafe void RefitLeaves() { //Update the bounding boxes of every leaf-owner. //Note the scalar-ness of this. It seems like there should exist some way to vectorize it properly, though it may require changing things around. for (int i = 0; i < leafCount; ++i) { BoundingBox box; leaves[i].Bounded.GetBoundingBox(out box); BoundingBoxWide wideBox = new BoundingBoxWide(ref box); //Console.WriteLine($"index reached: {i}, child index: {leaves[i].ChildIndex}, level: {leaves[i].LevelIndex}, node: { leaves[i].NodeIndex}"); BoundingBoxWide.ConditionalSelect(ref singleMasks[leaves[i].ChildIndex], ref wideBox, ref Levels[leaves[i].LevelIndex].Nodes[leaves[i].NodeIndex].BoundingBoxes, out Levels[leaves[i].LevelIndex].Nodes[leaves[i].NodeIndex].BoundingBoxes); //Console.WriteLine($"comp"); } }
//[MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Query <TResultList>(ref BoundingBox boundingBox, ref TResultList results) where TResultList : IList <int> { //TODO: could optimize this by keeping the next target out of the stack. var stackCapacity = (Vector <int> .Count - 1) * maximumDepth + 1; var stack = stackalloc TraversalTarget[stackCapacity]; int count = 0; var boundingBoxWide = new BoundingBoxWide(ref boundingBox); Test(stack, ref count, stackCapacity, 0, ref boundingBoxWide, ref Levels[0].Nodes[0], ref results); while (count > 0) { --count; var target = stack[count]; Test(stack, ref count, stackCapacity, target.Level, ref boundingBoxWide, ref Levels[target.Level].Nodes[target.Node], ref results); } }
void ComputeBoundingBox(ref BoundingBoxWide boundingBoxes, out BoundingBoxWide mergedWide) { //YIKES transposition BoundingBox merged; merged.Min = new Vector3(float.MaxValue); merged.Max = new Vector3(-float.MaxValue); for (int childIndex = 0; childIndex < Vector <int> .Count; ++childIndex) { var childMin = new Vector3( boundingBoxes.Min.X[childIndex], boundingBoxes.Min.Y[childIndex], boundingBoxes.Min.Z[childIndex]); var childMax = new Vector3( boundingBoxes.Max.X[childIndex], boundingBoxes.Max.Y[childIndex], boundingBoxes.Max.Z[childIndex]); merged.Min = Vector3.Min(merged.Min, childMin); merged.Max = Vector3.Max(merged.Max, childMax); } mergedWide = new BoundingBoxWide(ref merged); }
unsafe void TestLeafAgainstNode <TResultList>(int leaf, ref BoundingBoxWide leafBounds, int levelIndex, int nodeIndex, ref TResultList results) where TResultList : IList <Overlap> { int nextLevel = levelIndex + 1; Vector <int> intersectionMask; BoundingBoxWide.Intersects(ref leafBounds, ref Levels[levelIndex].Nodes[nodeIndex].BoundingBoxes, out intersectionMask); for (int i = 0; i < Vector <int> .Count; ++i) { if (intersectionMask[i] < 0) { if (Levels[levelIndex].Nodes[nodeIndex].Children[i] < -1) { var otherLeaf = Encode(Levels[levelIndex].Nodes[nodeIndex].Children[i]); results.Add(new Overlap { A = leaf, B = otherLeaf }); } else if (Levels[levelIndex].Nodes[nodeIndex].Children[i] >= 0) { TestLeafAgainstNode(leaf, ref leafBounds, nextLevel, Levels[levelIndex].Nodes[nodeIndex].Children[i], ref results); } } } }
public unsafe void QueryRecursive <TResultList>(ref BoundingBox boundingBox, ref TResultList results) where TResultList : IList <int> { var boundingBoxWide = new BoundingBoxWide(ref boundingBox); TestRecursive(0, 0, ref boundingBoxWide, ref results); }
public unsafe void Insert(T leaf) { int levelIndex = 0; int nodeIndex = 0; BoundingBox aosBox; leaf.GetBoundingBox(out aosBox); var box = new BoundingBoxWide(ref aosBox); #if OUTPUT List <int> choices = new List <int>(); #endif while (true) { var level = Levels[levelIndex]; //Which child should the leaf belong to? Vector <float> originalVolumes; BoundingBoxWide.ComputeVolume(ref level.Nodes[nodeIndex].BoundingBoxes, out originalVolumes); originalVolumes = Vector.Max(originalVolumes, Vector <float> .Zero); BoundingBoxWide merged; BoundingBoxWide.Merge(ref level.Nodes[nodeIndex].BoundingBoxes, ref box, out merged); Vector <float> mergedVolumes; BoundingBoxWide.ComputeVolume(ref merged, out mergedVolumes); //Give the leaf to whichever node had the least volume change. var volumeIncreases = mergedVolumes - originalVolumes; int minimumIndex = 0; var minimum = volumeIncreases[0]; for (int i = 1; i < Vector <float> .Count; ++i) { if (volumeIncreases[i] < minimum) { minimumIndex = i; minimum = volumeIncreases[i]; } } //var volumeIncreases = mergedVolumes - originalVolumes; //int minimumIndex = 0; //var minimum = float.MaxValue; //for (int i = 0; i < Vector<float>.Count; ++i) //{ // if (level.Nodes[nodeIndex].Children[i] == -1) // { // minimumIndex = i; // break; // } // if (volumeIncreases[i] < minimum) // { // minimumIndex = i; // minimum = volumeIncreases[i]; // } //} #if OUTPUT Console.WriteLine($"Minimum index: {minimumIndex}, minimum volume increase: {minimum}"); choices.Add(minimumIndex); #endif var childIndex = level.Nodes[nodeIndex].Children[minimumIndex]; if (childIndex < -1) { //It's a leaf node. //Create a new internal node with the new leaf and the old leaf as children. var nextLevel = levelIndex + 1; //this is the only place where a new level could potentially be created. EnsureLevel(nextLevel); Node newNode; InitializeNode(out newNode); //The first child of the new node is the old leaf. Insert its bounding box. //Since we don't have a great way of shuffling yet, just let it be in the same index. BoundingBoxWide.ConditionalSelect( ref singleMasks[minimumIndex], ref level.Nodes[nodeIndex].BoundingBoxes, ref newNode.BoundingBoxes, out newNode.BoundingBoxes); newNode.Children = Vector.ConditionalSelect(singleMasks[minimumIndex], level.Nodes[nodeIndex].Children, newNode.Children); //Insert the new leaf into the second child slot. //Just put it in the next slot over from the minimum. var newLeafChildIndex = (minimumIndex + 1) & (Vector <float> .Count - 1); BoundingBoxWide.ConditionalSelect( ref singleMasks[newLeafChildIndex], ref box, ref newNode.BoundingBoxes, out newNode.BoundingBoxes); var newNodeIndex = Levels[nextLevel].Add(ref newNode); var leafIndex = AddLeaf(leaf, nextLevel, newNodeIndex, newLeafChildIndex); var leafIndexVector = new Vector <int>(Encode(leafIndex)); Levels[nextLevel].Nodes[newNodeIndex].Children = Vector.ConditionalSelect(singleMasks[newLeafChildIndex], leafIndexVector, newNode.Children); //Update the old leaf node with the new index information. var oldLeafIndex = Encode(childIndex); leaves[oldLeafIndex].LevelIndex = nextLevel; leaves[oldLeafIndex].NodeIndex = newNodeIndex; //Since we inserted it into the same slot of the new node as it was in in the old node, there is no change to the child index. //Update the original node's child pointer and bounding box. var newNodeIndexVector = new Vector <int>(newNodeIndex); level.Nodes[nodeIndex].Children = Vector.ConditionalSelect(singleMasks[minimumIndex], newNodeIndexVector, level.Nodes[nodeIndex].Children); BoundingBoxWide.ConditionalSelect(ref singleMasks[minimumIndex], ref merged, ref level.Nodes[nodeIndex].BoundingBoxes, out level.Nodes[nodeIndex].BoundingBoxes); #if OUTPUT Console.WriteLine($"Leaf {leafIndex} merged with existing leaf.");// New Node Children: {newNode.Children}, Old Node children: {level.Nodes[nodeIndex].Children}"); Console.WriteLine($"Choices: {GetChoiceList(choices)}"); #endif break; } if (childIndex == -1) { //There is no child at all. //Put the new leaf here. var leafIndex = AddLeaf(leaf, levelIndex, nodeIndex, minimumIndex); var leafIndexVector = new Vector <int>(Encode(leafIndex)); level.Nodes[nodeIndex].Children = Vector.ConditionalSelect(singleMasks[minimumIndex], leafIndexVector, level.Nodes[nodeIndex].Children); BoundingBoxWide.ConditionalSelect(ref singleMasks[minimumIndex], ref merged, ref level.Nodes[nodeIndex].BoundingBoxes, out level.Nodes[nodeIndex].BoundingBoxes); #if OUTPUT Console.WriteLine($"Leaf {leafIndex} inserted in empty slot."); Console.WriteLine($"Choices: {GetChoiceList(choices)}"); #endif break; } //It's an internal node. Traverse to the next node. BoundingBoxWide.ConditionalSelect(ref singleMasks[minimumIndex], ref merged, ref level.Nodes[nodeIndex].BoundingBoxes, out level.Nodes[nodeIndex].BoundingBoxes); nodeIndex = level.Nodes[nodeIndex].Children[minimumIndex]; ++levelIndex; } }
public unsafe void GetSelfOverlapsViaStreamingQueries <TResultList>(ref TResultList results) where TResultList : IList <Overlap> { //var startTime = Stopwatch.GetTimestamp(); var rootTarget = new StreamingTarget { LeafGroups = new QuickList <StreamingLeafGroup>(BufferPools <StreamingLeafGroup> .Locking, BufferPool <StreamingLeafGroup> .GetPoolIndex(LeafCount)) }; rootTarget.LeafGroups.Add(new StreamingLeafGroup()); for (int i = 0; i < LeafCount; ++i) { BoundingBoxWide leafWide; BoundingBoxWide.GetBoundingBox(ref Levels[leaves[i].LevelIndex].Nodes[leaves[i].NodeIndex].BoundingBoxes, leaves[i].ChildIndex, out leafWide); var leafIndexWide = new Vector <int>(i); rootTarget.Add(ref leafIndexWide, ref leafWide, singleMasks); } //var endTime = Stopwatch.GetTimestamp(); //Console.WriteLine($"Initial target construction time: {(endTime - startTime) / (double)Stopwatch.Frequency}"); QuickQueue <StreamingTarget> targets = new QuickQueue <StreamingTarget>(BufferPools <StreamingTarget> .Locking, BufferPool <StreamingLeafGroup> .GetPoolIndex(LeafCount)); targets.Enqueue(ref rootTarget); QuickList <int> fallbackResults = new QuickList <int>(BufferPools <int> .Locking); StreamingTarget target; while (targets.TryDequeueLast(out target)) { const int GroupFallbackThreshold = 2; //unfortunately, this should be as high as possible right now because the regular query is faster, period. if (target.LeafGroups.Count <= GroupFallbackThreshold) { var max = target.LastLeavesCount == Vector <int> .Count ? target.LeafGroups.Count : target.LeafGroups.Count - 1; for (int leafGroupIndex = 0; leafGroupIndex < max; ++leafGroupIndex) { for (int leafInGroupIndex = 0; leafInGroupIndex < Vector <int> .Count; ++leafInGroupIndex) { BoundingBoxWide leafWide; BoundingBoxWide.GetBoundingBox(ref target.LeafGroups.Elements[leafGroupIndex].BoundingBoxes, leafInGroupIndex, out leafWide); TestRecursive(target.LevelIndex, target.NodeIndex, ref leafWide, ref fallbackResults); for (int resultIndex = 0; resultIndex < fallbackResults.Count; ++resultIndex) { var queryLeafIndex = target.LeafGroups.Elements[leafGroupIndex].Leaves[leafInGroupIndex]; if (queryLeafIndex < fallbackResults.Elements[resultIndex]) { results.Add(new Overlap { A = queryLeafIndex, B = fallbackResults.Elements[resultIndex] }); } } fallbackResults.Count = 0; } } if (target.LastLeavesCount < Vector <int> .Count) { var leafGroupIndex = target.LeafGroups.Count - 1; for (int leafInGroupIndex = 0; leafInGroupIndex < target.LastLeavesCount; ++leafInGroupIndex) { BoundingBoxWide leafWide; BoundingBoxWide.GetBoundingBox(ref target.LeafGroups.Elements[leafGroupIndex].BoundingBoxes, leafInGroupIndex, out leafWide); TestRecursive(target.LevelIndex, target.NodeIndex, ref leafWide, ref fallbackResults); for (int resultIndex = 0; resultIndex < fallbackResults.Count; ++resultIndex) { var queryLeafIndex = target.LeafGroups.Elements[leafGroupIndex].Leaves[leafInGroupIndex]; if (queryLeafIndex < fallbackResults.Elements[resultIndex]) { results.Add(new Overlap { A = queryLeafIndex, B = fallbackResults.Elements[resultIndex] }); } } fallbackResults.Count = 0; } } } else { var node = Levels[target.LevelIndex].Nodes[target.NodeIndex]; //Test each node child against all of the leaves for this node. for (int nodeChildIndex = 0; nodeChildIndex < Vector <int> .Count; ++nodeChildIndex) { if (node.Children[nodeChildIndex] == -1) { continue; } BoundingBoxWide nodeChildWide; BoundingBoxWide.GetBoundingBox(ref node.BoundingBoxes, nodeChildIndex, out nodeChildWide); if (node.Children[nodeChildIndex] >= 0) { //Internal node. Can spawn more targets. StreamingTarget newTarget = new StreamingTarget { LevelIndex = target.LevelIndex + 1, NodeIndex = node.Children[nodeChildIndex], LeafGroups = new QuickList <StreamingLeafGroup>(BufferPools <StreamingLeafGroup> .Locking, BufferPool <StreamingLeafGroup> .GetPoolIndex(target.LeafGroups.Count)) }; newTarget.LeafGroups.Add(new StreamingLeafGroup()); for (int leafGroupIndex = 0; leafGroupIndex < target.LeafGroups.Count; ++leafGroupIndex) { Vector <int> intersectionMask; BoundingBoxWide.Intersects(ref nodeChildWide, ref target.LeafGroups.Elements[leafGroupIndex].BoundingBoxes, out intersectionMask); int leafCountInGroup = leafGroupIndex == target.LeafGroups.Count - 1 ? target.LastLeavesCount : Vector <int> .Count; for (int leafIndexInGroup = 0; leafIndexInGroup < leafCountInGroup; ++leafIndexInGroup) { if (intersectionMask[leafIndexInGroup] < 0) { newTarget.Add(ref target, leafGroupIndex, leafIndexInGroup, singleMasks); } } } targets.Enqueue(ref newTarget); } else { //Leaf node. var nodeLeafIndex = Encode(node.Children[nodeChildIndex]); for (int leafGroupIndex = 0; leafGroupIndex < target.LeafGroups.Count; ++leafGroupIndex) { Vector <int> intersectionMask; BoundingBoxWide.Intersects(ref nodeChildWide, ref target.LeafGroups.Elements[leafGroupIndex].BoundingBoxes, out intersectionMask); int leafCountInGroup = leafGroupIndex == target.LeafGroups.Count - 1 ? target.LastLeavesCount : Vector <int> .Count; for (int leafIndexInGroup = 0; leafIndexInGroup < leafCountInGroup; ++leafIndexInGroup) { if (intersectionMask[leafIndexInGroup] < 0) { var leafIndex = target.LeafGroups[leafGroupIndex].Leaves[leafIndexInGroup]; if (leafIndex < nodeLeafIndex) //The other leaf will also find a collision! { results.Add(new Overlap { A = leafIndex, B = nodeLeafIndex }); } } } } } } } target.LeafGroups.Count = 0; //Don't bother forcing a clear on these. TODO: buffer safety check disable target.LeafGroups.Dispose(); } targets.Dispose(); fallbackResults.Dispose(); //Console.WriteLine("Streaming Query based results:"); //for (int i = 0; i < results.Count; ++i) //{ // Console.WriteLine($"{results[i].A}, {results[i].B}"); //} }
public void Add(ref BoundingBoxWide boundingBox, ref Vector <int> leafIndex, ref Vector <int> destinationMask) { Leaves = Vector.ConditionalSelect(destinationMask, leafIndex, Leaves); BoundingBoxWide.ConditionalSelect(ref destinationMask, ref boundingBox, ref BoundingBoxes, out BoundingBoxes); }