Ejemplo n.º 1
0
 internal void splitIfNecessary(SSBVHNodeAdaptor <GO> nAda)
 {
     if (gobjects.Count > nAda.BVH.LEAF_OBJ_MAX)
     {
         splitNode(nAda);
     }
 }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
 private void SplitIfNecessary(SSBVHNodeAdaptor <GO> nAda)
 {
     if (ContainedObjects.Count > nAda.BVH.LEAF_OBJ_MAX)
     {
         SplitNode(nAda);
     }
 }
Ejemplo n.º 4
0
        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
        }
Ejemplo n.º 5
0
        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 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);
        }
Ejemplo n.º 7
0
 internal void computeVolume(SSBVHNodeAdaptor <GO> nAda)
 {
     assignVolume(nAda.objectpos(gobjects[0]), nAda.radius(gobjects[0]));
     for (int i = 1; i < gobjects.Count; i++)
     {
         expandVolume(nAda, nAda.objectpos(gobjects[i]), nAda.radius(gobjects[i]));
     }
 }
 internal void computeVolume(SSBVHNodeAdaptor <GO> nAda)
 {
     box = nAda.boundingBox(gobjects[0]);
     for (int i = 1; i < gobjects.Count; i++)
     {
         expandVolume(nAda, nAda.boundingBox(gobjects[i]));
     }
 }
Ejemplo n.º 9
0
        internal static float SA(SSBVHNodeAdaptor <GO> nAda, GO obj)
        {
            float radius = nAda.radius(obj);

            float size = radius * 2;

            return(6.0f * (size * size));
        }
Ejemplo n.º 10
0
        private void ComputeVolume(SSBVHNodeAdaptor <GO> nAda)
        {
            Bounds = nAda.GetObjectBounds(ContainedObjects[0]);

            for (int i = 1; i < ContainedObjects.Count; i++)
            {
                ExpandVolume(nAda.GetObjectBounds(ContainedObjects[i]));
            }
        }
        internal float SAofList(SSBVHNodeAdaptor <GO> nAda, List <GO> list)
        {
            var box = AABBofOBJ(nAda, list[0]);

            list.ToList <GO>().GetRange(1, list.Count - 1).ForEach(obj => {
                var newbox = AABBofOBJ(nAda, obj);
                box.ExpandBy(newbox);
            });
            return(SA(box));
        }
Ejemplo n.º 12
0
        internal static SSAABB AABBofOBJ(SSBVHNodeAdaptor <GO> nAda, GO obj)
        {
            float  radius = nAda.radius(obj);
            SSAABB box;

            box.Min.X = -radius; box.Max.X = radius;
            box.Min.Y = -radius; box.Max.Y = radius;
            box.Min.Z = -radius; box.Max.Z = radius;
            return(box);
        }
Ejemplo n.º 13
0
        private double SAofList(SSBVHNodeAdaptor <GO> nAda, List <GO> list)
        {
            Bounds_d box = nAda.GetObjectBounds(list[0]);

            for (int i = 1; i < list.Count - 1; i++)
            {
                box.ExpandToFit(nAda.GetObjectBounds(list[i]));
            }

            return(SA(ref box));
        }
 void setDepth(SSBVHNodeAdaptor <GO> nAda, int newdepth)
 {
     this.depth = newdepth;
     if (newdepth > nAda.BVH.maxDepth)
     {
         nAda.BVH.maxDepth = newdepth;
     }
     if (gobjects == null)
     {
         left.setDepth(nAda, newdepth + 1);
         right.setDepth(nAda, newdepth + 1);
     }
 }
        private void expandVolume(SSBVHNodeAdaptor <GO> nAda, SSAABB targetBox)
        {
            var boxCopy = box;

            box.ExpandToFit(targetBox);

            var expanded = !boxCopy.Equals(box);

            if (expanded && parent != null)
            {
                parent.childExpanded(nAda, this);
            }
        }
Ejemplo n.º 16
0
        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);
            }
        }
Ejemplo n.º 17
0
        void SetDepth(SSBVHNodeAdaptor <GO> nAda, int newdepth)
        {
            Depth = newdepth;
            if (newdepth > nAda.BVH.MaxDepth)
            {
                nAda.BVH.MaxDepth = newdepth;
            }

            if (ContainedObjects == null)
            {
                Left.SetDepth(nAda, newdepth + 1);
                Right.SetDepth(nAda, newdepth + 1);
            }
        }
Ejemplo n.º 18
0
 internal void findOverlappingLeaves(SSBVHNodeAdaptor <GO> nAda, Vector3 origin, float radius, List <ssBVHNode <GO> > overlapList)
 {
     if (toAABB().IntersectsSphere(origin, radius))
     {
         if (gobjects != null)
         {
             overlapList.Add(this);
         }
         else
         {
             left.findOverlappingLeaves(nAda, origin, radius, overlapList);
             right.findOverlappingLeaves(nAda, origin, radius, overlapList);
         }
     }
 }
Ejemplo n.º 19
0
 internal void findOverlappingLeaves(SSBVHNodeAdaptor <GO> nAda, SSAABB aabb, List <ssBVHNode <GO> > overlapList)
 {
     if (toAABB().IntersectsAABB(aabb))
     {
         if (gobjects != null)
         {
             overlapList.Add(this);
         }
         else
         {
             left.findOverlappingLeaves(nAda, aabb, overlapList);
             right.findOverlappingLeaves(nAda, aabb, overlapList);
         }
     }
 }
Ejemplo n.º 20
0
        /// <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);
        }
Ejemplo n.º 22
0
        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);
        }
        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);
        }
Ejemplo n.º 24
0
        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);
        }
Ejemplo n.º 25
0
        /// <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
            }
        }
Ejemplo n.º 26
0
        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);
            }
        }
Ejemplo n.º 28
0
        public void refit_ObjectChanged(SSBVHNodeAdaptor <GO> nAda, GO obj)
        {
            if (ContainedObjects == null)
            {
                throw new Exception("dangling leaf!");
            }
            if (RefitVolume(nAda))
            {
                // add our parent to the optimize list...
                if (Parent != null)
                {
                    nAda.BVH.RefitNodes.Add(Parent);

                    // you can force an optimize every time something moves, but it's not very efficient
                    // instead we do this per-frame after a bunch of updates.
                    // nAda.BVH.optimize();
                }
            }
        }
Ejemplo n.º 29
0
        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
        }
Ejemplo n.º 30
0
        private bool RefitVolume(SSBVHNodeAdaptor <GO> nAda)
        {
            if (ContainedObjects.Count == 0)
            {
                throw new NotImplementedException();
            }             // TODO: fix this... we should never get called in this case...

            Bounds_d oldbox = Bounds;

            ComputeVolume(nAda);
            if (!Bounds.Equals(oldbox))
            {
                if (Parent != null)
                {
                    Parent.ChildRefit(nAda);
                }
                return(true);
            }

            return(false);
        }