public void Process(ref NavMeshCreateParams param, NavMeshTileBuildContext bc)
        {
            // Update poly flags from areas.
            for (int i = 0; i < param.polyCount; ++i)
            {
                if ((int)bc.LMesh.Areas[i] == (int)TileCacheAreas.RC_WALKABLE_AREA)
                {
                    bc.LMesh.Areas[i] = SamplePolyAreas.SAMPLE_POLYAREA_GROUND;
                }

                if (bc.LMesh.Areas[i] == SamplePolyAreas.SAMPLE_POLYAREA_GROUND ||
                    bc.LMesh.Areas[i] == SamplePolyAreas.SAMPLE_POLYAREA_GRASS ||
                    bc.LMesh.Areas[i] == SamplePolyAreas.SAMPLE_POLYAREA_ROAD)
                {
                    bc.LMesh.Flags[i] = SamplePolyFlagTypes.SAMPLE_POLYFLAGS_WALK;
                }
                else if (bc.LMesh.Areas[i] == SamplePolyAreas.SAMPLE_POLYAREA_WATER)
                {
                    bc.LMesh.Flags[i] = SamplePolyFlagTypes.SAMPLE_POLYFLAGS_SWIM;
                }
                else if (bc.LMesh.Areas[i] == SamplePolyAreas.SAMPLE_POLYAREA_DOOR)
                {
                    bc.LMesh.Flags[i] = SamplePolyFlagTypes.SAMPLE_POLYFLAGS_WALK | SamplePolyFlagTypes.SAMPLE_POLYFLAGS_DOOR;
                }
            }

            // Pass in off-mesh connections.
            if (m_geom != null)
            {
                param.offMeshCon      = m_geom.GetConnections().ToArray();
                param.offMeshConCount = m_geom.GetConnectionCount();
            }
        }
Esempio n. 2
0
        public bool BuildNavMeshTile(CompressedTile tile, NavMesh navmesh)
        {
            NavMeshTileBuildContext bc = new NavMeshTileBuildContext();
            int walkableClimbVx        = (int)(m_params.WalkableClimb / m_params.CellHeight);

            // Decompress tile layer data.
            if (!DetourTileCache.DecompressTileCacheLayer(tile.Header, tile.Data, 0, out var layer))
            {
                return(false);
            }
            bc.Layer = layer;

            // Rasterize obstacles.
            for (int i = 0; i < m_params.MaxObstacles; ++i)
            {
                var ob = m_obstacles[i];

                if (ob.State == ObstacleState.DT_OBSTACLE_EMPTY || ob.State == ObstacleState.DT_OBSTACLE_REMOVING)
                {
                    continue;
                }

                if (DetourTileCache.Contains(ob.Touched, ob.NTouched, tile))
                {
                    if (ob.Type == ObstacleType.DT_OBSTACLE_CYLINDER)
                    {
                        DetourTileCache.MarkCylinderArea(bc,
                                                         tile.Header.BBox.Minimum, m_params.CellSize, m_params.CellHeight,
                                                         ob.Cylinder.Pos, ob.Cylinder.Radius, ob.Cylinder.Height, 0);
                    }
                    else if (ob.Type == ObstacleType.DT_OBSTACLE_BOX)
                    {
                        DetourTileCache.MarkBoxArea(bc,
                                                    tile.Header.BBox.Minimum, m_params.CellSize, m_params.CellHeight,
                                                    ob.Box.BMin, ob.Box.BMax, 0);
                    }
                    else if (ob.Type == ObstacleType.DT_OBSTACLE_ORIENTED_BOX)
                    {
                        DetourTileCache.MarkBoxArea(bc,
                                                    tile.Header.BBox.Minimum, m_params.CellSize, m_params.CellHeight,
                                                    ob.OrientedBox.Center, ob.OrientedBox.HalfExtents, ob.OrientedBox.RotAux, 0);
                    }
                }
            }

            // Build navmesh
            if (!DetourTileCache.BuildTileCacheRegions(bc, walkableClimbVx))
            {
                return(false);
            }

            if (!DetourTileCache.BuildTileCacheContours(bc, walkableClimbVx, m_params.MaxSimplificationError))
            {
                return(false);
            }

            if (!DetourTileCache.BuildTileCachePolyMesh(bc))
            {
                return(false);
            }

            // Early out if the mesh tile is empty.
            if (bc.LMesh.NPolys == 0)
            {
                // Remove existing tile.
                navmesh.RemoveTile(navmesh.GetTileRefAt(tile.Header.TX, tile.Header.TY, tile.Header.TLayer), null, 0);
                return(true);
            }

            var param = new NavMeshCreateParams
            {
                Verts          = bc.LMesh.Verts,
                VertCount      = bc.LMesh.NVerts,
                Polys          = bc.LMesh.Polys,
                PolyAreas      = bc.LMesh.Areas,
                PolyFlags      = bc.LMesh.Flags,
                polyCount      = bc.LMesh.NPolys,
                nvp            = Detour.DT_VERTS_PER_POLYGON,
                walkableHeight = m_params.WalkableHeight,
                walkableRadius = m_params.WalkableRadius,
                walkableClimb  = m_params.WalkableClimb,
                tileX          = tile.Header.TX,
                tileY          = tile.Header.TY,
                tileLayer      = tile.Header.TLayer,
                cs             = m_params.CellSize,
                ch             = m_params.CellHeight,
                buildBvTree    = false,
                bmin           = tile.Header.BBox.Minimum,
                bmax           = tile.Header.BBox.Maximum,
            };

            if (m_tmproc != null)
            {
                m_tmproc.Process(ref param, bc);
            }

            if (!Detour.CreateNavMeshData(param, out MeshData navData))
            {
                return(false);
            }

            // Remove existing tile.
            navmesh.RemoveTile(navmesh.GetTileRefAt(tile.Header.TX, tile.Header.TY, tile.Header.TLayer), null, 0);

            // Add new tile, or leave the location empty.
            if (navData != null && !navmesh.AddTile(navData, TileFlagTypes.DT_TILE_FREE_DATA, 0, out int result))
            {
                // Let the navmesh own the data.
                navData = null;

                return(false);
            }

            return(true);
        }
Esempio n. 3
0
        public static bool CreateNavMeshData(NavMeshCreateParams param, out MeshData outData)
        {
            outData = null;

            if (param.nvp > DT_VERTS_PER_POLYGON)
            {
                return(false);
            }
            if (param.VertCount >= 0xffff)
            {
                return(false);
            }
            if (param.VertCount == 0 || param.Verts == null)
            {
                return(false);
            }
            if (param.polyCount == 0 || param.Polys == null)
            {
                return(false);
            }

            int nvp = param.nvp;

            // Classify off-mesh connection points. We store only the connections
            // whose start point is inside the tile.
            int[] offMeshConClass       = null;
            int   storedOffMeshConCount = 0;
            int   offMeshConLinkCount   = 0;

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

                // Find tight heigh bounds, used for culling out off-mesh start locations.
                float hmin = float.MaxValue;
                float hmax = float.MinValue;

                if (param.detailVerts != null && param.detailVertsCount > 0)
                {
                    for (int i = 0; i < param.detailVertsCount; ++i)
                    {
                        var h = param.detailVerts[i].Y;
                        hmin = Math.Min(hmin, h);
                        hmax = Math.Max(hmax, h);
                    }
                }
                else
                {
                    for (int i = 0; i < param.VertCount; ++i)
                    {
                        var   iv = param.Verts[i];
                        float h  = param.bmin.Y + iv.Y * param.ch;
                        hmin = Math.Min(hmin, h);
                        hmax = Math.Max(hmax, h);
                    }
                }
                hmin -= param.walkableClimb;
                hmax += param.walkableClimb;
                Vector3 bmin = param.bmin;
                Vector3 bmax = param.bmax;
                bmin.Y = hmin;
                bmax.Y = hmax;

                for (int i = 0; i < param.offMeshConCount; ++i)
                {
                    var p0 = param.offMeshCon[i].Start;
                    var p1 = param.offMeshCon[i].End;
                    offMeshConClass[i + 0] = ClassifyOffMeshPoint(p0, bmin, bmax);
                    offMeshConClass[i + 1] = ClassifyOffMeshPoint(p1, bmin, bmax);

                    // Zero out off-mesh start positions which are not even potentially touching the mesh.
                    if (offMeshConClass[i * 2] == 0xff && (p0.Y <bmin.Y || p0.Y> bmax.Y))
                    {
                        offMeshConClass[i * 2] = 0;
                    }

                    // Count how many links should be allocated for off-mesh connections.
                    if (offMeshConClass[i * 2] == 0xff)
                    {
                        offMeshConLinkCount++;
                    }
                    if (offMeshConClass[(i * 2) + 1] == 0xff)
                    {
                        offMeshConLinkCount++;
                    }

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

            // Off-mesh connectionss are stored as polygons, adjust values.
            int totPolyCount = param.polyCount + storedOffMeshConCount;
            int totVertCount = param.VertCount + storedOffMeshConCount * 2;

            // Find portal edges which are at tile borders.
            int edgeCount   = 0;
            int portalCount = 0;

            for (int i = 0; i < param.polyCount; ++i)
            {
                var p = param.Polys[i];
                for (int j = 0; j < nvp; ++j)
                {
                    if (p[j] == MESH_NULL_IDX)
                    {
                        break;
                    }

                    edgeCount++;

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

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

            // Find unique detail vertices.
            int uniqueDetailVertCount = 0;
            int detailTriCount        = 0;

            if (param.detailMeshes != null)
            {
                // Has detail mesh, count unique detail vertex count and use input detail tri count.
                detailTriCount = param.detailTriCount;
                for (int i = 0; i < param.polyCount; ++i)
                {
                    var p   = param.Polys[i];
                    var ndv = param.detailMeshes[i].Y;
                    int nv  = 0;
                    for (int j = 0; j < nvp; ++j)
                    {
                        if (p[j] == MESH_NULL_IDX)
                        {
                            break;
                        }
                        nv++;
                    }
                    ndv -= nv;
                    uniqueDetailVertCount += ndv;
                }
            }
            else
            {
                // No input detail mesh, build detail mesh from nav polys.
                uniqueDetailVertCount = 0; // No extra detail verts.
                detailTriCount        = 0;
                for (int i = 0; i < param.polyCount; ++i)
                {
                    var p  = param.Polys[i];
                    int nv = 0;
                    for (int j = 0; j < nvp; ++j)
                    {
                        if (p[j] == MESH_NULL_IDX)
                        {
                            break;
                        }
                        nv++;
                    }
                    detailTriCount += nv - 2;
                }
            }

            MeshData data = new MeshData
            {
                // Store header
                Header = new MeshHeader
                {
                    Magic           = DT_NAVMESH_MAGIC,
                    Version         = DT_NAVMESH_VERSION,
                    X               = param.tileX,
                    Y               = param.tileY,
                    Layer           = param.tileLayer,
                    UserId          = param.userId,
                    PolyCount       = totPolyCount,
                    VertCount       = totVertCount,
                    MaxLinkCount    = maxLinkCount,
                    BMin            = param.bmin,
                    BMax            = param.bmax,
                    DetailMeshCount = param.polyCount,
                    DetailVertCount = uniqueDetailVertCount,
                    DetailTriCount  = detailTriCount,
                    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
                }
            };

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

            // Store vertices
            // Mesh vertices
            for (int i = 0; i < param.VertCount; ++i)
            {
                var iv = param.Verts[i];
                var v  = new Vector3
                {
                    X = param.bmin.X + iv.X * param.cs,
                    Y = param.bmin.Y + iv.Y * param.ch,
                    Z = param.bmin.Z + iv.Z * param.cs
                };
                data.NavVerts.Add(v);
            }
            // Off-mesh link vertices.
            int n = 0;

            for (int i = 0; i < param.offMeshConCount; ++i)
            {
                // Only store connections which start from this tile.
                if (offMeshConClass[i * 2] == 0xff)
                {
                    var linkv = param.offMeshCon[i];
                    data.NavVerts.Add(linkv.Start);
                    data.NavVerts.Add(linkv.End);
                    n++;
                }
            }

            // Store polygons
            // Mesh polys
            int srcIndex = 0;

            for (int i = 0; i < param.polyCount; ++i)
            {
                var src = param.Polys[srcIndex];

                Poly p = new Poly
                {
                    VertCount = 0,
                    Flags     = param.PolyFlags[i],
                    Area      = param.PolyAreas[i],
                    Type      = PolyTypes.DT_POLYTYPE_GROUND,
                };

                for (int j = 0; j < nvp; ++j)
                {
                    if (src[j] == MESH_NULL_IDX)
                    {
                        break;
                    }

                    p.Verts[j] = src[j];

                    if ((src[nvp + j] & 0x8000) != 0)
                    {
                        // Border or portal edge.
                        var dir = src[nvp + j] & 0xf;
                        if (dir == 0xf) // Border
                        {
                            p.Neis[j] = 0;
                        }
                        else if (dir == 0) // Portal x-
                        {
                            p.Neis[j] = DT_EXT_LINK | 4;
                        }
                        else if (dir == 1) // Portal z+
                        {
                            p.Neis[j] = DT_EXT_LINK | 2;
                        }
                        else if (dir == 2) // Portal x+
                        {
                            p.Neis[j] = DT_EXT_LINK;
                        }
                        else if (dir == 3) // Portal z-
                        {
                            p.Neis[j] = DT_EXT_LINK | 6;
                        }
                    }
                    else
                    {
                        // Normal connection
                        p.Neis[j] = src[nvp + j] + 1;
                    }

                    p.VertCount++;
                }

                data.NavPolys.Add(p);

                srcIndex++;
            }

            // Off-mesh connection vertices.
            n = 0;
            for (int i = 0; i < param.offMeshConCount; ++i)
            {
                // Only store connections which start from this tile.
                if (offMeshConClass[i * 2] == 0xff)
                {
                    Poly p = new Poly
                    {
                        Flags = (SamplePolyFlagTypes)param.offMeshCon[i].FlagTypes,
                        Area  = (SamplePolyAreas)param.offMeshCon[i].AreaType,
                        Type  = PolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION
                    };
                    p.Verts[0]  = (offMeshVertsBase + (n * 2) + 0);
                    p.Verts[1]  = (offMeshVertsBase + (n * 2) + 1);
                    p.VertCount = 2;

                    data.NavPolys.Add(p);
                    n++;
                }
            }

            // Store detail meshes and vertices.
            // The nav polygon vertices are stored as the first vertices on each mesh.
            // We compress the mesh data by skipping them and using the navmesh coordinates.
            if (param.detailMeshes != null)
            {
                for (int i = 0; i < param.polyCount; ++i)
                {
                    int        vb  = param.detailMeshes[i][0];
                    int        ndv = param.detailMeshes[i][1];
                    int        nv  = data.NavPolys[i].VertCount;
                    PolyDetail dtl = new PolyDetail
                    {
                        VertBase  = data.NavDVerts.Count,
                        VertCount = (ndv - nv),
                        TriBase   = param.detailMeshes[i][2],
                        TriCount  = param.detailMeshes[i][3]
                    };
                    // Copy vertices except the first 'nv' verts which are equal to nav poly verts.
                    if (ndv - nv != 0)
                    {
                        var verts = param.detailVerts.Skip(vb + nv).Take(ndv - nv);
                        data.NavDVerts.AddRange(verts);
                    }
                    data.NavDMeshes.Add(dtl);
                }
                // Store triangles.
                data.NavDTris.AddRange(param.detailTris);
            }
            else
            {
                // Create dummy detail mesh by triangulating polys.
                int tbase = 0;
                for (int i = 0; i < param.polyCount; ++i)
                {
                    int        nv  = data.NavPolys[i].VertCount;
                    PolyDetail dtl = new PolyDetail
                    {
                        VertBase  = 0,
                        VertCount = 0,
                        TriBase   = tbase,
                        TriCount  = (nv - 2)
                    };
                    // Triangulate polygon (local indices).
                    for (int j = 2; j < nv; ++j)
                    {
                        var t = new Int4
                        {
                            X = 0,
                            Y = (j - 1),
                            Z = j,
                            // Bit for each edge that belongs to poly boundary.
                            W = (1 << 2)
                        };
                        if (j == 2)
                        {
                            t.W |= (1 << 0);
                        }
                        if (j == nv - 1)
                        {
                            t.W |= (1 << 4);
                        }
                        tbase++;

                        data.NavDTris.Add(t);
                    }
                    data.NavDMeshes.Add(dtl);
                }
            }

            // Store and create BVtree.
            if (param.buildBvTree)
            {
                CreateBVTree(param, out var nodes);

                data.NavBvtree.AddRange(nodes);
            }

            // Store Off-Mesh connections.
            n = 0;
            for (int i = 0; i < param.offMeshConCount; ++i)
            {
                // Only store connections which start from this tile.
                if (offMeshConClass[i * 2] == 0xff)
                {
                    // Copy connection end-points.
                    var endPts1 = param.offMeshCon[i].Start;
                    var endPts2 = param.offMeshCon[i].End;

                    var con = new OffMeshConnection
                    {
                        poly   = offMeshPolyBase + n,
                        rad    = param.offMeshCon[i].Radius,
                        flags  = param.offMeshCon[i].Direction != 0 ? DT_OFFMESH_CON_BIDIR : 0,
                        side   = offMeshConClass[i * 2 + 1],
                        start  = endPts1,
                        end    = endPts2,
                        userId = param.offMeshCon[i].Id,
                    };

                    data.OffMeshCons.Add(con);
                    n++;
                }
            }

            outData = data;

            return(true);
        }
Esempio n. 4
0
        public static int CreateBVTree(NavMeshCreateParams param, out List <BVNode> nodes)
        {
            nodes = new List <BVNode>();

            // Build tree
            float quantFactor = 1 / param.cs;

            BVItem[] items = new BVItem[param.polyCount];
            for (int i = 0; i < param.polyCount; i++)
            {
                var it = items[i];
                it.I = i;
                // Calc polygon bounds. Use detail meshes if available.
                if (param.detailMeshes != null)
                {
                    int vb   = param.detailMeshes[i][0];
                    int ndv  = param.detailMeshes[i][1];
                    var bmin = param.detailVerts[vb];
                    var bmax = param.detailVerts[vb];

                    for (int j = 1; j < ndv; j++)
                    {
                        bmin = Vector3.Min(bmin, param.detailVerts[vb + j]);
                        bmax = Vector3.Max(bmax, param.detailVerts[vb + j]);
                    }

                    // BV-tree uses cs for all dimensions
                    it.BMin = new Int3
                    {
                        X = MathUtil.Clamp((int)((bmin.X - param.bmin.X) * quantFactor), 0, 0xffff),
                        Y = MathUtil.Clamp((int)((bmin.Y - param.bmin.Y) * quantFactor), 0, 0xffff),
                        Z = MathUtil.Clamp((int)((bmin.Z - param.bmin.Z) * quantFactor), 0, 0xffff)
                    };

                    it.BMax = new Int3
                    {
                        X = MathUtil.Clamp((int)((bmax.X - param.bmin.X) * quantFactor), 0, 0xffff),
                        Y = MathUtil.Clamp((int)((bmax.Y - param.bmin.Y) * quantFactor), 0, 0xffff),
                        Z = MathUtil.Clamp((int)((bmax.Z - param.bmin.Z) * quantFactor), 0, 0xffff)
                    };
                }
                else
                {
                    var p = param.Polys[i];

                    var itBMin = param.Verts[p[0]];
                    var itBMax = param.Verts[p[0]];

                    for (int j = 1; j < param.nvp; ++j)
                    {
                        if (p[j] == MESH_NULL_IDX)
                        {
                            break;
                        }
                        var x = param.Verts[p[j]].X;
                        var y = param.Verts[p[j]].Y;
                        var z = param.Verts[p[j]].Z;

                        if (x < it.BMin.X)
                        {
                            itBMin.X = x;
                        }
                        if (y < it.BMin.Y)
                        {
                            itBMin.Y = y;
                        }
                        if (z < it.BMin.Z)
                        {
                            itBMin.Z = z;
                        }

                        if (x > it.BMax.X)
                        {
                            itBMax.X = x;
                        }
                        if (y > it.BMax.Y)
                        {
                            itBMax.Y = y;
                        }
                        if (z > it.BMax.Z)
                        {
                            itBMax.Z = z;
                        }
                    }
                    // Remap y
                    itBMin.Y = (int)Math.Floor(it.BMin.Y * param.ch / param.cs);
                    itBMax.Y = (int)Math.Ceiling(it.BMax.Y * param.ch / param.cs);

                    it.BMin = itBMin;
                    it.BMax = itBMax;
                }
                items[i] = it;
            }

            int curNode = 0;

            Subdivide(items, param.polyCount, 0, param.polyCount, ref curNode, ref nodes);

            return(curNode);
        }