Beispiel #1
0
        //public void Subdivide(KdTreeNode node, AxisAlignedBox box, int depth, int prims) {
        //    // recycle used split list nodes
        //    _list = null;
        //    // determine split axis
        //    Vector3D s = box.Size;
        //    if ((s.X >= s.Y) && (s.X >= s.Z))
        //        node.Axis = 0;
        //    else if ((s.Y >= s.X) && (s.Y >= s.Z))
        //        node.Axis = 1;
        //    int axis = node.Axis;
        //    // make a list of the split position candidates
        //    ObjectList l = node.List;
        //    double p1, p2;
        //    double pos1 = box.Position[axis];
        //    double pos2 = box.Position[axis] + box.Size[axis];
        //    bool[] pright = new bool[prims];
        //    double[] eleft = new double[prims];
        //    double[] eright = new double[prims];
        //    Primitive[] parray = new Primitive[prims];
        //    int aidx = 0;
        //    while (l != null) {
        //        parray[aidx] = l.Primitive;
        //        Primitive p = parray[aidx];
        //        pright[aidx] = true;
        //        p.CalculateRange(ref eleft[aidx], ref eright[aidx], axis);
        //        aidx++;
        //        if (p.Type == PrimitiveType.Sphere) {
        //            p1 = p.Center[axis] - p.Radius;
        //            p2 = p.Center[axis] + p.Radius;
        //            if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos( p1 );
        //            if ((p2 >= pos1) && (p2 <= pos2)) InsertSplitPos( p2 );
        //        }
        //        else {
        //            for (int i = 0; i < 3; i++ ) {
        //                p1 = p.GetVertex(i).Position[axis];
        //                if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos( p1 );
        //            }
        //        }
        //        l = l.Next;
        //    }
        //    // determine n1count / n2count for each split position
        //    AxisAlignedBox b1 = null, b2 = null, b3, b4;
        //    b3 = box;
        //    b4 = box;
        //    SplitList splist = _list;
        //    double b3p1 = b3.Position[axis];
        //    double b4p2 = b4.Position[axis] + b4.Size[axis];
        //    while (splist != null) {
        //        b4.Position[axis] = splist.SplitPos;
        //        b4.Size[axis] = pos2 - splist.SplitPos;
        //        b3.Size[axis] = splist.SplitPos - pos1;
        //        double b3p2 = b3.Position[axis] + b3.Size[axis];
        //        double b4p1 = b4.Position[axis];
        //        for (int i = 0; i < prims; i++) {
        //            if (pright[i]) {
        //                Primitive p = parray[i];
        //                if ((eleft[i] <= b3p2) && (eright[i] >= b3p1))
        //                    if (p.IntersectBox(b3))
        //                        splist.N1Count++;
        //                if ((eleft[i] <= b4p2) && (eright[i] >= b4p1))
        //                    if (p.IntersectBox(b4))
        //                        splist.N2Count++;
        //                    else
        //                        pright[i] = false;
        //            }
        //            else
        //                splist.N1Count++;
        //        }
        //        splist = splist.Next;
        //    }
        //    // calculate surface area for current node
        //    double SAV = 0.5f / (box.W * box.D + box.W * box.H + box.D * box.H);
        //    // calculate cost for not splitting
        //    double Cleaf = prims * 1.0f;
        //    // determine optimal split plane position
        //    splist = _list;
        //    double lowcost = 10000;
        //    double bestpos = 0;
        //    while (splist != null) {
        //        // calculate child node extends
        //        b4.Position[axis] = splist.SplitPos;
        //        b4.Size[axis] = pos2 - splist.SplitPos;
        //        b3.Size[axis] = splist.SplitPos - pos1;
        //        // calculate child node cost
        //        double SA1 = 2 * (b3.W * b3.D + b3.W * b3.H + b3.D * b3.H);
        //        double SA2 = 2 * (b4.W * b4.D + b4.W * b4.H + b4.D * b4.H);
        //        double splitcost = 0.3f + 1.0f * (SA1 * SAV * splist.N1Count + SA2 * SAV * splist.N2Count);
        //        // update best cost tracking variables
        //        if (splitcost < lowcost) {
        //            lowcost = splitcost;
        //            bestpos = splist.SplitPos;
        //            b1 = b3;
        //            b2 = b4;
        //        }
        //        splist = splist.Next;
        //    }
        //    if (lowcost > Cleaf)
        //        return;
        //    node.SplitPosition = bestpos;
        //    // construct child nodes
        //    KdTreeNode tmpLeft = new KdTreeNode();
        //    KdTreeNode tmpRight = new KdTreeNode();
        //    int n1count = 0, n2count = 0, total = 0;
        //    // assign primitives to both sides
        //    double b1p1 = b1.Position[axis];
        //    double b2p2 = b2.Position[axis] + b2.Size[axis];
        //    double b1p2 = b1.Position[axis] + b1.Size[axis];
        //    double b2p1 = b2.Position[axis];
        //    for ( int i = 0; i < prims; i++ ) {
        //        Primitive p = parray[i];
        //        total++;
        //        if (eleft[i] <= b1p2 && eright[i] >= b1p1) {
        //            if (p.IntersectBox(b1)) {
        //                tmpLeft.Add(p);
        //                n1count++;
        //            }
        //        }
        //        if (eleft[i] <= b2p2 && eright[i] >= b2p1) {
        //            if (p.IntersectBox(b2)) {
        //                tmpRight.Add(p);
        //                n2count++;
        //            }
        //        }
        //    }
        //    node.List = null;
        //    if (n1count > 0) node.Left = tmpLeft;
        //    if (n2count > 0) node.Right = tmpRight;
        //    if (depth < Constants.MaxTreeDepth) {
        //        if (n1count > 2) Subdivide(tmpLeft, b1, depth + 1, n1count);
        //        if (n2count > 2) Subdivide(tmpRight, b2, depth + 1, n2count);
        //    }
        //}
        public void Subdivide(KdTreeNode node, AxisAlignedBox box, int depth, int prims)
        {
            // recycle used split list nodes
            _sortedList = null;

            // determine split axis
            Vector3D s = box.Size;
            if ((s.X >= s.Y) && (s.X >= s.Z))
                node.Axis = 0;
            else if ((s.Y >= s.X) && (s.Y >= s.Z))
                node.Axis = 1;

            int axis = node.Axis;
            // make a list of the split position candidates
            ObjectList l = node.List;
            double p1, p2;
            double pos1 = box.Position[axis];
            double pos2 = box.Position[axis] + box.Size[axis];

            bool[] pright = new bool[prims];
            double[] eleft = new double[prims];
            double[] eright = new double[prims];
            Primitive[] parray = new Primitive[prims];

            int aidx = 0;
            while (l != null) {
                parray[aidx] = l.Primitive;
                Primitive p = parray[aidx];
                pright[aidx] = true;
                p.CalculateRange(ref eleft[aidx], ref eright[aidx], axis);
                aidx++;

                if (p.Type == PrimitiveType.Sphere) {
                    p1 = p.Center[axis] - p.Radius;
                    p2 = p.Center[axis] + p.Radius;
                    if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos(p1);
                    if ((p2 >= pos1) && (p2 <= pos2)) InsertSplitPos(p2);
                }
                else {
                    for (int i = 0; i < 3; i++) {
                        p1 = p.GetVertex(i).Position[axis];
                        if ((p1 >= pos1) && (p1 <= pos2)) InsertSplitPos(p1);
                    }
                }
                l = l.Next;
            }
            // determine n1count / n2count for each split position
            AxisAlignedBox b1 = null, b2 = null, b3, b4;
            b3 = box;
            b4 = box;

            double b3p1 = b3.Position[axis];
            double b4p2 = b4.Position[axis] + b4.Size[axis];

            if (_sortedList == null)
                return;

            foreach (KeyValuePair<double, SplitListItem> pair in _sortedList) {
                SplitListItem spListItem = pair.Value;

                b4.Position[axis] = spListItem.SplitPos;
                b4.Size[axis] = pos2 - spListItem.SplitPos;
                b3.Size[axis] = spListItem.SplitPos - pos1;
                double b3p2 = b3.Position[axis] + b3.Size[axis];
                double b4p1 = b4.Position[axis];
                for (int i = 0; i < prims; i++) {
                    if (pright[i]) {
                        Primitive p = parray[i];
                        if ((eleft[i] <= b3p2) && (eright[i] >= b3p1))
                            if (p.IntersectBox(b3))
                                spListItem.N1Count++;

                        if ((eleft[i] <= b4p2) && (eright[i] >= b4p1))
                            if (p.IntersectBox(b4))
                                spListItem.N2Count++;
                            else
                                pright[i] = false;
                    }
                    else
                        spListItem.N1Count++;
                }
            }

            // calculate surface area for current node
            double SAV = 0.5f / (box.W * box.D + box.W * box.H + box.D * box.H);
            // calculate cost for not splitting
            double Cleaf = prims * 1.0f;

            // determine optimal split plane position
            double lowcost = 10000;
            double bestpos = 0;

            foreach (KeyValuePair<double, SplitListItem> pair in _sortedList) {
                SplitListItem spListItem = pair.Value;

                // calculate child node extends
                b4.Position[axis] = spListItem.SplitPos;
                b4.Size[axis] = pos2 - spListItem.SplitPos;
                b3.Size[axis] = spListItem.SplitPos - pos1;
                // calculate child node cost
                double SA1 = 2 * (b3.W * b3.D + b3.W * b3.H + b3.D * b3.H);
                double SA2 = 2 * (b4.W * b4.D + b4.W * b4.H + b4.D * b4.H);
                double splitcost = 0.3f + 1.0f * (SA1 * SAV * spListItem.N1Count + SA2 * SAV * spListItem.N2Count);
                // update best cost tracking variables
                if (splitcost < lowcost) {
                    lowcost = splitcost;
                    bestpos = spListItem.SplitPos;
                    b1 = b3;
                    b2 = b4;
                }
            }

            if (lowcost > Cleaf)
                return;

            node.SplitPosition = bestpos;
            // construct child nodes
            KdTreeNode tmpLeft = new KdTreeNode();
            KdTreeNode tmpRight = new KdTreeNode();
            int n1count = 0, n2count = 0, total = 0;
            // assign primitives to both sides
            double b1p1 = b1.Position[axis];
            double b2p2 = b2.Position[axis] + b2.Size[axis];
            double b1p2 = b1.Position[axis] + b1.Size[axis];
            double b2p1 = b2.Position[axis];
            for (int i = 0; i < prims; i++) {
                Primitive p = parray[i];
                total++;
                if (eleft[i] <= b1p2 && eright[i] >= b1p1) {
                    if (p.IntersectBox(b1)) {
                        tmpLeft.Add(p);
                        n1count++;
                    }
                }
                if (eleft[i] <= b2p2 && eright[i] >= b2p1) {
                    if (p.IntersectBox(b2)) {
                        tmpRight.Add(p);
                        n2count++;
                    }
                }
            }

            node.List = null;
            if (n1count > 0) node.Left = tmpLeft;
            if (n2count > 0) node.Right = tmpRight;

            if (depth < Constants.MaxTreeDepth) {
                if (n1count > 2) Subdivide(tmpLeft, b1, depth + 1, n1count);
                if (n2count > 2) Subdivide(tmpRight, b2, depth + 1, n2count);
            }
        }
Beispiel #2
0
 public KdTree()
 {
     Root = new KdTreeNode();
 }
Beispiel #3
0
            public static KdTreeNode makeInternal(Axis axis, int aboveChildIndex, float split)
            {
                KdTreeNode node = new KdTreeNode();
                node.axis = axis;
                node.splitPos = split;
                node.aboveChild = aboveChildIndex;

                return node;
            }
Beispiel #4
0
            public static KdTreeNode makeLeaf(int[] primIndices, int primIndicesBase, int numPrims)
            {
                KdTreeNode node = new KdTreeNode();
                node.axis = Axis.none;
                node.nPrimitives = numPrims;
                if(numPrims == 0)
                {
                    node.primitiveIndex = 0;
                }
                else if (numPrims == 1)
                {
                    node.primitiveIndex = primIndices[primIndicesBase];
                }
                else
                {
                    node.primitiveIndices = new int[numPrims];
                    Array.Copy(primIndices, primIndicesBase, node.primitiveIndices, 0, numPrims);
                }

                return node;
            }
Beispiel #5
0
        private void buildTree(	int nodeNum, BBox nodeBounds, BBox[] allPrimBounds, 
								int[] primNums, int primNumsBase, 
								int nPrimitives, int depth, BoundEdge[][] edges,
								int[] prims0, 
								int[] prims1, int prims1base, 
								int badRefines)
        {
            string indent = "".PadLeft(_maxDepth - depth);

            if (nextFreeNode == nAllocedNodes)
            {
                int nAlloc = Math.Max(2*nAllocedNodes, 512);
                KdTreeNode[] n = new KdTreeNode[nAlloc];
                if (nAllocedNodes > 0)
                    Array.Copy(_nodes, n, nAllocedNodes);
                _nodes = n;
                nAllocedNodes = nAlloc;
            }
            ++nextFreeNode;

            // *** Termination: Stop recursion if we have few enough nodes or the max depth has been reached ***
            if (nPrimitives <= _maxPrims || depth == 0)
            {
                _nodes[nodeNum] = KdTreeNode.makeLeaf(primNums, primNumsBase, nPrimitives);
                #if DEBUG
                Console.WriteLine(indent + _nodes[nodeNum]);
                #endif
                return;
            }

            // *** Not reached termination, so pick split plane and continue recursion ***
            Axis bestAxis = Axis.none;
            int bestOffset = -1;
            float bestCost = float.PositiveInfinity;
            float oldCost = iCost * (float)nPrimitives;
            float totalSA = nodeBounds.surfaceArea();
            float invTotalSA = 1.0f/totalSA;
            Vector3 d = nodeBounds.pMax - nodeBounds.pMin;
            Axis axis = nodeBounds.longestAxis();	// Start by checking the longest axis
            int retries = 0;
            retrySplit:

            // Fill edge lists
            for (int i=0; i<nPrimitives; i++)
            {
                int primIndex = primNums[primNumsBase + i];
                BBox bbox = allPrimBounds[primIndex];
                edges[(int)axis][2*i] = new BoundEdge( getAxisVal(bbox.pMin, axis), primIndex, true);
                edges[(int)axis][2*i+1] = new BoundEdge(getAxisVal(bbox.pMax, axis), primIndex, false);
            }
            Array.Sort(edges[(int)axis], 0, 2*nPrimitives);	// sort the edges of the first n primitives (2*n edges)

            // Find the best split by checking cost of every possible split plane
            int nBelow = 0, nAbove = nPrimitives;
            for (int i=0; i<2*nPrimitives; i++)
            {
                if( edges[(int)axis][i].type == BoundType.End) --nAbove;
                float edgeT = edges[(int)axis][i].t;
                if (edgeT > getAxisVal(nodeBounds.pMin, axis) &&
                    edgeT < getAxisVal(nodeBounds.pMax, axis) )
                {
                    // Compute split cost for the ith edge
                    Axis otherAxis0 = (Axis) (	((int)axis+1)	% 3);
                    Axis otherAxis1 = (Axis) (	((int)axis+2)	% 3);
                    float belowSA = 2* (getAxisVal(d, otherAxis0) * getAxisVal(d, otherAxis1) +
                                        (edgeT - getAxisVal(nodeBounds.pMin, axis)) *
                                        (getAxisVal(d, otherAxis0) + getAxisVal(d, otherAxis1)));
                    float aboveSA = 2*(getAxisVal(d, otherAxis0) * getAxisVal(d, otherAxis1) +
                                        (getAxisVal(nodeBounds.pMax, axis) - edgeT) *
                                        (getAxisVal(d, otherAxis0) + getAxisVal(d, otherAxis1)));
                    float pBelow = belowSA * invTotalSA;
                    float pAbove = aboveSA * invTotalSA;
                    float eb = (nAbove == 0 || nBelow == 0) ? emptyBonus : 0;
                    float cost = tCost + iCost * (1-eb) * (pBelow * nBelow + pAbove * nAbove);

                    // Save this split if it is better the previous split position
                    if(cost < bestCost)
                    {
                        bestCost = cost;
                        bestAxis = axis;
                        bestOffset = i;
                    }

                }
                if (edges[(int)axis][i].type == BoundType.Start) ++nBelow;
            }

            // Create a leaf node if no good split was found
            if(bestAxis == Axis.none && retries < 2)
            {
                ++retries;
                axis = (Axis) ( ((int)axis+1) % 3);
                goto retrySplit;
            }
            if (bestCost > oldCost) ++badRefines;
            if (( bestCost > 4.0f * oldCost && nPrimitives < 16) || bestAxis == Axis.none || badRefines == 3)
            {
                _nodes[nodeNum] = KdTreeNode.makeLeaf(primNums, primNumsBase, nPrimitives);
                #if DEBUG
                Console.WriteLine(indent + _nodes[nodeNum] + " (split failure)");
                #endif
                return;
            }

            // Classifiy primitives with respect to split
            int n0=0, n1=0;
            for (int i=0; i<bestOffset; i++)
            {
                if(edges[(int)bestAxis][i].type == BoundType.Start)
                    prims0[n0++] = edges[(int)bestAxis][i].primNum;
            }
            for (int i=bestOffset+1; i<2*nPrimitives; i++)
            {
                if(edges[(int)bestAxis][i].type == BoundType.End)
                    prims1[prims1base + n1++] = edges[(int)bestAxis][i].primNum;
            }

            // Ititalize child nodes
            float tsplit = edges[(int)bestAxis][bestOffset].t;
            BBox bounds0 = nodeBounds, bounds1 = nodeBounds;
            setAxisVal(ref bounds0.pMax, bestAxis, tsplit);
            setAxisVal(ref bounds1.pMin, bestAxis, tsplit);

            #if DEBUG
            KdTreeNode testNode = KdTreeNode.makeInternal(bestAxis, nextFreeNode, tsplit);
            int[] aboveIndices = new int[n1];
            int[] belowIndices = new int[n0];
            Array.Copy(prims0, belowIndices, n0);
            Array.Copy(prims1, prims1base, aboveIndices, 0, n1);

            Console.WriteLine(indent + testNode);
            Console.WriteLine(indent + " Above: { " + String.Join(",", aboveIndices) + " }");
            Console.WriteLine(indent + " Below: { " + String.Join(",", belowIndices) + " }");
            #endif

            buildTree(nodeNum+1, bounds0, allPrimBounds, prims0, 0, n0,
                        depth-1, edges, prims0, prims1, prims1base+nPrimitives-1, badRefines);

            int aboveChild = nextFreeNode;
            _nodes[nodeNum] = KdTreeNode.makeInternal(bestAxis, aboveChild, tsplit);

            buildTree(aboveChild, bounds1, allPrimBounds, prims1, prims1base, n1,
                        depth-1, edges, prims0, prims1, prims1base+nPrimitives-1, badRefines);
        }