public bool Contains(AABB2D b) { return(Contains(b.Center + new float2(-b.Extents.x, -b.Extents.y)) && Contains(b.Center + new float2(-b.Extents.x, b.Extents.y)) && Contains(b.Center + new float2(b.Extents.x, -b.Extents.y)) && Contains(b.Center + new float2(b.Extents.x, b.Extents.y))); }
public void RangeQuery(AABB2D bounds, NativeList <QuadElement <T> > results) { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(safetyHandle); #endif new QuadTreeRangeQuery().Query(this, bounds, results); }
static void DrawBounds(Color[][] texture, AABB2D bounds, NativeQuadTree <T> tree) { var widthMult = texture.Length / tree.bounds.Extents.x * 2 / 2 / 2; var heightMult = texture[0].Length / tree.bounds.Extents.y * 2 / 2 / 2; var widthAdd = tree.bounds.Center.x + tree.bounds.Extents.x; var heightAdd = tree.bounds.Center.y + tree.bounds.Extents.y; var top = new float2(bounds.Center.x, bounds.Center.y - bounds.Extents.y); var left = new float2(bounds.Center.x - bounds.Extents.x, bounds.Center.y); for (int leftToRight = 0; leftToRight < bounds.Extents.x * 2; leftToRight++) { var poxX = left.x + leftToRight; texture[(int)((poxX + widthAdd) * widthMult)][(int)((bounds.Center.y + heightAdd + bounds.Extents.y) * heightMult)] = Color.blue; texture[(int)((poxX + widthAdd) * widthMult)][(int)((bounds.Center.y + heightAdd - bounds.Extents.y) * heightMult)] = Color.blue; } for (int topToBottom = 0; topToBottom < bounds.Extents.y * 2; topToBottom++) { var posY = top.y + topToBottom; texture[(int)((bounds.Center.x + widthAdd + bounds.Extents.x) * widthMult)][(int)((posY + heightAdd) * heightMult)] = Color.blue; texture[(int)((bounds.Center.x + widthAdd - bounds.Extents.x) * widthMult)][(int)((posY + heightAdd) * heightMult)] = Color.blue; } }
//NativeQuadTree.NativeQuadTree<int> QuadTree; protected override void OnCreate() { base.OnCreate(); NativeQuadTree.AABB2D aabb = new NativeQuadTree.AABB2D(new float2(500, 500), new float2(1000, 1000)); //initializeQuadTree(aabb); //entityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); }
public bool Intersects(AABB2D b) { //bool noOverlap = Min[0] > b.Max[0] || // b.Min[0] > Max[0]|| // Min[1] > b.Max[1] || // b.Min[1] > Max[1]; // //return !noOverlap; return((math.abs(Center[0] - b.Center[0]) < (Extents[0] + b.Extents[0])) && (math.abs(Center[1] - b.Center[1]) < (Extents[1] + b.Extents[1]))); }
public void Query(NativeQuadTree <T> tree, AABB2D bounds, NativeList <QuadElement <T> > results) { this.tree = tree; this.bounds = bounds; count = 0; // Get pointer to inner list data for faster writing fastResults = (UnsafeList *)NativeListUnsafeUtility.GetInternalListDataPtrUnchecked(ref results); RecursiveRangeQuery(tree.bounds, false, 1, 1); fastResults->Length = count; }
static AABB2D GetChildBounds(AABB2D parentBounds, int childZIndex) { var half = parentBounds.Extents.x * .5f; switch (childZIndex) { case 0: return(new AABB2D(new float2(parentBounds.Center.x - half, parentBounds.Center.y + half), half)); case 1: return(new AABB2D(new float2(parentBounds.Center.x + half, parentBounds.Center.y + half), half)); case 2: return(new AABB2D(new float2(parentBounds.Center.x - half, parentBounds.Center.y - half), half)); case 3: return(new AABB2D(new float2(parentBounds.Center.x + half, parentBounds.Center.y - half), half)); default: throw new Exception(); } }
AABB2D bounds; // NOTE: Currently assuming uniform /// <summary> /// Create a new QuadTree. /// - Ensure the bounds are not way bigger than needed, otherwise the buckets are very off. Probably best to calculate bounds /// - The higher the depth, the larger the overhead, it especially goes up at a depth of 7/8 /// </summary> public NativeQuadTree(AABB2D bounds, Allocator allocator = Allocator.Temp, int maxDepth = 6, short maxLeafElements = 16, int initialElementsCapacity = 256 ) : this() { this.bounds = bounds; this.maxDepth = maxDepth; this.maxLeafElements = maxLeafElements; elementsCount = 0; if (maxDepth > 8) { // Currently no support for higher depths, the morton code lookup tables would have to support it throw new InvalidOperationException(); } #if ENABLE_UNITY_COLLECTIONS_CHECKS CollectionHelper.CheckIsUnmanaged <T>(); DisposeSentinel.Create(out safetyHandle, out disposeSentinel, 1, allocator); #endif // Allocate memory for every depth, the nodes on all depths are stored in a single continuous array var totalSize = LookupTables.DepthSizeLookup[maxDepth + 1]; lookup = UnsafeList.Create(UnsafeUtility.SizeOf <int>(), UnsafeUtility.AlignOf <int>(), totalSize, allocator, NativeArrayOptions.ClearMemory); nodes = UnsafeList.Create(UnsafeUtility.SizeOf <QuadNode>(), UnsafeUtility.AlignOf <QuadNode>(), totalSize, allocator, NativeArrayOptions.ClearMemory); elements = UnsafeList.Create(UnsafeUtility.SizeOf <QuadElement <T> >(), UnsafeUtility.AlignOf <QuadElement <T> >(), initialElementsCapacity, allocator); }
public static void Draw(NativeQuadTree <T> tree, NativeList <QuadElement <T> > results, AABB2D range, Color[][] texture) { var widthMult = texture.Length / tree.bounds.Extents.x * 2 / 2 / 2; var heightMult = texture[0].Length / tree.bounds.Extents.y * 2 / 2 / 2; var widthAdd = tree.bounds.Center.x + tree.bounds.Extents.x; var heightAdd = tree.bounds.Center.y + tree.bounds.Extents.y; for (int i = 0; i < tree.nodes->Capacity; i++) { var node = UnsafeUtility.ReadArrayElement <QuadNode>(tree.nodes->Ptr, i); if (node.count > 0) { for (int k = 0; k < node.count; k++) { var element = UnsafeUtility.ReadArrayElement <QuadElement <T> >(tree.elements->Ptr, node.firstChildIndex + k); texture[(int)((element.pos.x + widthAdd) * widthMult)] [(int)((element.pos.y + heightAdd) * heightMult)] = Color.red; } } } foreach (var element in results) { texture[(int)((element.pos.x + widthAdd) * widthMult)] [(int)((element.pos.y + heightAdd) * heightMult)] = Color.green; } DrawBounds(texture, range, tree); }
public void RecursiveRangeQuery(AABB2D parentBounds, bool parentContained, int prevOffset, int depth) { if (count + 4 * tree.maxLeafElements > fastResults->Capacity) { fastResults->Resize <QuadElement <T> >(math.max(fastResults->Capacity * 2, count + 4 * tree.maxLeafElements)); } var depthSize = LookupTables.DepthSizeLookup[tree.maxDepth - depth + 1]; for (int l = 0; l < 4; l++) { var childBounds = GetChildBounds(parentBounds, l); var contained = parentContained; if (!contained) { if (bounds.Contains(childBounds)) { contained = true; } else if (!bounds.Intersects(childBounds)) { continue; } } var at = prevOffset + l * depthSize; var elementCount = UnsafeUtility.ReadArrayElement <int>(tree.lookup->Ptr, at); if (elementCount > tree.maxLeafElements && depth < tree.maxDepth) { RecursiveRangeQuery(childBounds, contained, at + 1, depth + 1); } else if (elementCount != 0) { var node = UnsafeUtility.ReadArrayElement <QuadNode>(tree.nodes->Ptr, at); if (contained) { var index = (void *)((IntPtr)tree.elements->Ptr + node.firstChildIndex * UnsafeUtility.SizeOf <QuadElement <T> >()); UnsafeUtility.MemCpy((void *)((IntPtr)fastResults->Ptr + count * UnsafeUtility.SizeOf <QuadElement <T> >()), index, node.count * UnsafeUtility.SizeOf <QuadElement <T> >()); count += node.count; } else { for (int k = 0; k < node.count; k++) { var element = UnsafeUtility.ReadArrayElement <QuadElement <T> >(tree.elements->Ptr, node.firstChildIndex + k); if (bounds.Contains(element.pos)) { UnsafeUtility.WriteArrayElement(fastResults->Ptr, count++, element); } } } } } }
protected override void OnUpdate() { // config BoardConfig config = new BoardConfig(); Entities.ForEach((in BoardConfig c) => config = c).WithoutBurst().Run(); // elements 収集 var alphaTree = config.quadTree.alphaTree; var betaTree = config.quadTree.betaTree; var alphaElements = config.quadTree.alphaElements; var betaElements = config.quadTree.betaElements; // main process var totalThresholdSq = lengthsq(config.neighborThreshold); var neighborThreshold = config.neighborThreshold; var forceFactor = config.forceFactor; var extent = float2(neighborThreshold); var alphaPawns = alphaQuery.ToComponentDataArray <Pawn>(Allocator.TempJob); var betaPawns = betaQuery.ToComponentDataArray <Pawn>(Allocator.TempJob); var deltaTime = Time.DeltaTime; Job.WithCode( () => { for (int t = 0; t < 2; t++) { var activeElements = t == 0 ? alphaElements : betaElements; var passiveElements = t == 0 ? betaElements : alphaElements; var quadTree = t == 0 ? betaTree : alphaTree; var activePawns = t == 0 ? alphaPawns : betaPawns; var passivePawns = t == 0 ? betaPawns : alphaPawns; var results = new NativeList <NativeQuadTree.QuadElement <int> >( activeElements.Length, Allocator.Temp); for (int i = 0; i < activeElements.Length; i++) { results.Clear(); float2 p = activeElements[i].pos; var bounds = new NativeQuadTree.AABB2D(p, extent); quadTree.RangeQuery(bounds, results); int k = -1; float nearestDistSq = totalThresholdSq; float2 nearestPosition = new float2(); for (int jj = 0; jj < results.Length; jj++) { int j = results[jj].element; float2 q = passiveElements[j].pos; float2 d = q - p; float distSq = lengthsq(d); if (distSq < nearestDistSq) { nearestDistSq = distSq; nearestPosition = q; k = j; } } if (0 <= k) { Pawn active = activePawns[i]; active.HitEffectInterval += deltaTime; active.HitEffectPosition = nearestPosition; activePawns[i] = active; Pawn passive = passivePawns[k]; passive.Health -= 30.0f * deltaTime; passivePawns[k] = passive; } } results.Dispose(); } }) // .WithoutBurst() .Schedule(); var commandBuffer = entityCommandBufferSystem.CreateCommandBuffer().AsParallelWriter(); Entities.ForEach( (Entity entity, int entityInQueryIndex, in Pawn p) => { if (p.Health <= 0) { commandBuffer.DestroyEntity(entityInQueryIndex, entity); } }) .ScheduleParallel(); entityCommandBufferSystem.AddJobHandleForProducer(Dependency); CompleteDependency(); alphaQuery.CopyFromComponentDataArray(alphaPawns); betaQuery.CopyFromComponentDataArray(betaPawns); alphaPawns.Dispose(); betaPawns.Dispose(); }
protected override void OnUpdate() { // config BoardConfig config = new BoardConfig(); Entities.ForEach((in BoardConfig c) => config = c).WithoutBurst().Run(); var quadTree = config.quadTree.allTree; var elements = config.quadTree.allElements; // main process var totalThresholdSq = lengthsq(config.neighborThreshold); var neighborThreshold = config.neighborThreshold; var forceFactor = config.forceFactor; var forces = query.ToComponentDataArray <Force>(Allocator.TempJob); Job.WithCode( () => { var results = new NativeList <NativeQuadTree.QuadElement <int> >( elements.Length, Allocator.Temp); for (int i = 0; i < elements.Length; i++) { results.Clear(); var bounds = new NativeQuadTree.AABB2D( elements[i].pos, float2(neighborThreshold)); quadTree.RangeQuery(bounds, results); float2 p = elements[i].pos; for (int jj = 0; jj < results.Length; jj++) { int j = results[jj].element; if (j <= i) { continue; } float2 q = elements[j].pos; float2 d = q - p; float distsq = lengthsq(d); if (distsq < totalThresholdSq) { var rd = lengthsq(neighborThreshold - sqrt(distsq)); float2 v = d * (rd * forceFactor); float activeWeight = 1.0f; float passiveWeight = 1.0f; float denominator = activeWeight + passiveWeight; if (0 < denominator) { float2 vv = v / denominator; float3 vvv = float3(v.x, 0, v.y); forces[i] += vvv * -passiveWeight; forces[j] += vvv * activeWeight; } } } } results.Dispose(); }) // .WithoutBurst() .Schedule(); CompleteDependency(); query.CopyFromComponentDataArray(forces); forces.Dispose(); }