Exemple #1
0
        public static NavMeshQuery CreateMeshQuery(NavMeshCreateParams meshCreateParams)
        {
            Detour.Detour.dtRawTileData navData;
            if (!Detour.Detour.dtCreateNavMeshData(meshCreateParams, out navData))
            {
                return(null);
            }

            var      m_navMesh = new NavMesh();
            dtStatus status;

            status = m_navMesh.init(navData, (int)Detour.Detour.dtTileFlags.DT_TILE_FREE_DATA);
            if (Detour.Detour.dtStatusFailed(status))
            {
                return(null);
            }

            var m_navQuery = new NavMeshQuery();

            status = m_navQuery.init(m_navMesh, 2048);
            if (Detour.Detour.dtStatusFailed(status))
            {
                return(null);
            }

            return(m_navQuery);
        }
        public virtual MeshData build(NavMeshCreateParams @params, int tileX, int tileY)
        {
            MeshData data = NavMeshBuilder.createNavMeshData(@params);

            if (data != null)
            {
                data.header.x = tileX;
                data.header.y = tileY;
            }
            return(data);
        }
Exemple #3
0
 public static void Serialize(string path, NavMeshCreateParams mesh)
 {
     File.WriteAllText(path, JsonConvert.SerializeObject(mesh));
 }
Exemple #4
0
    private LunaNav.NavMeshBuilder BuildTileMesh(int tx, int ty, RecastVertex min, RecastVertex max)
    {
        Config.Width        = Config.TileSize + Config.BorderSize * 2;
        Config.Height       = Config.TileSize + Config.BorderSize * 2;
        Config.MinBounds    = min;
        Config.MaxBounds    = max;
        Config.MinBounds.X -= Config.BorderSize * Config.CellSize;
        Config.MinBounds.Z -= Config.BorderSize * Config.CellSize;

        Config.MaxBounds.X += Config.BorderSize * Config.CellSize;
        Config.MaxBounds.Z += Config.BorderSize * Config.CellSize;

        HeightField heightfield = new HeightField(Config.Width, Config.Height, Config.MinBounds.ToArray(), Config.MaxBounds.ToArray(), Config.CellSize, Config.CellHeight);


        short[] triAreas = new short[Geometry.ChunkyTriMesh.MaxTrisPerChunk];

        float[] tbmin = new float[2], tbmax = new float[2];
        tbmin[0] = Config.MinBounds.X;
        tbmin[1] = Config.MinBounds.Z;

        tbmax[0] = Config.MaxBounds.X;
        tbmax[1] = Config.MaxBounds.Z;

        int[] cid = new int[512];

        int ncid = Geometry.ChunkyTriMesh.GetChunksOverlappingRect(tbmin, tbmax, ref cid, 512);

        if (ncid == 0)
        {
            return(null);
        }

        for (int i = 0; i < ncid; i++)
        {
            ChunkyTriMeshNode node = Geometry.ChunkyTriMesh.Nodes[cid[i]];
            int[]             tris = new int[node.n * 3];
            Array.Copy(Geometry.ChunkyTriMesh.Tris, node.i * 3, tris, 0, node.n * 3);
            List <int> ctris  = new List <int>(tris);
            int        nctris = node.n;

            Array.Clear(triAreas, 0, triAreas.Length);
            Geometry.MarkWalkableTriangles(Config.WalkableSlopeAngle, ctris, nctris, ref triAreas);

            heightfield.RasterizeTriangles(Geometry, ctris, nctris, triAreas, Config.WalkableClimb);
        }

        heightfield.FilterLowHangingWalkableObstacles(Config.WalkableClimb);
        heightfield.FilterLedgeSpans(Config.WalkableHeight, Config.WalkableClimb);
        heightfield.FilterWalkableLowHeightSpans(Config.WalkableHeight);


        CompactHeightfield compactHeightfield = new CompactHeightfield(Config.WalkableHeight, Config.WalkableClimb, heightfield);

        compactHeightfield.ErodeWalkableArea(Config.WalkableRadius);

        // optional convex volumes

        compactHeightfield.BuildDistanceField();
        compactHeightfield.BuildRegions(Config.BorderSize, Config.MinRegionArea, Config.MergeRegionArea);


        ContourSet contourSet = new ContourSet(compactHeightfield, Config.MaxSimplificationError, Config.MaxEdgeLength);

        if (contourSet.NConts == 0)
        {
            return(null);
        }

        PolyMesh polyMesh = new PolyMesh(contourSet, Config.MaxVertexesPerPoly);

        DetailPolyMesh detailPolyMesh = new DetailPolyMesh(polyMesh, compactHeightfield, Config.DetailSampleDistance,
                                                           Config.DetailSampleMaxError);

        // Convert the Areas and Flags for path weighting
        for (int i = 0; i < polyMesh.NPolys; i++)
        {
            if (polyMesh.Areas[i] == Geometry.WalkableArea)
            {
                polyMesh.Areas[i] = 0; // Sample_polyarea_ground
                polyMesh.Flags[i] = 1; // Samply_polyflags_walk
            }
        }
        NavMeshCreateParams param = new NavMeshCreateParams
        {
            Verts            = polyMesh.Verts,
            VertCount        = polyMesh.NVerts,
            Polys            = polyMesh.Polys,
            PolyAreas        = polyMesh.Areas,
            PolyFlags        = polyMesh.Flags,
            PolyCount        = polyMesh.NPolys,
            Nvp              = polyMesh.Nvp,
            DetailMeshes     = detailPolyMesh.Meshes,
            DetailVerts      = detailPolyMesh.Verts,
            DetailVertsCount = detailPolyMesh.NVerts,
            DetailTris       = detailPolyMesh.Tris,
            DetailTriCount   = detailPolyMesh.NTris,

            // Off Mesh data
            OffMeshConVerts  = Geometry.OffMeshConnectionVerts.ToArray(),
            OffMeshConRad    = Geometry.OffMeshConnectionRadii.ToArray(),
            OffMeshConDir    = Geometry.OffMeshConnectionDirections.ToArray(),
            OffMeshConAreas  = Geometry.OffMeshConnectionAreas.ToArray(),
            OffMeshConFlags  = Geometry.OffMeshConnectionFlags.ToArray(),
            OffMeshConUserId = Geometry.OffMeshConnectionIds.ToArray(),
            OffMeshConCount  = (int)Geometry.OffMeshConnectionCount,
            // end off mesh data

            WalkableHeight = Config.WalkableHeight,
            WalkableRadius = Config.WalkableRadius,
            WalkableClimb  = Config.WalkableClimb,
            BMin           = new float[] { polyMesh.BMin[0], polyMesh.BMin[1], polyMesh.BMin[2] },
            BMax           = new float[] { polyMesh.BMax[0], polyMesh.BMax[1], polyMesh.BMax[2] },
            Cs             = polyMesh.Cs,
            Ch             = polyMesh.Ch,
            BuildBvTree    = true,
            TileX          = tx,
            TileY          = ty,
            TileLayer      = 0
        };

        return(new LunaNav.NavMeshBuilder(param));
    }
Exemple #5
0
    private Detour.AtavismNavTile BuildTileMesh(int tx, int ty, RecastVertex min, RecastVertex max)
    {
        Config.Width = Config.TileSize + Config.BorderSize * 2;
        Config.Height = Config.TileSize + Config.BorderSize * 2;
        Config.MinBounds = min;
        Config.MaxBounds = max;
        Config.MinBounds.X -= Config.BorderSize * Config.CellSize;
        Config.MinBounds.Z -= Config.BorderSize * Config.CellSize;

        Config.MaxBounds.X += Config.BorderSize * Config.CellSize;
        Config.MaxBounds.Z += Config.BorderSize * Config.CellSize;

        HeightField heightfield = new HeightField(Config.Width, Config.Height, Config.MinBounds.ToArray(), Config.MaxBounds.ToArray(), Config.CellSize, Config.CellHeight);

        short[] triAreas = new short[Geometry.ChunkyTriMesh.MaxTrisPerChunk];

        float[] tbmin = new float[2], tbmax = new float[2];
        tbmin[0] = Config.MinBounds.X;
        tbmin[1] = Config.MinBounds.Z;

        tbmax[0] = Config.MaxBounds.X;
        tbmax[1] = Config.MaxBounds.Z;

        int[] cid = new int[512];

        int ncid = Geometry.ChunkyTriMesh.GetChunksOverlappingRect(tbmin, tbmax, ref cid, 512);

        if (ncid == 0)
            return null;

        for (int i = 0; i < ncid; i++)
        {
            ChunkyTriMeshNode node = Geometry.ChunkyTriMesh.Nodes[cid[i]];
            int[] tris = new int[node.n * 3];
            Array.Copy(Geometry.ChunkyTriMesh.Tris, node.i * 3, tris, 0, node.n * 3);
            List<int> ctris = new List<int>(tris);
            int nctris = node.n;

            Array.Clear(triAreas, 0, triAreas.Length);
            Geometry.MarkWalkableTriangles(Config.WalkableSlopeAngle, ctris, nctris, ref triAreas);

            heightfield.RasterizeTriangles(Geometry, ctris, nctris, triAreas, Config.WalkableClimb);
        }

        heightfield.FilterLowHangingWalkableObstacles(Config.WalkableClimb);
        heightfield.FilterLedgeSpans(Config.WalkableHeight, Config.WalkableClimb);
        heightfield.FilterWalkableLowHeightSpans(Config.WalkableHeight);

        CompactHeightfield compactHeightfield = new CompactHeightfield(Config.WalkableHeight, Config.WalkableClimb, heightfield);
        compactHeightfield.ErodeWalkableArea(Config.WalkableRadius);

        // optional convex volumes

        compactHeightfield.BuildDistanceField();
        compactHeightfield.BuildRegions(Config.BorderSize, Config.MinRegionArea, Config.MergeRegionArea);

        ContourSet contourSet = new ContourSet(compactHeightfield, Config.MaxSimplificationError, Config.MaxEdgeLength);

        if (contourSet.NConts == 0)
            return null;

        PolyMesh polyMesh = new PolyMesh(contourSet, Config.MaxVertexesPerPoly);

        DetailPolyMesh detailPolyMesh = new DetailPolyMesh(polyMesh, compactHeightfield, Config.DetailSampleDistance,
                                                            Config.DetailSampleMaxError);

        // Convert the Areas and Flags for path weighting
        for (int i = 0; i < polyMesh.NPolys; i++)
        {

            if (polyMesh.Areas[i] == Geometry.WalkableArea)
            {
                polyMesh.Areas[i] = 0; // Sample_polyarea_ground
                polyMesh.Flags[i] = 1; // Samply_polyflags_walk
            }
        }
        NavMeshCreateParams param = new NavMeshCreateParams
        {
            Verts = polyMesh.Verts,
            VertCount = polyMesh.NVerts,
            Polys = polyMesh.Polys,
            PolyAreas = polyMesh.Areas,
            PolyFlags = polyMesh.Flags,
            PolyCount = polyMesh.NPolys,
            Nvp = polyMesh.Nvp,
            DetailMeshes = detailPolyMesh.Meshes,
            DetailVerts = detailPolyMesh.Verts,
            DetailVertsCount = detailPolyMesh.NVerts,
            DetailTris = detailPolyMesh.Tris,
            DetailTriCount = detailPolyMesh.NTris,

            // Off Mesh data
            OffMeshConVerts = Geometry.OffMeshConnectionVerts.ToArray(),
            OffMeshConRad = Geometry.OffMeshConnectionRadii.ToArray(),
            OffMeshConDir = Geometry.OffMeshConnectionDirections.ToArray(),
            OffMeshConAreas = Geometry.OffMeshConnectionAreas.ToArray(),
            OffMeshConFlags = Geometry.OffMeshConnectionFlags.ToArray(),
            OffMeshConUserId = Geometry.OffMeshConnectionIds.ToArray(),
            OffMeshConCount = (int)Geometry.OffMeshConnectionCount,
            // end off mesh data

            WalkableHeight = Config.WalkableHeight,
            WalkableRadius = Config.WalkableRadius,
            WalkableClimb = Config.WalkableClimb,
            BMin = new float[] { polyMesh.BMin[0], polyMesh.BMin[1], polyMesh.BMin[2] },
            BMax = new float[] { polyMesh.BMax[0], polyMesh.BMax[1], polyMesh.BMax[2] },
            Cs = polyMesh.Cs,
            Ch = polyMesh.Ch,
            BuildBvTree = true,
            TileX = tx,
            TileY = ty,
            TileLayer = 0
        };
        return new Detour.AtavismNavTile(param);
    }
        public NavMeshBuilder(NavMeshCreateParams param)
        {
            if (param.Nvp > VertsPerPoly)
            {
                throw new ArgumentException("Too many Verts per Poly for NavMeshBuilder");
            }
            if (param.VertCount >= 0xffff)
            {
                throw new ArgumentException("Too many total verticies for NavMeshBuilder");
            }
            if (param.VertCount == 0 || param.Verts == null)
            {
                throw new ArgumentException("No vertices, cannot generate nav mesh");
            }
            if (param.PolyCount == 0 || param.Polys == null)
            {
                throw new ArgumentException("No Polygons, cannot generate nav mesh");
            }

            int nvp = param.Nvp;

            short[] offMeshConClass       = new short[0];
            int     storedOffMeshConCount = 0;
            int     offMeshConLinkCount   = 0;

            if (param.OffMeshConCount > 0)
            {
                offMeshConClass = new short[param.OffMeshConCount * 2];

                float hmin = float.MaxValue;
                float hmax = float.MinValue;

                if (param.DetailVerts != null && param.DetailVertsCount > 0)
                {
                    for (int i = 0; i < param.DetailVertsCount; i++)
                    {
                        int h = i * 3 + 1;
                        hmin = Math.Min(hmin, param.DetailVerts[h]);
                        hmax = Math.Max(hmax, param.DetailVerts[h]);
                    }
                }
                else
                {
                    for (int i = 0; i < param.VertCount; i++)
                    {
                        int   iv = i * 3;
                        float h  = param.BMin[1] + param.Verts[iv + 1] * param.Ch;
                        hmin = Math.Min(hmin, h);
                        hmax = Math.Max(hmax, h);
                    }
                }
                hmin -= param.WalkableClimb;
                hmax += param.WalkableClimb;
                float[] bmin = new float[3], bmax = new float[3];
                Array.Copy(param.BMin, bmin, 3);
                Array.Copy(param.BMax, bmax, 3);

                bmin[1] = hmin;
                bmax[1] = hmax;

                for (int i = 0; i < param.OffMeshConCount; i++)
                {
                    int p0 = (i * 2 + 0) * 3;
                    int p1 = (i * 2 + 1) * 3;
                    offMeshConClass[i * 2 + 0] = ClassifyOffMeshPoint(param.OffMeshConVerts[p0 + 0],
                                                                      param.OffMeshConVerts[p0 + 1],
                                                                      param.OffMeshConVerts[p0 + 2], bmin, bmax);
                    offMeshConClass[i * 2 + 1] = ClassifyOffMeshPoint(param.OffMeshConVerts[p1 + 0],
                                                                      param.OffMeshConVerts[p1 + 1],
                                                                      param.OffMeshConVerts[p1 + 2], bmin, bmax);

                    if (offMeshConClass[i * 2 + 0] == 0xff)
                    {
                        if (param.OffMeshConVerts[p0 + 1] < bmin[1] || param.OffMeshConVerts[p0 + 1] > bmax[1])
                        {
                            offMeshConClass[i * 2 + 0] = 0;
                        }
                    }

                    if (offMeshConClass[i * 2 + 0] == 0xff)
                    {
                        offMeshConLinkCount++;
                    }
                    if (offMeshConClass[i * 2 + 1] == 0xff)
                    {
                        offMeshConLinkCount++;
                    }

                    if (offMeshConClass[i * 2 + 0] == 0xff)
                    {
                        storedOffMeshConCount++;
                    }
                }
            }

            int totPolyCount = param.PolyCount + storedOffMeshConCount;
            int totVertCount = param.VertCount + storedOffMeshConCount * 2;

            int edgeCount   = 0;
            int portalCount = 0;

            for (int i = 0; i < param.PolyCount; i++)
            {
                int p = i * 2 * nvp;
                for (int j = 0; j < nvp; j++)
                {
                    if (param.Polys[p + j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    edgeCount++;

                    if ((param.Polys[p + nvp + j] & 0x8000) != 0)
                    {
                        int dir = param.Polys[p + nvp + j] & 0xf;
                        if (dir != 0xf)
                        {
                            portalCount++;
                        }
                    }
                }
            }

            int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2;

            int uniqueDetailVertCount = 0;
            int detailTryCount        = 0;

            if (param.DetailMeshes != null)
            {
                detailTryCount = param.DetailTriCount;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    int p   = i * nvp * 2;
                    int ndv = (int)param.DetailMeshes[i * 4 + 1];
                    int nv  = 0;
                    for (int j = 0; j < nvp; j++)
                    {
                        if (param.Polys[p + j] == PolyMesh.MeshNullIdx)
                        {
                            break;
                        }
                        nv++;
                    }
                    ndv -= nv;
                    uniqueDetailVertCount += ndv;
                }
            }
            else
            {
                uniqueDetailVertCount = 0;
                detailTryCount        = 0;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    int p  = i * nvp * 2;
                    int nv = 0;
                    for (int j = 0; j < nvp; j++)
                    {
                        if (param.Polys[p + j] == PolyMesh.MeshNullIdx)
                        {
                            break;
                        }
                        nv++;
                    }
                    detailTryCount += nv - 2;
                }
            }

            // Initialize the header and all nav data.
            Header = new MeshHeader
            {
                Magic           = Helper.NavMeshMagic,
                Version         = Helper.NavMeshVersion,
                X               = param.TileX,
                Y               = param.TileY,
                Layer           = param.TileLayer,
                UserId          = param.UserId,
                PolyCount       = totPolyCount,
                VertCount       = totVertCount,
                MaxLinkCount    = maxLinkCount,
                DetailMeshCount = param.PolyCount,
                DetailVertCount = uniqueDetailVertCount,
                DetailTriCount  = detailTryCount,
                BVQuantFactor   = 1.0f / param.Cs,
                OffMeshBase     = param.PolyCount,
                WalkableHeight  = param.WalkableHeight,
                WalkableRadius  = param.WalkableRadius,
                WalkableClimb   = param.WalkableClimb,
                OffMeshConCount = storedOffMeshConCount,
                BVNodeCount     = param.BuildBvTree ? param.PolyCount * 2 : 0,
                BMin            = new float[3],
                BMax            = new float[3]
            };
            Array.Copy(param.BMin, Header.BMin, 3);
            Array.Copy(param.BMax, Header.BMax, 3);

            NavVerts = new float[totVertCount * 3];
            NavPolys = new Poly[totPolyCount];
            for (int i = 0; i < totPolyCount; i++)
            {
                NavPolys[i] = new Poly();
            }
            NavLinks = new Link[maxLinkCount];
            for (int i = 0; i < maxLinkCount; i++)
            {
                NavLinks[i] = new Link();
            }
            NavDMeshes = new PolyDetail[param.PolyCount];
            for (int i = 0; i < param.PolyCount; i++)
            {
                NavDMeshes[i] = new PolyDetail();
            }
            NavDVerts = new float[3 * uniqueDetailVertCount];
            NavDTris  = new short[4 * detailTryCount];
            NavBvTree = param.BuildBvTree ? new BVNode[param.PolyCount * 2] : new BVNode[0];
            if (param.BuildBvTree)
            {
                for (int i = 0; i < param.PolyCount * 2; i++)
                {
                    NavBvTree[i] = new BVNode();
                }
            }
            OffMeshCons = new OffMeshConnection[storedOffMeshConCount];
            for (int i = 0; i < storedOffMeshConCount; i++)
            {
                OffMeshCons[i] = new OffMeshConnection();
            }


            int offMeshVertsBase = param.VertCount;
            int offMeshPolyBase  = param.PolyCount;

            // store vertices
            // Mesh
            for (int i = 0; i < param.VertCount; i++)
            {
                int iv = i * 3;
                int v  = i * 3;
                NavVerts[v + 0] = param.BMin[0] + param.Verts[iv + 0] * param.Cs;
                NavVerts[v + 1] = param.BMin[1] + param.Verts[iv + 1] * param.Ch;
                NavVerts[v + 2] = param.BMin[2] + param.Verts[iv + 2] * param.Cs;
            }
            // off-link
            int n = 0;

            for (int i = 0; i < param.OffMeshConCount; i++)
            {
                if (offMeshConClass[i * 2 + 0] == 0xff)
                {
                    int linkv = i * 2 * 3;
                    int v     = (offMeshVertsBase + n * 2) * 3;
                    Array.Copy(param.OffMeshConVerts, linkv, NavVerts, v, 3);
                    Array.Copy(param.OffMeshConVerts, linkv + 3, NavVerts, v + 3, 3);
                    n++;
                }
            }

            // store polygons
            // mesh
            int src = 0;

            for (int i = 0; i < param.PolyCount; i++)
            {
                Poly p = NavPolys[i];
                p.VertCount = 0;
                p.Flags     = param.PolyFlags[i];
                p.Area      = param.PolyAreas[i];
                p.Type      = PolyTypeGround;
                for (int j = 0; j < nvp; j++)
                {
                    if (param.Polys[src + j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    p.Verts[j] = param.Polys[src + j];
                    if ((param.Polys[src + nvp + j] & 0x8000) != 0)
                    {
                        int dir = param.Polys[src + nvp + j] & 0xf;
                        if (dir == 0xf)
                        {
                            p.Neis[j] = 0;
                        }
                        else if (dir == 0)
                        {
                            p.Neis[j] = ExtLink | 4;
                        }
                        else if (dir == 1)
                        {
                            p.Neis[j] = ExtLink | 2;
                        }
                        else if (dir == 2)
                        {
                            p.Neis[j] = ExtLink | 0;
                        }
                        else if (dir == 3)
                        {
                            p.Neis[j] = ExtLink | 6;
                        }
                    }
                    else
                    {
                        p.Neis[j] = param.Polys[src + nvp + j] + 1;
                    }
                    p.VertCount++;
                }
                src += nvp * 2;
            }
            // off mesh
            n = 0;
            for (int i = 0; i < param.OffMeshConCount; i++)
            {
                if (offMeshConClass[i * 2 + 0] == 0xff)
                {
                    Poly p = NavPolys[offMeshPolyBase + n];
                    p.VertCount = 2;
                    p.Verts[0]  = (offMeshVertsBase + n * 2 + 0);
                    p.Verts[1]  = (offMeshVertsBase + n * 2 + 1);
                    p.Flags     = param.OffMeshConFlags[i];
                    p.Area      = (short)param.OffMeshConAreas[i];
                    p.Type      = PolyTypeOffMeshConnection;
                    n++;
                }
            }

            // Store detail meshes and verts
            if (param.DetailMeshes != null)
            {
                int vbase = 0;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    PolyDetail dtl = NavDMeshes[i];
                    int        vb  = (int)param.DetailMeshes[i * 4 + 0];
                    int        ndv = (int)param.DetailMeshes[i * 4 + 1];
                    int        nv  = NavPolys[i].VertCount;
                    dtl.VertBase  = vbase;
                    dtl.VertCount = (short)(ndv - nv);
                    dtl.TriBase   = param.DetailMeshes[i * 4 + 2];
                    dtl.TriCount  = (short)param.DetailMeshes[i * 4 + 3];
                    if (ndv - nv > 0)
                    {
                        Array.Copy(param.DetailVerts, (vb + nv) * 3, NavDVerts, vbase * 3, (ndv - nv) * 3);
                        vbase += (short)(ndv - nv);
                    }
                }
                Array.Copy(param.DetailTris, NavDTris, param.DetailTriCount * 4);
            }
            else
            {
                // Create dummy detail mesh
                int tbase = 0;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    PolyDetail dtl = NavDMeshes[i];
                    int        nv  = NavPolys[i].VertCount;
                    dtl.VertBase  = 0;
                    dtl.VertCount = 0;
                    dtl.TriBase   = tbase;
                    dtl.TriCount  = (short)(nv - 2);
                    for (int j = 2; j < nv; j++)
                    {
                        int t = tbase * 4;
                        NavDTris[t + 0] = 0;
                        NavDTris[t + 1] = (short)(j - 1);
                        NavDTris[t + 2] = (short)j;
                        NavDTris[t + 3] = (1 << 2);
                        if (j == 2)
                        {
                            NavDTris[t + 3] |= (1 << 0);
                        }
                        if (j == nv - 1)
                        {
                            NavDTris[t + 3] |= (1 << 4);
                        }
                        tbase++;
                    }
                }
            }

            // Store and create BVTree
            if (param.BuildBvTree)
            {
                CreateBVTree(param.Verts, param.VertCount, param.Polys, param.PolyCount, nvp, param.Cs, param.Ch, param.PolyCount * 2);
            }

            // store off-mesh connections
            n = 0;
            for (int i = 0; i < param.OffMeshConCount; i++)
            {
                if (offMeshConClass[i * 2 + 0] == 0xff)
                {
                    OffMeshConnection con = OffMeshCons[n];
                    con.Poly = offMeshPolyBase + n;
                    int endPts = i * 2 * 3;
                    Array.Copy(param.OffMeshConVerts, endPts, con.Pos, 0, 3);
                    Array.Copy(param.OffMeshConVerts, endPts + 3, con.Pos, 3, 3);
                    con.Rad   = param.OffMeshConRad[i];
                    con.Flags = param.OffMeshConDir[i] > 0 ? OffMeshConBiDir : (short)0;
                    con.Side  = offMeshConClass[i * 2 + 1];
                    if (param.OffMeshConUserId != null)
                    {
                        con.UserId = param.OffMeshConUserId[i];
                    }
                    n++;
                }
            }
        }