예제 #1
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);
                    }
                }
            }
        }
예제 #2
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;
                }
            }
        }
예제 #3
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
        }
예제 #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);
        }