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);
        }
예제 #2
0
        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);
            }
        }
예제 #3
0
        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);
                    }
                }
            }
        }
예제 #4
0
        /// <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());
                }
            }
        }