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 bool refitVolume(SSBVHNodeAdaptor <GO> nAda) { if (gobjects.Count == 0) { throw new NotImplementedException(); } // TODO: fix this... we should never get called in this case... SSAABB oldbox = box; computeVolume(nAda); if (!box.Equals(oldbox)) { if (parent != null) { parent.childRefit(nAda); } return(true); } else { return(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); } } } }
/// <summary> /// tryRotate looks at all candidate rotations, and executes the rotation with the best resulting SAH (if any) /// </summary> /// <param name="bvh"></param> internal void tryRotate(ssBVH <GO> bvh) { SSBVHNodeAdaptor <GO> nAda = bvh.nAda; // if we are not a grandparent, then we can't rotate, so queue our parent and bail out if (left.IsLeaf && right.IsLeaf) { if (parent != null) { bvh.refitNodes.Add(parent); return; } } // for each rotation, check that there are grandchildren as necessary (aka not a leaf) // then compute total SAH cost of our branches after the rotation. float mySAH = SAH(left) + SAH(right); rotOpt bestRot = eachRot.Min((rot) => { switch (rot) { case Rot.NONE: return(new rotOpt(mySAH, Rot.NONE)); // child to grandchild rotations case Rot.L_RL: if (right.IsLeaf) { return(new rotOpt(float.MaxValue, Rot.NONE)); } else { return(new rotOpt(SAH(right.left) + SAH(AABBofPair(left, right.right)), rot)); } case Rot.L_RR: if (right.IsLeaf) { return(new rotOpt(float.MaxValue, Rot.NONE)); } else { return(new rotOpt(SAH(right.right) + SAH(AABBofPair(left, right.left)), rot)); } case Rot.R_LL: if (left.IsLeaf) { return(new rotOpt(float.MaxValue, Rot.NONE)); } else { return(new rotOpt(SAH(AABBofPair(right, left.right)) + SAH(left.left), rot)); } case Rot.R_LR: if (left.IsLeaf) { return(new rotOpt(float.MaxValue, Rot.NONE)); } else { return(new rotOpt(SAH(AABBofPair(right, left.left)) + SAH(left.right), rot)); } // grandchild to grandchild rotations case Rot.LL_RR: if (left.IsLeaf || right.IsLeaf) { return(new rotOpt(float.MaxValue, Rot.NONE)); } else { return(new rotOpt(SAH(AABBofPair(right.right, left.right)) + SAH(AABBofPair(right.left, left.left)), rot)); } case Rot.LL_RL: if (left.IsLeaf || right.IsLeaf) { return(new rotOpt(float.MaxValue, Rot.NONE)); } else { return(new rotOpt(SAH(AABBofPair(right.left, left.right)) + SAH(AABBofPair(left.left, right.right)), rot)); } // unknown... default: throw new NotImplementedException("missing implementation for BVH Rotation SAH Computation .. " + rot.ToString()); } }); // perform the best rotation... if (bestRot.rot != Rot.NONE) { // if the best rotation is no-rotation... we check our parents anyhow.. if (parent != null) { // but only do it some random percentage of the time. if ((DateTime.Now.Ticks % 100) < 2) { bvh.refitNodes.Add(parent); } } } else { if (parent != null) { bvh.refitNodes.Add(parent); } if (((mySAH - bestRot.SAH) / mySAH) < 0.3f) { return; // the benefit is not worth the cost } Console.WriteLine("BVH swap {0} from {1} to {2}", bestRot.rot.ToString(), mySAH, bestRot.SAH); // in order to swap we need to: // 1. swap the node locations // 2. update the depth (if child-to-grandchild) // 3. update the parent pointers // 4. refit the boundary box ssBVHNode <GO> swap = null; switch (bestRot.rot) { case Rot.NONE: break; // child to grandchild rotations case Rot.L_RL: swap = left; swap.depth++; left = right.left; left.parent = this; left.depth--; right.left = swap; swap.parent = right; right.childRefit(nAda); break; case Rot.L_RR: swap = left; swap.depth++; left = right.right; left.parent = this; left.depth--; right.right = swap; swap.parent = right; right.childRefit(nAda); break; case Rot.R_LL: swap = right; swap.depth++; right = left.left; right.parent = this; right.depth--; left.left = swap; swap.parent = left; left.childRefit(nAda); break; case Rot.R_LR: swap = right; swap.depth++; right = left.right; right.parent = this; right.depth--; left.right = swap; swap.parent = left; left.childRefit(nAda); break; // grandchild to grandchild rotations case Rot.LL_RR: swap = left.left; left.left = right.right; right.right = swap; left.left.parent = left; swap.parent = right; left.childRefit(nAda, recurse: false); right.childRefit(nAda); break; case Rot.LL_RL: swap = left.left; left.left = right.left; right.left = swap; left.left.parent = left; swap.parent = right; left.childRefit(nAda, recurse: false); right.childRefit(nAda); break; // unknown... default: throw new NotImplementedException("missing implementation for BVH Rotation .. " + bestRot.rot.ToString()); } } }