/// <summary> /// Initializes a new instance of the <see cref="TiledNavMesh"/> class. /// </summary> /// <param name="data">The Navigation Mesh data</param> public TiledNavMesh(NavMeshBuilder data) { this.origin = data.Header.Bounds.Min; this.tileWidth = data.Header.Bounds.Max.X - data.Header.Bounds.Min.X; this.tileHeight = data.Header.Bounds.Max.Z - data.Header.Bounds.Min.Z; this.maxTiles = 1; this.maxPolys = data.Header.PolyCount; //init tiles tileSet = new Dictionary<Vector2i, List<MeshTile>>(); tileRefs = new Dictionary<MeshTile, PolyId>(); tileList = new List<MeshTile>(); //init ID generator values int tileBits = MathHelper.Log2(MathHelper.NextPowerOfTwo(maxTiles)); int polyBits = MathHelper.Log2(MathHelper.NextPowerOfTwo(maxPolys)); //only allow 31 salt bits, since salt mask is calculated using 32-bit int and it will overflow int saltBits = Math.Min(31, 32 - tileBits - polyBits); //TODO handle this in a sane way/do we need this? if (saltBits < 10) return; idManager = new PolyIdManager(polyBits, tileBits, saltBits); AddTile(data); }
/// <summary> /// Generates a <see cref="NavMesh"/> given a collection of triangles and some settings. /// </summary> /// <param name="triangles">The triangles that form the level.</param> /// <param name="settings">The settings to generate with.</param> /// <returns>A <see cref="NavMesh"/>.</returns> public static NavMesh Generate(IEnumerable <Triangle3> triangles, NavMeshGenerationSettings settings) { BBox3 bounds = triangles.GetBoundingBox(settings.CellSize); var hf = new Heightfield(bounds, settings); hf.RasterizeTriangles(triangles); hf.FilterLedgeSpans(settings.VoxelAgentHeight, settings.VoxelMaxClimb); hf.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb); hf.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight); var chf = new CompactHeightfield(hf, settings); chf.Erode(settings.VoxelAgentRadius); chf.BuildDistanceField(); chf.BuildRegions(2, settings.MinRegionSize, settings.MergedRegionSize); var cont = chf.BuildContourSet(settings); var polyMesh = new PolyMesh(cont, settings); var polyMeshDetail = new PolyMeshDetail(polyMesh, chf, settings); var buildData = new NavMeshBuilder(polyMesh, polyMeshDetail, new Pathfinding.OffMeshConnection[0], settings); var navMesh = new NavMesh(buildData); return(navMesh); }
/// <summary> /// Initializes a new instance of the <see cref="TiledNavMesh"/> class. /// </summary> /// <param name="data">The Navigation Mesh data</param> public TiledNavMesh(NavMeshBuilder data) { this.origin = data.Header.Bounds.Min; this.tileWidth = data.Header.Bounds.Max.X - data.Header.Bounds.Min.X; this.tileHeight = data.Header.Bounds.Max.Z - data.Header.Bounds.Min.Z; this.maxTiles = 1; this.maxPolys = data.Header.PolyCount; //init tiles tileSet = new Dictionary <Vector2i, List <MeshTile> >(); tileRefs = new Dictionary <MeshTile, PolyId>(); tileList = new List <MeshTile>(); //init ID generator values int tileBits = MathHelper.Log2(MathHelper.NextPowerOfTwo(maxTiles)); int polyBits = MathHelper.Log2(MathHelper.NextPowerOfTwo(maxPolys)); //only allow 31 salt bits, since salt mask is calculated using 32-bit int and it will overflow int saltBits = Math.Min(31, 32 - tileBits - polyBits); //TODO handle this in a sane way/do we need this? if (saltBits < 10) { return; } idManager = new PolyIdManager(polyBits, tileBits, saltBits); AddTile(data); }
/// <summary> /// Generates a <see cref="NavMesh"/> given a collection of triangles and some settings. /// </summary> /// <param name="triangles">The triangles that form the level.</param> /// <param name="settings">The settings to generate with.</param> /// <returns>A <see cref="NavMesh"/>.</returns> public static NavMesh Generate(IEnumerable<Triangle3> triangles, NavMeshGenerationSettings settings) { BBox3 bounds = triangles.GetBoundingBox(settings.CellSize); var hf = new Heightfield(bounds, settings); hf.RasterizeTriangles(triangles); hf.FilterLedgeSpans(settings.VoxelAgentHeight, settings.VoxelMaxClimb); hf.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb); hf.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight); var chf = new CompactHeightfield(hf, settings); chf.Erode(settings.VoxelAgentRadius); chf.BuildDistanceField(); chf.BuildRegions(2, settings.MinRegionSize, settings.MergedRegionSize); var cont = chf.BuildContourSet(settings); var polyMesh = new PolyMesh(cont, settings); var polyMeshDetail = new PolyMeshDetail(polyMesh, chf, settings); var buildData = new NavMeshBuilder(polyMesh, polyMeshDetail, new Pathfinding.OffMeshConnection[0], settings); var navMesh = new NavMesh(buildData); return navMesh; }
/// <summary> /// Initializes a new instance of the <see cref="TiledNavMesh"/> class. /// </summary> /// <param name="data">The Navigation Mesh data</param> public TiledNavMesh(NavMeshBuilder data) { TiledNavMeshParams parameters; parameters.Origin = data.Header.Bounds.Min; parameters.TileWidth = data.Header.Bounds.Max.X - data.Header.Bounds.Min.X; parameters.TileHeight = data.Header.Bounds.Max.Z - data.Header.Bounds.Min.Z; parameters.MaxTiles = 1; parameters.MaxPolys = data.Header.PolyCount; if (!InitTileNavMesh(parameters)) return; int tileRef = 0; AddTile(data, 0, ref tileRef); }
/// <summary> /// Initializes a new instance of the <see cref="TiledNavMesh"/> class. /// </summary> /// <param name="data">The Navigation Mesh data</param> public TiledNavMesh(NavMeshBuilder data) { this.origin = data.Header.Bounds.Min; this.tileWidth = data.Header.Bounds.Max.X - data.Header.Bounds.Min.X; this.tileHeight = data.Header.Bounds.Max.Z - data.Header.Bounds.Min.Z; this.maxTiles = 1; this.maxPolys = data.Header.PolyCount; if (!InitTileNavMesh()) return; //HACK is tileRef actually being used for anything? PolyId tileRef = PolyId.Null; AddTile(data, PolyId.Null, out tileRef); }
private int polyBits; //number of poly bits in ID /// <summary> /// Initializes a new instance of the <see cref="TiledNavMesh"/> class. /// </summary> /// <param name="data">The Navigation Mesh data</param> public TiledNavMesh(NavMeshBuilder data) { this.origin = data.Header.Bounds.Min; this.tileWidth = data.Header.Bounds.Max.X - data.Header.Bounds.Min.X; this.tileHeight = data.Header.Bounds.Max.Z - data.Header.Bounds.Min.Z; this.maxTiles = 1; this.maxPolys = data.Header.PolyCount; if (!InitTileNavMesh()) { return; } //HACK is tileRef actually being used for anything? PolyId tileRef = PolyId.Null; AddTile(data, PolyId.Null, out tileRef); }
private void GeneratePathfinding() { if (!hasGenerated) return; Random rand = new Random(); NavQueryFilter filter = new NavQueryFilter(); buildData = new NavMeshBuilder(polyMesh, polyMeshDetail, new SharpNav.Pathfinding.OffMeshConnection[0], settings); tiledNavMesh = new TiledNavMesh(buildData); navMeshQuery = new NavMeshQuery(tiledNavMesh, 2048); //Find random start and end points on the poly mesh /*int startRef; navMeshQuery.FindRandomPoint(out startRef, out startPos);*/ SVector3 c = new SVector3(10, 0, 0); SVector3 e = new SVector3(5, 5, 5); navMeshQuery.FindNearestPoly(ref c, ref e, out startPt); navMeshQuery.FindRandomPointAroundCircle(ref startPt, 1000, out endPt); //calculate the overall path, which contains an array of polygon references int MAX_POLYS = 256; path = new Path(); navMeshQuery.FindPath(ref startPt, ref endPt, filter, path); //find a smooth path over the mesh surface int npolys = path.Count; SVector3 iterPos = new SVector3(); SVector3 targetPos = new SVector3(); navMeshQuery.ClosestPointOnPoly(startPt.Polygon, startPt.Position, ref iterPos); navMeshQuery.ClosestPointOnPoly(path[npolys - 1], endPt.Position, ref targetPos); smoothPath = new List<SVector3>(2048); smoothPath.Add(iterPos); float STEP_SIZE = 0.5f; float SLOP = 0.01f; while (npolys > 0 && smoothPath.Count < smoothPath.Capacity) { //find location to steer towards SVector3 steerPos = new SVector3(); StraightPathFlags steerPosFlag = 0; NavPolyId steerPosRef = NavPolyId.Null; if (!GetSteerTarget(navMeshQuery, iterPos, targetPos, SLOP, path, ref steerPos, ref steerPosFlag, ref steerPosRef)) break; bool endOfPath = (steerPosFlag & StraightPathFlags.End) != 0 ? true : false; bool offMeshConnection = (steerPosFlag & StraightPathFlags.OffMeshConnection) != 0 ? true : false; //find movement delta SVector3 delta = steerPos - iterPos; float len = (float)Math.Sqrt(SVector3.Dot(delta, delta)); //if steer target is at end of path or off-mesh link //don't move past location if ((endOfPath || offMeshConnection) && len < STEP_SIZE) len = 1; else len = STEP_SIZE / len; SVector3 moveTgt = new SVector3(); VMad(ref moveTgt, iterPos, delta, len); //move SVector3 result = new SVector3(); List<NavPolyId> visited = new List<NavPolyId>(16); NavPoint startPoint = new NavPoint(path[0], iterPos); navMeshQuery.MoveAlongSurface(ref startPoint, ref moveTgt, out result, visited); path.FixupCorridor(visited); npolys = path.Count; float h = 0; navMeshQuery.GetPolyHeight(path[0], result, ref h); result.Y = h; iterPos = result; //handle end of path when close enough if (endOfPath && InRange(iterPos, steerPos, SLOP, 1.0f)) { //reached end of path iterPos = targetPos; if (smoothPath.Count < smoothPath.Capacity) { smoothPath.Add(iterPos); } break; } //store results if (smoothPath.Count < smoothPath.Capacity) { smoothPath.Add(iterPos); } } }
/// <summary> /// Build a tile and link all the polygons togther, both internally and externally. /// Make sure to link off-mesh connections as well. /// </summary> /// <param name="data">Navigation Mesh data</param> /// <param name="lastRef">Last polygon reference</param> /// <param name="result">Last tile reference</param> public void AddTile(NavMeshBuilder data, int lastRef, ref int result) { //make sure data is in right format PathfindingCommon.NavMeshInfo header = data.Header; //make sure location is free if (GetTileAt(header.X, header.Y, header.Layer) != null) return; //allocate a tile MeshTile tile = null; if (lastRef == 0) { if (nextFree != null) { tile = nextFree; nextFree = tile.Next; tile.Next = null; } } else { //try to relocate tile to specific index with the same salt int tileIndex = DecodePolyIdTile(lastRef); if (tileIndex >= maxTiles) return; //try to find specific tile id from free list MeshTile target = tiles[tileIndex]; MeshTile prev = null; tile = nextFree; while (tile != null && tile != target) { prev = tile; tile = tile.Next; } //couldn't find correct location if (tile != target) return; //remove from freelist if (prev == null) nextFree = tile.Next; else prev.Next = tile.Next; //restore salt tile.Salt = DecodePolyIdSalt(lastRef); } //make sure we could allocate a tile if (tile == null) return; //insert tile into position LookUp Table (lut) int h = ComputeTileHash(header.X, header.Y, tileLookupTableMask); tile.Next = posLookup[h]; posLookup[h] = tile; if (header.BvNodeCount == 0) tile.BVTree = null; //patch header tile.Verts = data.NavVerts; tile.Polys = data.NavPolys; tile.DetailMeshes = data.NavDMeshes; tile.DetailVerts = data.NavDVerts; tile.DetailTris = data.NavDTris; tile.BVTree = data.NavBvTree; tile.OffMeshConnections = data.OffMeshCons; //build links freelist tile.LinksFreeList = 0; tile.Links = new Link[header.MaxLinkCount]; for (int i = 0; i < header.MaxLinkCount; i++) tile.Links[i] = new Link(); tile.Links[header.MaxLinkCount - 1].Next = Link.Null; for (int i = 0; i < header.MaxLinkCount - 1; i++) tile.Links[i].Next = i + 1; //init tile tile.Header = header; tile.Data = data; ConnectIntLinks(ref tile); BaseOffMeshLinks(ref tile); //create connections with neighbor tiles MeshTile[] neis = new MeshTile[32]; int nneis; //connect with layers in current tile nneis = GetTilesAt(header.X, header.Y, neis); for (int j = 0; j < nneis; j++) { if (neis[j] != tile) { ConnectExtLinks(ref tile, ref neis[j], BoundarySide.Internal); ConnectExtLinks(ref neis[j], ref tile, BoundarySide.Internal); } ConnectExtOffMeshLinks(ref tile, ref neis[j], BoundarySide.Internal); ConnectExtOffMeshLinks(ref neis[j], ref tile, BoundarySide.Internal); } //connect with neighbour tiles for (int i = 0; i < 8; i++) { BoundarySide b = (BoundarySide)i; BoundarySide bo = b.GetOpposite(); nneis = GetNeighbourTilesAt(header.X, header.Y, b, neis); for (int j = 0; j < nneis; j++) { ConnectExtLinks(ref tile, ref neis[j], b); ConnectExtLinks(ref neis[j], ref tile, bo); ConnectExtOffMeshLinks(ref tile, ref neis[j], b); ConnectExtOffMeshLinks(ref neis[j], ref tile, bo); } } result = GetTileRef(tile); }
/// <summary> /// Build a tile and link all the polygons togther, both internally and externally. /// Make sure to link off-mesh connections as well. /// </summary> /// <param name="data">Navigation Mesh data</param> /// <param name="lastRef">Last polygon reference</param> /// <param name="result">Last tile reference</param> public void AddTile(NavMeshBuilder data, PolyId lastRef, out PolyId result) { result = PolyId.Null; //make sure data is in right format PathfindingCommon.NavMeshInfo header = data.Header; //make sure location is free if (GetTileAt(header.X, header.Y, header.Layer) != null) { return; } //allocate a tile MeshTile tile = null; if (lastRef == PolyId.Null) { if (nextFree != null) { tile = nextFree; nextFree = tile.Next; tile.Next = null; } } else { //try to relocate tile to specific index with the same salt int tileIndex = lastRef.DecodeTileIndex(polyBits, tileBits); if (tileIndex >= maxTiles) { return; } //try to find specific tile id from free list MeshTile target = tiles[tileIndex]; MeshTile prev = null; tile = nextFree; while (tile != null && tile != target) { prev = tile; tile = tile.Next; } //couldn't find correct location if (tile != target) { return; } //remove from freelist if (prev == null) { nextFree = tile.Next; } else { prev.Next = tile.Next; } //restore salt tile.Salt = lastRef.DecodeSalt(polyBits, tileBits, saltBits); } //make sure we could allocate a tile if (tile == null) { return; } //insert tile into position LookUp Table (lut) int h = ComputeTileHash(header.X, header.Y, tileLookupTableMask); tile.Next = posLookup[h]; posLookup[h] = tile; if (header.BvNodeCount == 0) { tile.BVTree = null; } //patch header tile.Verts = data.NavVerts; tile.Polys = data.NavPolys; tile.DetailMeshes = data.NavDMeshes; tile.DetailVerts = data.NavDVerts; tile.DetailTris = data.NavDTris; tile.BVTree = data.NavBvTree; tile.OffMeshConnections = data.OffMeshCons; //build links freelist tile.LinksFreeList = 0; tile.Links = new Link[header.MaxLinkCount]; for (int i = 0; i < header.MaxLinkCount; i++) { tile.Links[i] = new Link(); } tile.Links[header.MaxLinkCount - 1].Next = Link.Null; for (int i = 0; i < header.MaxLinkCount - 1; i++) { tile.Links[i].Next = i + 1; } //init tile tile.Header = header; tile.Data = data; ConnectIntLinks(ref tile); BaseOffMeshLinks(ref tile); //create connections with neighbor tiles MeshTile[] neis = new MeshTile[32]; int nneis; //connect with layers in current tile nneis = GetTilesAt(header.X, header.Y, neis); for (int j = 0; j < nneis; j++) { if (neis[j] != tile) { ConnectExtLinks(ref tile, ref neis[j], BoundarySide.Internal); ConnectExtLinks(ref neis[j], ref tile, BoundarySide.Internal); } ConnectExtOffMeshLinks(ref tile, ref neis[j], BoundarySide.Internal); ConnectExtOffMeshLinks(ref neis[j], ref tile, BoundarySide.Internal); } //connect with neighbour tiles for (int i = 0; i < 8; i++) { BoundarySide b = (BoundarySide)i; BoundarySide bo = b.GetOpposite(); nneis = GetNeighbourTilesAt(header.X, header.Y, b, neis); for (int j = 0; j < nneis; j++) { ConnectExtLinks(ref tile, ref neis[j], b); ConnectExtLinks(ref neis[j], ref tile, bo); ConnectExtOffMeshLinks(ref tile, ref neis[j], b); ConnectExtOffMeshLinks(ref neis[j], ref tile, bo); } } result = GetTileRef(tile); }
/// <summary> /// Initializes a new instance of the <see cref="NavMesh" /> class. /// </summary> /// <param name="builder">The NavMeshBuilder data</param> public NavMesh(NavMeshBuilder builder) : base(builder) { }
/// <summary> /// Build a tile and link all the polygons togther, both internally and externally. /// Make sure to link off-mesh connections as well. /// </summary> /// <param name="data">Navigation Mesh data</param> /// <param name="lastRef">Last polygon reference</param> /// <param name="result">Last tile reference</param> public PolyId AddTile(NavMeshBuilder data) { //make sure data is in right format PathfindingCommon.NavMeshInfo header = data.Header; //make sure location is free if (GetTileAt(header.X, header.Y, header.Layer) != null) return PolyId.Null; PolyId newTileId = GetNextTileRef(); MeshTile tile = new MeshTile(new Vector2i(header.X, header.Y), header.Layer, idManager, newTileId); tile.Salt = idManager.DecodeSalt(ref newTileId); if (header.BvNodeCount == 0) tile.BVTree = null; //patch header tile.Verts = data.NavVerts; tile.Polys = data.NavPolys; tile.PolyCount = header.PolyCount; tile.DetailMeshes = data.NavDMeshes; tile.DetailVerts = data.NavDVerts; tile.DetailTris = data.NavDTris; tile.BVTree = data.NavBvTree; tile.OffMeshConnections = data.OffMeshCons; tile.OffMeshConnectionCount = header.OffMeshConCount; tile.BvQuantFactor = header.BvQuantFactor; tile.BvNodeCount = header.BvNodeCount; tile.Bounds = header.Bounds; tile.WalkableClimb = header.WalkableClimb; //create connections within tile tile.ConnectIntLinks(); tile.BaseOffMeshLinks(); //create connections with neighbor tiles //connect with layers in current tile foreach (MeshTile layerTile in GetTilesAt(header.X, header.Y)) { if (layerTile != tile) { tile.ConnectExtLinks(layerTile, BoundarySide.Internal); layerTile.ConnectExtLinks(tile, BoundarySide.Internal); } tile.ConnectExtOffMeshLinks(layerTile, BoundarySide.Internal); layerTile.ConnectExtOffMeshLinks(tile, BoundarySide.Internal); } //connect with neighbor tiles for (int i = 0; i < 8; i++) { BoundarySide b = (BoundarySide)i; BoundarySide bo = b.GetOpposite(); foreach (MeshTile neighborTile in GetNeighborTilesAt(header.X, header.Y, b)) { tile.ConnectExtLinks(neighborTile, b); neighborTile.ConnectExtLinks(tile, bo); tile.ConnectExtOffMeshLinks(neighborTile, b); neighborTile.ConnectExtOffMeshLinks(tile, bo); } } AddTileAt(tile, GetNextTileRef()); return newTileId; }
/// <summary> /// Build a tile and link all the polygons togther, both internally and externally. /// Make sure to link off-mesh connections as well. /// </summary> /// <param name="data">Navigation Mesh data</param> /// <param name="lastRef">Last polygon reference</param> /// <param name="result">Last tile reference</param> public PolyId AddTile(NavMeshBuilder data) { //make sure data is in right format PathfindingCommon.NavMeshInfo header = data.Header; //make sure location is free if (GetTileAt(header.X, header.Y, header.Layer) != null) { return(PolyId.Null); } PolyId newTileId = GetNextTileRef(); MeshTile tile = new MeshTile(new Vector2i(header.X, header.Y), header.Layer, idManager, newTileId); tile.Salt = idManager.DecodeSalt(ref newTileId); if (header.BvNodeCount == 0) { tile.BVTree = null; } //patch header tile.Verts = data.NavVerts; tile.Polys = data.NavPolys; tile.PolyCount = header.PolyCount; tile.DetailMeshes = data.NavDMeshes; tile.DetailVerts = data.NavDVerts; tile.DetailTris = data.NavDTris; tile.BVTree = data.NavBvTree; tile.OffMeshConnections = data.OffMeshCons; tile.OffMeshConnectionCount = header.OffMeshConCount; tile.BvQuantFactor = header.BvQuantFactor; tile.BvNodeCount = header.BvNodeCount; tile.Bounds = header.Bounds; tile.WalkableClimb = header.WalkableClimb; //create connections within tile tile.ConnectIntLinks(); tile.BaseOffMeshLinks(); //create connections with neighbor tiles //connect with layers in current tile foreach (MeshTile layerTile in GetTilesAt(header.X, header.Y)) { if (layerTile != tile) { tile.ConnectExtLinks(layerTile, BoundarySide.Internal); layerTile.ConnectExtLinks(tile, BoundarySide.Internal); } tile.ConnectExtOffMeshLinks(layerTile, BoundarySide.Internal); layerTile.ConnectExtOffMeshLinks(tile, BoundarySide.Internal); } //connect with neighbor tiles for (int i = 0; i < 8; i++) { BoundarySide b = (BoundarySide)i; BoundarySide bo = b.GetOpposite(); foreach (MeshTile neighborTile in GetNeighborTilesAt(header.X, header.Y, b)) { tile.ConnectExtLinks(neighborTile, b); neighborTile.ConnectExtLinks(tile, bo); tile.ConnectExtOffMeshLinks(neighborTile, b); neighborTile.ConnectExtOffMeshLinks(tile, bo); } } AddTileAt(tile, GetNextTileRef()); return(newTileId); }