Example #1
0
    public void UpdateDebugMesh(AstarPath astar)
    {
#if UNITY_3_5
        CreateDebugMesh();

        meshRenderer.transform.position   = Vector3.zero;
        meshRenderer.transform.localScale = Vector3.one;
#endif

        RecastGraph graph = target as RecastGraph;

        if (graph != null && graph.nodes != null && graph.vectorVertices != null)
        {
            if (navmeshRender == null)
            {
                navmeshRender = new Mesh();
            }

            navmeshRender.Clear();

            navmeshRender.vertices = graph.vectorVertices;

            int[]   tris    = new int[graph.nodes.Length * 3];
            Color[] vColors = new Color[graph.vectorVertices.Length];

            for (int i = 0; i < graph.nodes.Length; i++)
            {
                MeshNode node = graph.nodes[i] as MeshNode;
                tris[i * 3]     = node.v1;
                tris[i * 3 + 1] = node.v2;
                tris[i * 3 + 2] = node.v3;
                Color col = Mathfx.IntToColor(node.area, 1F);
                vColors[node.v1] = col;
                vColors[node.v2] = col;
                vColors[node.v3] = col;
            }
            navmeshRender.triangles = tris;
            navmeshRender.colors    = vColors;

            //meshRenderer.transform.position = graph.forcedBoundsCenter-graph.forcedBoundsSize*0.5F;
            //meshRenderer.transform.localScale = Int3.Precision*Voxelize.CellScale;
            navmeshRender.RecalculateNormals();
            navmeshRender.RecalculateBounds();

            if (navmeshMaterial == null)
            {
                navmeshMaterial = AssetDatabase.LoadAssetAtPath(AstarPathEditor.editorAssets + "/Materials/Navmesh.mat", typeof(Material)) as Material;
            }

            navmeshRender.hideFlags = HideFlags.HideAndDontSave;

#if UNITY_3_5
            navmeshRenderer.material = navmeshMaterial;
#endif
        }
    }
Example #2
0
    public void UpdateDebugMesh(AstarPath astar)
    {
        CreateDebugMesh();
        RecastGraph graph = target as RecastGraph;

        //if (graph.useCRecast) {
        meshRenderer.transform.position   = Vector3.zero;
        meshRenderer.transform.localScale = Vector3.one;
        //} else {
        //meshRenderer.transform.position = graph.forcedBounds.min;
        //meshRenderer.transform.localScale = new Vector3(graph.cellSize * 100, 1, graph.cellSize * 100);
        //}

        if (graph != null && graph.nodes != null && graph.vectorVertices != null)
        {
            navmeshRender.Clear();

            navmeshRender.vertices = graph.vectorVertices;
            //Vector3[] vs = new Vector3[graph.vertices.Length];
            //for (int i=0;i<graph.vertices.Length;i++) {
            //	vs[i] = graph.vertices[i];
            //}
            //navmeshRender.vertices = vs;

            int[]   tris    = new int[graph.nodes.Length * 3];
            Color[] vColors = new Color[graph.vectorVertices.Length];

            for (int i = 0; i < graph.nodes.Length; i++)
            {
                MeshNode node = graph.nodes[i] as MeshNode;
                tris[i * 3]     = node.v1;
                tris[i * 3 + 1] = node.v2;
                tris[i * 3 + 2] = node.v3;
                Color col = Mathfx.IntToColor(node.area, 1F);
                vColors[node.v1] = col;
                vColors[node.v2] = col;
                vColors[node.v3] = col;
            }
            navmeshRender.triangles = tris;
            navmeshRender.colors    = vColors;

            //meshRenderer.transform.position = graph.forcedBoundsCenter-graph.forcedBoundsSize*0.5F;
            //meshRenderer.transform.localScale = Int3.Precision*Voxelize.CellScale;
            navmeshRender.RecalculateNormals();
            navmeshRender.RecalculateBounds();

            if (navmeshMaterial == null)
            {
                navmeshMaterial = AssetDatabase.LoadAssetAtPath(AstarPathEditor.editorAssets + "/Materials/Navmesh.mat", typeof(Material)) as Material;
            }
            navmeshRenderer.material = navmeshMaterial;
        }
    }
        public void BuildRegions()
        {
            AstarProfiler.StartProfile("Build Regions");

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

            int wd = w * d;

            int expandIterations = 8;

            int spanCount = voxelArea.compactSpanCount;


            //new List<int>(1024);
            //List<int> visited = new List<int>(1024);



#if ASTAR_RECAST_BFS
            ushort[] srcReg = voxelArea.tmpUShortArr;
            if (srcReg.Length < spanCount)
            {
                srcReg = voxelArea.tmpUShortArr = new ushort[spanCount];
            }
            Pathfinding.Util.Memory.MemSet <ushort> (srcReg, 0, sizeof(ushort));
#else
            List <int> stack = Pathfinding.Util.ListPool <int> .Claim(1024);

            ushort[] srcReg  = new ushort[spanCount];
            ushort[] srcDist = new ushort[spanCount];
            ushort[] dstReg  = new ushort[spanCount];
            ushort[] dstDist = new ushort[spanCount];
#endif
            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++;



#if ASTAR_RECAST_BFS
            uint level = 0;

            List <Int3> basins = Pathfinding.Util.ListPool <Int3> .Claim(100);          //new List<Int3>();

            // Find "basins"
            DebugReplay.BeginGroup("Basins");
            for (int z = 0, pz = 0; z < wd; z += w, pz++)
            {
                for (int x = 0; x < voxelArea.width; x++)
                {
                    CompactVoxelCell c = voxelArea.compactCells[z + x];

                    for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; i++)
                    {
                        CompactVoxelSpan s        = voxelArea.compactSpans[i];
                        bool             anyBelow = false;

                        if (voxelArea.areaTypes[i] == UnwalkableArea || srcReg[i] != 0)
                        {
                            continue;
                        }

                        for (int dir = 0; dir < 4; dir++)
                        {
                            if (s.GetConnection(dir) != NotConnected)
                            {
                                int nx = x + voxelArea.DirectionX[dir];
                                int nz = z + voxelArea.DirectionZ[dir];

                                int ni2 = (int)(voxelArea.compactCells[nx + nz].index + s.GetConnection(dir));

                                if (voxelArea.dist[i] < voxelArea.dist[ni2])
                                {
                                    anyBelow = true;
                                    break;
                                }


                                //CompactVoxelSpan ns = voxelArea.compactSpans[ni];
                            }
                        }
                        if (!anyBelow)
                        {
                            //Debug.DrawRay (this.ConvertPosition(x,z,i),Vector3.down,Color.red);
                            DebugReplay.DrawCube(this.ConvertPosition(x, z, i), cellScale, Color.red);
                            basins.Add(new Int3(x, i, z));
                            //System.Console.WriteLine ("Basin at " + voxelArea.dist[i]);
                            level = System.Math.Max(level, voxelArea.dist[i]);
                        }
                    }
                }
            }

            //Start at maximum possible distance. & ~1 is rounding down to an even value
            level = (uint)((level + 1) & ~1);

            DebugReplay.EndGroup();
            DebugReplay.BeginGroup("BFS");

            List <Int3> st1 = Pathfinding.Util.ListPool <Int3> .Claim(300);

            List <Int3> st2 = Pathfinding.Util.ListPool <Int3> .Claim(300);

            //bool visited = new bool[voxelArea.compactSpanCount];

            for (;; level -= 2)
            {
                DebugReplay.BeginGroup("BFS " + level);
                //System.Console.WriteLine ("Starting level " + level + " with st1.Count = " + st1.Count);

                int ocount      = st1.Count;
                int expandCount = 0;

                if (ocount == 0)
                {
                    //int c = 0;
                    for (int q = 0; q < basins.Count; q++)
                    {
                        if (voxelArea.dist[basins[q].y] >= level)
                        {
                            DebugReplay.DrawCube(this.ConvertPosition(basins[q].x, basins[q].z, basins[q].y) + Vector3.up, cellScale, new Color(0, 1, 0, 0.5f));
                        }

                        if (srcReg[basins[q].y] == 0 && voxelArea.dist[basins[q].y] >= level)
                        {
                            srcReg[basins[q].y] = 1;
                            st1.Add(basins[q]);
                            //c++;
                            //visited[basins[i].y] = true;
                        }
                    }
                }

                for (int j = 0; j < st1.Count; j++)
                {
                    int x = st1[j].x;
                    int i = st1[j].y;
                    int z = st1[j].z;

                    ushort r = srcReg[i];

                    CompactVoxelSpan s = voxelArea.compactSpans[i];
                    int area           = voxelArea.areaTypes[i];

                    DebugReplay.DrawCube(this.ConvertPosition(x, z, i), cellScale, Mathfx.IntToColor(srcReg[i], 0.7f));

                    bool anyAbove = false;
                    for (int dir = 0; dir < 4; dir++)
                    {
                        if (s.GetConnection(dir) == NotConnected)
                        {
                            continue;
                        }

                        int nx = x + voxelArea.DirectionX[dir];
                        int nz = z + voxelArea.DirectionZ[dir];

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

                        if (area != voxelArea.areaTypes[ni])
                        {
                            continue;
                        }

                        if (voxelArea.dist[ni] < level)
                        {
                            anyAbove = true;
                            continue;
                        }


                        if (srcReg[ni] == 0)
                        {
                            bool same = false;
                            for (int v = (int)voxelArea.compactCells[nx + nz].index, vt = (int)voxelArea.compactCells[nx + nz].index + (int)voxelArea.compactCells[nx + nz].count; v < vt; v++)
                            {
                                if (srcReg[v] == srcReg[i])
                                {
                                    same = true;
                                    break;
                                }
                            }

                            if (!same)
                            {
                                //if ((int)srcDist[ni]+2 < (int)d2)
                                //{
                                //visited[i] = true;
                                srcReg[ni] = r;
                                //Debug.DrawRay (ConvertPosition(x,z,i),Vector3.up,Mathfx.IntToColor((int)level,0.6f));

                                //if (dstReg[ni] == 0)
                                st1.Add(new Int3(nx, ni, nz));
                                //d2 = (ushort)(srcDist[ni]+2);
                                //}
                            }
                        }
                    }

                    //Still on the edge
                    if (anyAbove)
                    {
                        st2.Add(st1[j]);
                    }

                    if (j == ocount - 1)
                    {
                        expandCount++;
                        ocount = st1.Count;

                        if (expandCount == 8 || j == st1.Count - 1)
                        {
                            //int c = 0;
                            for (int q = 0; q < basins.Count; q++)
                            {
                                if (voxelArea.dist[basins[q].y] >= level)
                                {
                                    DebugReplay.DrawCube(this.ConvertPosition(basins[q].x, basins[q].z, basins[q].y) + Vector3.up, cellScale, new Color(0, 1, 0, 0.5f));
                                }

                                if (srcReg[basins[q].y] == 0 && voxelArea.dist[basins[q].y] >= level)
                                {
                                    srcReg[basins[q].y] = 1;
                                    st1.Add(basins[q]);
                                    //c++;
                                    //visited[basins[i].y] = true;
                                }
                            }
                        }
                    }
                }



                List <Int3> tmpList = st1;
                st1 = st2;
                st2 = tmpList;
                st2.Clear();

                //System.Console.WriteLine ("Flooding basins");

                for (int i = 0; i < basins.Count; i++)
                {
                    if (srcReg[basins[i].y] == 1)
                    {
                        st2.Add(basins[i]);
                        FloodOnes(st2, srcReg, level, regionId); regionId++;
                        st2.Clear();
                    }
                }


                //System.Console.WriteLine ("Added " + c + " basins");
                DebugReplay.EndGroup();
                if (level == 0)
                {
                    break;
                }
            }

            DebugReplay.EndGroup();

            Pathfinding.Util.ListPool <Int3> .Release(st1);

            Pathfinding.Util.ListPool <Int3> .Release(st2);

            Pathfinding.Util.ListPool <Int3> .Release(basins);

            // Filter out small regions.
            voxelArea.maxRegions = regionId;

            FilterSmallRegions(srcReg, minRegionSize, voxelArea.maxRegions);


            // Write the result out.
            for (int i = 0; i < voxelArea.compactSpanCount; i++)
            {
                voxelArea.compactSpans[i].reg = srcReg[i];
            }
#else       /// ====== Use original recast code ====== //
            //Start at maximum possible distance. & ~1 is rounding down to an even value
            uint level = (uint)((voxelArea.maxDistance + 1) & ~1);

            int count = 0;



            while (level > 0)
            {
                level = level >= 2 ? level - 2 : 0;

                AstarProfiler.StartProfile("--Expand Regions");
                if (ExpandRegions(expandIterations, level, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
                {
                    ushort[] tmp = srcReg;
                    srcReg = dstReg;
                    dstReg = tmp;

                    tmp     = srcDist;
                    srcDist = dstDist;
                    dstDist = tmp;
                }

                AstarProfiler.EndProfile("--Expand Regions");

                AstarProfiler.StartProfile("--Mark Regions");


                // Mark new regions with IDs.
                // Find "basins"
                for (int z = 0, pz = 0; z < wd; z += w, pz++)
                {
                    for (int x = 0; x < voxelArea.width; x++)
                    {
                        CompactVoxelCell c = voxelArea.compactCells[z + x];

                        for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; i++)
                        {
                            if (voxelArea.dist[i] < level || srcReg[i] != 0 || voxelArea.areaTypes[i] == UnwalkableArea)
                            {
                                continue;
                            }

                            if (FloodRegion(x, z, i, level, regionId, srcReg, srcDist, stack))
                            {
                                regionId++;
                            }
                        }
                    }
                }


                AstarProfiler.EndProfile("--Mark Regions");

                count++;

                //if (count == 10) {
                //	return;
                //}
            }

            if (ExpandRegions(expandIterations * 8, 0, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
            {
                ushort[] tmp = srcReg;
                srcReg = dstReg;
                dstReg = tmp;

                tmp     = srcDist;
                srcDist = dstDist;
                dstDist = tmp;
            }

            // Filter out small regions.
            voxelArea.maxRegions = regionId;

            FilterSmallRegions(srcReg, minRegionSize, voxelArea.maxRegions);

            // Write the result out.
            for (int i = 0; i < voxelArea.compactSpanCount; i++)
            {
                voxelArea.compactSpans[i].reg = srcReg[i];
            }

            Pathfinding.Util.ListPool <int> .Release(stack);


            /*
             * int sCount = voxelArea.GetSpanCount ();
             * Vector3[] debugPointsTop = new Vector3[sCount];
             * Vector3[] debugPointsBottom = new Vector3[sCount];
             * Color[] debugColors = new Color[sCount];
             *
             * int debugPointsCount = 0;
             * //int wd = voxelArea.width*voxelArea.depth;
             *
             * for (int z=0, pz = 0;z < wd;z += voxelArea.width, pz++) {
             *      for (int x=0;x < voxelArea.width;x++) {
             *
             *              Vector3 p = new Vector3(x,0,pz)*cellSize+forcedBounds.min;
             *
             *              //CompactVoxelCell c = voxelArea.compactCells[x+z];
             *              CompactVoxelCell c = voxelArea.compactCells[x+z];
             *              //if (c.count == 0) {
             *              //	Debug.DrawRay (p,Vector3.up,Color.red);
             *              //}
             *
             *              //for (int i=(int)c.index, ni = (int)(c.index+c.count);i<ni;i++)
             *
             *              for (int i = (int)c.index; i < c.index+c.count; i++) {
             *                      CompactVoxelSpan s = voxelArea.compactSpans[i];
             *                      //CompactVoxelSpan s = voxelArea.compactSpans[i];
             *
             *                      p.y = ((float)(s.y+0.1F))*cellHeight+forcedBounds.min.y;
             *
             *                      debugPointsTop[debugPointsCount] = p;
             *
             *                      p.y = ((float)s.y)*cellHeight+forcedBounds.min.y;
             *                      debugPointsBottom[debugPointsCount] = p;
             *
             *                      debugColors[debugPointsCount] = Pathfinding.Mathfx.IntToColor(s.reg,0.7f);//s.reg == 1 ? Color.green : (s.reg == 2 ? Color.yellow : Color.red);
             *                      debugPointsCount++;
             *
             *                      //Debug.DrawRay (p,Vector3.up*0.5F,Color.green);
             *              }
             *      }
             * }
             *
             * DebugUtility.DrawCubes (debugPointsTop,debugPointsBottom,debugColors, cellSize);*/
#endif
            AstarProfiler.EndProfile("Build Regions");
        }
Example #4
0
        public void BuildContours(float maxError, int maxEdgeLength, VoxelContourSet cset, int buildFlags)
        {
            AstarProfiler.StartProfile("Build Contours");

            AstarProfiler.StartProfile("- Init");
            int w = voxelArea.width;
            int d = voxelArea.depth;

            int wd = w * d;

            //cset.bounds = voxelArea.bounds;

            int maxContours = Mathf.Max(8 /*Max Regions*/, 8);


            //cset.conts = new VoxelContour[maxContours];
            List <VoxelContour> contours = new List <VoxelContour>(maxContours);

            AstarProfiler.EndProfile("- Init");
            AstarProfiler.StartProfile("- Mark Boundaries");

            //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.
            ushort[] flags = voxelArea.tmpUShortArr;
            if (flags.Length < voxelArea.compactSpanCount)
            {
                flags = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount];
            }

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

                    for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++)
                    {
                        ushort           res = 0;
                        CompactVoxelSpan s   = voxelArea.compactSpans[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) != NotConnected)
                            {
                                int nx = x + voxelArea.DirectionX[dir];
                                int nz = z + voxelArea.DirectionZ[dir];

                                int ni = (int)voxelArea.compactCells[nx + nz].index + s.GetConnection(dir);
                                r = voxelArea.compactSpans[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);
                    }
                }
            }

            AstarProfiler.EndProfile("- Mark Boundaries");

            AstarProfiler.StartProfile("- Simplify Contours");
            List <int> verts = Pathfinding.Util.ListPool <int> .Claim(256);         //new List<int> (256);

            List <int> simplified = Pathfinding.Util.ListPool <int> .Claim(64);     //new List<int> (64);

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

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

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

                        int reg = voxelArea.compactSpans[i].reg;

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

                        int area = voxelArea.areaTypes[i];

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

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

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

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

                        contours.Add(contour);

                                                #if ASTARDEBUG
                        for (int q = 0, j = (simplified.Count / 4) - 1; q < (simplified.Count / 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)voxelArea.width)
                                    ),
                                cellScale)
                                         + voxelOffset;

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


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

            Pathfinding.Util.ListPool <int> .Release(verts);

            Pathfinding.Util.ListPool <int> .Release(simplified);

            AstarProfiler.EndProfile("- Simplify Contours");

            AstarProfiler.StartProfile("- Fix Contours");

            // Check and merge droppings.
            // Sometimes the previous algorithms can fail and create several contours
            // per area. This pass will try to merge the holes into the main region.
            for (int i = 0; i < contours.Count; i++)
            {
                VoxelContour cont = contours[i];
                // Check if the contour is would backwards.
                if (CalcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
                {
                    // Find another contour which has the same region ID.
                    int mergeIdx = -1;
                    for (int j = 0; j < contours.Count; j++)
                    {
                        if (i == j)
                        {
                            continue;
                        }
                        if (contours[j].nverts > 0 && contours[j].reg == cont.reg)
                        {
                            // Make sure the polygon is correctly oriented.
                            if (CalcAreaOfPolygon2D(contours[j].verts, contours[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 = contours[mergeIdx];
                        // Merge by closest points.
                        int ia = 0, ib = 0;
                        GetClosestIndices(mcont.verts, mcont.nverts, cont.verts, 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 ASTARDEBUG
                        int p4  = ia * 4;
                        int p42 = ib * 4;

                        Vector3 p12 = Vector3.Scale(
                            new Vector3(
                                mcont.verts[p4 + 0],
                                mcont.verts[p4 + 1],
                                (mcont.verts[p4 + 2] / (float)voxelArea.width)
                                ),
                            cellScale)
                                      + voxelOffset;

                        Vector3 p22 = Vector3.Scale(
                            new Vector3(
                                cont.verts[p42 + 0],
                                cont.verts[p42 + 1],
                                (cont.verts[p42 + 2] / (float)voxelArea.width)
                                )
                            , cellScale)
                                      + voxelOffset;

                        Debug.DrawLine(p12, p22, Color.green);
#endif

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

                        contours[mergeIdx] = mcont;
                        contours[i]        = cont;

                                                #if ASTARDEBUG
                        Debug.Log(mcont.nverts);

                        for (int q = 0, j = (mcont.nverts) - 1; q < (mcont.nverts); j = q, q++)
                        {
                            int i4 = q * 4;
                            int j4 = j * 4;

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

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

                            Debug.DrawLine(p1, p2, Color.red);
                            //}
                        }
                                                #endif
                    }
                }
            }

            cset.conts = contours;

            AstarProfiler.EndProfile("- Fix Contours");

            AstarProfiler.EndProfile("Build Contours");
        }