Exemplo n.º 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Heightfield"/> class.
        /// </summary>
        /// <param name="b">The world-space bounds.</param>
        /// <param name="cellSize">The world-space size of each cell in the XZ plane.</param>
        /// <param name="cellHeight">The world-space height of each cell.</param>
        public Heightfield(BBox3 b, float cellSize, float cellHeight)
        {
            if (!BBox3.IsValid(ref bounds))
                throw new ArgumentException("The bounds are considered invalid. See BBox3.IsValid for details.");

            if (cellSize <= 0)
                throw new ArgumentOutOfRangeException("cellSize", "Cell size must be greater than 0.");

            if (cellHeight <= 0)
                throw new ArgumentOutOfRangeException("cellHeight", "Cell height must be greater than 0.");

            this.cellSize = cellSize;
            this.cellHeight = cellHeight;
            this.bounds = b;

            //make sure the bbox contains all the possible voxels.
            width = (int)Math.Ceiling((b.Max.X - b.Min.X) / cellSize);
            height = (int)Math.Ceiling((b.Max.Y - b.Min.Y) / cellHeight);
            length = (int)Math.Ceiling((b.Max.Z - b.Min.Z) / cellSize);

            bounds.Max.X = bounds.Min.X + width * cellSize;
            bounds.Max.Y = bounds.Min.Y + height * cellHeight;
            bounds.Max.Z = bounds.Min.Z + length * cellSize;

            cells = new Cell[width * length];
            for (int i = 0; i < cells.Length; i++)
                cells[i] = new Cell(height);
        }
Exemplo n.º 2
0
 /// <summary>
 /// Generate a new random vector with an optional bounds.
 /// </summary>
 /// <param name="random">The `Random` object to generate from</param>
 /// <param name="bounds">If specified, the bounds within which the
 /// vectors will be generated. If not specified, vectors will be
 /// generated in the range (-1,-1,-1) to (1,1,1).</param>
 /// <returns></returns>
 public static Vector3 NextVector(this Random random, BBox3 bounds = default)
 {
     if (bounds == default)
     {
         bounds = new BBox3((-1, -1, -1), (1, 1, 1));
     }
     return(new Vector3(
                random.NextDouble().MapToDomain(bounds.XDomain),
                random.NextDouble().MapToDomain(bounds.YDomain),
                random.NextDouble().MapToDomain(bounds.ZDomain)));
 }
Exemplo n.º 3
0
        /// <summary>
        /// Construct a beam system under a slab.
        /// </summary>
        /// <param name="floor">The Floor under which to create Beams.</param>
        /// <param name="count">The number of Beams to create.</param>
        /// <param name="profile">The Profile to be used for all Beams.</param>
        /// <param name="material">The Beam material.</param>
        public BeamSystem(Floor floor, int count, Profile profile, Material material = null)
        {
            var edges = floor.Profile.Perimeter.Segments();
            var e1    = edges[0];
            var e2    = edges[2].Reversed();
            var bbox  = new BBox3(profile);
            var depth = bbox.Max.Y - bbox.Min.Y;
            var edge1 = new Line(new Vector3(e1.Start.X, e1.Start.Y, floor.Elevation - depth / 2), new Vector3(e1.End.X, e1.End.Y, floor.Elevation - depth / 2));
            var edge2 = new Line(new Vector3(e2.Start.X, e2.Start.Y, floor.Elevation - depth / 2), new Vector3(e2.End.X, e2.End.Y, floor.Elevation - depth / 2));

            CreateBeamsBetweenEdges(edge1, edge2, count, profile, material);
        }
        // This is identical to the one for RhinoBrep? Not sure how to make generic
        public static List <Envelope> SliceAtHeight(RhinoExtrusion rhinoBrep, double cutHeight, Boolean showDebugGeometry)
        {
            var vertices = new List <Vector3>();

            foreach (var solidOp in rhinoBrep.Representation.SolidOperations)
            {
                vertices.AddRange(solidOp.Solid.Vertices.Values.Select(v => TransformedPoint(v.Point, solidOp.LocalTransform)).ToList());
            }
            var bbox = new BBox3(TransformedPoints(vertices, rhinoBrep.Transform));
            var env  = new Envelope(Polygon.Rectangle(new Vector3(bbox.Min.X, bbox.Min.Y), new Vector3(bbox.Max.X, bbox.Max.Y)), bbox.Min.Z, bbox.Max.Z - bbox.Min.Z, Vector3.ZAxis, 0, new Transform(), _debugMaterial, rhinoBrep.Representation, false, Guid.NewGuid(), "");

            return(SliceAtHeight(env, cutHeight, showDebugGeometry));
        }
Exemplo n.º 5
0
        public void BBoxToCurves()
        {
            Name = "BBoxToCurves";
            var star     = Polygon.Star(10, 4, 5);
            var flatbbox = new BBox3(star.Vertices);
            var bbox     = new BBox3(new Vector3(4, 2, 5), new Vector3(10, 8, 14));
            var crvs     = bbox.ToModelCurves();

            Model.AddElements(crvs);
            var flatCrvs = flatbbox.ToModelCurves();

            Model.AddElements(flatCrvs);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Add graph section using bounding box, divided by a set of key points.
        /// Key points don't respect "MinimumResolution" at the moment.
        /// Any vertices that already exist are not created but reused.
        /// This way new region is connected with the rest of the graph.
        /// </summary>
        /// <param name="bBox">Box which region is populated with graph.</param>
        /// <param name="keyPoints">Set of 3D points, region is split with.</param>
        public void AddFromBbox(BBox3 bBox, List <Vector3> keyPoints)
        {
            var height   = bBox.Max.Z - bBox.Min.Z;
            var boundary = new Polygon(new List <Vector3>
            {
                new Vector3(bBox.Min.X, bBox.Min.Y),
                new Vector3(bBox.Min.X, bBox.Max.Y),
                new Vector3(bBox.Max.X, bBox.Max.Y),
                new Vector3(bBox.Max.X, bBox.Min.Y)
            }).TransformedPolygon(new Transform(new Vector3(0, 0, bBox.Min.Z)));

            AddFromExtrude(boundary, Vector3.ZAxis, height, keyPoints);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Construct a beam system under a slab.
        /// </summary>
        /// <param name="floor">The floor under which to create beams.</param>
        /// <param name="count">The number of beams to create.</param>
        /// <param name="framingType">The structural framing type to be used for all beams.</param>
        public BeamSystem(Floor floor, int count, StructuralFramingType framingType)
        {
            this.Elements = new List <Element>();
            var edges = floor.Profile.Perimeter.Segments();
            var e1    = edges[0];
            var e2    = edges[2].Reversed();
            var bbox  = new BBox3(framingType.Profile);
            var depth = bbox.Max.Y - bbox.Min.Y;
            var edge1 = new Line(new Vector3(e1.Start.X, e1.Start.Y, floor.Elevation - depth / 2), new Vector3(e1.End.X, e1.End.Y, floor.Elevation - depth / 2));
            var edge2 = new Line(new Vector3(e2.Start.X, e2.Start.Y, floor.Elevation - depth / 2), new Vector3(e2.End.X, e2.End.Y, floor.Elevation - depth / 2));

            CreateBeamsBetweenEdges(edge1, edge2, count, framingType);
        }
Exemplo n.º 8
0
        //private void GenerateNavMesh()
        //{
        //    Console.WriteLine("Generating NavMesh");

        //    long prevMs = 0;
        //    try
        //    {
        //        var levelTris = level.GetTriangles();
        //        var triEnumerable = TriangleEnumerable.FromTriangle(levelTris, 0, levelTris.Length);
        //        BBox3 bounds = triEnumerable.GetBoundingBox();

        //        settings = NavMeshGenerationSettings.Default;
        //        heightfield = new Heightfield(bounds, settings);

        //        heightfield.RasterizeTriangles(levelTris, Area.Default);
        //        heightfield.FilterLedgeSpans(settings.VoxelAgentHeight, settings.VoxelMaxClimb);
        //        heightfield.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb);
        //        heightfield.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight);
        //        compactHeightfield = new CompactHeightfield(heightfield, settings);
        //        compactHeightfield.Erode(settings.VoxelAgentRadius);
        //        compactHeightfield.BuildDistanceField();
        //        compactHeightfield.BuildRegions(0, settings.MinRegionSize, settings.MergedRegionSize);

        //        contourSet = compactHeightfield.BuildContourSet(settings);

        //        polyMesh = new PolyMesh(contourSet, settings);

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


        //        buildData = new NavMeshBuilder(polyMesh, polyMeshDetail, new SharpNav.Pathfinding.OffMeshConnection[0], settings);
        //        tiledNavMesh = new TiledNavMesh(buildData);
        //        navMeshQuery = new NavMeshQuery(tiledNavMesh, 2048);
        //    }
        //    catch (Exception e)
        //    {
        //        //if (!interceptExceptions)
        //        //    throw;
        //        //else
        //        //    Console.WriteLine("Navmesh generation failed with exception:" + Environment.NewLine + e.ToString());
        //    }
        //    finally
        //    {
        //        //sw.Stop();
        //    }
        //}


        private void GenerateNavMesh()
        {
            Console.WriteLine("Generating NavMesh");

            long prevMs = 0;
            //try
            //{
            var   levelTris     = level.GetTriangles();
            var   triEnumerable = TriangleEnumerable.FromTriangle(levelTris, 0, levelTris.Length);
            BBox3 bounds        = triEnumerable.GetBoundingBox();

            settings    = NavMeshGenerationSettings.Default;
            heightfield = new Heightfield(bounds, settings);

            heightfield.RasterizeTriangles(levelTris, Area.Default);
            heightfield.FilterLedgeSpans(settings.VoxelAgentHeight, settings.VoxelMaxClimb);
            heightfield.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb);
            heightfield.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight);
            compactHeightfield = new CompactHeightfield(heightfield, settings);
            compactHeightfield.Erode(settings.VoxelAgentRadius);
            compactHeightfield.BuildDistanceField();
            compactHeightfield.BuildRegions(0, settings.MinRegionSize, settings.MergedRegionSize);

            contourSet = compactHeightfield.BuildContourSet(settings);

            polyMesh = new PolyMesh(contourSet, settings);

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


            buildData    = new NavMeshBuilder(polyMesh, polyMeshDetail, new SharpNav.Pathfinding.OffMeshConnection[0], settings);
            tiledNavMesh = new TiledNavMesh(buildData);
            navMeshQuery = new NavMeshQuery(tiledNavMesh, 2048);
            OutMesh();
            //}
            //catch (Exception e)
            //{
            //    //if (!interceptExceptions)
            //    //    throw;
            //    //else
            //    //    Console.WriteLine("Navmesh generation failed with exception:" + Environment.NewLine + e.ToString());
            //}
            //finally
            //{
            //    //sw.Stop();
            //}
        }
Exemplo n.º 9
0
        /// <summary>
        /// Create a topography from a custom mesh. It is assumed that the mesh is an open mesh that's roughly parallel to the XY plane.
        /// </summary>
        /// <param name="mesh">The mesh geometry of the topography.</param>
        /// <param name="material">The topography's material.</param>
        /// <param name="transform">The topography's transform.</param>
        /// <param name="id">The topography's id.</param>
        /// <param name="name">The topography's name.</param>
        /// <returns></returns>
        public Topography(Mesh mesh, Material material, Transform transform, Guid id, string name) : base(material,
                                                                                                          transform,
                                                                                                          false,
                                                                                                          id,
                                                                                                          name)
        {
            var newMesh = new Mesh(mesh);
            var bbox    = new BBox3(mesh.Vertices.Select(v => v.Position));

            this._minElevation = bbox.Min.Z;
            this._maxElevation = bbox.Max.Z;
            double absoluteMinimumElevation = this.AbsoluteMinimumElevation ?? this.MinElevation - this.DepthBelowMinimumElevation;
            var    nakedBoundaries          = mesh.GetNakedBoundaries();
            var    basePlane = new Plane((0, 0, absoluteMinimumElevation), (0, 0, 1));

            foreach (var polygon in nakedBoundaries)
            {
                // construct bottom
                var bottomPolygon = polygon.Project(basePlane);
                var tess          = new Tess
                {
                    NoEmptyPolygons = true
                };
                tess.AddContour(bottomPolygon.Reversed().ToContourVertexArray());
                tess.Tessellate(WindingRule.Positive, ElementType.Polygons, 3);
                var faceMesh = tess.ToMesh(normal: (0, 0, -1));
                this._baseVerts = new List <Vertex>(faceMesh.Vertices);
                newMesh.AddMesh(faceMesh);
                // construct sides
                var upperSegments = polygon.Segments();
                var lowerSegments = bottomPolygon.Segments();
                for (int i = 0; i < upperSegments.Count(); i++)
                {
                    var topEdge    = upperSegments[i];
                    var bottomEdge = lowerSegments[i];
                    var normal     = topEdge.Direction().Cross(Vector3.ZAxis).Unitized();
                    var a          = newMesh.AddVertex(topEdge.Start, normal: normal);
                    var b          = newMesh.AddVertex(topEdge.End, normal: normal);
                    var c          = newMesh.AddVertex(bottomEdge.End, normal: normal);
                    var d          = newMesh.AddVertex(bottomEdge.Start, normal: normal);
                    newMesh.AddTriangle(a, c, b);
                    newMesh.AddTriangle(a, d, c);
                }
            }
            this.Mesh = newMesh;
        }
Exemplo n.º 10
0
        private void AddLines(long id, Vector3[] vertices, Gltf gltf, int material, Transform t = null)
        {
            var vBuff   = vertices.ToArray();
            var vCount  = vertices.Length;
            var indices = new List <ushort>();

            for (ushort i = 0; i < vertices.Length; i += 2)
            {
                indices.Add(i);
                indices.Add((ushort)(i + 1));
            }
            // var indices = Enumerable.Range(0, vCount).Select(i => (ushort)i).ToArray();
            var bbox = new BBox3(vertices);

            gltf.AddLineLoop($"{id}_curve", _buffer, vBuff, indices.ToArray(), bbox.Min.ToArray(),
                             bbox.Max.ToArray(), 0, (ushort)(vCount - 1), material, MeshPrimitive.ModeEnum.LINES, t);
        }
Exemplo n.º 11
0
        public void ArrayResults(ComponentDefinition def, List <List <Vector3> > targetAnchors)
        {
            var currentX = 0.0;

            foreach (var anchorSet in targetAnchors)
            {
                var bbox           = new BBox3(anchorSet);
                var xForm          = new Transform(currentX - bbox.Min.X, 0, 0);
                var transformedSet = anchorSet.Select(v => xForm.OfPoint(v)).ToList();
                foreach (var anchor in transformedSet)
                {
                    Model.AddElement(new ModelCurve(Polygon.Ngon(10, 0.1), transform: new Transform(anchor)));
                }
                Model.AddElement(def.Instantiate(transformedSet));
                currentX += (bbox.Max.X - bbox.Min.X + 1);
            }
        }
Exemplo n.º 12
0
        private void AddArrow(long id, Vector3 origin, Vector3 direction, Gltf gltf, int material, Transform t)
        {
            var scale = 0.5;
            var end   = origin + direction * scale;
            var up    = direction.IsParallelTo(Vector3.ZAxis) ? Vector3.YAxis : Vector3.ZAxis;
            var tr    = new Transform(Vector3.Origin, direction.Cross(up), direction);

            tr.Rotate(up, -45.0);
            var arrow1  = tr.OfPoint(Vector3.XAxis * 0.1);
            var pts     = new[] { origin, end, end + arrow1 };
            var vBuff   = pts.ToArray();
            var vCount  = 3;
            var indices = Enumerable.Range(0, vCount).Select(i => (ushort)i).ToArray();
            var bbox    = new BBox3(pts);

            gltf.AddLineLoop($"{id}_curve", _buffer, vBuff, indices, bbox.Min.ToArray(),
                             bbox.Max.ToArray(), 0, (ushort)(vCount - 1), material, MeshPrimitive.ModeEnum.LINE_STRIP, t);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Decide which sector the offmesh point is a part of.
        /// </summary>
        /// <param name="pt">The point</param>
        /// <param name="bounds">The bounds</param>
        /// <returns>An integer representing a sector, or -1 if it is not in any.</returns>
        private static int ClassifyOffMeshPoint(Vector3 pt, BBox3 bounds)
        {
            const int PlusX  = 1;
            const int PlusZ  = 2;
            const int MinusX = 4;
            const int MinusZ = 8;

            int outcode = 0;

            outcode |= (pt.X >= bounds.Max.X) ? PlusX : 0;
            outcode |= (pt.Z >= bounds.Max.Z) ? PlusZ : 0;
            outcode |= (pt.X < bounds.Min.X) ? MinusX : 0;
            outcode |= (pt.Z < bounds.Min.Z) ? MinusZ : 0;

            switch (outcode)
            {
            case PlusX:
                return(0);

            case PlusX | PlusZ:
                return(1);

            case PlusZ:
                return(2);

            case MinusX | PlusZ:
                return(3);

            case MinusX:
                return(4);

            case MinusX | MinusZ:
                return(5);

            case MinusZ:
                return(6);

            case PlusX | MinusZ:
                return(7);

            default:
                return(-1);
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Gets the boundary side of a point relative to a bounding box.
        /// </summary>
        /// <param name="pt">A point.</param>
        /// <param name="bounds">A bounding box.</param>
        /// <returns>The point's position relative to the bounding box.</returns>
        public static BoundarySide FromPoint(Vector3 pt, BBox3 bounds)
        {
            const int PlusX  = 0x1;
            const int PlusZ  = 0x2;
            const int MinusX = 0x4;
            const int MinusZ = 0x8;

            int outcode = 0;

            outcode |= (pt.X >= bounds.Max.X) ? PlusX : 0;
            outcode |= (pt.Z >= bounds.Max.Z) ? PlusZ : 0;
            outcode |= (pt.X < bounds.Min.X) ? MinusX : 0;
            outcode |= (pt.Z < bounds.Min.Z) ? MinusZ : 0;

            switch (outcode)
            {
            case PlusX:
                return(BoundarySide.PlusX);

            case PlusX | PlusZ:
                return(BoundarySide.PlusXPlusZ);

            case PlusZ:
                return(BoundarySide.PlusZ);

            case MinusX | PlusZ:
                return(BoundarySide.MinusXPlusZ);

            case MinusX:
                return(BoundarySide.MinusX);

            case MinusX | MinusZ:
                return(BoundarySide.MinusXMinusZ);

            case MinusZ:
                return(BoundarySide.MinusZ);

            case PlusX | MinusZ:
                return(BoundarySide.PlusXMinusZ);

            default:
                return(BoundarySide.Internal);
            }
        }
        public static List <Envelope> SliceAtHeight(MeshElement meshElement, double cutHeight, Boolean showDebugGeometry)
        {
            var bbox   = new BBox3(TransformedVertices(meshElement.Mesh.Vertices, meshElement.Transform));
            var bottom = bbox.Min.Z;
            var top    = bbox.Max.Z;
            var solids = new List <Elements.Geometry.Solids.SolidOperation>();
            var solid  = new Elements.Geometry.Solids.Solid();

            foreach (var face in meshElement.Mesh.Triangles)
            {
                var vertices = TransformedVertices(face.Vertices, meshElement.Transform);
                solid.AddFace(new Polygon(vertices));
            }
            solids.Add(new Elements.Geometry.Solids.ConstructedSolid(solid));
            var rep = new Representation(solids);
            var env = new Envelope(Polygon.Rectangle(new Vector3(bbox.Min.X, bbox.Min.Y), new Vector3(bbox.Max.X, bbox.Max.Y)), bottom, top - bottom, Vector3.ZAxis, 0, new Transform(), _debugMaterial, rep, false, Guid.NewGuid(), "");

            return(SliceAtHeight(env, cutHeight, showDebugGeometry));
        }
Exemplo n.º 16
0
 public ContentElement(string @gltfLocation,
                       BBox3 @boundingBox,
                       double @gltfScaleToMeters,
                       Vector3 @sourceDirection,
                       IList <Symbol> @symbols,
                       Transform @transform           = null,
                       Material @material             = null,
                       Representation @representation = null,
                       bool @isElementDefinition      = false,
                       System.Guid @id = default,
                       string @name    = null)
     : base(transform, material, representation, isElementDefinition, id, name)
 {
     this.GltfLocation      = @gltfLocation;
     this.BoundingBox       = @boundingBox;
     this.GltfScaleToMeters = @gltfScaleToMeters;
     this.SourceDirection   = @sourceDirection;
     this.Symbols           = @symbols;
 }
Exemplo n.º 17
0
        /// <summary>
        /// Compare two objects of type T.
        /// </summary>
        /// <param name="t1">The first object.</param>
        /// <param name="t2">The second object.</param>
        public int Compare(T t1, T t2)
        {
            var a = _getSegment(t1);
            var b = _getSegment(t2);

            var aLeft = a.Start.X <= a.End.X ? a.Start : a.End;
            var bLeft = b.Start.X <= b.End.X ? b.Start : b.End;

            if (aLeft == bLeft)
            {
                // The left-most points of the lines are equal, but the lines
                // themselves are not neccessarily equal. Use the lines'
                // bounding boxes to get the max points.
                var bb1 = new BBox3();
                bb1.Extend(a.Start);
                bb1.Extend(a.End);
                var bb2 = new BBox3();
                bb2.Extend(b.Start);
                bb2.Extend(b.End);
                if (bb1.Max.Y > bb2.Max.Y)
                {
                    return(-1);
                }
                else if (bb1.Max.Y < bb2.Max.Y)
                {
                    return(1);
                }
                return(0);
            }
            else
            {
                if (aLeft.Y.ApproximatelyEquals(bLeft.Y))
                {
                    return(0);
                }
                if (aLeft.Y > bLeft.Y)
                {
                    return(-1);
                }
                return(1);
            }
        }
Exemplo n.º 18
0
        private static void AddLines(long id,
                                     IList <Vector3> vertices,
                                     Gltf gltf,
                                     int material,
                                     List <byte> buffer,
                                     List <BufferView> bufferViews,
                                     List <Accessor> accessors,
                                     List <glTFLoader.Schema.Mesh> meshes,
                                     List <glTFLoader.Schema.Node> nodes,
                                     bool lineLoop,
                                     Transform t = null)
        {
            var floatSize  = sizeof(float);
            var ushortSize = sizeof(ushort);
            var vBuff      = new byte[vertices.Count * 3 * floatSize];
            var indices    = new byte[vertices.Count * 2 * ushortSize];

            var vi = 0;
            var ii = 0;

            for (var i = 0; i < vertices.Count; i++)
            {
                var v = vertices[i];
                System.Buffer.BlockCopy(BitConverter.GetBytes((float)v.X), 0, vBuff, vi, floatSize);
                System.Buffer.BlockCopy(BitConverter.GetBytes((float)v.Y), 0, vBuff, vi + floatSize, floatSize);
                System.Buffer.BlockCopy(BitConverter.GetBytes((float)v.Z), 0, vBuff, vi + 2 * floatSize, floatSize);
                vi += 3 * floatSize;

                var write = lineLoop ? (i < vertices.Count - 1) : (i % 2 == 0 && i < vertices.Count - 1);
                if (write)
                {
                    System.Buffer.BlockCopy(BitConverter.GetBytes((ushort)i), 0, indices, ii, ushortSize);
                    System.Buffer.BlockCopy(BitConverter.GetBytes((ushort)(i + 1)), 0, indices, ii + ushortSize, ushortSize);
                    ii += 2 * ushortSize;
                }
            }

            var bbox = new BBox3(vertices);

            gltf.AddLineLoop($"{id}_curve", buffer, bufferViews, accessors, vBuff, indices, bbox.Min.ToArray(),
                             bbox.Max.ToArray(), 0, (ushort)(vertices.Count - 1), material, MeshPrimitive.ModeEnum.LINES, meshes, nodes, t);
        }
Exemplo n.º 19
0
        public ContentElement(string @gltfLocation, BBox3 @boundingBox, double @gltfScaleToMeters, Vector3 @sourceDirection, Transform @transform, Material @material, Representation @representation, bool @isElementDefinition, System.Guid @id, string @name)
            : base(transform, material, representation, isElementDefinition, id, name)
        {
            var validator = Validator.Instance.GetFirstValidatorForType <ContentElement>();

            if (validator != null)
            {
                validator.PreConstruct(new object[] { @gltfLocation, @boundingBox, @gltfScaleToMeters, @sourceDirection, @transform, @material, @representation, @isElementDefinition, @id, @name });
            }

            this.GltfLocation      = @gltfLocation;
            this.BoundingBox       = @boundingBox;
            this.GltfScaleToMeters = @gltfScaleToMeters;
            this.SourceDirection   = @sourceDirection;

            if (validator != null)
            {
                validator.PostConstruct(this);
            }
        }
Exemplo n.º 20
0
        public void AdaptiveGridSubtractBoxSmallDifference()
        {
            var adaptiveGrid = new AdaptiveGrid(new Transform());
            var polygon      = Polygon.Rectangle(new Vector3(-41, -51), new Vector3(-39, -49));

            var points = new List <Vector3>();

            points.Add(new Vector3(-40, -49.9, 1));
            points.Add(new Vector3(-40, -49.80979, 1));

            adaptiveGrid.AddFromExtrude(polygon, Vector3.ZAxis, 2, points);
            Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(-40, -49.9, 1), out _));
            Assert.True(adaptiveGrid.TryGetVertexIndex(new Vector3(-40, -49.9, 2), out _));

            var box = new BBox3(new Vector3(-40.2, -50.190211303259034, 0),
                                new Vector3(-39.8, -49.809788696740966, 2));

            adaptiveGrid.SubtractBox(box);
            Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(-40, -49.9, 1), out _));
            Assert.False(adaptiveGrid.TryGetVertexIndex(new Vector3(-40, -49.9, 2), out _));
        }
Exemplo n.º 21
0
        private void GetRenderDataForElement(Element e, Gltf gltf, Dictionary <string, int> materials)
        {
            if (e is ITessellateMesh)
            {
                var       mp        = e as ITessellateMesh;
                var       mesh      = mp.Mesh();
                Transform transform = null;
                if (e.Transform != null)
                {
                    transform = e.Transform;
                }
                gltf.AddTriangleMesh(e.Id + "_mesh", _buffer, mesh.Vertices.ToArray(), mesh.Normals.ToArray(),
                                     mesh.Indices.ToArray(), mesh.VertexColors.ToArray(),
                                     mesh.VMin, mesh.VMax, mesh.NMin, mesh.NMax, mesh.CMin, mesh.CMax,
                                     mesh.IMin, mesh.IMax, materials[e.Material.Name], null, transform);
            }

            if (e is ITessellateCurves)
            {
                var cp      = e as ITessellateCurves;
                var curves  = cp.Curves();
                var counter = 0;
                foreach (var c in curves)
                {
                    var vBuff   = c.ToArray();
                    var indices = Enumerable.Range(0, c.Count).Select(i => (ushort)i).ToArray();
                    var bbox    = new BBox3(c);
                    gltf.AddLineLoop($"{e.Id}_curve_{counter}", _buffer, vBuff, indices, bbox.Min.ToArray(),
                                     bbox.Max.ToArray(), 0, (ushort)(c.Count - 1), materials[BuiltInMaterials.Black.Name], e.Transform);
                }
            }

            if (e.SubElements.Count > 0)
            {
                foreach (var esub in e.SubElements)
                {
                    GetRenderDataForElement(esub, gltf, materials);
                }
            }
        }
Exemplo n.º 22
0
        private static List <Elements.Geometry.Solids.Face> filterFaces(IEnumerable <Elements.Geometry.Solids.Face> faces, Circle maxCircle)
        {
            var filtered  = new List <Elements.Geometry.Solids.Face>();
            var elevation = maxCircle.Center.Z;
            var xyOrigin  = new Vector3(maxCircle.Center.X, maxCircle.Center.Y);

            foreach (var face in faces)
            {
                var poly     = face.Outer.ToPolygon();
                var normal   = poly.Normal();
                var centroid = poly.Centroid();

                var rayvec = centroid - maxCircle.Center;
                //check normal
                var rayvecU = rayvec.Unitized();
                if (rayvecU.Dot(normal) >= 0)
                {
                    continue;
                }

                //check distance
                var rayVecProjected = new Vector3(rayvec.X, rayvec.Y);
                if (rayVecProjected.Length() > maxCircle.Radius)
                {
                    continue;
                }

                //check elevation
                var bbox = new BBox3(poly.Vertices);
                if (bbox.Min.Z > elevation || bbox.Max.Z < elevation)
                {
                    continue;
                }


                filtered.Add(face);
            }
            return(filtered);
        }
Exemplo n.º 23
0
        /// <summary>
        /// Create a Grid2d from a list of boundary polygons and an optional transform.
        /// If the transform is null or not supplied, a transform will be generated automatically from the boundaries' normal.
        /// Currently only transforms parallel to the supplied polygons are supported.
        /// The polygons' bounding box parallel to the supplied transform will be
        /// used as the grid extents.
        /// </summary>
        /// <param name="boundaries">The external boundaries of this grid system.</param>
        /// <param name="transform">A transform representing the alignment of the grid.</param>
        public Grid2d(IList <Polygon> boundaries, Transform transform = null)
        {
            if (transform == null)
            {
                //if no transform is supplied, calculate one from the normal.
                var planeTransform = boundaries.First().Vertices.ToTransform();
                // If we are calculating the transform automatically, then the user has not
                // supplied any rotational orientation information; we only care about
                // direction. So if the polygon is nearly XY-parallel, let's just use
                // the XY plane at the boundary's location to be consistent with default behavior.
                transform = Math.Abs(planeTransform.ZAxis.Dot(Vector3.ZAxis)).ApproximatelyEquals(1) ?
                            new Transform(planeTransform.Origin) :
                            planeTransform;
            }

            fromGrid = new Transform(transform);
            toGrid   = new Transform(transform);
            toGrid.Invert();

            var transformedBoundaries = toGrid.OfPolygons(boundaries);

            // verify that all boundaries are in XY plane after transform
            foreach (var boundary in transformedBoundaries)
            {
                foreach (var vertex in boundary.Vertices)
                {
                    if (!vertex.Z.ApproximatelyEquals(0))
                    {
                        throw new Exception("The Grid2d could not be constructed. After transform, this polygon was not in the XY Plane. Please ensure that all your geometry as well as any provided transform all lie in the same plane.");
                    }
                }
            }
            var bbox = new BBox3(transformedBoundaries);

            boundariesInGridSpace = transformedBoundaries;

            InitializeUV(new Domain1d(bbox.Min.X, bbox.Max.X), new Domain1d(bbox.Min.Y, bbox.Max.Y));
        }
Exemplo n.º 24
0
        /// <summary>
        /// Convert a Bounding Box to a set of model curves.
        /// </summary>
        /// <param name="box">The bounding box to convert.</param>
        /// <param name="context">An optional transform in which these curves should be drawn.</param>
        /// <param name="material">An optional material to use for these curves.</param>
        public static IList <ModelCurve> ToModelCurves(this BBox3 box, Transform context = null, Material material = null)
        {
            var mat = material ?? BuiltInMaterials.Black;
            var min = box.Min;
            var max = box.Max;
            var a   = new Vector3(min.X, min.Y, min.Z);
            var b   = new Vector3(max.X, min.Y, min.Z);
            var c   = new Vector3(max.X, max.Y, min.Z);
            var d   = new Vector3(min.X, max.Y, min.Z);
            var e   = new Vector3(min.X, min.Y, max.Z);
            var f   = new Vector3(max.X, min.Y, max.Z);
            var g   = new Vector3(max.X, max.Y, max.Z);
            var h   = new Vector3(min.X, max.Y, max.Z);
            var mc  = new List <ModelCurve>();
            Action <Vector3, Vector3> tryAddLine = (Vector3 from, Vector3 to) =>
            {
                if (from.DistanceTo(to) > Vector3.EPSILON)
                {
                    mc.Add(new ModelCurve(new Line(from, to), mat, context));
                }
            };

            tryAddLine(a, b);
            tryAddLine(b, c);
            tryAddLine(c, d);
            tryAddLine(d, a);
            tryAddLine(e, f);
            tryAddLine(f, g);
            tryAddLine(g, h);
            tryAddLine(h, e);
            tryAddLine(a, e);
            tryAddLine(b, f);
            tryAddLine(c, g);
            tryAddLine(d, h);

            return(mc);
        }
Exemplo n.º 25
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Heightfield"/> class.
        /// </summary>
        /// <param name="b">The world-space bounds.</param>
        /// <param name="cellSize">The world-space size of each cell in the XZ plane.</param>
        /// <param name="cellHeight">The world-space height of each cell.</param>
        public Heightfield(BBox3 b, float cellSize, float cellHeight)
        {
            if (!BBox3.IsValid(ref bounds))
            {
                throw new ArgumentException("The bounds are considered invalid. See BBox3.IsValid for details.");
            }

            if (cellSize <= 0)
            {
                throw new ArgumentOutOfRangeException("cellSize", "Cell size must be greater than 0.");
            }

            if (cellHeight <= 0)
            {
                throw new ArgumentOutOfRangeException("cellHeight", "Cell height must be greater than 0.");
            }

            this.cellSize   = cellSize;
            this.cellHeight = cellHeight;
            this.bounds     = b;

            //make sure the bbox contains all the possible voxels.
            width  = (int)Math.Ceiling((b.Max.X - b.Min.X) / cellSize);
            height = (int)Math.Ceiling((b.Max.Y - b.Min.Y) / cellHeight);
            length = (int)Math.Ceiling((b.Max.Z - b.Min.Z) / cellSize);

            bounds.Max.X = bounds.Min.X + width * cellSize;
            bounds.Max.Y = bounds.Min.Y + height * cellHeight;
            bounds.Max.Z = bounds.Min.Z + length * cellSize;

            cells = new Cell[width * length];
            for (int i = 0; i < cells.Length; i++)
            {
                cells[i] = new Cell(height);
            }
        }
Exemplo n.º 26
0
        /// <summary>
        /// Create a Grid2d from a list of boundary polygons and an optional transform.
        /// If the plane is null or not supplied, the identity transform will be used for the grid origin and orientation.
        /// Currently only transforms parallel to the world XY are supported.
        /// The polygons' bounding box parallel to the supplied transform will be
        /// used as the grid extents.
        /// </summary>
        /// <param name="boundaries">The external boundaries of this grid system.</param>
        /// <param name="transform">A transform representing the alignment of the grid.</param>
        public Grid2d(IList <Polygon> boundaries, Transform transform = null)
        {
            if (transform == null)
            {
                transform = new Transform();
            }

            if (!transform.ZAxis.IsParallelTo(Vector3.ZAxis))
            {
                throw new ArgumentException("Currently transforms that are not parallel to the XY Plane are not supported.");
            }


            fromGrid = new Transform(transform);
            toGrid   = new Transform(transform);
            toGrid.Invert();

            var transformedBoundaries = toGrid.OfPolygons(boundaries);

            boundariesInGridSpace = transformedBoundaries;
            var bbox = new BBox3(transformedBoundaries);

            InitializeUV(new Domain1d(bbox.Min.X, bbox.Max.X), new Domain1d(bbox.Min.Y, bbox.Max.Y));
        }
Exemplo n.º 27
0
        public void BoundingBoxIntersections()
        {
            // Contained
            var b1 = new BBox3(Vector3.Origin, new Vector3(5, 5, 5));
            var b2 = new BBox3(new Vector3(1, 1, 1), new Vector3(6, 6, 6));

            Assert.True(b1.Intersects(b2));

            // Coincident at corner
            b2 = new BBox3(new Vector3(-1, -1, -1), Vector3.Origin);
            Assert.True(b1.Intersects(b2));

            // Not contained or touching
            b2 = new BBox3(new Vector3(6, 6, 6), new Vector3(10, 10, 10));
            Assert.False(b1.Intersects(b2));

            // Full overlap
            b2 = b1;
            Assert.True(b1.Intersects(b2));

            // Coincident at face
            b2 = new BBox3(new Vector3(0, -5, 0), new Vector3(5, 0, 5));
            Assert.True(b1.Intersects(b2));
        }
Exemplo n.º 28
0
        internal static void ToGlb(this Solid solid, string path)
        {
            var gltf  = new Gltf();
            var asset = new Asset();

            asset.Version   = "2.0";
            asset.Generator = "hypar-gltf";

            gltf.Asset = asset;

            var root = new Node();

            root.Translation = new[] { 0.0f, 0.0f, 0.0f };
            root.Scale       = new[] { 1.0f, 1.0f, 1.0f };

            // Set Z up by rotating -90d around the X Axis
            var q = new Quaternion(new Vector3(1, 0, 0), -Math.PI / 2);

            root.Rotation = new[] {
                (float)q.X, (float)q.Y, (float)q.Z, (float)q.W
            };

            gltf.Nodes = new[] { root };

            gltf.Scene = 0;
            var scene = new Scene();

            scene.Nodes = new[] { 0 };
            gltf.Scenes = new[] { scene };

            gltf.ExtensionsUsed = new[] { "KHR_materials_pbrSpecularGlossiness" };

            var materials = gltf.AddMaterials(new[] { BuiltInMaterials.Default, BuiltInMaterials.Edges, BuiltInMaterials.EdgesHighlighted });

            var buffer = new List <byte>();
            var mesh   = new Elements.Geometry.Mesh();

            solid.Tessellate(ref mesh);

            gltf.AddTriangleMesh("mesh", buffer, mesh.Vertices.ToArray(), mesh.Normals.ToArray(),
                                 mesh.Indices.ToArray(), mesh.VertexColors.ToArray(),
                                 mesh.VMin, mesh.VMax, mesh.NMin, mesh.NMax, mesh.CMin, mesh.CMax,
                                 mesh.IMin, mesh.IMax, materials[BuiltInMaterials.Default.Name], null, null);

            var edgeCount           = 0;
            var vertices            = new List <Vector3>();
            var verticesHighlighted = new List <Vector3>();

            foreach (var e in solid.Edges.Values)
            {
                if (e.Left.Loop == null || e.Right.Loop == null)
                {
                    verticesHighlighted.AddRange(new[] { e.Left.Vertex.Point, e.Right.Vertex.Point });
                }
                else
                {
                    vertices.AddRange(new[] { e.Left.Vertex.Point, e.Right.Vertex.Point });
                }

                edgeCount++;
            }

            if (vertices.Count > 0)
            {
                // Draw standard edges
                var vBuff  = vertices.ToArray().ToArray();
                var vCount = vertices.Count;
                // var indices = Enumerable.Range(0, vCount).Select(i => (ushort)i).ToArray();
                var indices = new List <ushort>();
                for (var i = 0; i < vertices.Count; i += 2)
                {
                    indices.Add((ushort)i);
                    indices.Add((ushort)(i + 1));
                }
                var bbox = new BBox3(vertices.ToArray());
                gltf.AddLineLoop($"edge_{edgeCount}", buffer, vBuff, indices.ToArray(), bbox.Min.ToArray(), bbox.Max.ToArray(), 0, (ushort)(vCount - 1), materials[BuiltInMaterials.Edges.Name], MeshPrimitive.ModeEnum.LINES, null);
            }

            if (verticesHighlighted.Count > 0)
            {
                // Draw highlighted edges
                var vBuff   = vertices.ToArray().ToArray();
                var vCount  = vertices.Count;
                var indices = new List <ushort>();
                for (var i = 0; i < vertices.Count; i += 2)
                {
                    indices.Add((ushort)i);
                    indices.Add((ushort)(i + 1));
                }
                var bbox = new BBox3(vertices.ToArray());
                gltf.AddLineLoop($"edge_{edgeCount}", buffer, vBuff, indices.ToArray(), bbox.Min.ToArray(), bbox.Max.ToArray(), 0, (ushort)(vCount - 1), materials[BuiltInMaterials.EdgesHighlighted.Name], MeshPrimitive.ModeEnum.LINES, null);
            }

            var buff = new glTFLoader.Schema.Buffer();

            buff.ByteLength = buffer.Count;
            gltf.Buffers    = new[] { buff };

            if (File.Exists(path))
            {
                File.Delete(path);
            }

            gltf.SaveBinaryModel(buffer.ToArray(), path);
        }
Exemplo n.º 29
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Heightfield"/> class.
 /// </summary>
 /// <param name="b">The world-space bounds.</param>
 /// <param name="settings">The settings to build with.</param>
 public Heightfield(BBox3 b, NavMeshGenerationSettings settings)
     : this(b, settings.CellSize, settings.CellHeight)
 {
 }
Exemplo n.º 30
0
        public void GenNavMesh()
        {
            // MeshCollider
            List <Triangle3> colliderTringles = new List <Triangle3>();

            Collider[]        colliders  = GameObject.FindObjectsOfType <Collider>();
            List <GameObject> destroyGos = new List <GameObject>();

            foreach (Collider collider in colliders)
            {
                Mesh      mesh = null;
                Matrix4x4 localToWorldMatrix = Matrix4x4.identity;

                if (collider is MeshCollider)
                {
                    MeshCollider mc = collider as MeshCollider;
                    mesh = mc.sharedMesh;
                    localToWorldMatrix = mc.transform.localToWorldMatrix;
                }
                if (collider is BoxCollider)
                {
                    BoxCollider bc = collider as BoxCollider;
                    GameObject  go = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    mesh = go.GetComponent <MeshFilter>().sharedMesh;
                    go.transform.parent        = collider.transform;
                    go.transform.localRotation = Quaternion.identity;
                    go.transform.localPosition = bc.center;
                    go.transform.localScale    = bc.size;
                    localToWorldMatrix         = go.transform.localToWorldMatrix;
                    destroyGos.Add(go);
                }
                if (null != mesh)
                {
                    UnityEngine.Vector3[] vertices = mesh.vertices;
                    for (int i = 0; i < mesh.subMeshCount; ++i)
                    {
                        var subMesh = mesh.GetIndices(i);
                        for (int j = 0; j < subMesh.Length; j += 3)
                        {
                            UnityEngine.Vector3 a = localToWorldMatrix.MultiplyPoint(vertices[subMesh[j]]);
                            UnityEngine.Vector3 b = localToWorldMatrix.MultiplyPoint(vertices[subMesh[j + 1]]);
                            UnityEngine.Vector3 c = localToWorldMatrix.MultiplyPoint(vertices[subMesh[j + 2]]);
                            colliderTringles.Add(new Triangle3(ConvertVector3(a), ConvertVector3(b), ConvertVector3(c)));
                        }
                    }
                }
            }
            foreach (var go in destroyGos)
            {
                GameObject.DestroyImmediate(go);
            }
            destroyGos.Clear();

            Triangle3[] levelTris     = colliderTringles.ToArray();
            var         triEnumerable = TriangleEnumerable.FromTriangle(levelTris, 0, levelTris.Length);
            BBox3       bounds        = triEnumerable.GetBoundingBox();

            heightfield = new Heightfield(bounds, settings);
            heightfield.RasterizeTriangles(levelTris, Area.Default);
            heightfield.FilterLedgeSpans(settings.VoxelAgentHeight, settings.VoxelMaxClimb);
            heightfield.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb);
            heightfield.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight);
            compactHeightfield = new CompactHeightfield(heightfield, settings);
            compactHeightfield.Erode(settings.VoxelAgentRadius);
            compactHeightfield.BuildDistanceField();
            compactHeightfield.BuildRegions(0, settings.MinRegionSize, settings.MergedRegionSize);
            contourSet     = compactHeightfield.BuildContourSet(settings);
            polyMesh       = new PolyMesh(contourSet, settings);
            polyMeshDetail = new PolyMeshDetail(polyMesh, compactHeightfield, settings);
            buildData      = new NavMeshBuilder(polyMesh, polyMeshDetail, new SharpNav.Pathfinding.OffMeshConnection[0], settings);
            tiledNavMesh   = new TiledNavMesh(buildData);
            navMeshQuery   = new NavMeshQuery(tiledNavMesh, 2048);

            {
                System.Random r = new System.Random();
                regionColors    = new Color[compactHeightfield.MaxRegions];
                regionColors[0] = Color.black;
                for (int i = 1; i < regionColors.Length; i++)
                {
                    regionColors[i] = new Color((byte)r.Next(0, 255), (byte)r.Next(0, 255), (byte)r.Next(0, 255), 255);
                }
            }

            this.GenDrawMesh();

            Debug.Log("GenNavMesh Done!");
        }
Exemplo n.º 31
0
		/// <summary>
		/// Generates a bounding box for a collection of triangles.
		/// </summary>
		/// <param name="tris">The triangles to create a bounding box from.</param>
		/// <param name="padding">Padding to the bounding box</param>
		/// <returns>A bounding box containing every triangle.</returns>
		public static BBox3 GetBoundingBox(this IEnumerable<Triangle3> tris, float padding)
		{
			BBox3 bounds = new BBox3();
			Vector3 va, vb, vc;
			foreach (Triangle3 tri in tris)
			{
				va = tri.A;
				vb = tri.B;
				vc = tri.C;
				ApplyVertexToBounds(ref va, ref bounds);
				ApplyVertexToBounds(ref vb, ref bounds);
				ApplyVertexToBounds(ref vc, ref bounds);
			}

			//pad the bounding box a bit to make sure outer triangles are fully contained.
			ApplyPaddingToBounds(padding, ref bounds);

			return bounds;
		}
Exemplo n.º 32
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;
                                }
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 33
0
        /// <summary>
        /// Finds nearby polygons within a certain range.
        /// </summary>
        /// <param name="center">The starting point</param>
        /// <param name="extent">The range to search within</param>
        /// <param name="polys">A list of polygons</param>
        /// <returns>True, if successful. False, if otherwise.</returns>
        public bool QueryPolygons(ref Vector3 center, ref Vector3 extent, List<NavPolyId> polys)
        {
            Vector3 bmin = center - extent;
            Vector3 bmax = center + extent;

            int minx, miny, maxx, maxy;
            nav.CalcTileLoc(ref bmin, out minx, out miny);
            nav.CalcTileLoc(ref bmax, out maxx, out maxy);

            BBox3 bounds = new BBox3(bmin, bmax);
            int n = 0;
            for (int y = miny; y <= maxy; y++)
            {
                for (int x = minx; x <= maxx; x++)
                {
                    foreach (NavTile neighborTile in nav.GetTilesAt(x, y))
                    {
                        n += neighborTile.QueryPolygons(bounds, polys);
                        if (n >= polys.Capacity)
                        {
                            return true;
                        }
                    }
                }
            }

            return polys.Count != 0;
        }
Exemplo n.º 34
0
        /// <summary>
        /// Finds nearby polygons within a certain range.
        /// </summary>
        /// <param name="center">The starting point</param>
        /// <param name="extent">The range to search within</param>
        /// <param name="polys">A list of polygons</param>
        /// <returns>True, if successful. False, if otherwise.</returns>
        public bool QueryPolygons(ref Vector3 center, ref Vector3 extent, List<PolyId> polys)
        {
            Vector3 bmin = center - extent;
            Vector3 bmax = center + extent;

            int minx, miny, maxx, maxy;
            nav.CalcTileLoc(ref bmin, out minx, out miny);
            nav.CalcTileLoc(ref bmax, out maxx, out maxy);

            MeshTile[] neis = new MeshTile[32];

            BBox3 bounds = new BBox3(bmin, bmax);
            int n = 0;
            for (int y = miny; y <= maxy; y++)
            {
                for (int x = minx; x <= maxx; x++)
                {
                    int nneis = nav.GetTilesAt(x, y, neis);
                    for (int j = 0; j < nneis; j++)
                    {
                        n += nav.QueryPolygonsInTile(neis[j], bounds, polys);
                        if (n >= polys.Capacity)
                        {
                            return true;
                        }
                    }
                }
            }

            return polys.Count != 0;
        }
Exemplo n.º 35
0
		/// <summary>
		/// Gets the boundary side of a point relative to a bounding box.
		/// </summary>
		/// <param name="pt">A point.</param>
		/// <param name="bounds">A bounding box.</param>
		/// <returns>The point's position relative to the bounding box.</returns>
		public static BoundarySide FromPoint(Vector3 pt, BBox3 bounds)
		{
			const int PlusX = 0x1;
			const int PlusZ = 0x2;
			const int MinusX = 0x4;
			const int MinusZ = 0x8;

			int outcode = 0;
			outcode |= (pt.X >= bounds.Max.X) ? PlusX : 0;
			outcode |= (pt.Z >= bounds.Max.Z) ? PlusZ : 0;
			outcode |= (pt.X < bounds.Min.X) ? MinusX : 0;
			outcode |= (pt.Z < bounds.Min.Z) ? MinusZ : 0;

			switch (outcode)
			{
				case PlusX:
					return BoundarySide.PlusX;

				case PlusX | PlusZ:
					return BoundarySide.PlusXPlusZ;

				case PlusZ:
					return BoundarySide.PlusZ;

				case MinusX | PlusZ:
					return BoundarySide.MinusXPlusZ;

				case MinusX:
					return BoundarySide.MinusX;

				case MinusX | MinusZ:
					return BoundarySide.MinusXMinusZ;

				case MinusZ:
					return BoundarySide.MinusZ;

				case PlusX | MinusZ:
					return BoundarySide.PlusXMinusZ;

				default:
					return BoundarySide.Internal;
			}
		}
Exemplo n.º 36
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NavMeshBuilder" /> class.
        /// Add all the PolyMesh and PolyMeshDetail attributes to the Navigation Mesh.
        /// Then, add Off-Mesh connection support.
        /// </summary>
        /// <param name="polyMesh">The PolyMesh</param>
        /// <param name="polyMeshDetail">The PolyMeshDetail</param>
        /// <param name="offMeshCons">Offmesh connection data</param>
        /// <param name="settings">The settings used to build.</param>
        public NavMeshBuilder(PolyMesh polyMesh, PolyMeshDetail polyMeshDetail, OffMeshConnection[] offMeshCons, NavMeshGenerationSettings settings)
        {
            if (settings.VertsPerPoly > PathfindingCommon.VERTS_PER_POLYGON)
            {
                throw new InvalidOperationException("The number of vertices per polygon is above SharpNav's limit");
            }
            if (polyMesh.VertCount == 0)
            {
                throw new InvalidOperationException("The provided PolyMesh has no vertices.");
            }
            if (polyMesh.PolyCount == 0)
            {
                throw new InvalidOperationException("The provided PolyMesh has not polys.");
            }

            int nvp = settings.VertsPerPoly;

            //classify off-mesh connection points
            BoundarySide[] offMeshSides          = new BoundarySide[offMeshCons.Length * 2];
            int            storedOffMeshConCount = 0;
            int            offMeshConLinkCount   = 0;

            if (offMeshCons.Length > 0)
            {
                //find height bounds
                float hmin = float.MaxValue;
                float hmax = -float.MaxValue;

                if (polyMeshDetail != null)
                {
                    for (int i = 0; i < polyMeshDetail.VertCount; i++)
                    {
                        float h = polyMeshDetail.Verts[i].Y;
                        hmin = Math.Min(hmin, h);
                        hmax = Math.Max(hmax, h);
                    }
                }
                else
                {
                    for (int i = 0; i < polyMesh.VertCount; i++)
                    {
                        PolyVertex iv = polyMesh.Verts[i];
                        float      h  = polyMesh.Bounds.Min.Y + iv.Y * settings.CellHeight;
                        hmin = Math.Min(hmin, h);
                        hmax = Math.Max(hmax, h);
                    }
                }

                hmin -= settings.MaxClimb;
                hmax += settings.MaxClimb;
                BBox3 bounds = polyMesh.Bounds;
                bounds.Min.Y = hmin;
                bounds.Max.Y = hmax;

                for (int i = 0; i < offMeshCons.Length; i++)
                {
                    Vector3 p0 = offMeshCons[i].Pos0;
                    Vector3 p1 = offMeshCons[i].Pos1;

                    offMeshSides[i * 2 + 0] = BoundarySideExtensions.FromPoint(p0, bounds);
                    offMeshSides[i * 2 + 1] = BoundarySideExtensions.FromPoint(p1, bounds);

                    //off-mesh start position isn't touching mesh
                    if (offMeshSides[i * 2 + 0] == BoundarySide.Internal)
                    {
                        if (p0.Y < bounds.Min.Y || p0.Y > bounds.Max.Y)
                        {
                            offMeshSides[i * 2 + 0] = 0;
                        }
                    }

                    //count number of links to allocate
                    if (offMeshSides[i * 2 + 0] == BoundarySide.Internal)
                    {
                        offMeshConLinkCount++;
                    }
                    if (offMeshSides[i * 2 + 1] == BoundarySide.Internal)
                    {
                        offMeshConLinkCount++;
                    }

                    if (offMeshSides[i * 2 + 0] == BoundarySide.Internal)
                    {
                        storedOffMeshConCount++;
                    }
                }
            }

            //off-mesh connections stored as polygons, adjust values
            int totPolyCount = polyMesh.PolyCount + storedOffMeshConCount;
            int totVertCount = polyMesh.VertCount + storedOffMeshConCount * 2;

            //find portal edges
            int edgeCount   = 0;
            int portalCount = 0;

            for (int i = 0; i < polyMesh.PolyCount; i++)
            {
                PolyMesh.Polygon p = polyMesh.Polys[i];
                for (int j = 0; j < nvp; j++)
                {
                    if (p.Vertices[j] == PolyMesh.NullId)
                    {
                        break;
                    }

                    edgeCount++;

                    if (PolyMesh.IsBoundaryEdge(p.NeighborEdges[j]))
                    {
                        int dir = p.NeighborEdges[j] % 16;
                        if (dir != 15)
                        {
                            portalCount++;
                        }
                    }
                }
            }

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

            //find unique detail vertices
            int uniqueDetailVertCount = 0;
            int detailTriCount        = 0;

            if (polyMeshDetail != null)
            {
                detailTriCount = polyMeshDetail.TrisCount;
                for (int i = 0; i < polyMesh.PolyCount; i++)
                {
                    int numDetailVerts = polyMeshDetail.Meshes[i].VertexCount;
                    int numPolyVerts   = polyMesh.Polys[i].VertexCount;
                    uniqueDetailVertCount += numDetailVerts - numPolyVerts;
                }
            }
            else
            {
                uniqueDetailVertCount = 0;
                detailTriCount        = 0;
                for (int i = 0; i < polyMesh.PolyCount; i++)
                {
                    int numPolyVerts = polyMesh.Polys[i].VertexCount;
                    uniqueDetailVertCount += numPolyVerts - 2;
                }
            }

            //allocate data
            header             = new PathfindingCommon.NavMeshInfo();
            navVerts           = new Vector3[totVertCount];
            navPolys           = new Poly[totPolyCount];
            navDMeshes         = new PolyMeshDetail.MeshData[polyMesh.PolyCount];
            navDVerts          = new Vector3[uniqueDetailVertCount];
            navDTris           = new PolyMeshDetail.TriangleData[detailTriCount];
            offMeshConnections = new OffMeshConnection[storedOffMeshConCount];

            //store header
            //HACK TiledNavMesh should figure out the X/Y/layer instead of the user maybe?
            header.X               = 0;
            header.Y               = 0;
            header.Layer           = 0;
            header.PolyCount       = totPolyCount;
            header.VertCount       = totVertCount;
            header.MaxLinkCount    = maxLinkCount;
            header.Bounds          = polyMesh.Bounds;
            header.DetailMeshCount = polyMesh.PolyCount;
            header.DetailVertCount = uniqueDetailVertCount;
            header.DetailTriCount  = detailTriCount;
            header.OffMeshBase     = polyMesh.PolyCount;
            header.WalkableHeight  = settings.AgentHeight;
            header.WalkableRadius  = settings.AgentRadius;
            header.WalkableClimb   = settings.MaxClimb;
            header.OffMeshConCount = storedOffMeshConCount;
            header.BvNodeCount     = settings.BuildBoundingVolumeTree ? polyMesh.PolyCount * 2 : 0;
            header.BvQuantFactor   = 1f / settings.CellSize;

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

            //store vertices
            for (int i = 0; i < polyMesh.VertCount; i++)
            {
                PolyVertex iv = polyMesh.Verts[i];
                navVerts[i].X = polyMesh.Bounds.Min.X + iv.X * settings.CellSize;
                navVerts[i].Y = polyMesh.Bounds.Min.Y + iv.Y * settings.CellHeight;
                navVerts[i].Z = polyMesh.Bounds.Min.Z + iv.Z * settings.CellSize;
            }

            //off-mesh link vertices
            int n = 0;

            for (int i = 0; i < offMeshCons.Length; i++)
            {
                //only store connections which start from this tile
                if (offMeshSides[i * 2 + 0] == BoundarySide.Internal)
                {
                    navVerts[offMeshVertsBase + (n * 2 + 0)] = offMeshCons[i].Pos0;
                    navVerts[offMeshVertsBase + (n * 2 + 1)] = offMeshCons[i].Pos1;
                    n++;
                }
            }

            //store polygons
            for (int i = 0; i < polyMesh.PolyCount; i++)
            {
                navPolys[i]           = new Poly();
                navPolys[i].VertCount = 0;
                navPolys[i].Tag       = polyMesh.Polys[i].Tag;
                navPolys[i].Area      = polyMesh.Polys[i].Area;
                navPolys[i].PolyType  = PolygonType.Ground;
                navPolys[i].Verts     = new int[nvp];
                navPolys[i].Neis      = new int[nvp];
                for (int j = 0; j < nvp; j++)
                {
                    if (polyMesh.Polys[i].Vertices[j] == PolyMesh.NullId)
                    {
                        break;
                    }

                    navPolys[i].Verts[j] = polyMesh.Polys[i].Vertices[j];
                    if (PolyMesh.IsBoundaryEdge(polyMesh.Polys[i].NeighborEdges[j]))
                    {
                        //border or portal edge
                        int dir = polyMesh.Polys[i].NeighborEdges[j] % 16;
                        if (dir == 0xf)                         //border
                        {
                            navPolys[i].Neis[j] = 0;
                        }
                        else if (dir == 0)                         //portal x-
                        {
                            navPolys[i].Neis[j] = Link.External | 4;
                        }
                        else if (dir == 1)                         //portal z+
                        {
                            navPolys[i].Neis[j] = Link.External | 2;
                        }
                        else if (dir == 2)                         //portal x+
                        {
                            navPolys[i].Neis[j] = Link.External | 0;
                        }
                        else if (dir == 3)                         //portal z-
                        {
                            navPolys[i].Neis[j] = Link.External | 6;
                        }
                    }
                    else
                    {
                        //normal connection
                        navPolys[i].Neis[j] = polyMesh.Polys[i].NeighborEdges[j] + 1;
                    }

                    navPolys[i].VertCount++;
                }
            }

            //off-mesh connection vertices
            n = 0;
            for (int i = 0; i < offMeshCons.Length; i++)
            {
                //only store connections which start from this tile
                if (offMeshSides[i * 2 + 0] == BoundarySide.Internal)
                {
                    navPolys[offMeshPolyBase + n].VertCount = 2;
                    navPolys[offMeshPolyBase + n].Verts     = new int[nvp];
                    navPolys[offMeshPolyBase + n].Verts[0]  = offMeshVertsBase + (n * 2 + 0);
                    navPolys[offMeshPolyBase + n].Verts[1]  = offMeshVertsBase + (n * 2 + 1);
                    navPolys[offMeshPolyBase + n].Tag       = offMeshCons[i].Flags;
                    navPolys[offMeshPolyBase + n].Area      = polyMesh.Polys[offMeshCons[i].Poly].Area;                //HACK is this correct?
                    navPolys[offMeshPolyBase + n].PolyType  = PolygonType.OffMeshConnection;
                    n++;
                }
            }

            //store detail meshes and vertices
            if (polyMeshDetail != null)
            {
                int            vbase             = 0;
                List <Vector3> storedDetailVerts = new List <Vector3>();
                for (int i = 0; i < polyMesh.PolyCount; i++)
                {
                    int vb             = polyMeshDetail.Meshes[i].VertexIndex;
                    int numDetailVerts = polyMeshDetail.Meshes[i].VertexCount;
                    int numPolyVerts   = navPolys[i].VertCount;
                    navDMeshes[i].VertexIndex   = vbase;
                    navDMeshes[i].VertexCount   = numDetailVerts - numPolyVerts;
                    navDMeshes[i].TriangleIndex = polyMeshDetail.Meshes[i].TriangleIndex;
                    navDMeshes[i].TriangleCount = polyMeshDetail.Meshes[i].TriangleCount;

                    //Copy detail vertices
                    //first 'nv' verts are equal to nav poly verts
                    //the rest are detail verts
                    for (int j = 0; j < navDMeshes[i].VertexCount; j++)
                    {
                        storedDetailVerts.Add(polyMeshDetail.Verts[vb + numPolyVerts + j]);
                    }

                    vbase += numDetailVerts - numPolyVerts;
                }

                navDVerts = storedDetailVerts.ToArray();

                //store triangles
                for (int j = 0; j < polyMeshDetail.TrisCount; j++)
                {
                    navDTris[j] = polyMeshDetail.Tris[j];
                }
            }
            else
            {
                //create dummy detail mesh by triangulating polys
                int tbase = 0;
                for (int i = 0; i < polyMesh.PolyCount; i++)
                {
                    int numPolyVerts = navPolys[i].VertCount;
                    navDMeshes[i].VertexIndex   = 0;
                    navDMeshes[i].VertexCount   = 0;
                    navDMeshes[i].TriangleIndex = tbase;
                    navDMeshes[i].TriangleCount = numPolyVerts - 2;

                    //triangulate polygon
                    for (int j = 2; j < numPolyVerts; j++)
                    {
                        navDTris[tbase].VertexHash0 = 0;
                        navDTris[tbase].VertexHash1 = j - 1;
                        navDTris[tbase].VertexHash2 = j;

                        //bit for each edge that belongs to the poly boundary
                        navDTris[tbase].Flags = 1 << 2;
                        if (j == 2)
                        {
                            navDTris[tbase].Flags |= 1 << 0;
                        }
                        if (j == numPolyVerts - 1)
                        {
                            navDTris[tbase].Flags |= 1 << 4;
                        }

                        tbase++;
                    }
                }
            }

            //store and create BV tree
            if (settings.BuildBoundingVolumeTree)
            {
                //build tree
                navBvTree = new BVTree(polyMesh.Verts, polyMesh.Polys, nvp, settings.CellSize, settings.CellHeight);
            }

            //store off-mesh connections
            n = 0;
            for (int i = 0; i < offMeshConnections.Length; i++)
            {
                //only store connections which start from this tile
                if (offMeshSides[i * 2 + 0] == BoundarySide.Internal)
                {
                    offMeshConnections[n].Poly = offMeshPolyBase + n;

                    //copy connection end points
                    offMeshConnections[n].Pos0 = offMeshCons[i].Pos0;
                    offMeshConnections[n].Pos1 = offMeshCons[i].Pos1;

                    offMeshConnections[n].Radius = offMeshCons[i].Radius;
                    offMeshConnections[n].Flags  = offMeshCons[i].Flags;
                    offMeshConnections[n].Side   = offMeshSides[i * 2 + 1];
                    offMeshConnections[n].Tag    = offMeshCons[i].Tag;

                    n++;
                }
            }
        }
Exemplo n.º 37
0
		/// <summary>
		/// Generates a bounding box for a collection of vectors.
		/// </summary>
		/// <param name="vecs">The vectors to create a bounding box from.</param>
		/// <returns>A bounding box containing every vector.</returns>
		public static BBox3 GetBoundingBox(this IEnumerable<Vector3> vecs)
		{
			BBox3 bounds = new BBox3();
			Vector3 v;
			foreach (Vector3 vec in vecs)
			{
				v = vec;
				ApplyVertexToBounds(ref v, ref bounds);
			}

			ApplyPaddingToBounds(1.0f, ref bounds);

			return bounds;
		}
Exemplo n.º 38
0
		/// <summary>
		/// Applies a padding to the bounding box.
		/// </summary>
		/// <param name="pad">The amount to pad the bounding box on all sides.</param>
		/// <param name="b">The bounding box to pad.</param>
		private static void ApplyPaddingToBounds(float pad, ref BBox3 b)
		{
			b.Min.X -= pad;
			b.Min.Y -= pad;
			b.Min.Z -= pad;
			b.Max.X += pad;
			b.Max.Y += pad;
			b.Max.Z += pad;
		}
Exemplo n.º 39
0
		/// <summary>
		/// Adjusts a bounding box to include a vertex.
		/// </summary>
		/// <param name="v">The vertex to include.</param>
		/// <param name="b">The bounding box to adjust.</param>
		private static void ApplyVertexToBounds(ref Vector3 v, ref BBox3 b)
		{
			if (v.X < b.Min.X)
				b.Min.X = v.X;
			if (v.Y < b.Min.Y)
				b.Min.Y = v.Y;
			if (v.Z < b.Min.Z)
				b.Min.Z = v.Z;
			if (v.X > b.Max.X)
				b.Max.X = v.X;
			if (v.Y > b.Max.Y)
				b.Max.Y = v.Y;
			if (v.Z > b.Max.Z)
				b.Max.Z = v.Z;
		}
Exemplo n.º 40
0
        /// <summary>
        /// Initializes a new instance of the <see cref="PolyMesh"/> class by creating polygons from contours.
        /// </summary>
        /// <param name="contSet">The <see cref="ContourSet"/> to generate polygons from.</param>
        /// <param name="cellSize">The size of one voxel/cell.</param>
        /// <param name="cellHeight">The height of one voxel/cell.</param>
        /// <param name="borderSize">The size of the border around the mesh.</param>
        /// <param name="numVertsPerPoly">The maximum number of vertices per polygon.</param>
        public PolyMesh(ContourSet contSet, float cellSize, float cellHeight, int borderSize, int numVertsPerPoly)
        {
            //copy contour data
            this.bounds = contSet.Bounds;
            this.cellSize = cellSize;
            this.cellHeight = cellHeight;
            this.borderSize = borderSize;

            //get maximum limits
            //TODO move to ContourSet?
            int maxVertices = 0;
            int maxTris = 0;
            int maxVertsPerCont = 0;
            foreach (var cont in contSet)
            {
                int vertCount = cont.Vertices.Length;

                //skip null contours
                if (vertCount < 3)
                    continue;

                maxVertices += vertCount;
                maxTris += vertCount - 2;
                maxVertsPerCont = Math.Max(maxVertsPerCont, vertCount);
            }

            //initialize the mesh members
            var verts = new List<PolyVertex>(maxVertices);
            var polys = new List<Polygon>(maxTris);

            Queue<int> vertRemoveQueue = new Queue<int>(maxVertices);

            this.numVertsPerPoly = numVertsPerPoly;

            var vertDict = new Dictionary<PolyVertex, int>(new PolyVertex.RoughYEqualityComparer(2));

            int[] indices = new int[maxVertsPerCont]; //keep track of vertex hash codes
            Triangle[] tris = new Triangle[maxVertsPerCont];
            List<Polygon> contPolys = new List<Polygon>(maxVertsPerCont + 1);

            //extract contour data
            foreach (Contour cont in contSet)
            {
                //skip null contours
                if (cont.IsNull)
                    continue;

                PolyVertex[] vertices = new PolyVertex[cont.Vertices.Length];

                //triangulate contours
                for (int i = 0; i < cont.Vertices.Length; i++)
                {
                    var cv = cont.Vertices[i];
                    vertices[i] = new PolyVertex(cv.X, cv.Y, cv.Z);
                    indices[i] = i;
                }

                //Form triangles inside the area bounded by the contours
                int ntris = Triangulate(vertices, indices, tris);
                if (ntris <= 0) //TODO notify user when this happens. Logging?
                    ntris = -ntris;

                //add and merge vertices
                for (int i = 0; i < cont.Vertices.Length; i++)
                {
                    var cv = cont.Vertices[i];
                    var pv = vertices[i];

                    //save the hash code for each vertex
                    indices[i] = AddVertex(vertDict, pv, verts);

                    if (RegionId.HasFlags(cv.RegionId, RegionFlags.VertexBorder))
                    {
                        //the vertex should be removed
                        vertRemoveQueue.Enqueue(indices[i]);
                    }
                }

                contPolys.Clear();

                //iterate through all the triangles
                for (int i = 0; i < ntris; i++)
                {
                    Triangle ti = tris[i];

                    //make sure there are three distinct vertices. anything less can't be a polygon.
                    if (ti.Index0 == ti.Index1
                        || ti.Index0 == ti.Index2
                        || ti.Index1 == ti.Index2)
                        continue;

                    //each polygon has numVertsPerPoly
                    //index 0, 1, 2 store triangle vertices
                    //other polygon indexes (3 to numVertsPerPoly - 1) should be used for storing extra vertices when two polygons merge together
                    Polygon p = new Polygon(numVertsPerPoly, Area.Null, RegionId.Null, 0);
                    p.Vertices[0] = RemoveDiagonalFlag(indices[ti.Index0]);
                    p.Vertices[1] = RemoveDiagonalFlag(indices[ti.Index1]);
                    p.Vertices[2] = RemoveDiagonalFlag(indices[ti.Index2]);
                    contPolys.Add(p);
                }

                //no polygons generated, so skip
                if (contPolys.Count == 0)
                    continue;

                //merge polygons
                if (numVertsPerPoly > 3)
                {
                    while (true)
                    {
                        //find best polygons
                        int bestMergeVal = 0;
                        int bestPolyA = 0, bestPolyB = 0, bestEdgeA = 0, bestEdgeB = 0;

                        for (int i = 0; i < contPolys.Count - 1; i++)
                        {
                            int pj = i;

                            for (int j = i + 1; j < contPolys.Count; j++)
                            {
                                int pk = j;
                                int ea = 0, eb = 0;
                                int v = GetPolyMergeValue(contPolys, pj, pk, verts, out ea, out eb);
                                if (v > bestMergeVal)
                                {
                                    bestMergeVal = v;
                                    bestPolyA = i;
                                    bestPolyB = j;
                                    bestEdgeA = ea;
                                    bestEdgeB = eb;
                                }
                            }
                        }

                        if (bestMergeVal > 0)
                        {
                            int pa = bestPolyA;
                            int pb = bestPolyB;
                            MergePolys(contPolys, pa, pb, bestEdgeA, bestEdgeB);
                            contPolys[pb] = contPolys[contPolys.Count - 1];
                            contPolys.RemoveAt(contPolys.Count - 1);
                        }
                        else
                        {
                            //no more merging
                            break;
                        }
                    }
                }

                //store polygons
                for (int i = 0; i < contPolys.Count; i++)
                {
                    Polygon p = contPolys[i];
                    Polygon p2 = new Polygon(numVertsPerPoly, cont.Area, cont.RegionId, 0);

                    Buffer.BlockCopy(p.Vertices, 0, p2.Vertices, 0, numVertsPerPoly * sizeof(int));

                    polys.Add(p2);
                }
            }

            //remove edge vertices
            while (vertRemoveQueue.Count > 0)
            {
                int i = vertRemoveQueue.Dequeue();

                if (CanRemoveVertex(polys, i))
                    RemoveVertex(verts, polys, i);
            }

            //calculate adjacency (edges)
            BuildMeshAdjacency(verts, polys, numVertsPerPoly);

            //find portal edges
            if (this.borderSize > 0)
            {
                //iterate through all the polygons
                for (int i = 0; i < polys.Count; i++)
                {
                    Polygon p = polys[i];

                    //iterate through all the vertices
                    for (int j = 0; j < numVertsPerPoly; j++)
                    {
                        if (p.Vertices[j] == NullId)
                            break;

                        //skip connected edges
                        if (p.NeighborEdges[j] != NullId)
                            continue;

                        int nj = j + 1;
                        if (nj >= numVertsPerPoly || p.Vertices[nj] == NullId)
                            nj = 0;

                        //grab two consecutive vertices
                        int va = p.Vertices[j];
                        int vb = p.Vertices[nj];

                        //set some flags
                        if (verts[va].X == 0 && verts[vb].X == 0)
                            p.NeighborEdges[j] = NeighborEdgeFlag | 0;
                        else if (verts[va].Z == contSet.Height && verts[vb].Z == contSet.Height)
                            p.NeighborEdges[j] = NeighborEdgeFlag | 1;
                        else if (verts[va].X == contSet.Width && verts[vb].X == contSet.Width)
                            p.NeighborEdges[j] = NeighborEdgeFlag | 2;
                        else if (verts[va].Z == 0 && verts[vb].Z == 0)
                            p.NeighborEdges[j] = NeighborEdgeFlag | 3;
                    }
                }
            }

            this.vertices = verts.ToArray();
            this.polygons = polys.ToArray();
        }
Exemplo n.º 41
0
        /// <summary>
        /// Find all the polygons within a certain bounding box.
        /// </summary>
        /// <param name="tile">Current tile</param>
        /// <param name="qbounds">The bounds</param>
        /// <param name="polys">List of polygons</param>
        /// <returns>Number of polygons found</returns>
        public int QueryPolygonsInTile(MeshTile tile, BBox3 qbounds, List<int> polys)
        {
            if (tile.BVTree.Count != 0)
            {
                int node = 0;
                int end = tile.Header.BvNodeCount;
                Vector3 tbmin = tile.Header.Bounds.Min;
                Vector3 tbmax = tile.Header.Bounds.Max;

                //Clamp query box to world box
                Vector3 qbmin = qbounds.Min;
                Vector3 qbmax = qbounds.Max;
                PolyBounds b;
                float bminx = MathHelper.Clamp(qbmin.X, tbmin.X, tbmax.X) - tbmin.X;
                float bminy = MathHelper.Clamp(qbmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y;
                float bminz = MathHelper.Clamp(qbmin.Z, tbmin.Z, tbmax.Z) - tbmin.Z;
                float bmaxx = MathHelper.Clamp(qbmax.X, tbmin.X, tbmax.X) - tbmin.X;
                float bmaxy = MathHelper.Clamp(qbmax.Y, tbmin.Y, tbmax.Y) - tbmin.Y;
                float bmaxz = MathHelper.Clamp(qbmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z;

                const int MinMask = unchecked((int)0xfffffffe);

                b.Min.X = (int)(bminx * tile.Header.BvQuantFactor) & MinMask;
                b.Min.Y = (int)(bminy * tile.Header.BvQuantFactor) & MinMask;
                b.Min.Z = (int)(bminz * tile.Header.BvQuantFactor) & MinMask;
                b.Max.X = (int)(bmaxx * tile.Header.BvQuantFactor + 1) | 1;
                b.Max.Y = (int)(bmaxy * tile.Header.BvQuantFactor + 1) | 1;
                b.Max.Z = (int)(bmaxz * tile.Header.BvQuantFactor + 1) | 1;

                //traverse tree
                int polyBase = GetPolyRefBase(tile);

                while (node < end)
                {
                    BVTree.Node bvNode = tile.BVTree[node];
                    bool overlap = PolyBounds.Overlapping(ref b, ref bvNode.Bounds);
                    bool isLeafNode = bvNode.Index >= 0;

                    if (isLeafNode && overlap)
                    {
                        if (polys.Count < polys.Capacity)
                            polys.Add(GetReference(polyBase, bvNode.Index));
                    }

                    if (overlap || isLeafNode)
                    {
                        node++;
                    }
                    else
                    {
                        int escapeIndex = -bvNode.Index;
                        node += escapeIndex;
                    }
                }

                return polys.Count;
            }
            else
            {
                BBox3 b;
                int polyBase = GetPolyRefBase(tile);

                for (int i = 0; i < tile.Header.PolyCount; i++)
                {
                    var poly = tile.Polys[i];

                    //don't return off-mesh connection polygons
                    if (poly.PolyType == PolygonType.OffMeshConnection)
                        continue;

                    //calculate polygon bounds
                    b.Max = b.Min = tile.Verts[poly.Verts[0]];
                    for (int j = 1; j < poly.VertCount; j++)
                    {
                        Vector3 v = tile.Verts[poly.Verts[j]];
                        Vector3Extensions.ComponentMin(ref b.Min, ref v, out b.Min);
                        Vector3Extensions.ComponentMax(ref b.Max, ref v, out b.Max);
                    }

                    if (BBox3.Overlapping(ref qbounds, ref b))
                    {
                        if (polys.Count < polys.Capacity)
                            polys.Add(GetReference(polyBase, i));
                    }
                }

                return polys.Count;
            }
        }
Exemplo n.º 42
0
        private void GenerateNavMesh()
        {
            //Debug.Log("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);

                //Debug.Log("Heightfield");
                //Debug.Log(" + 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);

                //Debug.Log(" + Rasterization\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                //Debug.Log(" + Filtering");
                prevMs = sw.ElapsedMilliseconds;

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

                //Debug.Log("   + Ledge Spans\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                heightfield.FilterLowHangingWalkableObstacles(settings.VoxelMaxClimb);

                //Debug.Log("   + Low Hanging Obstacles\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                heightfield.FilterWalkableLowHeightSpans(settings.VoxelAgentHeight);

                //Debug.Log("   + Low Height Spans\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield = new CompactHeightfield(heightfield, settings);

                //Debug.Log("CompactHeightfield");
                //Debug.Log(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield.Erode(settings.VoxelAgentRadius);

                //Debug.Log(" + Erosion\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                compactHeightfield.BuildDistanceField();

                //Debug.Log(" + Distance Field\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

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

                //Debug.Log(" + Regions\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;


                contourSet = compactHeightfield.BuildContourSet(settings);

                //Debug.Log("ContourSet");
                //Debug.Log(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                polyMesh = new PolyMesh(contourSet, settings);

                //Debug.Log("PolyMesh");
                //Debug.Log(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

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

                //Debug.Log("PolyMeshDetail");
                //Debug.Log(" + Ctor\t\t\t\t" + (sw.ElapsedMilliseconds - prevMs).ToString("D3") + " ms");
                prevMs = sw.ElapsedMilliseconds;

                hasGenerated = true;
            }
            catch (Exception e)
            {
                if (!interceptExceptions)
                {
                    throw;
                }
                else
                {
                    //Debug.Log("Navmesh generation failed with exception:" + Environment.NewLine + e.ToString());
                }
            }
            finally
            {
                sw.Stop();
            }

            if (hasGenerated)
            {
                try
                {
                    GeneratePathfinding();

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

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

                //Debug.Log("Navmesh generated successfully in " + sw.ElapsedMilliseconds + "ms.");
                //Debug.Log("Rasterized " + level.GetTriangles().Length + " triangles.");
                //Debug.Log("Generated " + contourSet.Count + " regions.");
                //Debug.Log("PolyMesh contains " + polyMesh.VertCount + " vertices in " + polyMesh.PolyCount + " polys.");
                //Debug.Log("PolyMeshDetail contains " + polyMeshDetail.VertCount + " vertices and " + polyMeshDetail.TrisCount + " tris in " + polyMeshDetail.MeshCount + " meshes.");
            }
        }
Exemplo n.º 43
0
		/// <summary>
		/// Generate the PolyMeshDetail using the PolyMesh and HeightPatch
		/// </summary>
		/// <param name="polyMeshVerts">PolyMesh Vertex data</param>
		/// <param name="numMeshVerts">Number of PolyMesh vertices</param>
		/// <param name="sampleDist">Sampling distance</param>
		/// <param name="sampleMaxError">Maximum sampling error</param>
		/// <param name="compactField">THe compactHeightfield</param>
		/// <param name="hp">The heightPatch</param>
		/// <param name="verts">Detail verts</param>
		/// <param name="tris">Detail triangles</param>
		/// <param name="edges">The edge array</param>
		/// <param name="samples">The samples array</param>
		private void BuildPolyDetail(Vector3[] polyMeshVerts, int numMeshVerts, float sampleDist, float sampleMaxError, CompactHeightfield compactField, HeightPatch hp, List<Vector3> verts, List<TriangleData> tris, List<EdgeInfo> edges, List<SamplingData> samples)
		{
			const int MAX_VERTS = 127;
			const int MAX_TRIS = 255;
			const int MAX_VERTS_PER_EDGE = 32;
			Vector3[] edge = new Vector3[MAX_VERTS_PER_EDGE + 1];
			List<int> hull = new List<int>(MAX_VERTS);

			//fill up vertex array
			for (int i = 0; i < numMeshVerts; ++i)
				verts.Add(polyMeshVerts[i]);

			float cs = compactField.CellSize;
			float ics = 1.0f / cs;

			float minExtent = PolyMinExtent(polyMeshVerts);

			//tessellate outlines
			if (sampleDist > 0)
			{
				for (int i = 0, j = verts.Count - 1; i < verts.Count; j = i++)
				{
					Vector3 vi = verts[i];
					Vector3 vj = verts[j];
					bool swapped = false;

					//make sure order is correct, otherwise swap data
					if (Math.Abs(vj.X - vi.X) < 1E-06f)
					{
						if (vj.Z > vi.Z)
						{
							Vector3 temp = vj;
							vj = vi;
							vi = temp;
							swapped = true;
						}
					}
					else if (vj.X > vi.X)
					{
						Vector3 temp = vj;
						vj = vi;
						vi = temp;
						swapped = true;
					}

					//create samples along the edge
					Vector3 dv;
					Vector3.Subtract(ref vi, ref vj, out dv);
					float d = (float)Math.Sqrt(dv.X * dv.X + dv.Z * dv.Z);
					int nn = 1 + (int)Math.Floor(d / sampleDist);

					if (nn >= MAX_VERTS_PER_EDGE)
						nn = MAX_VERTS_PER_EDGE - 1;

					if (verts.Count + nn >= MAX_VERTS)
						nn = MAX_VERTS - 1 - verts.Count;

					for (int k = 0; k <= nn; k++)
					{
						float u = (float)k / (float)nn;
						Vector3 pos;

						Vector3 tmp;
						Vector3.Multiply(ref dv, u, out tmp);
						Vector3.Add(ref vj, ref tmp, out pos);

						pos.Y = GetHeight(pos, ics, compactField.CellHeight, hp) * compactField.CellHeight;

						edge[k] = pos;
					}

					//simplify samples
					int[] idx = new int[MAX_VERTS_PER_EDGE];
					idx[0] = 0;
					idx[1] = nn;
					int nidx = 2;

					for (int k = 0; k < nidx - 1;)
					{
						int a = idx[k];
						int b = idx[k + 1];
						Vector3 va = edge[a];
						Vector3 vb = edge[b];

						//find maximum deviation along segment
						float maxd = 0;
						int maxi = -1;
						for (int m = a + 1; m < b; m++)
						{
							float dev = Distance.PointToSegmentSquared(ref edge[m], ref va, ref vb);
							if (dev > maxd)
							{
								maxd = dev;
								maxi = m;
							}
						}

						if (maxi != -1 && maxd > (sampleMaxError * sampleMaxError))
						{
							//shift data to the right
							for (int m = nidx; m > k; m--)
								idx[m] = idx[m - 1];

							//set new value
							idx[k + 1] = maxi;
							nidx++;
						}
						else
						{
							k++;
						}
					}

					hull.Add(j);

					//add new vertices
					if (swapped)
					{
						for (int k = nidx - 2; k > 0; k--)
						{
							hull.Add(verts.Count);
							verts.Add(edge[idx[k]]);
						}
					}
					else
					{
						for (int k = 1; k < nidx - 1; k++)
						{
							hull.Add(verts.Count);
							verts.Add(edge[idx[k]]);
						}
					}
				}
			}

			//tesselate base mesh
			edges.Clear();
			tris.Clear();

			if (minExtent < sampleDist * 2)
			{
				TriangulateHull(verts, hull, tris);
				return;
			}

			TriangulateHull(verts, hull, tris);

			if (tris.Count == 0)
			{
				Console.WriteLine("Can't triangulate polygon, adding default data.");
				return;
			}

			if (sampleDist > 0)
			{
				//create sample locations
				BBox3 bounds = new BBox3();
				bounds.Min = polyMeshVerts[0];
				bounds.Max = polyMeshVerts[0];

				for (int i = 1; i < numMeshVerts; i++)
				{
					Vector3Extensions.ComponentMin(ref bounds.Min, ref polyMeshVerts[i], out bounds.Min);
					Vector3Extensions.ComponentMax(ref bounds.Max, ref polyMeshVerts[i], out bounds.Max); 
				}

				int x0 = (int)Math.Floor(bounds.Min.X / sampleDist);
				int x1 = (int)Math.Ceiling(bounds.Max.X / sampleDist);
				int z0 = (int)Math.Floor(bounds.Min.Z / sampleDist);
				int z1 = (int)Math.Ceiling(bounds.Max.Z / sampleDist);

				samples.Clear();

				for (int z = z0; z < z1; z++)
				{
					for (int x = x0; x < x1; x++)
					{
						Vector3 pt = new Vector3(x * sampleDist, (bounds.Max.Y + bounds.Min.Y) * 0.5f, z * sampleDist);

						//make sure samples aren't too close to edge
						if (Distance.PointToPolygonSquared(pt, polyMeshVerts, numMeshVerts) > -sampleDist * 0.5f)
							continue;

						SamplingData sd = new SamplingData(x, GetHeight(pt, ics, compactField.CellHeight, hp), z, false);
						samples.Add(sd);
					}
				}

				//added samples
				for (int iter = 0; iter < samples.Count; iter++)
				{
					if (verts.Count >= MAX_VERTS)
						break;

					//find sample with most error
					Vector3 bestPt = Vector3.Zero;
					float bestDistance = 0;
					int bestIndex = -1;

					for (int i = 0; i < samples.Count; i++)
					{
						SamplingData sd = samples[i];
						if (sd.IsSampled)
							continue;

						//jitter sample location to remove effects of bad triangulation
						Vector3 pt;
						pt.X = sd.X * sampleDist + GetJitterX(i) * compactField.CellSize * 0.1f;
						pt.Y = sd.Y * compactField.CellHeight;
						pt.Z = sd.Z * sampleDist + GetJitterY(i) * compactField.CellSize * 0.1f;
						float d = DistanceToTriMesh(pt, verts, tris);

						if (d < 0)
							continue;

						if (d > bestDistance)
						{
							bestDistance = d;
							bestIndex = i;
							bestPt = pt;
						}
					}

					if (bestDistance <= sampleMaxError || bestIndex == -1)
						break;

					SamplingData bsd = samples[bestIndex];
					bsd.IsSampled = true;
					samples[bestIndex] = bsd;

					verts.Add(bestPt);

					//create new triangulation
					edges.Clear();
					tris.Clear();
					DelaunayHull(verts, hull, tris, edges);
				}
			}

			int ntris = tris.Count;
			if (ntris > MAX_TRIS)
			{
				//TODO we're using lists... let the user have super detailed meshes?
				//Perhaps just a warning saying there's a lot of tris?
				//tris.RemoveRange(MAX_TRIS + 1, tris.Count - MAX_TRIS);
				//Console.WriteLine("WARNING: shrinking number of triangles.");
			}
		}