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 void RemoveLeaf(IBVHNodeAdapter <T> nAda, BVHNode <T> removeLeaf)
        {
            if (Left == null || Right == null)
            {
                throw new Exception("bad intermediate node");
            }
            BVHNode <T> 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(nAda, 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 BVHNode(BVH <T> bvh, BVHNode <T> lparent, List <T> gobjectlist, Axis lastSplitAxis, int curdepth)
        {
            IBVHNodeAdapter <T> 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 static void Add(IBVHNodeAdapter <T> nAda, BVHNode <T> curNode, T newOb, ref Bounds 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);

                //Create new bounds to avoid modifying originals when using encapsulate
                Bounds leftExpanded = new Bounds
                {
                    min = left.Box.min,
                    max = left.Box.max
                };

                Bounds rightExpanded = new Bounds
                {
                    min = right.Box.min,
                    max = right.Box.max
                };

                leftExpanded.Encapsulate(newObBox);
                rightExpanded.Encapsulate(newObBox);

                float sendLeftSAH           = rightSAH + SA(leftExpanded);            // (L+N,R)
                float sendRightSAH          = leftSAH + SA(rightExpanded);            // (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)
                {
                    AddObjectPushdown(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);
        }