Пример #1
0
        void subdivide(int left, int right, List <uint> tempTree, buildData dat, AABound gridBox, AABound nodeBox, int nodeIndex, int depth, BuildStats stats)
        {
            if ((right - left + 1) <= dat.maxPrims || depth >= 64)
            {
                // write leaf node
                stats.updateLeaf(depth, right - left + 1);
                createNode(tempTree, nodeIndex, left, right);
                return;
            }
            // calculate extents
            int   axis = -1, prevAxis, rightOrig;
            float clipL = float.NaN, clipR = float.NaN, prevClip = float.NaN;
            float split = float.NaN, prevSplit;
            bool  wasLeft = true;

            while (true)
            {
                prevAxis  = axis;
                prevSplit = split;
                // perform quick consistency checks
                Vector3 d = gridBox.hi - gridBox.lo;
                for (int i = 0; i < 3; i++)
                {
                    if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i])
                    {
                        Log.outError(LogFilter.Server, "Reached tree area in error - discarding node with: {0} objects", right - left + 1);
                    }
                }
                // find longest axis
                axis  = (int)d.primaryAxis();
                split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]);
                // partition L/R subsets
                clipL     = float.NegativeInfinity;
                clipR     = float.PositiveInfinity;
                rightOrig = right; // save this for later
                float nodeL = float.PositiveInfinity;
                float nodeR = float.NegativeInfinity;
                for (int i = left; i <= right;)
                {
                    int   obj    = (int)dat.indices[i];
                    float minb   = dat.primBound[obj].Lo[axis];
                    float maxb   = dat.primBound[obj].Hi[axis];
                    float center = (minb + maxb) * 0.5f;
                    if (center <= split)
                    {
                        // stay left
                        i++;
                        if (clipL < maxb)
                        {
                            clipL = maxb;
                        }
                    }
                    else
                    {
                        // move to the right most
                        int t = (int)dat.indices[i];
                        dat.indices[i]     = dat.indices[right];
                        dat.indices[right] = (uint)t;
                        right--;
                        if (clipR > minb)
                        {
                            clipR = minb;
                        }
                    }
                    nodeL = Math.Min(nodeL, minb);
                    nodeR = Math.Max(nodeR, maxb);
                }
                // check for empty space
                if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis])
                {
                    float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis];
                    float nodeNewW = nodeR - nodeL;
                    // node box is too big compare to space occupied by primitives?
                    if (1.3f * nodeNewW < nodeBoxW)
                    {
                        stats.updateBVH2();
                        int nextIndex1 = tempTree.Count;
                        // allocate child
                        tempTree.Add(0);
                        tempTree.Add(0);
                        tempTree.Add(0);
                        // write bvh2 clip node
                        stats.updateInner();
                        tempTree[nodeIndex + 0] = (uint)((axis << 30) | (1 << 29) | nextIndex1);
                        tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL);
                        tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR);
                        // update nodebox and recurse
                        nodeBox.lo[axis] = nodeL;
                        nodeBox.hi[axis] = nodeR;
                        subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex1, depth + 1, stats);
                        return;
                    }
                }
                // ensure we are making progress in the subdivision
                if (right == rightOrig)
                {
                    // all left
                    if (prevAxis == axis && MathFunctions.fuzzyEq(prevSplit, split))
                    {
                        // we are stuck here - create a leaf
                        stats.updateLeaf(depth, right - left + 1);
                        createNode(tempTree, nodeIndex, left, right);
                        return;
                    }
                    if (clipL <= split)
                    {
                        // keep looping on left half
                        gridBox.hi[axis] = split;
                        prevClip         = clipL;
                        wasLeft          = true;
                        continue;
                    }
                    gridBox.hi[axis] = split;
                    prevClip         = float.NaN;
                }
                else if (left > right)
                {
                    // all right
                    right = rightOrig;
                    if (prevAxis == axis && MathFunctions.fuzzyEq(prevSplit, split))
                    {
                        // we are stuck here - create a leaf
                        stats.updateLeaf(depth, right - left + 1);
                        createNode(tempTree, nodeIndex, left, right);
                        return;
                    }

                    if (clipR >= split)
                    {
                        // keep looping on right half
                        gridBox.lo[axis] = split;
                        prevClip         = clipR;
                        wasLeft          = false;
                        continue;
                    }
                    gridBox.lo[axis] = split;
                    prevClip         = float.NaN;
                }
                else
                {
                    // we are actually splitting stuff
                    if (prevAxis != -1 && !float.IsNaN(prevClip))
                    {
                        // second time through - lets create the previous split
                        // since it produced empty space
                        int nextIndex0 = tempTree.Count;
                        // allocate child node
                        tempTree.Add(0);
                        tempTree.Add(0);
                        tempTree.Add(0);
                        if (wasLeft)
                        {
                            // create a node with a left child
                            // write leaf node
                            stats.updateInner();
                            tempTree[nodeIndex + 0] = (uint)((prevAxis << 30) | nextIndex0);
                            tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip);
                            tempTree[nodeIndex + 2] = floatToRawIntBits(float.PositiveInfinity);
                        }
                        else
                        {
                            // create a node with a right child
                            // write leaf node
                            stats.updateInner();
                            tempTree[nodeIndex + 0] = (uint)((prevAxis << 30) | (nextIndex0 - 3));
                            tempTree[nodeIndex + 1] = floatToRawIntBits(float.NegativeInfinity);
                            tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip);
                        }
                        // count stats for the unused leaf
                        depth++;
                        stats.updateLeaf(depth, 0);
                        // now we keep going as we are, with a new nodeIndex:
                        nodeIndex = nextIndex0;
                    }
                    break;
                }
            }
            // compute index of child nodes
            int nextIndex = tempTree.Count;
            // allocate left node
            int nl = right - left + 1;
            int nr = rightOrig - (right + 1) + 1;

            if (nl > 0)
            {
                tempTree.Add(0);
                tempTree.Add(0);
                tempTree.Add(0);
            }
            else
            {
                nextIndex -= 3;
            }
            // allocate right node
            if (nr > 0)
            {
                tempTree.Add(0);
                tempTree.Add(0);
                tempTree.Add(0);
            }
            // write leaf node
            stats.updateInner();
            tempTree[nodeIndex + 0] = (uint)((axis << 30) | nextIndex);
            tempTree[nodeIndex + 1] = floatToRawIntBits(clipL);
            tempTree[nodeIndex + 2] = floatToRawIntBits(clipR);
            // prepare L/R child boxes
            AABound gridBoxL = gridBox;
            AABound gridBoxR = gridBox;
            AABound nodeBoxL = nodeBox;
            AABound nodeBoxR = nodeBox;

            gridBoxL.hi[axis] = gridBoxR.lo[axis] = split;
            nodeBoxL.hi[axis] = clipL;
            nodeBoxR.lo[axis] = clipR;
            // recurse
            if (nl > 0)
            {
                subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
            }
            else
            {
                stats.updateLeaf(depth + 1, 0);
            }
            if (nr > 0)
            {
                subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
            }
            else
            {
                stats.updateLeaf(depth + 1, 0);
            }
        }
Пример #2
0
        private void buildTree(float minx, float maxx, float miny, float maxy, float minz, float maxz, BuildTask task, int depth, List <int> tempTree, int offset, List <int> tempList, BuildStats stats)
        {
            // get node bounding box extents
            if (task.numObjects > maxPrims && depth < MAX_DEPTH)
            {
                float dx = maxx - minx;
                float dy = maxy - miny;
                float dz = maxz - minz;
                // search for best possible split
                float bestCost = INTERSECT_COST * task.numObjects;
                int   bestAxis = -1;
                int   bestOffsetStart = -1;
                int   bestOffsetEnd = -1;
                float bestSplit = 0;
                bool  bestPlanarLeft = false;
                int   bnl = 0, bnr = 0;
                // inverse area of the bounding box (factor of 2 ommitted)
                float area       = (dx * dy + dy * dz + dz * dx);
                float ISECT_COST = INTERSECT_COST / area;
                // setup counts for each axis
                int[] nl = { 0, 0, 0 };
                int[] nr = { task.numObjects, task.numObjects, task.numObjects };
                // setup bounds for each axis
                float[] dp      = { dy *dz, dz *dx, dx *dy };
                float[] ds      = { dy + dz, dz + dx, dx + dy };
                float[] nodeMin = { minx, miny, minz };
                float[] nodeMax = { maxx, maxy, maxz };
                // search for best cost
                int    nSplits = task.n;
                long[] splits  = task.splits;
                byte[] lrtable = task.leftRightTable;
                for (int i = 0; i < nSplits;)
                {
                    // extract current split
                    long  ptr   = splits[i];
                    float split = unpackSplit(ptr);
                    int   axis  = unpackAxis(ptr);
                    // mark current position
                    int currentOffset = i;
                    // count number of primitives start/stopping/lying on the
                    // current plane
                    int  pClosed = 0, pPlanar = 0, pOpened = 0;
                    long ptrMasked = (long)((ulong)ptr & (~((ulong)TYPE_MASK) & 0xFFFFFFFFF0000000L));
                    long ptrClosed = ptrMasked | CLOSED;
                    long ptrPlanar = ptrMasked | PLANAR;
                    long ptrOpened = ptrMasked | OPENED;
                    while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrClosed)
                    {
                        int obj = unpackObject(splits[i]);
                        lrtable[(uint)obj >> 2] = 0;//>>>
                        pClosed++;
                        i++;
                    }
                    while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrPlanar)
                    {
                        int obj = unpackObject(splits[i]);
                        lrtable[(uint)obj >> 2] = 0;//>>>
                        pPlanar++;
                        i++;
                    }
                    while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrOpened)
                    {
                        int obj = unpackObject(splits[i]);
                        lrtable[(uint)obj >> 2] = 0;//>>>
                        pOpened++;
                        i++;
                    }
                    // now we have summed all contributions from this plane
                    nr[axis] -= pPlanar + pClosed;
                    // compute cost
                    if (split >= nodeMin[axis] && split <= nodeMax[axis])
                    {
                        // left and right surface area (factor of 2 ommitted)
                        float dl = split - nodeMin[axis];
                        float dr = nodeMax[axis] - split;
                        float lp = dp[axis] + dl * ds[axis];
                        float rp = dp[axis] + dr * ds[axis];
                        // planar prims go to smallest cell always
                        bool  planarLeft = dl < dr;
                        int   numLeft    = nl[axis] + (planarLeft ? pPlanar : 0);
                        int   numRight   = nr[axis] + (planarLeft ? 0 : pPlanar);
                        float eb         = ((numLeft == 0 && dl > 0) || (numRight == 0 && dr > 0)) ? EMPTY_BONUS : 0;
                        float cost       = TRAVERSAL_COST + ISECT_COST * (1 - eb) * (lp * numLeft + rp * numRight);
                        if (cost < bestCost)
                        {
                            bestCost        = cost;
                            bestAxis        = axis;
                            bestSplit       = split;
                            bestOffsetStart = currentOffset;
                            bestOffsetEnd   = i;
                            bnl             = numLeft;
                            bnr             = numRight;
                            bestPlanarLeft  = planarLeft;
                        }
                    }
                    // move objects left
                    nl[axis] += pOpened + pPlanar;
                }
                // debug check for correctness of the scan
                for (int axis = 0; axis < 3; axis++)
                {
                    int numLeft  = nl[axis];
                    int numRight = nr[axis];
                    if (numLeft != task.numObjects || numRight != 0)
                    {
                        UI.printError(UI.Module.ACCEL, "Didn't scan full range of objects @depth={0}. Left overs for axis {1}: [L: {2}] [R: {3}]", depth, axis, numLeft, numRight);
                    }
                }
                // found best split?
                if (bestAxis != -1)
                {
                    // allocate space for child nodes
                    BuildTask taskL = new BuildTask(bnl, task);
                    BuildTask taskR = new BuildTask(bnr, task);
                    int       lk = 0, rk = 0;
                    for (int i = 0; i < bestOffsetStart; i++)
                    {
                        long ptr = splits[i];
                        if (unpackAxis(ptr) == bestAxis)
                        {
                            if (unpackSplitType(ptr) != CLOSED)
                            {
                                int obj = unpackObject(ptr);
                                lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>>
                                lk++;
                            }
                        }
                    }
                    for (int i = bestOffsetStart; i < bestOffsetEnd; i++)
                    {
                        long ptr = splits[i];
                        Debug.Assert(unpackAxis(ptr) == bestAxis);
                        if (unpackSplitType(ptr) == PLANAR)
                        {
                            if (bestPlanarLeft)
                            {
                                int obj = unpackObject(ptr);
                                lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>>
                                lk++;
                            }
                            else
                            {
                                int obj = unpackObject(ptr);
                                lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>>
                                rk++;
                            }
                        }
                    }
                    for (int i = bestOffsetEnd; i < nSplits; i++)
                    {
                        long ptr = splits[i];
                        if (unpackAxis(ptr) == bestAxis)
                        {
                            if (unpackSplitType(ptr) != OPENED)
                            {
                                int obj = unpackObject(ptr);
                                lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>>
                                rk++;
                            }
                        }
                    }
                    // output new splits while maintaining order
                    long[] splitsL = taskL.splits;
                    long[] splitsR = taskR.splits;
                    int    nsl = 0, nsr = 0;
                    for (int i = 0; i < nSplits; i++)
                    {
                        long ptr  = splits[i];
                        int  obj  = unpackObject(ptr);
                        int  idx  = (int)((uint)obj >> 2);//>>>
                        int  mask = 1 << ((obj & 3) << 1);
                        if ((lrtable[idx] & mask) != 0)
                        {
                            splitsL[nsl] = ptr;
                            nsl++;
                        }
                        if ((lrtable[idx] & (mask << 1)) != 0)
                        {
                            splitsR[nsr] = ptr;
                            nsr++;
                        }
                    }
                    taskL.n = nsl;
                    taskR.n = nsr;
                    // free more memory
                    task.splits = splits = splitsL = splitsR = null;
                    task        = null;
                    // allocate child nodes
                    int nextOffset = tempTree.Count;
                    tempTree.Add(0);
                    tempTree.Add(0);
                    tempTree.Add(0);
                    tempTree.Add(0);
                    // create current node
                    tempTree[offset + 0] = (bestAxis << 30) | nextOffset;
                    tempTree[offset + 1] = ByteUtil.floatToRawIntBits(bestSplit);
                    // recurse for child nodes - free object arrays after each step
                    stats.updateInner();
                    switch (bestAxis)
                    {
                    case 0:
                        buildTree(minx, bestSplit, miny, maxy, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
                        taskL = null;
                        buildTree(bestSplit, maxx, miny, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
                        taskR = null;
                        return;

                    case 1:
                        buildTree(minx, maxx, miny, bestSplit, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
                        taskL = null;
                        buildTree(minx, maxx, bestSplit, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
                        taskR = null;
                        return;

                    case 2:
                        buildTree(minx, maxx, miny, maxy, minz, bestSplit, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
                        taskL = null;
                        buildTree(minx, maxx, miny, maxy, bestSplit, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
                        taskR = null;
                        return;

                    default:
                        Debug.Assert(false);
                        break;
                    }
                }
            }
            // create leaf node
            int listOffset = tempList.Count;
            int n          = 0;

            for (int i = 0; i < task.n; i++)
            {
                long ptr = task.splits[i];
                if (unpackAxis(ptr) == 0 && unpackSplitType(ptr) != CLOSED)
                {
                    tempList.Add(unpackObject(ptr));
                    n++;
                }
            }
            stats.updateLeaf(depth, n);
            if (n != task.numObjects)
            {
                UI.printError(UI.Module.ACCEL, "Error creating leaf node - expecting {0} found {1}", task.numObjects, n);
            }
            tempTree[offset + 0] = (3 << 30) | listOffset;
            tempTree[offset + 1] = task.numObjects;
            // free some memory
            task.splits = null;
        }
        private void subdivide(int left, int right, List <int> tempTree, int[] indices, float[] gridBox, float[] nodeBox, int nodeIndex, int depth, BuildStats stats)
        {
            if ((right - left + 1) <= maxPrims || depth >= 64)
            {
                // write leaf node
                stats.updateLeaf(depth, right - left + 1);
                createNode(tempTree, nodeIndex, left, right);
                return;
            }
            // calculate extents
            int   axis = -1, prevAxis, rightOrig;
            float clipL = float.NaN, clipR = float.NaN, prevClip = float.NaN;
            float split = float.NaN, prevSplit;
            bool  wasLeft = true;

            while (true)
            {
                prevAxis  = axis;
                prevSplit = split;
                // perform quick consistency checks
                float[] d = { gridBox[1] - gridBox[0], gridBox[3] - gridBox[2],
                              gridBox[5] - gridBox[4] };
                if (d[0] < 0 || d[1] < 0 || d[2] < 0)
                {
                    throw new Exception("negative node extents");
                }
                for (int i = 0; i < 3; i++)
                {
                    if (nodeBox[2 * i + 1] < gridBox[2 * i] || nodeBox[2 * i] > gridBox[2 * i + 1])
                    {
                        UI.printError(UI.Module.ACCEL, "Reached tree area in error - discarding node with: {0} objects", right - left + 1);
                        throw new Exception("invalid node overlap");
                    }
                }
                // find longest axis
                if (d[0] > d[1] && d[0] > d[2])
                {
                    axis = 0;
                }
                else if (d[1] > d[2])
                {
                    axis = 1;
                }
                else
                {
                    axis = 2;
                }
                split = 0.5f * (gridBox[2 * axis] + gridBox[2 * axis + 1]);
                // partition L/R subsets
                clipL     = float.NegativeInfinity;
                clipR     = float.PositiveInfinity;
                rightOrig = right; // save this for later
                float nodeL = float.PositiveInfinity;
                float nodeR = float.NegativeInfinity;
                for (int i = left; i <= right;)
                {
                    int   obj    = indices[i];
                    float minb   = primitives.getPrimitiveBound(obj, 2 * axis + 0);
                    float maxb   = primitives.getPrimitiveBound(obj, 2 * axis + 1);
                    float center = (minb + maxb) * 0.5f;
                    if (center <= split)
                    {
                        // stay left
                        i++;
                        if (clipL < maxb)
                        {
                            clipL = maxb;
                        }
                    }
                    else
                    {
                        // move to the right most
                        int t = indices[i];
                        indices[i]     = indices[right];
                        indices[right] = t;
                        right--;
                        if (clipR > minb)
                        {
                            clipR = minb;
                        }
                    }
                    if (nodeL > minb)
                    {
                        nodeL = minb;
                    }
                    if (nodeR < maxb)
                    {
                        nodeR = maxb;
                    }
                }
                // check for empty space
                if (nodeL > nodeBox[2 * axis + 0] && nodeR < nodeBox[2 * axis + 1])
                {
                    float nodeBoxW = nodeBox[2 * axis + 1] - nodeBox[2 * axis + 0];
                    float nodeNewW = nodeR - nodeL;
                    // node box is too big compare to space occupied by primitives?
                    if (1.3f * nodeNewW < nodeBoxW)
                    {
                        stats.updateBVH2();
                        int nextIndex = tempTree.Count;
                        // allocate child
                        tempTree.Add(0);
                        tempTree.Add(0);
                        tempTree.Add(0);
                        // write bvh2 clip node
                        stats.updateInner();
                        tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
                        tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(nodeL);
                        tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(nodeR);
                        // update nodebox and recurse
                        nodeBox[2 * axis + 0] = nodeL;
                        nodeBox[2 * axis + 1] = nodeR;
                        subdivide(left, rightOrig, tempTree, indices, gridBox, nodeBox, nextIndex, depth + 1, stats);
                        return;
                    }
                }
                // ensure we are making progress in the subdivision
                if (right == rightOrig)
                {
                    // all left
                    if (clipL <= split)
                    {
                        // keep looping on left half
                        gridBox[2 * axis + 1] = split;
                        prevClip = clipL;
                        wasLeft  = true;
                        continue;
                    }
                    if (prevAxis == axis && prevSplit == split)
                    {
                        // we are stuck here - create a leaf
                        stats.updateLeaf(depth, right - left + 1);
                        createNode(tempTree, nodeIndex, left, right);
                        return;
                    }
                    gridBox[2 * axis + 1] = split;
                    prevClip = float.NaN;
                }
                else if (left > right)
                {
                    // all right
                    right = rightOrig;
                    if (clipR >= split)
                    {
                        // keep looping on right half
                        gridBox[2 * axis + 0] = split;
                        prevClip = clipR;
                        wasLeft  = false;
                        continue;
                    }
                    if (prevAxis == axis && prevSplit == split)
                    {
                        // we are stuck here - create a leaf
                        stats.updateLeaf(depth, right - left + 1);
                        createNode(tempTree, nodeIndex, left, right);
                        return;
                    }
                    gridBox[2 * axis + 0] = split;
                    prevClip = float.NaN;
                }
                else
                {
                    // we are actually splitting stuff
                    if (prevAxis != -1 && !float.IsNaN(prevClip))
                    {
                        // second time through - lets create the previous split
                        // since it produced empty space
                        int nextIndex = tempTree.Count;
                        // allocate child node
                        tempTree.Add(0);
                        tempTree.Add(0);
                        tempTree.Add(0);
                        if (wasLeft)
                        {
                            // create a node with a left child
                            // write leaf node
                            stats.updateInner();
                            tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
                            tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(prevClip);
                            tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(float.PositiveInfinity);
                        }
                        else
                        {
                            // create a node with a right child
                            // write leaf node
                            stats.updateInner();
                            tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
                            tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(float.NegativeInfinity);
                            tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(prevClip);
                        }
                        // count stats for the unused leaf
                        depth++;
                        stats.updateLeaf(depth, 0);
                        // now we keep going as we are, with a new nodeIndex:
                        nodeIndex = nextIndex;
                    }
                    break;
                }
            }
            // compute index of child nodes
            int nextIndex1 = tempTree.Count;
            // allocate left node
            int nl = right - left + 1;
            int nr = rightOrig - (right + 1) + 1;

            if (nl > 0)
            {
                tempTree.Add(0);
                tempTree.Add(0);
                tempTree.Add(0);
            }
            else
            {
                nextIndex1 -= 3;
            }
            // allocate right node
            if (nr > 0)
            {
                tempTree.Add(0);
                tempTree.Add(0);
                tempTree.Add(0);
            }
            // write leaf node
            stats.updateInner();
            tempTree[nodeIndex + 0] = (axis << 30) | nextIndex1;
            tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(clipL);
            tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(clipR);
            // prepare L/R child boxes
            float[] gridBoxL = new float[6];
            float[] gridBoxR = new float[6];
            float[] nodeBoxL = new float[6];
            float[] nodeBoxR = new float[6];
            for (int i = 0; i < 6; i++)
            {
                gridBoxL[i] = gridBoxR[i] = gridBox[i];
                nodeBoxL[i] = nodeBoxR[i] = nodeBox[i];
            }
            gridBoxL[2 * axis + 1] = gridBoxR[2 * axis] = split;
            nodeBoxL[2 * axis + 1] = clipL;
            nodeBoxR[2 * axis + 0] = clipR;
            // free memory
            gridBox = nodeBox = null;
            // recurse
            if (nl > 0)
            {
                subdivide(left, right, tempTree, indices, gridBoxL, nodeBoxL, nextIndex1, depth + 1, stats);
            }
            else
            {
                stats.updateLeaf(depth + 1, 0);
            }
            if (nr > 0)
            {
                subdivide(right + 1, rightOrig, tempTree, indices, gridBoxR, nodeBoxR, nextIndex1 + 3, depth + 1, stats);
            }
            else
            {
                stats.updateLeaf(depth + 1, 0);
            }
        }
Пример #4
0
 private void buildTree(float minx, float maxx, float miny, float maxy, float minz, float maxz, BuildTask task, int depth, List<int> tempTree, int offset, List<int> tempList, BuildStats stats)
 {
     // get node bounding box extents
     if (task.numObjects > maxPrims && depth < MAX_DEPTH)
     {
         float dx = maxx - minx;
         float dy = maxy - miny;
         float dz = maxz - minz;
         // search for best possible split
         float bestCost = INTERSECT_COST * task.numObjects;
         int bestAxis = -1;
         int bestOffsetStart = -1;
         int bestOffsetEnd = -1;
         float bestSplit = 0;
         bool bestPlanarLeft = false;
         int bnl = 0, bnr = 0;
         // inverse area of the bounding box (factor of 2 ommitted)
         float area = (dx * dy + dy * dz + dz * dx);
         float ISECT_COST = INTERSECT_COST / area;
         // setup counts for each axis
         int[] nl = { 0, 0, 0 };
         int[] nr = { task.numObjects, task.numObjects, task.numObjects };
         // setup bounds for each axis
         float[] dp = { dy * dz, dz * dx, dx * dy };
         float[] ds = { dy + dz, dz + dx, dx + dy };
         float[] nodeMin = { minx, miny, minz };
         float[] nodeMax = { maxx, maxy, maxz };
         // search for best cost
         int nSplits = task.n;
         long[] splits = task.splits;
         byte[] lrtable = task.leftRightTable;
         for (int i = 0; i < nSplits; )
         {
             // extract current split
             long ptr = splits[i];
             float split = unpackSplit(ptr);
             int axis = unpackAxis(ptr);
             // mark current position
             int currentOffset = i;
             // count number of primitives start/stopping/lying on the
             // current plane
             int pClosed = 0, pPlanar = 0, pOpened = 0;
             long ptrMasked = (long)((ulong)ptr & (~((ulong)TYPE_MASK) & 0xFFFFFFFFF0000000L));
             long ptrClosed = ptrMasked | CLOSED;
             long ptrPlanar = ptrMasked | PLANAR;
             long ptrOpened = ptrMasked | OPENED;
             while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrClosed)
             {
                 int obj = unpackObject(splits[i]);
                 lrtable[(uint)obj >> 2] = 0;//>>>
                 pClosed++;
                 i++;
             }
             while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrPlanar)
             {
                 int obj = unpackObject(splits[i]);
                 lrtable[(uint)obj >> 2] = 0;//>>>
                 pPlanar++;
                 i++;
             }
             while (i < nSplits && ((ulong)splits[i] & 0xFFFFFFFFF0000000L) == (ulong)ptrOpened)
             {
                 int obj = unpackObject(splits[i]);
                 lrtable[(uint)obj >> 2] = 0;//>>>
                 pOpened++;
                 i++;
             }
             // now we have summed all contributions from this plane
             nr[axis] -= pPlanar + pClosed;
             // compute cost
             if (split >= nodeMin[axis] && split <= nodeMax[axis])
             {
                 // left and right surface area (factor of 2 ommitted)
                 float dl = split - nodeMin[axis];
                 float dr = nodeMax[axis] - split;
                 float lp = dp[axis] + dl * ds[axis];
                 float rp = dp[axis] + dr * ds[axis];
                 // planar prims go to smallest cell always
                 bool planarLeft = dl < dr;
                 int numLeft = nl[axis] + (planarLeft ? pPlanar : 0);
                 int numRight = nr[axis] + (planarLeft ? 0 : pPlanar);
                 float eb = ((numLeft == 0 && dl > 0) || (numRight == 0 && dr > 0)) ? EMPTY_BONUS : 0;
                 float cost = TRAVERSAL_COST + ISECT_COST * (1 - eb) * (lp * numLeft + rp * numRight);
                 if (cost < bestCost)
                 {
                     bestCost = cost;
                     bestAxis = axis;
                     bestSplit = split;
                     bestOffsetStart = currentOffset;
                     bestOffsetEnd = i;
                     bnl = numLeft;
                     bnr = numRight;
                     bestPlanarLeft = planarLeft;
                 }
             }
             // move objects left
             nl[axis] += pOpened + pPlanar;
         }
         // debug check for correctness of the scan
         for (int axis = 0; axis < 3; axis++)
         {
             int numLeft = nl[axis];
             int numRight = nr[axis];
             if (numLeft != task.numObjects || numRight != 0)
                 UI.printError(UI.Module.ACCEL, "Didn't scan full range of objects @depth={0}. Left overs for axis {1}: [L: {2}] [R: {3}]", depth, axis, numLeft, numRight);
         }
         // found best split?
         if (bestAxis != -1)
         {
             // allocate space for child nodes
             BuildTask taskL = new BuildTask(bnl, task);
             BuildTask taskR = new BuildTask(bnr, task);
             int lk = 0, rk = 0;
             for (int i = 0; i < bestOffsetStart; i++)
             {
                 long ptr = splits[i];
                 if (unpackAxis(ptr) == bestAxis)
                 {
                     if (unpackSplitType(ptr) != CLOSED)
                     {
                         int obj = unpackObject(ptr);
                         lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>>
                         lk++;
                     }
                 }
             }
             for (int i = bestOffsetStart; i < bestOffsetEnd; i++)
             {
                 long ptr = splits[i];
                 Debug.Assert(unpackAxis(ptr) == bestAxis);
                 if (unpackSplitType(ptr) == PLANAR)
                 {
                     if (bestPlanarLeft)
                     {
                         int obj = unpackObject(ptr);
                         lrtable[(uint)obj >> 2] |= (byte)(1 << ((obj & 3) << 1));//>>>
                         lk++;
                     }
                     else
                     {
                         int obj = unpackObject(ptr);
                         lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>>
                         rk++;
                     }
                 }
             }
             for (int i = bestOffsetEnd; i < nSplits; i++)
             {
                 long ptr = splits[i];
                 if (unpackAxis(ptr) == bestAxis)
                 {
                     if (unpackSplitType(ptr) != OPENED)
                     {
                         int obj = unpackObject(ptr);
                         lrtable[(uint)obj >> 2] |= (byte)(2 << ((obj & 3) << 1));//>>>
                         rk++;
                     }
                 }
             }
             // output new splits while maintaining order
             long[] splitsL = taskL.splits;
             long[] splitsR = taskR.splits;
             int nsl = 0, nsr = 0;
             for (int i = 0; i < nSplits; i++)
             {
                 long ptr = splits[i];
                 int obj = unpackObject(ptr);
                 int idx = (int)((uint)obj >> 2);//>>>
                 int mask = 1 << ((obj & 3) << 1);
                 if ((lrtable[idx] & mask) != 0)
                 {
                     splitsL[nsl] = ptr;
                     nsl++;
                 }
                 if ((lrtable[idx] & (mask << 1)) != 0)
                 {
                     splitsR[nsr] = ptr;
                     nsr++;
                 }
             }
             taskL.n = nsl;
             taskR.n = nsr;
             // free more memory
             task.splits = splits = splitsL = splitsR = null;
             task = null;
             // allocate child nodes
             int nextOffset = tempTree.Count;
             tempTree.Add(0);
             tempTree.Add(0);
             tempTree.Add(0);
             tempTree.Add(0);
             // create current node
             tempTree[offset + 0] = (bestAxis << 30) | nextOffset;
             tempTree[offset + 1] = ByteUtil.floatToRawIntBits(bestSplit);
             // recurse for child nodes - free object arrays after each step
             stats.updateInner();
             switch (bestAxis)
             {
                 case 0:
                     buildTree(minx, bestSplit, miny, maxy, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
                     taskL = null;
                     buildTree(bestSplit, maxx, miny, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
                     taskR = null;
                     return;
                 case 1:
                     buildTree(minx, maxx, miny, bestSplit, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
                     taskL = null;
                     buildTree(minx, maxx, bestSplit, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
                     taskR = null;
                     return;
                 case 2:
                     buildTree(minx, maxx, miny, maxy, minz, bestSplit, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
                     taskL = null;
                     buildTree(minx, maxx, miny, maxy, bestSplit, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
                     taskR = null;
                     return;
                 default:
                     Debug.Assert(false);
                     break;
             }
         }
     }
     // create leaf node
     int listOffset = tempList.Count;
     int n = 0;
     for (int i = 0; i < task.n; i++)
     {
         long ptr = task.splits[i];
         if (unpackAxis(ptr) == 0 && unpackSplitType(ptr) != CLOSED)
         {
             tempList.Add(unpackObject(ptr));
             n++;
         }
     }
     stats.updateLeaf(depth, n);
     if (n != task.numObjects)
         UI.printError(UI.Module.ACCEL, "Error creating leaf node - expecting {0} found {1}", task.numObjects, n);
     tempTree[offset + 0] = (3 << 30) | listOffset;
     tempTree[offset + 1] = task.numObjects;
     // free some memory
     task.splits = null;
 }
 private void subdivide(int left, int right, List<int> tempTree, int[] indices, float[] gridBox, float[] nodeBox, int nodeIndex, int depth, BuildStats stats)
 {
     if ((right - left + 1) <= maxPrims || depth >= 64)
     {
         // write leaf node
         stats.updateLeaf(depth, right - left + 1);
         createNode(tempTree, nodeIndex, left, right);
         return;
     }
     // calculate extents
     int axis = -1, prevAxis, rightOrig;
     float clipL = float.NaN, clipR = float.NaN, prevClip = float.NaN;
     float split = float.NaN, prevSplit;
     bool wasLeft = true;
     while (true)
     {
         prevAxis = axis;
         prevSplit = split;
         // perform quick consistency checks
         float[] d = { gridBox[1] - gridBox[0], gridBox[3] - gridBox[2],
             gridBox[5] - gridBox[4] };
         if (d[0] < 0 || d[1] < 0 || d[2] < 0)
             throw new Exception("negative node extents");
         for (int i = 0; i < 3; i++)
         {
             if (nodeBox[2 * i + 1] < gridBox[2 * i] || nodeBox[2 * i] > gridBox[2 * i + 1])
             {
                 UI.printError(UI.Module.ACCEL, "Reached tree area in error - discarding node with: {0} objects", right - left + 1);
                 throw new Exception("invalid node overlap");
             }
         }
         // find longest axis
         if (d[0] > d[1] && d[0] > d[2])
             axis = 0;
         else if (d[1] > d[2])
             axis = 1;
         else
             axis = 2;
         split = 0.5f * (gridBox[2 * axis] + gridBox[2 * axis + 1]);
         // partition L/R subsets
         clipL = float.NegativeInfinity;
         clipR = float.PositiveInfinity;
         rightOrig = right; // save this for later
         float nodeL = float.PositiveInfinity;
         float nodeR = float.NegativeInfinity;
         for (int i = left; i <= right; )
         {
             int obj = indices[i];
             float minb = primitives.getPrimitiveBound(obj, 2 * axis + 0);
             float maxb = primitives.getPrimitiveBound(obj, 2 * axis + 1);
             float center = (minb + maxb) * 0.5f;
             if (center <= split)
             {
                 // stay left
                 i++;
                 if (clipL < maxb)
                     clipL = maxb;
             }
             else
             {
                 // move to the right most
                 int t = indices[i];
                 indices[i] = indices[right];
                 indices[right] = t;
                 right--;
                 if (clipR > minb)
                     clipR = minb;
             }
             if (nodeL > minb)
                 nodeL = minb;
             if (nodeR < maxb)
                 nodeR = maxb;
         }
         // check for empty space
         if (nodeL > nodeBox[2 * axis + 0] && nodeR < nodeBox[2 * axis + 1])
         {
             float nodeBoxW = nodeBox[2 * axis + 1] - nodeBox[2 * axis + 0];
             float nodeNewW = nodeR - nodeL;
             // node box is too big compare to space occupied by primitives?
             if (1.3f * nodeNewW < nodeBoxW)
             {
                 stats.updateBVH2();
                 int nextIndex = tempTree.Count;
                 // allocate child
                 tempTree.Add(0);
                 tempTree.Add(0);
                 tempTree.Add(0);
                 // write bvh2 clip node
                 stats.updateInner();
                 tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
                 tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(nodeL);
                 tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(nodeR);
                 // update nodebox and recurse
                 nodeBox[2 * axis + 0] = nodeL;
                 nodeBox[2 * axis + 1] = nodeR;
                 subdivide(left, rightOrig, tempTree, indices, gridBox, nodeBox, nextIndex, depth + 1, stats);
                 return;
             }
         }
         // ensure we are making progress in the subdivision
         if (right == rightOrig)
         {
             // all left
             if (clipL <= split)
             {
                 // keep looping on left half
                 gridBox[2 * axis + 1] = split;
                 prevClip = clipL;
                 wasLeft = true;
                 continue;
             }
             if (prevAxis == axis && prevSplit == split)
             {
                 // we are stuck here - create a leaf
                 stats.updateLeaf(depth, right - left + 1);
                 createNode(tempTree, nodeIndex, left, right);
                 return;
             }
             gridBox[2 * axis + 1] = split;
             prevClip = float.NaN;
         }
         else if (left > right)
         {
             // all right
             right = rightOrig;
             if (clipR >= split)
             {
                 // keep looping on right half
                 gridBox[2 * axis + 0] = split;
                 prevClip = clipR;
                 wasLeft = false;
                 continue;
             }
             if (prevAxis == axis && prevSplit == split)
             {
                 // we are stuck here - create a leaf
                 stats.updateLeaf(depth, right - left + 1);
                 createNode(tempTree, nodeIndex, left, right);
                 return;
             }
             gridBox[2 * axis + 0] = split;
             prevClip = float.NaN;
         }
         else
         {
             // we are actually splitting stuff
             if (prevAxis != -1 && !float.IsNaN(prevClip))
             {
                 // second time through - lets create the previous split
                 // since it produced empty space
                 int nextIndex = tempTree.Count;
                 // allocate child node
                 tempTree.Add(0);
                 tempTree.Add(0);
                 tempTree.Add(0);
                 if (wasLeft)
                 {
                     // create a node with a left child
                     // write leaf node
                     stats.updateInner();
                     tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
                     tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(prevClip);
                     tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(float.PositiveInfinity);
                 }
                 else
                 {
                     // create a node with a right child
                     // write leaf node
                     stats.updateInner();
                     tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
                     tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(float.NegativeInfinity);
                     tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(prevClip);
                 }
                 // count stats for the unused leaf
                 depth++;
                 stats.updateLeaf(depth, 0);
                 // now we keep going as we are, with a new nodeIndex:
                 nodeIndex = nextIndex;
             }
             break;
         }
     }
     // compute index of child nodes
     int nextIndex1 = tempTree.Count;
     // allocate left node
     int nl = right - left + 1;
     int nr = rightOrig - (right + 1) + 1;
     if (nl > 0)
     {
         tempTree.Add(0);
         tempTree.Add(0);
         tempTree.Add(0);
     }
     else
         nextIndex1 -= 3;
     // allocate right node
     if (nr > 0)
     {
         tempTree.Add(0);
         tempTree.Add(0);
         tempTree.Add(0);
     }
     // write leaf node
     stats.updateInner();
     tempTree[nodeIndex + 0] = (axis << 30) | nextIndex1;
     tempTree[nodeIndex + 1] = ByteUtil.floatToRawIntBits(clipL);
     tempTree[nodeIndex + 2] = ByteUtil.floatToRawIntBits(clipR);
     // prepare L/R child boxes
     float[] gridBoxL = new float[6];
     float[] gridBoxR = new float[6];
     float[] nodeBoxL = new float[6];
     float[] nodeBoxR = new float[6];
     for (int i = 0; i < 6; i++)
     {
         gridBoxL[i] = gridBoxR[i] = gridBox[i];
         nodeBoxL[i] = nodeBoxR[i] = nodeBox[i];
     }
     gridBoxL[2 * axis + 1] = gridBoxR[2 * axis] = split;
     nodeBoxL[2 * axis + 1] = clipL;
     nodeBoxR[2 * axis + 0] = clipR;
     // free memory
     gridBox = nodeBox = null;
     // recurse
     if (nl > 0)
         subdivide(left, right, tempTree, indices, gridBoxL, nodeBoxL, nextIndex1, depth + 1, stats);
     else
         stats.updateLeaf(depth + 1, 0);
     if (nr > 0)
         subdivide(right + 1, rightOrig, tempTree, indices, gridBoxR, nodeBoxR, nextIndex1 + 3, depth + 1, stats);
     else
         stats.updateLeaf(depth + 1, 0);
 }