protected override void OnUpdate() { // create kdtree PerfMarker1.Begin(); var ballEntities = _ballQuery.ToEntityArray(Allocator.TempJob); var balls = GetComponentDataFromEntity <BallData>(true); var kdRoot = new KdRoot(); Job.WithCode(() => { var ballBounds = new NativeArray <Aabb>(ballEntities.Length, Allocator.Temp); for (var i = 0; i < ballEntities.Length; i++) { ballBounds[i] = balls[ballEntities[i]].GetAabb(ballEntities[i]); } kdRoot.Init(ballBounds, Allocator.TempJob); }).Run(); ballEntities.Dispose(); PerfMarker1.End(); var overlappingEntities = GetBufferFromEntity <OverlappingDynamicBufferElement>(); var marker = PerfMarker2; Entities .WithName("StaticBroadPhaseJob") .WithNativeDisableParallelForRestriction(overlappingEntities) .ForEach((Entity entity, in BallData ball) => { // don't play with frozen balls if (ball.IsFrozen) { return; } marker.Begin(); var colliderEntities = overlappingEntities[entity]; colliderEntities.Clear(); kdRoot.GetAabbOverlaps(in entity, in ball, ref colliderEntities); marker.End(); }).Run(); kdRoot.Dispose(); }
protected override void OnUpdate() { // create kdtree PerfMarker1.Begin(); var ballBounds = new NativeArray <Aabb>(_ballQuery.CalculateEntityCount(), Allocator.TempJob); Entities.ForEach((Entity ballEntity, int entityInQueryIndex, in BallData ballData) => { ballBounds[entityInQueryIndex] = ballData.GetAabb(ballEntity); }).Run(); var kdRoot = new KdRoot(); Job.WithCode(() => kdRoot.Init(ballBounds, Allocator.TempJob)).Run(); PerfMarker1.End(); var overlappingEntities = GetBufferFromEntity <OverlappingDynamicBufferElement>(); var marker = PerfMarker2; Entities .WithName("DynamicBroadPhaseJob") .WithDisposeOnCompletion(kdRoot) .WithNativeDisableParallelForRestriction(overlappingEntities) .ForEach((Entity entity, in BallData ball) => { // don't play with frozen balls if (ball.IsFrozen) { return; } marker.Begin(); var colliderEntities = overlappingEntities[entity]; colliderEntities.Clear(); kdRoot.GetAabbOverlaps(in entity, in ball, ref colliderEntities); marker.End(); }).Run(); }
public void CreateNextLevel(int level, int levelEmpty, KdRoot hitOct) { var orgItems = Items & 0x3FFFFFFF; // !! magic if (orgItems <= 4 || level >= 128 / 2) { return; } var vDiag = new float3( Bounds.Right - Bounds.Left, Bounds.Bottom - Bounds.Top, Bounds.ZHigh - Bounds.ZLow ); int axis; if (vDiag.x > vDiag.y && vDiag.x > vDiag.z) { if (vDiag.x < 0.0001) { return; } axis = 0; } else if (vDiag.y > vDiag.z) { if (vDiag.y < 0.0001) { return; } axis = 1; } else { if (vDiag.z < 0.0001) { return; } axis = 2; } //!! weight this with ratio of elements going to middle vs left&right! (avoids volume split that goes directly through object) // create children if (!hitOct.HasNodesAvailable()) { // ran out of nodes - abort return; } var childA = new KdNode(Bounds); var childB = new KdNode(Bounds); var vCenter = new float3( (Bounds.Left + Bounds.Right) * 0.5f, (Bounds.Top + Bounds.Bottom) * 0.5f, (Bounds.ZLow + Bounds.ZHigh) * 0.5f ); switch (axis) { case 0: childA.Bounds.Right = vCenter.x; childB.Bounds.Left = vCenter.x; break; case 1: childA.Bounds.Bottom = vCenter.y; childB.Bounds.Top = vCenter.y; break; default: childA.Bounds.ZHigh = vCenter.z; childB.Bounds.ZLow = vCenter.z; break; } childA.Reset(); childB.Reset(); // determine amount of items that cross splitplane, or are passed on to the children if (axis == 0) { for (var i = Start; i < Start + orgItems; ++i) { var bounds = hitOct.GetItemAt(i); if (bounds.Right < vCenter.x) { childA.Items++; } else if (bounds.Left > vCenter.x) { childB.Items++; } } } else if (axis == 1) { for (var i = Start; i < Start + orgItems; ++i) { var bounds = hitOct.GetItemAt(i); if (bounds.Bottom < vCenter.y) { childA.Items++; } else if (bounds.Top > vCenter.y) { childB.Items++; } } } else { // axis == 2 for (var i = Start; i < Start + orgItems; ++i) { var bounds = hitOct.GetItemAt(i); if (bounds.ZHigh < vCenter.z) { childA.Items++; } else if (bounds.ZLow > vCenter.z) { childB.Items++; } } } // check if at least two nodes feature objects, otherwise don"t bother subdividing further var countEmpty = 0; if (childA.Items == 0) { countEmpty = 1; } if (childB.Items == 0) { ++countEmpty; } if (orgItems - childA.Items - childB.Items == 0) { ++countEmpty; } if (countEmpty >= 2) { ++levelEmpty; } else { levelEmpty = 0; } if (levelEmpty > 8) { // If 8 levels were all just subdividing the same objects without luck, exit & Free the nodes again (but at least empty space was cut off) // no need to update NumNodes, since we didn't increment them yet. _childA = -1; _childB = -1; return; } childA.Start = Start + orgItems - childA.Items - childB.Items; childB.Start = childA.Start + childA.Items; var items = 0; childA.Items = 0; childB.Items = 0; switch (axis) { // sort items that cross splitplane in-place, the others are sorted into a temporary case 0: { for (var i = Start; i < Start + orgItems; ++i) { var bounds = hitOct.GetItemAt(i); if (bounds.Right < vCenter.x) { hitOct.Indices[childA.Start + childA.Items++] = hitOct.OrgIdx[i]; } else if (bounds.Left > vCenter.x) { hitOct.Indices[childB.Start + childB.Items++] = hitOct.OrgIdx[i]; } else { hitOct.OrgIdx[Start + items++] = hitOct.OrgIdx[i]; } } break; } case 1: { for (var i = Start; i < Start + orgItems; ++i) { var bounds = hitOct.GetItemAt(i); if (bounds.Bottom < vCenter.y) { hitOct.Indices[childA.Start + childA.Items++] = hitOct.OrgIdx[i]; } else if (bounds.Top > vCenter.y) { hitOct.Indices[childB.Start + childB.Items++] = hitOct.OrgIdx[i]; } else { hitOct.OrgIdx[Start + items++] = hitOct.OrgIdx[i]; } } break; } default: { // axis == 2 for (var i = Start; i < Start + orgItems; ++i) { var bounds = hitOct.GetItemAt(i); if (bounds.ZHigh < vCenter.z) { hitOct.Indices[childA.Start + childA.Items++] = hitOct.OrgIdx[i]; } else if (bounds.ZLow > vCenter.z) { hitOct.Indices[childB.Start + childB.Items++] = hitOct.OrgIdx[i]; } else { hitOct.OrgIdx[Start + items++] = hitOct.OrgIdx[i]; } } break; } } // The following assertions hold after this step: //assert( this.Start + items == this.Children[0].This.Start ); //assert( this.Children[0].This.Start + this.Children[0].This.Items == this.Children[1].This.Start ); //assert( this.Children[1].This.Start + this.Children[1].This.Items == this.Start + org_items ); //assert( this.Start + org_items <= this.HitOct->tmp.Size() ); Items = items | (axis << 30); // copy temporary back //!! could omit this by doing everything inplace for (var i = 0; i < childA.Items; i++) { hitOct.OrgIdx[childA.Start + i] = hitOct.Indices[childA.Start + i]; } for (var i = 0; i < childB.Items; i++) { hitOct.OrgIdx[childB.Start + i] = hitOct.Indices[childB.Start + i]; } hitOct.AddNodes(childA, childB, out _childA, out _childB); childA.CreateNextLevel(level + 1, levelEmpty, hitOct); childB.CreateNextLevel(level + 1, levelEmpty, hitOct); }
public void GetAabbOverlaps(ref KdRoot hitOct, in Entity entity, in BallData ball, ref DynamicBuffer <OverlappingDynamicBufferElement> overlappingEntities)