private int _numItems; // m_num_items public void Init(NativeArray <Aabb> bounds, Allocator allocator) { _bounds = bounds; _numItems = bounds.Length; OrgIdx = new NativeArray <int>(_numItems, allocator); Indices = new NativeArray <int>(_numItems, allocator); _nodes = new NativeArray <KdNode>(_numItems * 2 + 1, allocator); NumNodes = 0; _rootNode = new KdNode(); _rootNode.Reset(); FillFromVector(bounds); }
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); }