Ejemplo n.º 1
0
        public static void CalculateDistanceField(CompactVoxelField field, NativeArray <ushort> output)
        {
            int wd = field.width * field.depth;

            // Mark boundary cells
            for (int z = 0; z < wd; z += field.width)
            {
                for (int x = 0; x < field.width; x++)
                {
                    CompactVoxelCell c = field.cells[x + z];

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        CompactVoxelSpan s = field.spans[i];

                        int numConnections = 0;
                        for (int d = 0; d < 4; d++)
                        {
                            if (s.GetConnection(d) != CompactVoxelField.NotConnected)
                            {
                                //This function (CalculateDistanceField) is used for both ErodeWalkableArea and by itself.
                                //The C++ recast source uses different code for those two cases, but I have found it works with one function
                                //the field.areaTypes[ni] will actually only be one of two cases when used from ErodeWalkableArea
                                //so it will have the same effect as
                                // if (area != UnwalkableArea) {
                                //This line is the one where the differ most

                                numConnections++;
                            }
                            else
                            {
                                break;
                            }
                        }

                        // TODO: Check initialization
                        output[i] = numConnections == 4 ? ushort.MaxValue : (ushort)0;
                    }
                }
            }

            // Grassfire transform
            // Pass 1

            for (int z = 0; z < wd; z += field.width)
            {
                for (int x = 0; x < field.width; x++)
                {
                    int cellIndex      = x + z;
                    CompactVoxelCell c = field.cells[cellIndex];

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        CompactVoxelSpan s = field.spans[i];
                        var dist           = (int)output[i];

                        if (s.GetConnection(0) != CompactVoxelField.NotConnected)
                        {
                            // (-1,0)
                            int neighbourCell = field.GetNeighbourIndex(cellIndex, 0);

                            int ni = (int)(field.cells[neighbourCell].index + s.GetConnection(0));

                            dist = math.min(dist, (int)output[ni] + 2);

                            CompactVoxelSpan ns = field.spans[ni];

                            if (ns.GetConnection(3) != CompactVoxelField.NotConnected)
                            {
                                // (-1,0) + (0,-1) = (-1,-1)
                                int neighbourCell2 = field.GetNeighbourIndex(neighbourCell, 3);

                                int nni = (int)(field.cells[neighbourCell2].index + ns.GetConnection(3));

                                dist = math.min(dist, (int)output[nni] + 3);
                            }
                        }

                        if (s.GetConnection(3) != CompactVoxelField.NotConnected)
                        {
                            // (0,-1)
                            int neighbourCell = field.GetNeighbourIndex(cellIndex, 3);

                            int ni = (int)(field.cells[neighbourCell].index + s.GetConnection(3));

                            dist = math.min(dist, (int)output[ni] + 2);

                            CompactVoxelSpan ns = field.spans[ni];

                            if (ns.GetConnection(2) != CompactVoxelField.NotConnected)
                            {
                                // (0,-1) + (1,0) = (1,-1)
                                int neighbourCell2 = field.GetNeighbourIndex(neighbourCell, 2);

                                int nni = (int)(field.cells[neighbourCell2].index + ns.GetConnection(2));

                                dist = math.min(dist, (int)output[nni] + 3);
                            }
                        }

                        output[i] = (ushort)dist;
                    }
                }
            }

            // Pass 2

            for (int z = wd - field.width; z >= 0; z -= field.width)
            {
                for (int x = field.width - 1; x >= 0; x--)
                {
                    int cellIndex      = x + z;
                    CompactVoxelCell c = field.cells[cellIndex];

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        CompactVoxelSpan s = field.spans[i];
                        var dist           = (int)output[i];

                        if (s.GetConnection(2) != CompactVoxelField.NotConnected)
                        {
                            // (-1,0)
                            int neighbourCell = field.GetNeighbourIndex(cellIndex, 2);

                            int ni = (int)(field.cells[neighbourCell].index + s.GetConnection(2));

                            dist = math.min(dist, (int)output[ni] + 2);

                            CompactVoxelSpan ns = field.spans[ni];

                            if (ns.GetConnection(1) != CompactVoxelField.NotConnected)
                            {
                                // (-1,0) + (0,-1) = (-1,-1)
                                int neighbourCell2 = field.GetNeighbourIndex(neighbourCell, 1);

                                int nni = (int)(field.cells[neighbourCell2].index + ns.GetConnection(1));

                                dist = math.min(dist, (int)output[nni] + 3);
                            }
                        }

                        if (s.GetConnection(1) != CompactVoxelField.NotConnected)
                        {
                            // (0,-1)
                            int neighbourCell = field.GetNeighbourIndex(cellIndex, 1);

                            int ni = (int)(field.cells[neighbourCell].index + s.GetConnection(1));

                            dist = math.min(dist, (int)output[ni] + 2);

                            CompactVoxelSpan ns = field.spans[ni];

                            if (ns.GetConnection(0) != CompactVoxelField.NotConnected)
                            {
                                // (0,-1) + (1,0) = (1,-1)
                                int neighbourCell2 = field.GetNeighbourIndex(neighbourCell, 0);

                                int nni = (int)(field.cells[neighbourCell2].index + ns.GetConnection(0));

                                dist = math.min(dist, (int)output[nni] + 3);
                            }
                        }

                        output[i] = (ushort)dist;
                    }
                }
            }

// #if ASTAR_DEBUGREPLAY && FALSE
//          DebugReplay.BeginGroup("Distance Field");
//          for (int z = wd-field.width; z >= 0; z -= field.width) {
//              for (int x = field.width-1; x >= 0; x--) {
//                  CompactVoxelCell c = field.cells[x+z];

//                  for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
//                      DebugReplay.DrawCube(CompactSpanToVector(x, z/field.width, i), Vector3.one*cellSize, new Color((float)output[i]/maxDist, (float)output[i]/maxDist, (float)output[i]/maxDist));
//                  }
//              }
//          }
//          DebugReplay.EndGroup();
// #endif
        }
Ejemplo n.º 2
0
        public static void BoxBlur(CompactVoxelField field, NativeArray <ushort> src, NativeArray <ushort> dst)
        {
            ushort thr = 20;

            int wd = field.width * field.depth;

            for (int z = wd - field.width; z >= 0; z -= field.width)
            {
                for (int x = field.width - 1; x >= 0; x--)
                {
                    int cellIndex      = x + z;
                    CompactVoxelCell c = field.cells[cellIndex];

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        CompactVoxelSpan s = field.spans[i];

                        ushort cd = src[i];

                        if (cd < thr)
                        {
                            dst[i] = cd;
                            continue;
                        }

                        int total = (int)cd;

                        for (int d = 0; d < 4; d++)
                        {
                            if (s.GetConnection(d) != CompactVoxelField.NotConnected)
                            {
                                var neighbourIndex = field.GetNeighbourIndex(cellIndex, d);
                                int ni             = (int)(field.cells[neighbourIndex].index + s.GetConnection(d));

                                total += (int)src[ni];

                                CompactVoxelSpan ns = field.spans[ni];

                                int d2 = (d + 1) & 0x3;

                                if (ns.GetConnection(d2) != CompactVoxelField.NotConnected)
                                {
                                    var neighbourIndex2 = field.GetNeighbourIndex(neighbourIndex, d2);

                                    int nni = (int)(field.cells[neighbourIndex2].index + ns.GetConnection(d2));
                                    total += (int)src[nni];
                                }
                                else
                                {
                                    total += cd;
                                }
                            }
                            else
                            {
                                total += cd * 2;
                            }
                        }
                        dst[i] = (ushort)((total + 5) / 9F);
                    }
                }
            }
        }
Ejemplo n.º 3
0
        /// <summary>Filters out or merges small regions.</summary>
        public void FilterSmallRegions(CompactVoxelField field, NativeArray <ushort> reg, int minRegionSize, int maxRegions)
        {
            // RelevantGraphSurface c = RelevantGraphSurface.Root;
            // Need to use ReferenceEquals because it might be called from another thread
            // bool anySurfaces = !RelevantGraphSurface.ReferenceEquals(c, null) && (relevantGraphSurfaceMode != RecastGraph.RelevantGraphSurfaceMode.DoNotRequire);
            bool anySurfaces = false;

            // Nothing to do here
            if (!anySurfaces && minRegionSize <= 0)
            {
                return;
            }

            var counter = new NativeArray <int>(maxRegions, Allocator.Temp);
            var bits    = new NativeArray <ushort>(maxRegions, Allocator.Temp, NativeArrayOptions.ClearMemory);

            for (int i = 0; i < counter.Length; i++)
            {
                counter[i] = -1;
            }

            int nReg = counter.Length;

            int wd = field.width * field.depth;

            // const int RelevantSurfaceSet = 1 << 1;
            const int BorderBit = 1 << 0;

            // Mark RelevantGraphSurfaces

            // If they can also be adjacent to tile borders, this will also include the BorderBit
            // int RelevantSurfaceCheck = RelevantSurfaceSet | ((relevantGraphSurfaceMode == RecastGraph.RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) ? BorderBit : 0x0);
            int RelevantSurfaceCheck = 0;

            // TODO
            // if (anySurfaces) {
            //  // Need to use ReferenceEquals because it might be called from another thread
            //  while (!RelevantGraphSurface.ReferenceEquals(c, null)) {
            //      int x, z;
            //      this.VectorToIndex(c.Position, out x, out z);

            //      // Check for out of bounds
            //      if (x >= 0 && z >= 0 && x < field.width && z < field.depth) {
            //          int y = (int)((c.Position.y - voxelOffset.y)/cellHeight);
            //          int rad = (int)(c.maxRange / cellHeight);

            //          CompactVoxelCell cell = field.cells[x+z*field.width];
            //          for (int i = (int)cell.index; i < cell.index+cell.count; i++) {
            //              CompactVoxelSpan s = field.spans[i];
            //              if (System.Math.Abs(s.y - y) <= rad && reg[i] != 0) {
            //                  bits[union_find_find(counter, (int)reg[i] & ~BorderReg)] |= RelevantSurfaceSet;
            //              }
            //          }
            //      }

            //      c = c.Next;
            //  }
            // }

            const ushort BorderReg = VoxelUtilityBurst.BorderReg;

            for (int z = 0, pz = 0; z < wd; z += field.width, pz++)
            {
                for (int x = 0; x < field.width; x++)
                {
                    CompactVoxelCell cell = field.cells[x + z];

                    for (int i = (int)cell.index; i < cell.index + cell.count; i++)
                    {
                        CompactVoxelSpan s = field.spans[i];

                        int r = (int)reg[i];

                        // Check if this is an unwalkable span
                        if ((r & ~BorderReg) == 0)
                        {
                            continue;
                        }

                        if (r >= nReg)                           //Probably border
                        {
                            bits[union_find_find(counter, r & ~BorderReg)] |= BorderBit;
                            continue;
                        }

                        int root = union_find_find(counter, r);
                        // Count this span
                        counter[root]--;

                        // Iterate through all neighbours of the span.
                        for (int dir = 0; dir < 4; dir++)
                        {
                            if (s.GetConnection(dir) == CompactVoxelField.NotConnected)
                            {
                                continue;
                            }

                            int nx = x + VoxelUtilityBurst.DX[dir];
                            int nz = z + VoxelUtilityBurst.DZ[dir] * field.width;

                            int ni = (int)field.cells[nx + nz].index + s.GetConnection(dir);

                            int r2 = (int)reg[ni];

                            // Check if the other span belongs to a different region and is walkable
                            if (r != r2 && (r2 & ~BorderReg) != 0)
                            {
                                if ((r2 & BorderReg) != 0)
                                {
                                    // If it's a border region we just mark the current region as being adjacent to a border
                                    bits[root] |= BorderBit;
                                }
                                else
                                {
                                    // Join the adjacent region with this region.
                                    union_find_union(counter, root, r2);
                                }
                                //counter[r] = minRegionSize;
                            }
                        }
                        //counter[r]++;
                    }
                }
            }

            // Propagate bits to the region group representative using the union find structure
            for (int i = 0; i < counter.Length; i++)
            {
                bits[union_find_find(counter, i)] |= bits[i];
            }

            for (int i = 0; i < counter.Length; i++)
            {
                int ctr = union_find_find(counter, i);

                // Check if the region is adjacent to border.
                // Mark it as being just large enough to always be included in the graph.
                if ((bits[ctr] & BorderBit) != 0)
                {
                    counter[ctr] = -minRegionSize - 2;
                }

                // Not in any relevant surface
                // or it is adjacent to a border (see RelevantSurfaceCheck)
                if (anySurfaces && (bits[ctr] & RelevantSurfaceCheck) == 0)
                {
                    counter[ctr] = -1;
                }
            }

            for (int i = 0; i < reg.Length; i++)
            {
                int r = (int)reg[i];
                // Ignore border regions
                if (r >= nReg)
                {
                    continue;
                }

                // If the region group is too small then make the span unwalkable
                if (counter[union_find_find(counter, r)] >= -minRegionSize - 1)
                {
                    reg[i] = 0;
                }
            }
        }
Ejemplo n.º 4
0
        public static bool FloodRegion(int x, int z, int i, uint level, ushort r,
                                       CompactVoxelField field,
                                       NativeArray <ushort> distanceField,
                                       NativeArray <ushort> srcReg,
                                       NativeArray <ushort> srcDist,
                                       NativeArray <Int3> stack,
                                       NativeArray <int> flags,
                                       NativeArray <bool> closed)
        {
            int area = field.areaTypes[i];

            // Flood f mark region.
            int stackSize = 1;

            stack[0] = new Int3 {
                x = x,
                y = i,
                z = z,
            };

            srcReg[i]  = r;
            srcDist[i] = 0;

            int lev = (int)(level >= 2 ? level - 2 : 0);

            int count = 0;

            // Store these in local variables (for performance, avoids an extra indirection)
            var compactCells = field.cells;
            var compactSpans = field.spans;
            var areaTypes    = field.areaTypes;

            while (stackSize > 0)
            {
                stackSize--;
                var c = stack[stackSize];
                //Similar to the Pop operation of an array, but Pop is not implemented in List<>
                int ci = c.y;
                int cx = c.x;
                int cz = c.z;

                CompactVoxelSpan cs = compactSpans[ci];

                //Debug.DrawRay (ConvertPosition(cx,cz,ci),Vector3.up, Color.cyan);

                // Check if any of the neighbours already have a valid region set.
                ushort ar = 0;

                // Loop through four neighbours
                // then check one neighbour of the neighbour
                // to get the diagonal neighbour
                for (int dir = 0; dir < 4; dir++)
                {
                    // 8 connected
                    if (cs.GetConnection(dir) != CompactVoxelField.NotConnected)
                    {
                        int ax = cx + VoxelUtilityBurst.DX[dir];
                        int az = cz + VoxelUtilityBurst.DZ[dir] * field.width;

                        int ai = (int)compactCells[ax + az].index + cs.GetConnection(dir);

                        if (areaTypes[ai] != area)
                        {
                            continue;
                        }

                        ushort nr = srcReg[ai];

                        if ((nr & VoxelUtilityBurst.BorderReg) == VoxelUtilityBurst.BorderReg)                         // Do not take borders into account.
                        {
                            continue;
                        }

                        if (nr != 0 && nr != r)
                        {
                            ar = nr;
                            // Found a valid region, skip checking the rest
                            break;
                        }

                        // Rotate dir 90 degrees
                        int dir2       = (dir + 1) & 0x3;
                        var neighbour2 = compactSpans[ai].GetConnection(dir2);
                        // Check the diagonal connection
                        if (neighbour2 != CompactVoxelField.NotConnected)
                        {
                            int ax2 = ax + VoxelUtilityBurst.DX[dir2];
                            int az2 = az + VoxelUtilityBurst.DZ[dir2] * field.width;

                            int ai2 = (int)compactCells[ax2 + az2].index + neighbour2;

                            if (areaTypes[ai2] != area)
                            {
                                continue;
                            }

                            ushort nr2 = srcReg[ai2];

                            if ((nr2 & VoxelUtilityBurst.BorderReg) == VoxelUtilityBurst.BorderReg)                             // Do not take borders into account.
                            {
                                continue;
                            }

                            if (nr2 != 0 && nr2 != r)
                            {
                                ar = nr2;
                                // Found a valid region, skip checking the rest
                                break;
                            }
                        }
                    }
                }

                if (ar != 0)
                {
                    srcReg[ci]  = 0;
                    srcDist[ci] = (ushort)0xFFFF;
                    continue;
                }
                count++;
                if (closed != null)
                {
                    closed[ci] = true;
                }


                // Expand neighbours.
                for (int dir = 0; dir < 4; ++dir)
                {
                    if (cs.GetConnection(dir) != CompactVoxelField.NotConnected)
                    {
                        int ax = cx + VoxelUtilityBurst.DX[dir];
                        int az = cz + VoxelUtilityBurst.DZ[dir] * field.width;
                        int ai = (int)compactCells[ax + az].index + cs.GetConnection(dir);

                        if (areaTypes[ai] != area)
                        {
                            continue;
                        }

                        if (srcReg[ai] == 0)
                        {
                            if (distanceField[ai] >= lev && flags[ai] == 0)
                            {
                                srcReg[ai]  = r;
                                srcDist[ai] = 0;

                                stack[stackSize] = new Int3 {
                                    x = ax,
                                    y = ai,
                                    z = az,
                                };
                                stackSize++;
                            }
                            else if (flags != null)
                            {
                                flags[ai]   = r;
                                srcDist[ai] = 2;
                            }
                        }
                    }
                }
            }


            return(count > 0);
        }
Ejemplo n.º 5
0
        public int GetCornerHeight(int x, int z, int i, int dir, ref bool isBorderVertex)
        {
            CompactVoxelSpan s = field.spans[i];

            int ch = (int)s.y;

            //dir + clockwise direction
            int dirp = (dir + 1) & 0x3;

            //int dirp = (dir+3) & 0x3;

            // TODO: Optimize
            var regs = new NativeArray <uint>(4, Allocator.Temp);

            regs[0] = (uint)field.spans[i].reg | ((uint)field.areaTypes[i] << 16);

            if (s.GetConnection(dir) != CompactVoxelField.NotConnected)
            {
                int neighbourCell = field.GetNeighbourIndex(x + z, dir);
                int ni            = (int)field.cells[neighbourCell].index + s.GetConnection(dir);

                CompactVoxelSpan ns = field.spans[ni];

                ch      = System.Math.Max(ch, (int)ns.y);
                regs[1] = (uint)ns.reg | ((uint)field.areaTypes[ni] << 16);

                if (ns.GetConnection(dirp) != CompactVoxelField.NotConnected)
                {
                    int neighbourCell2 = field.GetNeighbourIndex(neighbourCell, dirp);
                    int ni2            = (int)field.cells[neighbourCell2].index + ns.GetConnection(dirp);

                    CompactVoxelSpan ns2 = field.spans[ni2];

                    ch      = System.Math.Max(ch, (int)ns2.y);
                    regs[2] = (uint)ns2.reg | ((uint)field.areaTypes[ni2] << 16);
                }
            }

            if (s.GetConnection(dirp) != CompactVoxelField.NotConnected)
            {
                int neighbourCell = field.GetNeighbourIndex(x + z, dirp);
                int ni            = (int)field.cells[neighbourCell].index + s.GetConnection(dirp);

                CompactVoxelSpan ns = field.spans[ni];

                ch      = System.Math.Max(ch, (int)ns.y);
                regs[3] = (uint)ns.reg | ((uint)field.areaTypes[ni] << 16);

                if (ns.GetConnection(dir) != CompactVoxelField.NotConnected)
                {
                    int neighbourCell2 = field.GetNeighbourIndex(neighbourCell, dir);
                    int ni2            = (int)field.cells[neighbourCell2].index + ns.GetConnection(dir);

                    CompactVoxelSpan ns2 = field.spans[ni2];

                    ch      = System.Math.Max(ch, (int)ns2.y);
                    regs[2] = (uint)ns2.reg | ((uint)field.areaTypes[ni2] << 16);
                }
            }

            // Check if the vertex is special edge vertex, these vertices will be removed later.
            for (int j = 0; j < 4; ++j)
            {
                int a = j;
                int b = (j + 1) & 0x3;
                int c = (j + 2) & 0x3;
                int d = (j + 3) & 0x3;

                // The vertex is a border vertex there are two same exterior cells in a row,
                // followed by two interior cells and none of the regions are out of bounds.
                bool twoSameExts  = (regs[a] & regs[b] & VoxelUtilityBurst.BorderReg) != 0 && regs[a] == regs[b];
                bool twoInts      = ((regs[c] | regs[d]) & VoxelUtilityBurst.BorderReg) == 0;
                bool intsSameArea = (regs[c] >> 16) == (regs[d] >> 16);
                bool noZeros      = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
                if (twoSameExts && twoInts && intsSameArea && noZeros)
                {
                    isBorderVertex = true;
                    break;
                }
            }

            return(ch);
        }
Ejemplo n.º 6
0
        public void WalkContour(int x, int z, int i, NativeArray <ushort> flags, NativeList <int> verts)
        {
            // Choose the first non-connected edge
            int dir = 0;

            while ((flags[i] & (ushort)(1 << dir)) == 0)
            {
                dir++;
            }

            int startDir = dir;
            int startI   = i;

            int area = field.areaTypes[i];

            int iter = 0;


            while (iter++ < 40000)
            {
                //Are we facing a region edge
                if ((flags[i] & (ushort)(1 << dir)) != 0)
                {
                    //Choose the edge corner
                    bool isBorderVertex = false;
                    bool isAreaBorder   = false;

                    int px = x;
                    int py = GetCornerHeight(x, z, i, dir, ref isBorderVertex);
                    int pz = z;

                    switch (dir)
                    {
                    case 0: pz += field.width;; break;

                    case 1: px++; pz += field.width; break;

                    case 2: px++; break;
                    }

                    /*case 1: px++; break;
                     *  case 2: px++; pz += field.width; break;
                     *  case 3: pz += field.width; break;
                     */

                    int r = 0;
                    CompactVoxelSpan s = field.spans[i];

                    if (s.GetConnection(dir) != CompactVoxelField.NotConnected)
                    {
                        int ni = (int)field.cells[field.GetNeighbourIndex(x + z, dir)].index + s.GetConnection(dir);
                        r = (int)field.spans[ni].reg;

                        if (area != field.areaTypes[ni])
                        {
                            isAreaBorder = true;
                        }
                    }

                    if (isBorderVertex)
                    {
                        r |= VoxelUtilityBurst.RC_BORDER_VERTEX;
                    }
                    if (isAreaBorder)
                    {
                        r |= VoxelUtilityBurst.RC_AREA_BORDER;
                    }

                    verts.Add(px);
                    verts.Add(py);
                    verts.Add(pz);
                    verts.Add(r);

                    //Debug.DrawRay (previousPos,new Vector3 ((dir == 1 || dir == 2) ? 1 : 0, 0, (dir == 0 || dir == 1) ? 1 : 0),Color.cyan);

                    flags[i] = (ushort)(flags[i] & ~(1 << dir));                     // Remove visited edges

                    // & 0x3 is the same as % 4 (for positive numbers)
                    dir = (dir + 1) & 0x3;                    // Rotate CW
                }
                else
                {
                    int ni = -1;
                    int nx = x + VoxelUtilityBurst.DX[dir];
                    int nz = z + VoxelUtilityBurst.DZ[dir] * field.width;

                    CompactVoxelSpan s = field.spans[i];

                    if (s.GetConnection(dir) != CompactVoxelField.NotConnected)
                    {
                        CompactVoxelCell nc = field.cells[nx + nz];
                        ni = (int)nc.index + s.GetConnection(dir);
                    }

                    if (ni == -1)
                    {
                        Debug.LogWarning("Degenerate triangles might have been generated.\n" +
                                         "Usually this is not a problem, but if you have a static level, try to modify the graph settings slightly to avoid this edge case.");
                        return;
                    }
                    x = nx;
                    z = nz;
                    i = ni;

                    // & 0x3 is the same as % 4 (modulo 4)
                    dir = (dir + 3) & 0x3;                      // Rotate CCW
                }

                if (startI == i && startDir == dir)
                {
                    break;
                }
            }
        }
Ejemplo n.º 7
0
        public void Execute()
        {
            outputContours.Clear();
            outputVerts.Clear();

            int w = field.width;
            int d = field.depth;

            int wd = w * d;

            //cset.bounds = field.bounds;


            const ushort BorderReg = VoxelUtilityBurst.BorderReg;

            //cset.nconts = 0;

            //NOTE: This array may contain any data, but since we explicitly set all data in it before we use it, it's OK.
            var flags = new NativeArray <ushort>(field.spans.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            // Mark boundaries. (@?)
            for (int z = 0; z < wd; z += field.width)
            {
                for (int x = 0; x < field.width; x++)
                {
                    CompactVoxelCell c = field.cells[x + z];

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        ushort           res = 0;
                        CompactVoxelSpan s   = field.spans[i];

                        if (s.reg == 0 || (s.reg & BorderReg) == BorderReg)
                        {
                            flags[i] = 0;
                            continue;
                        }

                        for (int dir = 0; dir < 4; dir++)
                        {
                            int r = 0;

                            if (s.GetConnection(dir) != CompactVoxelField.NotConnected)
                            {
                                int ni = (int)field.cells[field.GetNeighbourIndex(x + z, dir)].index + s.GetConnection(dir);
                                r = field.spans[ni].reg;
                            }

                            //@TODO - Why isn't this inside the previous IF
                            if (r == s.reg)
                            {
                                res |= (ushort)(1 << dir);
                            }
                        }

                        //Inverse, mark non connected edges.
                        flags[i] = (ushort)(res ^ 0xf);
                    }
                }
            }


            NativeList <int> verts      = new NativeList <int>(256, Allocator.Temp);
            NativeList <int> simplified = new NativeList <int>(64, Allocator.Temp);

            for (int z = 0; z < wd; z += field.width)
            {
                for (int x = 0; x < field.width; x++)
                {
                    CompactVoxelCell c = field.cells[x + z];

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        //CompactVoxelSpan s = field.spans[i];

                        if (flags[i] == 0 || flags[i] == 0xf)
                        {
                            flags[i] = 0;
                            continue;
                        }

                        int reg = field.spans[i].reg;

                        if (reg == 0 || (reg & BorderReg) == BorderReg)
                        {
                            continue;
                        }

                        int area = field.areaTypes[i];

                        verts.Clear();
                        simplified.Clear();

                        WalkContour(x, z, i, flags, verts);

                        SimplifyContour(verts, simplified, maxError, buildFlags);
                        RemoveDegenerateSegments(simplified);

                        VoxelContour contour = new VoxelContour();
                        contour.vertexStartIndex = outputVerts.Length;
                        outputVerts.AddRange(simplified);
// #if ASTAR_RECAST_INCLUDE_RAW_VERTEX_CONTOUR
//                      //Not used at the moment, just debug stuff
//                      contour.rverts = ClaimIntArr(verts.Length);
//                      for (int j = 0; j < verts.Length; j++) contour.rverts[j] = verts[j];
// #endif
                        contour.nverts = simplified.Length / 4;
                        contour.reg    = reg;
                        contour.area   = area;

                        outputContours.Add(contour);

                        // #if ASTARDEBUG
                        // for (int q = 0, j = (simplified.Length/4)-1; q < (simplified.Length/4); j = q, q++) {
                        //  int i4 = q*4;
                        //  int j4 = j*4;

                        //  Vector3 p1 = Vector3.Scale(
                        //      new Vector3(
                        //          simplified[i4+0],
                        //          simplified[i4+1],
                        //          (simplified[i4+2]/(float)field.width)
                        //          ),
                        //      cellScale)
                        //               +voxelOffset;

                        //  Vector3 p2 = Vector3.Scale(
                        //      new Vector3(
                        //          simplified[j4+0],
                        //          simplified[j4+1],
                        //          (simplified[j4+2]/(float)field.width)
                        //          )
                        //      , cellScale)
                        //               +voxelOffset;


                        //  if (CalcAreaOfPolygon2D(contour.verts, contour.nverts) > 0) {
                        //      Debug.DrawLine(p1, p2, AstarMath.IntToColor(reg, 0.5F));
                        //  } else {
                        //      Debug.DrawLine(p1, p2, Color.red);
                        //  }
                        // }
                        // #endif
                    }
                }
            }

            verts.Dispose();
            simplified.Dispose();



            // Check and merge droppings.
            // Sometimes the previous algorithms can fail and create several outputContours
            // per area. This pass will try to merge the holes into the main region.
            for (int i = 0; i < outputContours.Length; i++)
            {
                VoxelContour cont = outputContours[i];
                // Check if the contour is would backwards.
                if (CalcAreaOfPolygon2D(outputVerts, cont.vertexStartIndex, cont.nverts) < 0)
                {
                    // Find another contour which has the same region ID.
                    int mergeIdx = -1;
                    for (int j = 0; j < outputContours.Length; j++)
                    {
                        if (i == j)
                        {
                            continue;
                        }
                        if (outputContours[j].nverts > 0 && outputContours[j].reg == cont.reg)
                        {
                            // Make sure the polygon is correctly oriented.
                            if (CalcAreaOfPolygon2D(outputVerts, outputContours[j].vertexStartIndex, outputContours[j].nverts) > 0)
                            {
                                mergeIdx = j;
                                break;
                            }
                        }
                    }
                    if (mergeIdx == -1)
                    {
                        // Debug.LogError("rcBuildContours: Could not find merge target for bad contour "+i+".");
                    }
                    else
                    {
                        // Debugging
                        // Debug.LogWarning ("Fixing contour");

                        VoxelContour mcont = outputContours[mergeIdx];
                        // Merge by closest points.
                        int ia = 0, ib = 0;
                        GetClosestIndices(outputVerts, mcont.vertexStartIndex, mcont.nverts, cont.vertexStartIndex, cont.nverts, ref ia, ref ib);

                        if (ia == -1 || ib == -1)
                        {
                            // Debug.LogWarning("rcBuildContours: Failed to find merge points for "+i+" and "+mergeIdx+".");
                            continue;
                        }


                        if (!MergeContours(outputVerts, ref mcont, ref cont, ia, ib))
                        {
                            //Debug.LogWarning("rcBuildContours: Failed to merge contours "+i+" and "+mergeIdx+".");
                            continue;
                        }

                        outputContours[mergeIdx] = mcont;
                        outputContours[i]        = cont;
                    }
                }
            }
        }