internal static void AddObjectPushdown(IBVHNodeAdapter <T> nAda, BVHNode <T> curNode, T newOb) { var left = curNode.Left; var right = curNode.Right; // merge and pushdown left and right as a new node.. var mergedSubnode = new BVHNode <T>(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 BVHNode <T>(nAda.BVH); newSubnode.Parent = curNode; newSubnode.GObjects = new List <T> { 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(IBVHNodeAdapter <T> nAda) { if (GObjects.Count == 0) { // TODO: fix this... we should never get called in this case... throw new NotImplementedException(); } Bounds oldbox = Box; ComputeVolume(nAda); if (!Box.Equals(oldbox)) { if (Parent != null) { Parent.ChildRefit(nAda); } return(true); } else { return(false); } }
/// <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(BVH <T> bvh) { IBVHNodeAdapter <T> 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 mySA = SA(Left) + SA(Right); RotOpt bestRot = EachRot.Min((rot) => { switch (rot) { case Rot.NONE: return(new RotOpt(mySA, Rot.NONE)); // child to grandchild rotations case Rot.L_RL: if (Right.IsLeaf) { return(new RotOpt(float.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(Right.Left) + SA(AABBofPair(Left, Right.Right)), rot)); } case Rot.L_RR: if (Right.IsLeaf) { return(new RotOpt(float.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(Right.Right) + SA(AABBofPair(Left, Right.Left)), rot)); } case Rot.R_LL: if (Left.IsLeaf) { return(new RotOpt(float.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(AABBofPair(Right, Left.Right)) + SA(Left.Left), rot)); } case Rot.R_LR: if (Left.IsLeaf) { return(new RotOpt(float.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(AABBofPair(Right, Left.Left)) + SA(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(SA(AABBofPair(Right.Right, Left.Right)) + SA(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(SA(AABBofPair(Right.Left, Left.Right)) + SA(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 (((mySA - bestRot.SAH) / mySA) < 0.3f) { return; // the benefit is not worth the cost } Console.WriteLine("BVH swap {0} from {1} to {2}", bestRot.rot.ToString(), mySA, 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 BVHNode <T> swap = null; switch (bestRot.rot) { case Rot.NONE: break; // child to grandchild rotations case Rot.L_RL: swap = Left; Left = Right.Left; Left.Parent = this; Right.Left = swap; swap.Parent = Right; Right.ChildRefit(nAda, propagate: false); break; case Rot.L_RR: swap = Left; Left = Right.Right; Left.Parent = this; Right.Right = swap; swap.Parent = Right; Right.ChildRefit(nAda, propagate: false); break; case Rot.R_LL: swap = Right; Right = Left.Left; Right.Parent = this; Left.Left = swap; swap.Parent = Left; Left.ChildRefit(nAda, propagate: false); break; case Rot.R_LR: swap = Right; Right = Left.Right; Right.Parent = this; Left.Right = swap; swap.Parent = Left; Left.ChildRefit(nAda, propagate: false); 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, propagate: false); Right.ChildRefit(nAda, propagate: false); 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, propagate: false); Right.ChildRefit(nAda, propagate: false); break; // unknown... default: throw new NotImplementedException("missing implementation for BVH Rotation .. " + bestRot.rot.ToString()); } // fix the depths if necessary.... switch (bestRot.rot) { case Rot.L_RL: case Rot.L_RR: case Rot.R_LL: case Rot.R_LR: this.SetDepth(nAda, this.Depth); break; } } }