public void BuildRegions_Success()
        {
            //Build a 3x3 heightfield
            Heightfield hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), (float)(1.0f / 3.0f), 0.02f);
            for (int i = 0; i < 9; i++)
            {
                hf[i].AddSpan(new Span(10, 20, Area.Default));
                hf[i].AddSpan(new Span(25, 30, Area.Default));
            }
            CompactHeightfield chf = new CompactHeightfield(hf, 2, 1);

            chf.BuildDistanceField();
            chf.BuildRegions(1, 2, 3);

            //Most spans do not have a region id because those spans are part of the border
            //Most region ids won't be assigned to any span

            //Total number of regions right now
            Assert.AreEqual(chf.MaxRegions, 7);

            //Center spans should have region id
            Assert.AreEqual((int)chf.Spans[4 * 2 + 0].Region, 5);
            Assert.AreEqual((int)chf.Spans[4 * 2 + 1].Region, 6);

            //Check that the rest of the region ids are not assigned to a span
            for (int i = 0; i < chf.Spans.Length; i++)
            {
                for (int j = 0; j <= 4; j++)
                {
                    Assert.AreNotEqual((int)chf.Spans[i].Region, j);
                }
            }
        }
Пример #2
0
        /// <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);
        }
Пример #3
0
		/// <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;
		}
        public void ConvertSpans_OneCell()
        {
            Heightfield hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.02f);
            hf[0].AddSpan(new Span(10, 20, Area.Default));
            hf[0].AddSpan(new Span(25, 30, Area.Default));

            CompactHeightfield chf = new CompactHeightfield(hf, 2, 1);

            Assert.AreEqual(chf.Spans.Length, 2);

            Assert.AreEqual(chf.Spans[0].Minimum, 20);
            Assert.AreEqual(chf.Spans[0].Height, 5);

            Assert.AreEqual(chf.Spans[1].Minimum, 30);
            Assert.AreEqual(chf.Spans[1].Height, int.MaxValue);
        }
Пример #5
0
		public void ConvertSpans_TwoCells()
		{
			Heightfield hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.02f);
			hf[0].AddSpan(new Span(10, 20, Area.Default));
			hf[0].AddSpan(new Span(25, 30, Area.Default));
			hf[1].AddSpan(new Span(5, 15, Area.Default));
			hf[1].AddSpan(new Span(25, 30, Area.Default));
			hf[1].AddSpan(new Span(40, 55, Area.Default));
			CompactHeightfield chf = new CompactHeightfield(hf, 2, 1);

			Assert.AreEqual(chf.Cells.Length, 4);

			Assert.AreEqual(chf.Cells[0].StartIndex, 0);
			Assert.AreEqual(chf.Cells[0].Count, 2);
			Assert.AreEqual(chf.Cells[1].StartIndex, 2);
			Assert.AreEqual(chf.Cells[1].Count, 3);
		}
Пример #6
0
		public void SetConnection_TwoCells()
		{
			Heightfield hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.02f);
			hf[0].AddSpan(new Span(10, 20, Area.Default));
			hf[0].AddSpan(new Span(25, 30, Area.Default));
			hf[1].AddSpan(new Span(10, 21, Area.Default));
			hf[1].AddSpan(new Span(25, 30, Area.Default));
			CompactHeightfield chf = new CompactHeightfield(hf, 2, 1);

			Assert.IsTrue(chf.Spans[0].IsConnected(Direction.East));
			Assert.IsTrue(chf.Spans[1].IsConnected(Direction.East));
			Assert.IsTrue(chf.Spans[2].IsConnected(Direction.West));
			Assert.IsTrue(chf.Spans[3].IsConnected(Direction.West));

			Assert.AreEqual(chf.Spans[0].ConnectionEast, 0);
			Assert.AreEqual(chf.Spans[1].ConnectionEast, 1);
			Assert.AreEqual(chf.Spans[2].ConnectionWest, 0);
			Assert.AreEqual(chf.Spans[3].ConnectionWest, 1);
		}
Пример #7
0
        private void GenerateNavMesh()
        {
            Console.WriteLine("Generating NavMesh");

            Stopwatch sw = new Stopwatch();
            sw.Start();
            long prevMs = 0;
            try
            {
                //level.SetBoundingBoxOffset(new SVector3(settings.CellSize * 0.5f, settings.CellHeight * 0.5f, settings.CellSize * 0.5f));
                var levelTris = level.GetTriangles();
                var triEnumerable = TriangleEnumerable.FromTriangle(levelTris, 0, levelTris.Length);
                BBox3 bounds = triEnumerable.GetBoundingBox();

                heightfield = new Heightfield(bounds, settings);

                Console.WriteLine("Heightfield");
                Console.WriteLine(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                /*Area[] areas = AreaGenerator.From(triEnumerable, Area.Default)
                    .MarkAboveHeight(areaSettings.MaxLevelHeight, Area.Null)
                    .MarkBelowHeight(areaSettings.MinLevelHeight, Area.Null)
                    .MarkBelowSlope(areaSettings.MaxTriSlope, Area.Null)
                    .ToArray();
                heightfield.RasterizeTrianglesWithAreas(levelTris, areas);*/
                heightfield.RasterizeTriangles(levelTris, Area.Default);

                Console.WriteLine(" + Rasterization\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                Console.WriteLine(" + Filtering");
                prevMs = sw.ElapsedMilliseconds;

                heightfield.FilterLedgeSpans(settings.VoxelAgentHeight, settings.VoxelMaxClimb);

                Console.WriteLine("   + Ledge Spans\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                heightfield.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb);

                Console.WriteLine("   + Low Hanging Obstacles\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                heightfield.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight);

                Console.WriteLine("   + Low Height Spans\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield = new CompactHeightfield(heightfield, settings);

                Console.WriteLine("CompactHeightfield");
                Console.WriteLine(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield.Erode(settings.VoxelAgentRadius);

                Console.WriteLine(" + Erosion\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield.BuildDistanceField();

                Console.WriteLine(" + Distance Field\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield.BuildRegions(0, settings.MinRegionSize, settings.MergedRegionSize);

                Console.WriteLine(" + Regions\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                Random r = new Random();
                regionColors = new Color4[compactHeightfield.MaxRegions];
                regionColors[0] = Color4.Black;
                for (int i = 1; i < regionColors.Length; i++)
                    regionColors[i] = new Color4((byte)r.Next(0, 255), (byte)r.Next(0, 255), (byte)r.Next(0, 255), 255);

                Console.WriteLine(" + Colors\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                contourSet = compactHeightfield.BuildContourSet(settings);

                Console.WriteLine("ContourSet");
                Console.WriteLine(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                polyMesh = new PolyMesh(contourSet, settings);

                Console.WriteLine("PolyMesh");
                Console.WriteLine(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                polyMeshDetail = new PolyMeshDetail(polyMesh, compactHeightfield, settings);

                Console.WriteLine("PolyMeshDetail");
                Console.WriteLine(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                hasGenerated = true;

            }
            catch (Exception e)
            {
                if (!interceptExceptions)
                    throw;
                else
                    Console.WriteLine("Navmesh generation failed with exception:" + Environment.NewLine + e.ToString());
            }
            finally
            {
                sw.Stop();
            }

            if (hasGenerated)
            {
                try
                {
                    GeneratePathfinding();

                    //Pathfinding with multiple units
                    GenerateCrowd();
                }
                catch (Exception e)
                {
                    Console.WriteLine("Pathfinding generation failed with exception" + Environment.NewLine + e.ToString());
                    hasGenerated = false;
                }

                Label l = (Label)statusBar.FindChildByName("GenTime");
                l.Text = "Generation Time: " + sw.ElapsedMilliseconds + "ms";

                Console.WriteLine("Navmesh generated successfully in " + sw.ElapsedMilliseconds + "ms.");
                Console.WriteLine("Rasterized " + level.GetTriangles().Length + " triangles.");
                Console.WriteLine("Generated " + contourSet.Count + " regions.");
                Console.WriteLine("PolyMesh contains " + polyMesh.VertCount + " vertices in " + polyMesh.PolyCount + " polys.");
                Console.WriteLine("PolyMeshDetail contains " + polyMeshDetail.VertCount + " vertices and " + polyMeshDetail.TrisCount + " tris in " + polyMeshDetail.MeshCount + " meshes.");
            }
        }
Пример #8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CompactHeightfield"/> class.
        /// </summary>
        /// <param name="field">A <see cref="Heightfield"/> to build from.</param>
        /// <param name="walkableHeight">The maximum difference in height to filter.</param>
        /// <param name="walkableClimb">The maximum difference in slope to filter.</param>
        public CompactHeightfield(Heightfield field, int walkableHeight, int walkableClimb)
        {
            this.bounds = field.Bounds;
            this.width = field.Width;
            this.height = field.Height;
            this.length = field.Length;
            this.cellSize = field.CellSizeXZ;
            this.cellHeight = field.CellHeight;

            int spanCount = field.SpanCount;
            cells = new CompactCell[width * length];
            spans = new CompactSpan[spanCount];
            areas = new Area[spanCount];

            //iterate over the Heightfield's cells
            int spanIndex = 0;
            for (int i = 0; i < cells.Length; i++)
            {
                //get the heightfield span list, skip if empty
                var fs = field[i].Spans;
                if (fs.Count == 0)
                    continue;

                CompactCell c = new CompactCell(spanIndex, 0);

                //convert the closed spans to open spans
                int lastInd = fs.Count - 1;
                for (int j = 0; j < lastInd; j++)
                {
                    var s = fs[j];
                    if (s.Area.IsWalkable)
                    {
                        CompactSpan.FromMinMax(s.Maximum, fs[j + 1].Minimum, out spans[spanIndex]);
                        areas[spanIndex] = s.Area;
                        spanIndex++;
                        c.Count++;
                    }
                }

                //the last closed span that has an "infinite" height
                var lastS = fs[lastInd];
                if (lastS.Area.IsWalkable)
                {
                    spans[spanIndex] = new CompactSpan(fs[lastInd].Maximum, int.MaxValue);
                    areas[spanIndex] = lastS.Area;
                    spanIndex++;
                    c.Count++;
                }

                cells[i] = c;
            }

            //set neighbor connections
            for (int z = 0; z < length; z++)
            {
                for (int x = 0; x < width; x++)
                {
                    CompactCell c = cells[z * width + x];
                    for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
                    {
                        CompactSpan s = spans[i];

                        for (var dir = Direction.West; dir <= Direction.South; dir++)
                        {
                            int dx = x + dir.GetHorizontalOffset();
                            int dz = z + dir.GetVerticalOffset();

                            if (dx < 0 || dz < 0 || dx >= width || dz >= length)
                                continue;

                            CompactCell dc = cells[dz * width + dx];
                            for (int j = dc.StartIndex, cellEnd = dc.StartIndex + dc.Count; j < cellEnd; j++)
                            {
                                CompactSpan ds = spans[j];

                                int overlapBottom, overlapTop;
                                CompactSpan.OverlapMin(ref s, ref ds, out overlapBottom);
                                CompactSpan.OverlapMax(ref s, ref ds, out overlapTop);

                                //Make sure that the agent can walk to the next span and that the span isn't a huge drop or climb
                                if ((overlapTop - overlapBottom) >= walkableHeight && Math.Abs(ds.Minimum - s.Minimum) <= walkableClimb)
                                {
                                    int con = j - dc.StartIndex;
                                    CompactSpan.SetConnection(dir, con, ref spans[i]);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
Пример #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CompactHeightfield"/> class.
 /// </summary>
 /// <param name="field">A <see cref="Heightfield"/> to build from.</param>
 /// <param name="settings">The settings to build with.</param>
 public CompactHeightfield(Heightfield field, NavMeshGenerationSettings settings)
     : this(field, settings.VoxelAgentHeight, settings.VoxelMaxClimb)
 {
 }
Пример #10
0
		public void Filter_LowHangingWalkable_Success()
		{
			var hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.02f);
			var span = new Span(10, 15, Area.Default);
			var span2 = new Span(16, 20, Area.Null);

			hf[0].AddSpan(span);
			hf[0].AddSpan(span2);

			hf.FilterLowHangingWalkableObstacles(20);

			Assert.AreEqual(hf[0].Spans[0].Area, hf[0].Spans[1].Area);
		}
Пример #11
0
		public void Indexer_CellOutOfRange_Throws()
		{
			var hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.5f);
			Assert.Throws<ArgumentOutOfRangeException>(() => { var c = hf[5]; });
		}
Пример #12
0
		public void Indexer_Valid_ReturnsCell()
		{
			var hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.5f);

			Assert.IsNotNull(hf[0, 1]);
		}
Пример #13
0
		public void Filter_WalkableLowHeight_Success()
		{
			var hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.02f);
			var span = new Span(10, 20, Area.Default);
			var span2 = new Span(25, 30, Area.Default);

			hf[0].AddSpan(span);
			hf[0].AddSpan(span2);

			//too low to walk through. there is only a gap of 5 units to walk through,
			//but at least 15 units is needed
			hf.FilterWalkableLowHeightSpans(15);

			//so one span is unwalkable and the other is fine
			Assert.AreEqual(hf[0].Spans[0].Area, Area.Null);
			Assert.AreEqual(hf[0].Spans[1].Area, Area.Default);
		}
Пример #14
0
		public void Filter_LowHangingWalkable_Fail()
		{
			var hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.5f, 0.02f);
			var span = new Span(1, 2, Area.Default);
			var span2 = new Span(10, 20, Area.Null);

			hf[2].AddSpan(span);
			hf[2].AddSpan(span2);

			//walkable step cannot cover the gap (difference between span2 maximum and span 1 maximum) so fail
			hf.FilterLowHangingWalkableObstacles(10);
			Assert.AreNotEqual(hf[0, 1].Spans[0].Area, hf[0, 1].Spans[1].Area);
		}
Пример #15
0
        public void DistanceField_Simple_Success()
        {
            //Build a 3x3 heightfield
            Heightfield hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), (float)(1.0f/3.0f), 0.02f);
            for (int i = 0; i < 9; i++)
            {
                hf[i].AddSpan(new Span(10, 20, Area.Default));
                hf[i].AddSpan(new Span(25, 30, Area.Default));
            }
            CompactHeightfield chf = new CompactHeightfield(hf, 2, 1);

            //make sure connections are set
            Assert.AreEqual(chf.Spans[0 * 2].ConnectionCount, 2); //corner
            Assert.AreEqual(chf.Spans[1 * 2].ConnectionCount, 3); //edge
            Assert.AreEqual(chf.Spans[2 * 2].ConnectionCount, 2); //corner
            Assert.AreEqual(chf.Spans[3 * 2].ConnectionCount, 3); //edge
            Assert.AreEqual(chf.Spans[4 * 2].ConnectionCount, 4); //center

            chf.BuildDistanceField();

            //check distance field values
            Assert.AreEqual(chf.MaxDistance, 2);

            //1st row
            Assert.AreEqual(chf.Distances[0 * 2], 0); //boundary
            Assert.AreEqual(chf.Distances[1 * 2], 0); //boundary
            Assert.AreEqual(chf.Distances[2 * 2], 0); //boundary

            //2nd row
            Assert.AreEqual(chf.Distances[3 * 2], 0); //boundary
            Assert.AreEqual(chf.Distances[4 * 2], 2); //center span
            Assert.AreEqual(chf.Distances[5 * 2], 0); //boundary
        }
Пример #16
0
        public void DistanceField_Medium_Success()
        {
            //Build a 5x5 heightfield
            Heightfield hf = new Heightfield(new BBox3(Vector3.Zero, Vector3.One), 0.2f, 0.02f);
            for (int i = 0; i < 25; i++)
            {
                hf[i].AddSpan(new Span(10, 20, Area.Default));
                hf[i].AddSpan(new Span(25, 30, Area.Default));
            }
            CompactHeightfield chf = new CompactHeightfield(hf, 2, 1);

            chf.BuildDistanceField();

            //Before box blur, MaxDistance is 4
            //After box blur, MaxDistance is 2
            Assert.AreEqual(chf.MaxDistance, 2);

            //1st row
            Assert.AreEqual(chf.Distances[0 * 2], 0); //boundary
            Assert.AreEqual(chf.Distances[4 * 2], 0); //boundary

            //2nd row
            Assert.AreEqual(chf.Distances[5 * 2], 0); //boundary
            Assert.AreEqual(chf.Distances[6 * 2], 2); //near boundary
            Assert.AreEqual(chf.Distances[7 * 2], 2); //near boundary
            Assert.AreEqual(chf.Distances[8 * 2], 2); //near boundary
            Assert.AreEqual(chf.Distances[9 * 2], 0); //boundary

            //3rd row
            Assert.AreEqual(chf.Distances[10 * 2], 0); //boundary
            Assert.AreEqual(chf.Distances[11 * 2], 2); //near boundary
            Assert.AreEqual(chf.Distances[12 * 2], 2); //center (box blurred distance is (2*8 + 5)/9)
            Assert.AreEqual(chf.Distances[13 * 2], 2); //near boundary
            Assert.AreEqual(chf.Distances[14 * 2], 0); //boundary
        }