internal static void addObject_Pushdown(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> curNode, GO newOb) { var left = curNode.left; var right = curNode.right; // merge and pushdown left and right as a new node.. var mergedSubnode = new ssBVHNode <GO>(nAda.BVH); mergedSubnode.left = left; mergedSubnode.right = right; mergedSubnode.parent = curNode; mergedSubnode.gobjects = null; // we need to be an interior node... so null out our object list.. left.parent = mergedSubnode; right.parent = mergedSubnode; mergedSubnode.childRefit(nAda, propagate: false); // make new subnode for obj var newSubnode = new ssBVHNode <GO>(nAda.BVH); newSubnode.parent = curNode; newSubnode.gobjects = new List <GO> { newOb }; nAda.mapObjectToBVHLeaf(newOb, newSubnode); newSubnode.computeVolume(nAda); // make assignments.. curNode.left = mergedSubnode; curNode.right = newSubnode; curNode.setDepth(nAda, curNode.depth); // propagate new depths to our children. curNode.childRefit(nAda); }
internal static void addObject(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> curNode, GO newOb, ref SSAABB newObBox, float newObSAH) { // 1. first we traverse the node looking for the best leaf while (curNode.gobjects == null) { // find the best way to add this object.. 3 options.. // 1. send to left node (L+N,R) // 2. send to right node (L,R+N) // 3. merge and pushdown left-and-right node (L+R,N) var left = curNode.left; var right = curNode.right; float leftSAH = SA(left); float rightSAH = SA(right); float sendLeftSAH = rightSAH + SA(left.box.ExpandedBy(newObBox)); // (L+N,R) float sendRightSAH = leftSAH + SA(right.box.ExpandedBy(newObBox)); // (L,R+N) float mergedLeftAndRightSAH = SA(AABBofPair(left, right)) + newObSAH; // (L+R,N) // Doing a merge-and-pushdown can be expensive, so we only do it if it's notably better const float MERGE_DISCOUNT = 0.3f; if (mergedLeftAndRightSAH < (Math.Min(sendLeftSAH, sendRightSAH)) * MERGE_DISCOUNT) { addObject_Pushdown(nAda, curNode, newOb); return; } else { if (sendLeftSAH < sendRightSAH) { curNode = left; } else { curNode = right; } } } // 2. then we add the object and map it to our leaf curNode.gobjects.Add(newOb); nAda.mapObjectToBVHLeaf(newOb, curNode); curNode.refitVolume(nAda); // split if necessary... curNode.splitIfNecessary(nAda); }
internal void removeLeaf(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> removeLeaf) { if (left == null || right == null) { throw new Exception("bad intermediate node"); } ssBVHNode <GO> keepLeaf; if (removeLeaf == left) { keepLeaf = right; } else if (removeLeaf == right) { keepLeaf = left; } else { throw new Exception("removeLeaf doesn't match any leaf!"); } // "become" the leaf we are keeping. box = keepLeaf.box; left = keepLeaf.left; right = keepLeaf.right; gobjects = keepLeaf.gobjects; // clear the leaf.. // keepLeaf.left = null; keepLeaf.right = null; keepLeaf.gobjects = null; keepLeaf.parent = null; if (gobjects == null) { left.parent = this; right.parent = this; // reassign child parents.. this.setDepth(this.depth); // this reassigns depth for our children } else { // map the objects we adopted to us... gobjects.ForEach(o => { nAda.mapObjectToBVHLeaf(o, this); }); } // propagate our new volume.. if (parent != null) { parent.childRefit(nAda); } }
private ssBVHNode(ssBVH <GO> bvh, ssBVHNode <GO> lparent, List <GO> gobjectlist, Axis lastSplitAxis, int curdepth) { SSBVHNodeAdaptor <GO> nAda = bvh.nAda; this.nodeNumber = bvh.nodeCount++; this.parent = lparent; // save off the parent BVHGObj Node this.depth = curdepth; if (bvh.maxDepth < curdepth) { bvh.maxDepth = curdepth; } // Early out check due to bad data // If the list is empty then we have no BVHGObj, or invalid parameters are passed in if (gobjectlist == null || gobjectlist.Count < 1) { throw new Exception("ssBVHNode constructed with invalid paramaters"); } // Check if we’re at our LEAF node, and if so, save the objects and stop recursing. Also store the min/max for the leaf node and update the parent appropriately if (gobjectlist.Count <= bvh.LEAF_OBJ_MAX) { // once we reach the leaf node, we must set prev/next to null to signify the end left = null; right = null; // at the leaf node we store the remaining objects, so initialize a list gobjects = gobjectlist; gobjects.ForEach(o => nAda.mapObjectToBVHLeaf(o, this)); computeVolume(nAda); splitIfNecessary(nAda); } else { // -------------------------------------------------------------------------------------------- // if we have more than (bvh.LEAF_OBJECT_COUNT) objects, then compute the volume and split gobjects = gobjectlist; computeVolume(nAda); splitNode(nAda); childRefit(nAda, propagate: false); } }
internal void addObject(SSBVHNodeAdaptor <GO> nAda, GO newOb, ref SSAABB newObBox, float newObSAH) { if (gobjects != null) { // add the object and map it to our leaf gobjects.Add(newOb); nAda.mapObjectToBVHLeaf(newOb, this); refitVolume(nAda); // split if necessary... splitIfNecessary(nAda); } else { // find the best way to add this object.. 3 options.. // 1. send to left node (L+N,R) // 2. send to right node (L,R+N) // 3. merge and pushdown left-and-right node (L+R,N) float leftSAH = SAH(left); float rightSAH = SAH(right); float sendLeftSAH = rightSAH + SAH(left.box.ExpandedBy(newObBox)); // (L+N,R) float sendRightSAH = leftSAH + SAH(right.box.ExpandedBy(newObBox)); // (L,R+N) float mergedLeftAndRightSAH = SAH(AABBofPair(left, right)) + newObSAH; // (L+R,N) // Doing a merge-and-pushdown can be expensive, so we only do it if it's notably better const float MERGE_DISCOUNT = 0.3f; if (mergedLeftAndRightSAH < (Math.Min(sendLeftSAH, sendRightSAH)) * MERGE_DISCOUNT) { // merge and pushdown left and right as a new node.. var mSubnode = new ssBVHNode <GO>(nAda.BVH); mSubnode.left = left; mSubnode.right = right; mSubnode.parent = this; mSubnode.gobjects = null; // we need to be an interior node... so null out our object list.. left.parent = mSubnode; right.parent = mSubnode; mSubnode.childRefit(nAda, recurse: false); // make new subnode for obj var nSubnode = new ssBVHNode <GO>(nAda.BVH); nSubnode.parent = this; nSubnode.gobjects = new List <GO> { newOb }; nAda.mapObjectToBVHLeaf(newOb, nSubnode); nSubnode.computeVolume(nAda); // make assignments.. this.left = mSubnode; this.right = nSubnode; this.setDepth(this.depth); // propagate new depths to our children. this.childRefit(nAda); } else { if (sendLeftSAH < sendRightSAH) { // send left left.addObject(nAda, newOb, ref newObBox, newObSAH); } else { // send right right.addObject(nAda, newOb, ref newObBox, newObSAH); } } } }