Beispiel #1
0
 static bool canMergeWithRegion(rcRegion rega, rcRegion regb)
 {
     if (rega.areaType != regb.areaType)
         return false;
     int n = 0;
     for (int i = 0; i < rega.connections.Count; ++i)
     {
         if (rega.connections[i] == regb.id)
             n++;
     }
     if (n > 1)
         return false;
     for (int i = 0; i < rega.floors.Count; ++i)
     {
         if (rega.floors[i] == regb.id)
             return false;
     }
     return true;
 }
Beispiel #2
0
    static bool filterSmallRegions(rcContext ctx, int minRegionArea, int mergeRegionSize,
							       ref ushort maxRegionId,
							       rcCompactHeightfield chf,
							       ushort[] srcReg)
    {
        int w = chf.width;
        int h = chf.height;

        int nreg = maxRegionId+1;

        rcRegion[] regions = new rcRegion[nreg];
        if (regions == null)
        {
            ctx.log(rcLogCategory.RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (" +nreg+ ").");
            return false;
        }

        // Construct regions
        for (int i = 0; i < nreg; ++i){
            regions[i] = new rcRegion((ushort) i);
        }

        // Find edge of a region and find connections around the contour.
        for (int y = 0; y < h; ++y)
        {
            for (int x = 0; x < w; ++x)
            {
                rcCompactCell c = chf.cells[x+y*w];
                for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
                {
                    ushort r = srcReg[i];
                    if (r == 0 || r >= nreg)
                        continue;

                    rcRegion reg = regions[r];
                    reg.spanCount++;

                    // Update floors.
                    for (int j = (int)c.index; j < ni; ++j)
                    {
                        if (i == j) continue;
                        ushort floorId = srcReg[j];
                        if (floorId == 0 || floorId >= nreg)
                            continue;
                        addUniqueFloorRegion(reg, floorId);
                    }

                    // Have found contour
                    if (reg.connections.Count > 0)
                        continue;

                    reg.areaType = chf.areas[i];

                    // Check if this cell is next to a border.
                    int ndir = -1;
                    for (int dir = 0; dir < 4; ++dir)
                    {
                        if (isSolidEdge(chf, srcReg, x, y, i, dir))
                        {
                            ndir = dir;
                            break;
                        }
                    }

                    if (ndir != -1)
                    {
                        // The cell is at border.
                        // Walk around the contour to find all the neighbours.
                        walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
                    }
                }
            }
        }

        // Remove too small regions.
        List<int> stack = new List<int>();//(32);
        List<int> trace= new List<int>();//(32);
        stack.Capacity = 32;
        trace.Capacity = 32;
        for (int i = 0; i < nreg; ++i)
        {
            rcRegion reg = regions[i];
            if (reg.id == 0 || (reg.id & RC_BORDER_REG) != 0)
                continue;
            if (reg.spanCount == 0)
                continue;
            if (reg.visited)
                continue;

            // Count the total size of all the connected regions.
            // Also keep track of the regions connects to a tile border.
            bool connectsToBorder = false;
            int spanCount = 0;
            stack.Clear();
            trace.Clear();

            reg.visited = true;
            stack.Add(i);

            while (stack.Count != 0)
            {
                // Pop
                int ri = rccsPop(stack);

                rcRegion creg = regions[ri];

                spanCount += creg.spanCount;
                trace.Add(ri);

                for (int j = 0; j < creg.connections.Count; ++j)
                {
                    if ((creg.connections[j] & RC_BORDER_REG) != 0)
                    {
                        connectsToBorder = true;
                        continue;
                    }
                    rcRegion neireg = regions[creg.connections[j]];
                    if (neireg.visited)
                        continue;
                    if (neireg.id == 0 || (neireg.id & RC_BORDER_REG) != 0)
                        continue;
                    // Visit
                    stack.Add(neireg.id);
                    neireg.visited = true;
                }
            }

            // If the accumulated regions size is too small, remove it.
            // Do not remove areas which connect to tile borders
            // as their size cannot be estimated correctly and removing them
            // can potentially remove necessary areas.
            if (spanCount < minRegionArea && !connectsToBorder)
            {
                // Kill all visited regions.
                for (int j = 0; j < trace.Count; ++j)
                {
                    regions[trace[j]].spanCount = 0;
                    regions[trace[j]].id = 0;
                }
            }
        }

        // Merge too small regions to neighbour regions.
        int mergeCount = 0 ;
        do
        {
            mergeCount = 0;
            for (int i = 0; i < nreg; ++i)
            {
                rcRegion reg = regions[i];
                if (reg.id == 0 || (reg.id & RC_BORDER_REG) != 0)
                    continue;
                if (reg.spanCount == 0)
                    continue;

                // Check to see if the region should be merged.
                if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
                    continue;

                // Small region with more than 1 connection.
                // Or region which is not connected to a border at all.
                // Find smallest neighbour region that connects to this one.
                int smallest = 0xfffffff;
                ushort mergeId = reg.id;
                for (int j = 0; j < reg.connections.Count; ++j)
                {
                    if ((reg.connections[j] & RC_BORDER_REG) != 0)
                        continue;
                    rcRegion mreg = regions[reg.connections[j]];
                    if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) != 0)
                        continue;
                    if (mreg.spanCount < smallest &&
                        canMergeWithRegion(reg, mreg) &&
                        canMergeWithRegion(mreg, reg))
                    {
                        smallest = mreg.spanCount;
                        mergeId = mreg.id;
                    }
                }
                // Found new id.
                if (mergeId != reg.id)
                {
                    ushort oldId = reg.id;
                    rcRegion target = regions[mergeId];

                    // Merge neighbours.
                    if (   mergeRegions(target, reg))
                    {
                        // Fixup regions pointing to current region.
                        for (int j = 0; j < nreg; ++j)
                        {

                            if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG) != 0)
                                continue;
                            // If another region was already merged into current region
                            // change the nid of the previous region too.
                            if (regions[j].id == oldId)
                                regions[j].id = mergeId;
                            // Replace the current region with the new one if the
                            // current regions is neighbour.
                            replaceNeighbour(regions[j], oldId, mergeId);
                        }
                        mergeCount++;
                    }

                }
            }
        }
        while (mergeCount > 0);

        // Compress region Ids.
        for (int i = 0; i < nreg; ++i)
        {
            regions[i].remap = false;
            if (regions[i].id == 0)
                continue;       // Skip nil regions.
            if ((regions[i].id & RC_BORDER_REG) != 0)
                continue;    // Skip external regions.
            regions[i].remap = true;
        }

        ushort regIdGen = 0;
        for (int i = 0; i < nreg; ++i)
        {
            if (!regions[i].remap)
                continue;
            ushort oldId = regions[i].id;
            ushort newId = ++regIdGen;
            for (int j = i; j < nreg; ++j)
            {
                if (regions[j].id == oldId)
                {
                    regions[j].id = newId;
                    regions[j].remap = false;
                }
            }
        }
        maxRegionId = regIdGen;

        // Remap regions.
        for (int i = 0; i < chf.spanCount; ++i)
        {
            if ((srcReg[i] & RC_BORDER_REG) == 0)
                srcReg[i] = regions[srcReg[i]].id;
        }

        return true;
    }
Beispiel #3
0
 static void addUniqueFloorRegion(rcRegion reg, int n)
 {
     for (int i = 0; i < reg.floors.Count; ++i)
         if (reg.floors[i] == n)
             return;
     reg.floors.Add(n);
 }
Beispiel #4
0
 static void replaceNeighbour(rcRegion reg, ushort oldId, ushort newId)
 {
     bool neiChanged = false;
     for (int i = 0; i < reg.connections.Count; ++i)
     {
         if (reg.connections[i] == oldId)
         {
             reg.connections[i] = newId;
             neiChanged = true;
         }
     }
     for (int i = 0; i < reg.floors.Count; ++i)
     {
         if (reg.floors[i] == oldId)
             reg.floors[i] = newId;
     }
     if (neiChanged)
         removeAdjacentNeighbours(reg);
 }
Beispiel #5
0
 static void removeAdjacentNeighbours(rcRegion reg)
 {
     // Remove adjacent duplicates.
     for (int i = 0; i < reg.connections.Count && reg.connections.Count > 1; )
     {
         int ni = (i+1) % reg.connections.Count;
         if (reg.connections[i] == reg.connections[ni])
         {
             // Remove duplicate
             for (int j = i; j < reg.connections.Count-1; ++j){
                 reg.connections[j] = reg.connections[j+1];
             }
             rccsPop(reg.connections);
         }
         else
             ++i;
     }
 }
Beispiel #6
0
    static bool mergeRegions(rcRegion rega, rcRegion regb)
    {
        ushort aid = rega.id;
        ushort bid = regb.id;

        // Duplicate current neighbourhood.
        List<int> acon = new List<int>();

        for (int i = 0; i < rega.connections.Count; ++i)
            acon.Add( rega.connections[i] );
        List<int> bcon = regb.connections;

        // Find insertion point on A.
        int insa = -1;
        for (int i = 0; i < acon.Count; ++i)
        {
            if (acon[i] == bid)
            {
                insa = i;
                break;
            }
        }
        if (insa == -1)
            return false;

        // Find insertion point on B.
        int insb = -1;
        for (int i = 0; i < bcon.Count; ++i)
        {
            if (bcon[i] == aid)
            {
                insb = i;
                break;
            }
        }
        if (insb == -1)
            return false;

        // Merge neighbours.
        rega.connections.Clear();
        for (int i = 0, ni = acon.Count; i < ni-1; ++i)
            rega.connections.Add(acon[(insa+1+i) % ni]);

        for (int i = 0, ni = bcon.Count; i < ni-1; ++i)
            rega.connections.Add(bcon[(insb+1+i) % ni]);

        removeAdjacentNeighbours(rega);

        for (int j = 0; j < regb.floors.Count; ++j)
            addUniqueFloorRegion(rega, regb.floors[j]);
        rega.spanCount += regb.spanCount;
        regb.spanCount = 0;
        regb.connections.Clear();

        return true;
    }
Beispiel #7
0
 static bool isRegionConnectedToBorder(rcRegion reg)
 {
     // Region is connected to border if
     // one of the neighbours is null id.
     for (int i = 0; i < reg.connections.Count; ++i)
     {
         if (reg.connections[i] == 0)
             return true;
     }
     return false;
 }