internal void childExpanded(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> child) { bool expanded = false; if (child.box.Min.X < box.Min.X) { box.Min.X = child.box.Min.X; expanded = true; } if (child.box.Max.X > box.Max.X) { box.Max.X = child.box.Max.X; expanded = true; } if (child.box.Min.Y < box.Min.Y) { box.Min.Y = child.box.Min.Y; expanded = true; } if (child.box.Max.Y > box.Max.Y) { box.Max.Y = child.box.Max.Y; expanded = true; } if (child.box.Min.Z < box.Min.Z) { box.Min.Z = child.box.Min.Z; expanded = true; } if (child.box.Max.Z > box.Max.Z) { box.Max.Z = child.box.Max.Z; expanded = true; } if (expanded && parent != null) { parent.childExpanded(nAda, this); } }
internal float SAHofPair(ssBVHNode <GO> nodea, ssBVHNode <GO> nodeb) { SSAABB box = nodea.box; box.ExpandToFit(nodeb.box); return(SAH(ref box)); }
internal void splitNode(SSBVHNodeAdaptor <GO> nAda) { // second, decide which axis to split on, and sort.. List <GO> splitlist = gobjects; splitlist.ForEach(o => nAda.unmapObject(o)); Axis splitAxis = pickSplitAxis(); switch (splitAxis) // sort along the appropriate axis { case Axis.X: splitlist.Sort(delegate(GO go1, GO go2) { return(nAda.objectpos(go1).X.CompareTo(nAda.objectpos(go2).X)); }); break; case Axis.Y: splitlist.Sort(delegate(GO go1, GO go2) { return(nAda.objectpos(go1).Y.CompareTo(nAda.objectpos(go2).Y)); }); break; case Axis.Z: splitlist.Sort(delegate(GO go1, GO go2) { return(nAda.objectpos(go1).Z.CompareTo(nAda.objectpos(go2).Z)); }); break; default: throw new NotImplementedException(); } int center = (int)(splitlist.Count / 2); // Find the center object in our current sub-list gobjects = null; // create the new left and right nodes... left = new ssBVHNode <GO>(nAda.BVH, this, splitlist.GetRange(0, center), splitAxis, this.depth + 1); // Split the Hierarchy to the left right = new ssBVHNode <GO>(nAda.BVH, this, splitlist.GetRange(center, splitlist.Count - center), splitAxis, this.depth + 1); // Split the Hierarchy to the right }
private static Bounds_d BoundsOfPair(ssBVHNode <GO> nodea, ssBVHNode <GO> nodeb) { Bounds_d box = nodea.Bounds; box.ExpandToFit(nodeb.Bounds); return(box); }
internal SSAABB AABBofPair(ssBVHNode <GO> nodea, ssBVHNode <GO> nodeb) { SSAABB box = nodea.box; box.ExpandToFit(nodeb.box); return(box); }
internal ssBVHNode(ssBVH <GO> bvh) { gobjects = new List <GO>(); left = right = null; parent = null; this.nodeNumber = bvh.nodeCount++; }
private 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) { Left = left, Right = right, Parent = curNode, ContainedObjects = null }; // we need to be an interior node... so null out our object list.. left.Parent = mergedSubnode; right.Parent = mergedSubnode; mergedSubnode.ChildRefit(nAda, false); // make new subnode for obj var newSubnode = new ssBVHNode <GO>(nAda.BVH); newSubnode.Parent = curNode; newSubnode.ContainedObjects = 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_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 float SAH(ssBVHNode <GO> node) { float x_size = node.box.Max.X - node.box.Min.X; float y_size = node.box.Max.Y - node.box.Min.Y; float z_size = node.box.Max.Z - node.box.Min.Z; return(2.0f * ((x_size * y_size) + (x_size * z_size) + (y_size * z_size))); }
private static double SA(ssBVHNode <GO> node) { double x_size = node.Bounds.max.x - node.Bounds.min.x; double y_size = node.Bounds.max.y - node.Bounds.min.y; double z_size = node.Bounds.max.z - node.Bounds.min.z; return(2.0f * ((x_size * y_size) + (x_size * z_size) + (y_size * z_size))); }
public void mapObjectToBVHLeaf(SSObject obj, ssBVHNode <SSObject> leaf) { // this allows us to be notified when an object moves, so we can adjust the BVH obj.OnChanged += obj_OnChanged; // TODO: add a hook to handle SSObject deletion... (either a weakref GC notify, or OnDestroy) ssToLeafMap[obj] = leaf; }
internal ssBVHNode <GO> rootNode() { ssBVHNode <GO> cur = this; while (cur.parent != null) { cur = cur.parent; } return(cur); }
// internal functional traversal... private void _traverse(ssBVHNode <GO> curNode, NodeTest hitTest, List <ssBVHNode <GO> > hitlist) { if (curNode == null) { return; } if (hitTest(curNode.box)) { hitlist.Add(curNode); _traverse(curNode.left, hitTest, hitlist); _traverse(curNode.right, hitTest, hitlist); } }
public void renderCells(ssBVHNode <SSObject> n, ref SSAABB parentbox, int depth) { float nudge = 0f; if (parentbox.Equals(n.box)) { // attempt to nudge out of z-fighting nudge = 0.2f; } if (highlightNodes.Contains(n)) { if (n.gobjects == null) { GL.Color4(Color.FromArgb(255, 25, 25, 100)); } else { GL.Color4(Color.Green); } } else { if (n.gobjects == null) { GL.Color4(Color.FromArgb(255, 20, 20, 20)); } else { GL.Color4(Color.DarkRed); } } Vector3 nudgeVect = new Vector3(nudge); Vector3 scale = n.box.max - n.box.min - 2f * nudgeVect; Matrix4 mat = Matrix4.CreateScale(scale) * Matrix4.CreateTranslation(n.box.min + nudgeVect); GL.PushMatrix(); GL.MultMatrix(ref mat); ibo.DrawElements(PrimitiveType.Lines, false); GL.PopMatrix(); if (n.right != null) { renderCells(n.right, ref n.box, depth: depth + 1); } if (n.left != null) { renderCells(n.left, ref n.box, depth: depth + 1); } }
private 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. Bounds = keepLeaf.Bounds; Left = keepLeaf.Left; Right = keepLeaf.Right; ContainedObjects = keepLeaf.ContainedObjects; // clear the leaf.. // keepLeaf.left = null; keepLeaf.right = null; keepLeaf.gobjects = null; keepLeaf.parent = null; if (ContainedObjects == null) { Left.Parent = this; Right.Parent = this; // reassign child parents.. SetDepth(nAda, Depth); // this reassigns depth for our children } else { // map the objects we adopted to us... ContainedObjects.ForEach( o => { nAda.MapObjectToBvhLeaf(o, this); }); } // propagate our new volume.. if (Parent != null) { Parent.ChildRefit(nAda); } }
// internal functional traversal... private static void _query(ssBVHNode <T> curNode, NodeTest hitTest, ICollection <ssBVHNode <T> > hitlist) { if (curNode == null) { return; } if (hitTest(curNode.Bounds)) { hitlist.Add(curNode); _query(curNode.Left, hitTest, hitlist); _query(curNode.Right, hitTest, hitlist); } }
/// <summary> /// initializes a BVH with a given nodeAdaptor, and object list. /// </summary> /// <param name="nodeAdaptor"></param> /// <param name="objects"></param> /// <param name="LEAF_OBJ_MAX">WARNING! currently this must be 1 to use dynamic BVH updates</param> public ssBVH(SSBVHNodeAdaptor <GO> nodeAdaptor, List <GO> objects, int LEAF_OBJ_MAX = 1) { this.LEAF_OBJ_MAX = LEAF_OBJ_MAX; nodeAdaptor.setBVH(this); this.nAda = nodeAdaptor; if (objects.Count > 0) { rootBVH = new ssBVHNode <GO>(this, objects); } else { rootBVH = new ssBVHNode <GO>(this); rootBVH.gobjects = new List <GO>(); // it's a leaf, so give it an empty object list } }
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); }
private static void ChildRefit(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> curNode, bool propagate = true) { do { ssBVHNode <GO> left = curNode.Left; ssBVHNode <GO> right = curNode.Right; // start with the left box Bounds_d newBox = left.Bounds.ExpandedToFit(right.Bounds); // now set our box to the newly created box curNode.Bounds = newBox; // and walk up the tree curNode = curNode.Parent; } while (propagate && curNode != null); }
private static void AddObject(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> curNode, GO newOb, ref Bounds_d newObBox, double newObSAH) { // 1. first we traverse the node looking for the best leaf while (curNode.ContainedObjects == 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; double leftSAH = SA(left); double rightSAH = SA(right); double sendLeftSAH = rightSAH + SA(left.Bounds.ExpandedToFit(newObBox)); // (L+N,R) double sendRightSAH = leftSAH + SA(right.Bounds.ExpandedToFit(newObBox)); // (L,R+N) double mergedLeftAndRightSAH = SA(BoundsOfPair(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 double MERGE_DISCOUNT = 0.3f; if (mergedLeftAndRightSAH < (Math.Min(sendLeftSAH, sendRightSAH)) * MERGE_DISCOUNT) { addObject_Pushdown(nAda, curNode, newOb); return; } if (sendLeftSAH < sendRightSAH) { curNode = left; } else { curNode = right; } } // 2. then we add the object and map it to our leaf curNode.ContainedObjects.Add(newOb); nAda.MapObjectToBvhLeaf(newOb, curNode); curNode.RefitVolume(nAda); // split if necessary... curNode.SplitIfNecessary(nAda); }
internal static void childRefit(SSBVHNodeAdaptor <GO> nAda, ssBVHNode <GO> curNode, bool propagate = true) { do { SSAABB oldbox = curNode.box; ssBVHNode <GO> left = curNode.left; ssBVHNode <GO> right = curNode.right; // start with the left box SSAABB newBox = left.box; // expand any dimension bigger in the right node if (right.box.Min.X < newBox.Min.X) { newBox.Min.X = right.box.Min.X; } if (right.box.Min.Y < newBox.Min.Y) { newBox.Min.Y = right.box.Min.Y; } if (right.box.Min.Z < newBox.Min.Z) { newBox.Min.Z = right.box.Min.Z; } if (right.box.Max.X > newBox.Max.X) { newBox.Max.X = right.box.Max.X; } if (right.box.Max.Y > newBox.Max.Y) { newBox.Max.Y = right.box.Max.Y; } if (right.box.Max.Z > newBox.Max.Z) { newBox.Max.Z = right.box.Max.Z; } // now set our box to the newly created box curNode.box = newBox; // and walk up the tree curNode = curNode.parent; } while (propagate && curNode != null); }
/// <summary> /// initializes a BVH with a given nodeAdaptor, and object list. /// </summary> /// <param name="nodeAdaptor"></param> /// <param name="objects"></param> /// <param name="LEAF_OBJ_MAX">WARNING! currently this must be 1 to use dynamic BVH updates</param> public ssBVH(SSBVHNodeAdaptor <T> nodeAdaptor, List <T> objects, int LEAF_OBJ_MAX = 1) { this.LEAF_OBJ_MAX = LEAF_OBJ_MAX; nodeAdaptor.BVH = this; nAda = nodeAdaptor; if (objects.Count > 0) { RootBVH = new ssBVHNode <T>(this, objects); } else { RootBVH = new ssBVHNode <T>(this) { ContainedObjects = new List <T>() }; // it's a leaf, so give it an empty object list } }
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); } }
private void SplitNode(SSBVHNodeAdaptor <GO> nAda) { // second, decide which axis to split on, and sort.. List <GO> splitlist = ContainedObjects; splitlist.ForEach(nAda.UnmapObject); int center = splitlist.Count / 2; // find the center object SplitAxisOpt <GO> bestSplit = eachAxis.Min( axis => { var orderedlist = new List <GO>(splitlist); switch (axis) { case Axis.X: orderedlist.Sort((go1, go2) => nAda.GetObjectPosition(go1).x.CompareTo(nAda.GetObjectPosition(go2).x)); break; case Axis.Y: orderedlist.Sort((go1, go2) => nAda.GetObjectPosition(go1).y.CompareTo(nAda.GetObjectPosition(go2).y)); break; case Axis.Z: orderedlist.Sort((go1, go2) => nAda.GetObjectPosition(go1).z.CompareTo(nAda.GetObjectPosition(go2).z)); break; default: throw new NotImplementedException("unknown split axis: " + axis.ToString()); } var left_s = orderedlist.GetRange(0, center); var right_s = orderedlist.GetRange(center, splitlist.Count - center); double SAH = SAofList(nAda, left_s) * left_s.Count + SAofList(nAda, right_s) * right_s.Count; return(new SplitAxisOpt <GO>(SAH, axis, left_s, right_s)); }); // perform the split ContainedObjects = null; Left = new ssBVHNode <GO>(nAda.BVH, this, bestSplit.left, Depth + 1); // Split the Hierarchy to the left Right = new ssBVHNode <GO>(nAda.BVH, this, bestSplit.right, Depth + 1); // Split the Hierarchy to the right }
// TODO: will need to calculate center of AABB internal void splitNode(SSBVHNodeAdaptor <GO> nAda) { // second, decide which axis to split on, and sort.. List <GO> splitlist = gobjects; splitlist.ForEach(o => nAda.unmapObject(o)); int center = (int)(splitlist.Count / 2); // find the center object SplitAxisOpt <GO> bestSplit = eachAxis.Min((axis) => { var orderedlist = new List <GO>(splitlist); switch (axis) { case Axis.X: orderedlist.Sort(delegate(GO go1, GO go2) { return(nAda.boundingBox(go1).Center().X.CompareTo(nAda.boundingBox(go2).Center().X)); }); break; case Axis.Y: orderedlist.Sort(delegate(GO go1, GO go2) { return(nAda.boundingBox(go1).Center().Y.CompareTo(nAda.boundingBox(go2).Center().Y)); }); break; case Axis.Z: orderedlist.Sort(delegate(GO go1, GO go2) { return(nAda.boundingBox(go1).Center().Z.CompareTo(nAda.boundingBox(go2).Center().Z)); }); break; default: throw new NotImplementedException("unknown split axis: " + axis.ToString()); } var left_s = orderedlist.GetRange(0, center); var right_s = orderedlist.GetRange(center, splitlist.Count - center); float SAH = SAofList(nAda, left_s) * left_s.Count + SAofList(nAda, right_s) * right_s.Count; return(new SplitAxisOpt <GO>(SAH, axis, left_s, right_s)); }); // perform the split gobjects = null; this.left = new ssBVHNode <GO>(nAda.BVH, this, bestSplit.left, bestSplit.axis, this.depth + 1); // Split the Hierarchy to the left this.right = new ssBVHNode <GO>(nAda.BVH, this, bestSplit.right, bestSplit.axis, this.depth + 1); // Split the Hierarchy to the right }
internal void RemoveObject(SSBVHNodeAdaptor <GO> nAda, GO newOb) { if (ContainedObjects == null) { throw new Exception("removeObject() called on nonLeaf!"); } nAda.UnmapObject(newOb); ContainedObjects.Remove(newOb); if (ContainedObjects.Count > 0) { RefitVolume(nAda); } else { // our leaf is empty, so collapse it if we are not the root... if (Parent != null) { ContainedObjects = null; Parent.RemoveLeaf(nAda, this); Parent = null; } } }
/// <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. double 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(double.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(Right.Left) + SA(BoundsOfPair(Left, Right.Right)), rot)); } case Rot.L_RR: if (Right.IsLeaf) { return(new RotOpt(double.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(Right.Right) + SA(BoundsOfPair(Left, Right.Left)), rot)); } case Rot.R_LL: if (Left.IsLeaf) { return(new RotOpt(double.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(BoundsOfPair(Right, Left.Right)) + SA(Left.Left), rot)); } case Rot.R_LR: if (Left.IsLeaf) { return(new RotOpt(double.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(BoundsOfPair(Right, Left.Left)) + SA(Left.Right), rot)); } // grandchild to grandchild rotations case Rot.LL_RR: if (Left.IsLeaf || Right.IsLeaf) { return(new RotOpt(double.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(BoundsOfPair(Right.Right, Left.Right)) + SA(BoundsOfPair(Right.Left, Left.Left)), rot)); } case Rot.LL_RL: if (Left.IsLeaf || Right.IsLeaf) { return(new RotOpt(double.MaxValue, Rot.NONE)); } else { return(new RotOpt(SA(BoundsOfPair(Right.Left, Left.Right)) + SA(BoundsOfPair(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 ssBVHNode <GO> 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); } // fix the depths if necessary.... switch (bestRot.rot) { case Rot.L_RL: case Rot.L_RR: case Rot.R_LL: case Rot.R_LR: SetDepth(nAda, Depth); break; } } }
public void MapObjectToBvhLeaf(Shape shape, ssBVHNode <Shape> leaf) { _shapeToLeafMap[shape] = leaf; }
internal ssBVHNode(ssBVH <GO> bvh) { ContainedObjects = new List <GO>(); Left = Right = null; Parent = null; }