public void Execute()
        {
            int wd = field.width * field.depth;

            // Build voxel connections
            for (int z = 0, pz = 0; z < wd; z += field.width, pz++)
            {
                for (int x = 0; x < field.width; x++)
                {
                    CompactVoxelCell c = field.cells[x + z];

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

                        for (int d = 0; d < 4; d++)
                        {
                            int nx = x + VoxelUtilityBurst.DX[d];
                            int nz = z + VoxelUtilityBurst.DZ[d] * field.width;

                            if (nx < 0 || nz < 0 || nz >= wd || nx >= field.width)
                            {
                                continue;
                            }

                            CompactVoxelCell nc = field.cells[nx + nz];

                            for (int k = nc.index, nk = (int)(nc.index + nc.count); k < nk; k++)
                            {
                                CompactVoxelSpan ns = field.spans[k];

                                int bottom = System.Math.Max(s.y, ns.y);

                                int top = System.Math.Min((int)s.y + (int)s.h, (int)ns.y + (int)ns.h);

                                if ((top - bottom) >= voxelWalkableHeight && System.Math.Abs((int)ns.y - (int)s.y) <= voxelWalkableClimb)
                                {
                                    uint connIdx = (uint)k - (uint)nc.index;

                                    if (connIdx > CompactVoxelField.MaxLayers)
                                    {
                                        throw new System.Exception("Too many layers");
                                    }

                                    s.SetConnection(d, connIdx);
                                    break;
                                }
                            }
                        }

                        field.spans[i] = s;
                    }
                }
            }
        }
Пример #2
0
        void MarkRectWithRegion(int minx, int maxx, int minz, int maxz, ushort region, NativeArray <ushort> srcReg)
        {
            int md = maxz * field.width;

            for (int z = minz * field.width; z < md; z += field.width)
            {
                for (int x = minx; x < maxx; x++)
                {
                    CompactVoxelCell c = field.cells[z + x];

                    for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; i++)
                    {
                        if (field.areaTypes[i] != CompactVoxelField.UnwalkableArea)
                        {
                            srcReg[i] = region;
                        }
                    }
                }
            }
        }
Пример #3
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);
                    }
                }
            }
        }
Пример #4
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
        }
Пример #5
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;
                }
            }
        }
Пример #6
0
        public void Execute()
        {
            srcQue.Clear();
            dstQue.Clear();

            /*System.Diagnostics.Stopwatch w0 = new System.Diagnostics.Stopwatch();
             * System.Diagnostics.Stopwatch w1 = new System.Diagnostics.Stopwatch();
             * System.Diagnostics.Stopwatch w2 = new System.Diagnostics.Stopwatch();
             * System.Diagnostics.Stopwatch w3 = new System.Diagnostics.Stopwatch();
             * System.Diagnostics.Stopwatch w4 = new System.Diagnostics.Stopwatch();
             * System.Diagnostics.Stopwatch w5 = new System.Diagnostics.Stopwatch();
             * w3.Start();*/

            int w         = field.width;
            int d         = field.depth;
            int wd        = w * d;
            int spanCount = field.spans.Length;

            int expandIterations = 8;

            var srcReg    = new NativeArray <ushort>(spanCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var srcDist   = new NativeArray <ushort>(spanCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var closed    = new NativeArray <bool>(spanCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var spanFlags = new NativeArray <int>(spanCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var stack     = new NativeArray <Int3>(spanCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            // The array pool arrays may contain arbitrary data. We need to zero it out.
            for (int i = 0; i < spanCount; i++)
            {
                srcReg[i]    = (ushort)0;
                srcDist[i]   = (ushort)0xFFFF;
                closed[i]    = false;
                spanFlags[i] = 0;
            }

            var          spanDistances = distanceField;
            var          areaTypes     = field.areaTypes;
            var          compactCells  = field.cells;
            const ushort BorderReg     = VoxelUtilityBurst.BorderReg;

            ushort regionId = 2;

            MarkRectWithRegion(0, borderSize, 0, d, (ushort)(regionId | BorderReg), srcReg);    regionId++;
            MarkRectWithRegion(w - borderSize, w, 0, d, (ushort)(regionId | BorderReg), srcReg);    regionId++;
            MarkRectWithRegion(0, w, 0, borderSize, (ushort)(regionId | BorderReg), srcReg);    regionId++;
            MarkRectWithRegion(0, w, d - borderSize, d, (ushort)(regionId | BorderReg), srcReg);    regionId++;

            // TODO: Can be optimized
            int maxDistance = 0;

            for (int i = 0; i < distanceField.Length; i++)
            {
                maxDistance = math.max((int)distanceField[i], maxDistance);
            }

            // A distance is 2 to an adjacent span and 1 for a diagonally adjacent one.
            NativeArray <int> sortedSpanCounts = new NativeArray <int>((maxDistance) / 2 + 1, Allocator.Temp);

            for (int i = 0; i < field.spans.Length; i++)
            {
                // Do not take borders or unwalkable spans into account.
                if ((srcReg[i] & BorderReg) == BorderReg || areaTypes[i] == CompactVoxelField.UnwalkableArea)
                {
                    continue;
                }

                sortedSpanCounts[distanceField[i] / 2]++;
            }

            var distanceIndexOffsets = new NativeArray <int>(sortedSpanCounts.Length, Allocator.Temp);

            for (int i = 1; i < distanceIndexOffsets.Length; i++)
            {
                distanceIndexOffsets[i] = distanceIndexOffsets[i - 1] + sortedSpanCounts[i - 1];
            }
            var totalRelevantSpans = distanceIndexOffsets[distanceIndexOffsets.Length - 1] + sortedSpanCounts[sortedSpanCounts.Length - 1];

            var bucketSortedSpans = new NativeArray <Int3>(totalRelevantSpans, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            // Bucket sort the spans based on distance
            for (int z = 0, pz = 0; z < wd; z += w, pz++)
            {
                for (int x = 0; x < field.width; x++)
                {
                    CompactVoxelCell c = compactCells[z + x];

                    for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; i++)
                    {
                        // Do not take borders or unwalkable spans into account.
                        if ((srcReg[i] & BorderReg) == BorderReg || areaTypes[i] == CompactVoxelField.UnwalkableArea)
                        {
                            continue;
                        }

                        int distIndex = distanceField[i] / 2;
                        bucketSortedSpans[distanceIndexOffsets[distIndex]++] = new Int3(x, i, z);
                    }
                }
            }
            if (distanceIndexOffsets[distanceIndexOffsets.Length - 1] != totalRelevantSpans)
            {
                throw new System.Exception("Unexpected span count");
            }

            // Go through spans in reverse order (i.e largest distances first)
            for (int distIndex = sortedSpanCounts.Length - 1; distIndex >= 0; distIndex--)
            {
                var level        = (uint)distIndex * 2;
                var spansAtLevel = sortedSpanCounts[distIndex];
                for (int i = 0; i < spansAtLevel; i++)
                {
                    // Go through the spans stored in bucketSortedSpans for this distance index.
                    // Note that distanceIndexOffsets[distIndex] will point to the element after the end of the group of spans.
                    // There is no particular reason for this, the code just turned out to be a bit simpler to implemen that way.
                    var spanInfo  = bucketSortedSpans[distanceIndexOffsets[distIndex] - i - 1];
                    int spanIndex = spanInfo.y;

                    // This span is adjacent to a region, so we should start the BFS search from it
                    if (spanFlags[spanIndex] != 0 && srcReg[spanIndex] == 0)
                    {
                        srcReg[spanIndex] = (ushort)spanFlags[spanIndex];
                        srcQue.Enqueue(spanInfo);
                        closed[spanIndex] = true;
                    }
                }

                // Expand a few iterations out from every known node
                for (int expansionIteration = 0; expansionIteration < expandIterations && srcQue.Count > 0; expansionIteration++)
                {
                    while (srcQue.Count > 0)
                    {
                        Int3 spanInfo = srcQue.Dequeue();
                        var  area     = areaTypes[spanInfo.y];
                        var  span     = field.spans[spanInfo.y];
                        var  region   = srcReg[spanInfo.y];
                        closed[spanInfo.y] = true;
                        ushort nextDist = (ushort)(srcDist[spanInfo.y] + 2);

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

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

                            int ni = (int)compactCells[nx + nz].index + neighbour;

                            if ((srcReg[ni] & BorderReg) == BorderReg)                             // Do not take borders into account.
                            {
                                continue;
                            }

                            // Do not combine different area types
                            if (area == areaTypes[ni])
                            {
                                if (nextDist < srcDist[ni])
                                {
                                    if (spanDistances[ni] < level)
                                    {
                                        srcDist[ni]   = nextDist;
                                        spanFlags[ni] = region;
                                    }
                                    else if (!closed[ni])
                                    {
                                        srcDist[ni] = nextDist;
                                        if (srcReg[ni] == 0)
                                        {
                                            dstQue.Enqueue(new Int3(nx, ni, nz));
                                        }
                                        srcReg[ni] = region;
                                    }
                                }
                            }
                        }
                    }
                    Util.Memory.Swap(ref srcQue, ref dstQue);
                }

                // Find the first span that has not been seen yet and start a new region that expands from there
                for (int i = 0; i < spansAtLevel; i++)
                {
                    var info = bucketSortedSpans[distanceIndexOffsets[distIndex] - i - 1];
                    if (srcReg[info.y] == 0)
                    {
                        if (!FloodRegion(info.x, info.z, info.y, level, regionId, field, distanceField, srcReg, srcDist, stack, spanFlags, closed))
                        {
                            // The starting voxel was already adjacent to an existing region so we skip flooding it.
                            // It will be visited in the next area expansion.
                        }
                        else
                        {
                            regionId++;
                        }
                    }
                }
            }

            var maxRegions = regionId;

            // Filter out small regions.
            FilterSmallRegions(field, srcReg, minRegionSize, maxRegions);

            // Write the result out.
            for (int i = 0; i < spanCount; i++)
            {
                var span = field.spans[i];
                span.reg       = srcReg[i];
                field.spans[i] = span;
            }

            // TODO:
            // field.maxRegions = maxRegions;

// #if ASTAR_DEBUGREPLAY
//          DebugReplay.BeginGroup("Regions");
//          for (int z = 0, pz = 0; z < wd; z += field.width, pz++) {
//              for (int x = 0; x < field.width; x++) {
//                  CompactVoxelCell c = field.cells[x+z];
//                  for (int i = (int)c.index; i < c.index+c.count; i++) {
//                      CompactVoxelSpan s = field.spans[i];
//                      DebugReplay.DrawCube(CompactSpanToVector(x, pz, i), UnityEngine.Vector3.one*cellSize, AstarMath.IntToColor(s.reg, 1.0f));
//                  }
//              }
//          }

//          DebugReplay.EndGroup();

//          int maxDist = 0;
//          for (int i = 0; i < srcDist.Length; i++) if (srcDist[i] != 0xFFFF) maxDist = Mathf.Max(maxDist, srcDist[i]);

//          DebugReplay.BeginGroup("Distances");
//          for (int z = 0, pz = 0; z < wd; z += field.width, pz++) {
//              for (int x = 0; x < field.width; x++) {
//                  CompactVoxelCell c = field.cells[x+z];
//                  for (int i = (int)c.index; i < c.index+c.count; i++) {
//                      CompactVoxelSpan s = field.spans[i];
//                      float f = (float)srcDist[i]/maxDist;
//                      DebugReplay.DrawCube(CompactSpanToVector(x, z/field.width, i), Vector3.one*cellSize, new Color(f, f, f));
//                  }
//              }
//          }

//          DebugReplay.EndGroup();
// #endif
        }
Пример #7
0
        public void BuildFromLinkedField(LinkedVoxelField field)
        {
            int idx = 0;

            Assert.AreEqual(this.width, field.width);
            Assert.AreEqual(this.depth, field.depth);

            this.depth = field.depth;

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

            int spanCount = field.GetSpanCount();

            spans.Resize(spanCount, NativeArrayOptions.UninitializedMemory);
            areaTypes.Resize(spanCount, NativeArrayOptions.UninitializedMemory);
            cells.Resize(wd, NativeArrayOptions.UninitializedMemory);

            if (this.voxelWalkableHeight >= ushort.MaxValue)
            {
                throw new System.Exception("Too high walkable height to guarantee correctness. Increase voxel height or lower walkable height.");
            }

            var linkedSpans = field.linkedSpans;

            for (int z = 0, pz = 0; z < wd; z += w, pz++)
            {
                for (int x = 0; x < w; x++)
                {
                    int spanIndex = x + z;
                    if (linkedSpans[spanIndex].bottom == LinkedVoxelField.InvalidSpanValue)
                    {
                        cells[x + z] = new CompactVoxelCell(0, 0);
                        continue;
                    }

                    int index = idx;
                    int count = 0;

                    while (spanIndex != -1)
                    {
                        if (linkedSpans[spanIndex].area != UnwalkableArea)
                        {
                            int bottom = (int)linkedSpans[spanIndex].top;
                            int next   = linkedSpans[spanIndex].next;
                            int top    = next != -1 ? (int)linkedSpans[next].bottom : VoxelArea.MaxHeightInt;

                            // TODO: Why is top-bottom clamped to a ushort range?
                            spans[idx]     = new CompactVoxelSpan((ushort)math.min(bottom, ushort.MaxValue), (uint)math.min(top - bottom, ushort.MaxValue));
                            areaTypes[idx] = linkedSpans[spanIndex].area;
                            idx++;
                            count++;
                        }
                        spanIndex = linkedSpans[spanIndex].next;
                    }

                    cells[x + z] = new CompactVoxelCell(index, count);
                }
            }

            if (idx != spanCount)
            {
                throw new System.Exception("Found span count does not match expected value");
            }
        }
Пример #8
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;
                }
            }
        }
Пример #9
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;
                    }
                }
            }
        }