예제 #1
0
        /// <summary>
        /// Connect Off-Mesh links between polygons from two different tiles.
        /// </summary>
        /// <param name="tile">Current Tile</param>
        /// <param name="target">Target Tile</param>
        /// <param name="side">Polygon edge</param>
        public void ConnectExtOffMeshLinks(ref MeshTile tile, ref MeshTile target, BoundarySide side)
        {
            if (tile == null)
            {
                return;
            }

            //Connect off-mesh links, specifically links which land from target tile to this tile
            BoundarySide oppositeSide = side.GetOpposite();

            //Iterate through all the off-mesh connections of target tile
            for (int i = 0; i < target.Header.OffMeshConCount; i++)
            {
                OffMeshConnection targetCon = target.OffMeshConnections[i];
                if (targetCon.Side != oppositeSide)
                {
                    continue;
                }

                Poly targetPoly = target.Polys[targetCon.Poly];

                //Skip off-mesh connections which start location could not be connected at all
                if (!IsLinkAllocated(targetPoly.FirstLink))
                {
                    continue;
                }

                Vector3 extents = new Vector3(targetCon.Radius, target.Header.WalkableClimb, targetCon.Radius);

                //Find polygon to connect to
                Vector3 p         = targetCon.Pos1;
                Vector3 nearestPt = new Vector3();
                PolyId  reference = FindNearestPolyInTile(tile, p, extents, ref nearestPt);
                if (reference == PolyId.Null)
                {
                    continue;
                }

                //Further checks
                if ((nearestPt.X - p.X) * (nearestPt.X - p.X) + (nearestPt.Z - p.Z) * (nearestPt.Z - p.Z) >
                    (targetCon.Radius * targetCon.Radius))
                {
                    continue;
                }

                //Make sure the location is on the current mesh
                target.Verts[targetPoly.Verts[1]] = nearestPt;

                //Link off-mesh connection to target poly
                int idx = AllocLink(target);
                if (IsLinkAllocated(idx))
                {
                    target.Links[idx].Reference = reference;
                    target.Links[idx].Edge      = i;
                    target.Links[idx].Side      = oppositeSide;
                    target.Links[idx].BMin      = target.Links[idx].BMax = 0;

                    //add to linked list
                    target.Links[idx].Next    = target.Polys[i].FirstLink;
                    target.Polys[i].FirstLink = idx;
                }

                //link target poly to off-mesh connection
                if ((targetCon.Flags & OffMeshConnectionFlags.Bidirectional) != 0)
                {
                    int tidx = AllocLink(tile);
                    if (IsLinkAllocated(tidx))
                    {
                        int    landPolyIdx = reference.DecodePolyIndex(polyBits);
                        PolyId id;
                        id = GetPolyRefBase(target);
                        PolyId.SetPolyIndex(ref id, targetCon.Poly, out id);
                        tile.Links[tidx].Reference = id;
                        tile.Links[tidx].Edge      = 0xff;
                        tile.Links[tidx].Side      = side;
                        tile.Links[tidx].BMin      = tile.Links[tidx].BMax = 0;

                        //add to linked list
                        tile.Links[tidx].Next             = tile.Polys[landPolyIdx].FirstLink;
                        tile.Polys[landPolyIdx].FirstLink = tidx;
                    }
                }
            }
        }
예제 #2
0
        public byte[] Build(int i = 0, int j = 0)
        {
            float[] bbMin, bbMax;
            CalculateTileBounds(out bbMin, out bbMax, false, i, j);

#if (TIMETHIS)
            System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
            stopWatch.Start();
            long cur = 0;
            #endif
            // add border
            bbMin[0] -= Config.BorderSize * Config.CellSize;
            bbMin[2] -= Config.BorderSize * Config.CellSize;
            bbMax[0] += Config.BorderSize * Config.CellSize;
            bbMax[2] += Config.BorderSize * Config.CellSize;

            // get raw geometry - lots of slowness here
            float[] vertices;
            int[]   triangles;
            byte[]  areas;

            Geometry.GetRawData(out vertices, out triangles, out areas);
#if (TIMETHIS)
            Console.WriteLine("GetRawData: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
#endif
            // Following code would check if we have Index out of range error while computing the sum. the result itself is useless.

            /*float sum = 0;
             * foreach (int i2 in triangles)
             *  sum += vertices[i2*3+0] + vertices[i2*3+1] + vertices[i2*3+2];
             * Console.WriteLine(sum);*/

            // now we can find the min/max height for THIS tile
            float MinHeight, MaxHeight;
            Geometry.CalculateMinMaxHeight(out MinHeight, out MaxHeight, bbMin, bbMax);
            //Geometry.Triangles.Clear(); // We should not clear triangles and stuff because we have severals files for one tile.
            //Geometry.Vertices.Clear(); // It will be reset at the next tile anyway, no memory issues.
            bbMin[1] = MinHeight;
            bbMax[1] = MaxHeight;

            Heightfield hf;
            int         width = Config.TileWidth + (Config.BorderSize * 2);
            if (!Context.CreateHeightfield(out hf, width, width, bbMin, bbMax, Config.CellSize, Config.CellHeight))
            {
                throw new OutOfMemoryException("CreateHeightfield ran out of memory");
            }

#if (TIMETHIS)
            Console.WriteLine("CreateHeightfield: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            if (triangles.Count() > 0)
            {
                /*Console.WriteLine("Context.ClearUnwalkableTriangles: verticles: " + vertices.Length + ", triangles: " + triangles.Length + ", areas: " + areas.Length);
                 * Console.WriteLine("Memory allocated GC: " + GC.GetTotalMemory(true));/*/
                Context.ClearUnwalkableTriangles(Config.WalkableSlopeAngle, ref vertices, ref triangles, areas);
#if (TIMETHIS)
                Console.WriteLine("ClearUnwalkableTriangles: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
                cur = stopWatch.ElapsedMilliseconds;
                #endif
                Context.RasterizeTriangles(ref vertices, ref triangles, ref areas, hf, Config.WalkableClimb);
#if (TIMETHIS)
                Console.WriteLine("RasterizeTriangles: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
                cur = stopWatch.ElapsedMilliseconds;
                #endif

                GC.KeepAlive(vertices); // force C# to keep vertices, triangles and areas alive while it's in unamanaged code.
                GC.KeepAlive(triangles);
                GC.KeepAlive(areas);
            }
            vertices  = null;
            triangles = null;
            areas     = null;
            GC.Collect();

            // Once all geometry is rasterized, we do initial pass of filtering to
            // remove unwanted overhangs caused by the conservative rasterization
            // as well as filter spans where the character cannot possibly stand.
            Context.FilterLowHangingWalkableObstacles(Config.WalkableClimb, hf);
#if (TIMETHIS)
            Console.WriteLine("FilterLowHangingWalkableObstacles: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif
            Context.FilterLedgeSpans(Config.WalkableHeight, Config.WalkableClimb, hf);
#if (TIMETHIS)
            Console.WriteLine("FilterLedgeSpans: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif
            Context.FilterWalkableLowHeightSpans(Config.WalkableHeight, hf);
#if (TIMETHIS)
            Console.WriteLine("FilterWalkableLowHeightSpans: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            // Rasterize once again after the cleanup we did
            //Context.RasterizeTriangles(ref vertices, ref triangles, ref areas, hf, Config.WalkableClimb);
            //#if (TIMETHIS)
            //    Console.WriteLine("RasterizeTriangles: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            //    cur = stopWatch.ElapsedMilliseconds;
            //#endif

            // Compact the heightfield so that it is faster to handle from now on.
            // This will result in more cache coherent data as well as the neighbours
            // between walkable cells will be calculated.
            CompactHeightfield chf;
            if (!Context.BuildCompactHeightfield(Config.WalkableHeight, Config.WalkableClimb, hf, out chf))
            {
                throw new OutOfMemoryException("BuildCompactHeightfield ran out of memory");
            }
#if (TIMETHIS)
            Console.WriteLine("BuildCompactHeightfield: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif
            hf.Delete();

            // Erode the walkable area by agent radius.
            if (!Context.ErodeWalkableArea(Config.WalkableRadius, chf))
            {
                throw new OutOfMemoryException("ErodeWalkableArea ran out of memory");
            }
#if (TIMETHIS)
            Console.WriteLine("ErodeWalkableArea: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            // Prepare for region partitioning, by calculating distance field along the walkable surface.
            if (!Context.BuildDistanceField(chf))
            {
                throw new OutOfMemoryException("BuildDistanceField ran out of memory");
            }
#if (TIMETHIS)
            Console.WriteLine("BuildDistanceField: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            // Partition the walkable surface into simple regions without holes.
            if (!Context.BuildRegions(chf, Config.BorderSize, Config.MinRegionArea, Config.MergeRegionArea))
            {
                throw new OutOfMemoryException("BuildRegions ran out of memory");
            }
#if (TIMETHIS)
            Console.WriteLine("BuildRegions: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            // Create contours.
            ContourSet cset;
            if (!Context.BuildContours(chf, Config.MaxSimplificationError, Config.MaxEdgeLength, out cset))
            {
                throw new OutOfMemoryException("BuildContours ran out of memory");
            }
#if (TIMETHIS)
            Console.WriteLine("BuildContours: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            // Build polygon navmesh from the contours.
            PolyMesh pmesh;
            if (!Context.BuildPolyMesh(cset, Config.MaxVertsPerPoly, out pmesh))
            {
                throw new OutOfMemoryException("BuildPolyMesh ran out of memory");
            }
#if (TIMETHIS)
            Console.WriteLine("BuildPolyMesh: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            cur = stopWatch.ElapsedMilliseconds;
            #endif

            // Build detail mesh.
            PolyMeshDetail dmesh = null;
            //if (!Context.BuildPolyMeshDetail(pmesh, chf, Config.DetailSampleDistance, Config.DetailSampleMaxError,out dmesh))
            //    throw new OutOfMemoryException("BuildPolyMeshDetail ran out of memory");
            //#if (TIMETHIS)
            //    Console.WriteLine("BuildPolyMeshDetail: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            //   cur = stopWatch.ElapsedMilliseconds;
            //#endif

            chf.Delete();
            cset.Delete();

            // Set flags according to area types (e.g. Swim for Water)
            pmesh.MarkAll();

            // get original bounds
            float[] tilebMin, tilebMax;
            CalculateTileBounds(out tilebMin, out tilebMax, false, i, j);
            tilebMin[1] = bbMin[1];
            tilebMax[1] = bbMax[1];

            // build off mesh connections for flightmasters
            // bMax and bMin are switched here because of the coordinate system transformation

            var connections = new List <OffMeshConnection>();

            if (MapId == 1220)
            {
                SlimDX.Vector3    from = new SlimDX.Vector3(1685.681f, 4709.121f, 139.4296f);
                SlimDX.Vector3    to   = new SlimDX.Vector3(1682.194f, 4712.713f, 139.6532f);
                OffMeshConnection conn = new OffMeshConnection
                {
                    AreaId = PolyArea.Road,
                    Flags  = PolyFlag.Walk,
                    From   = from.ToRecast().ToFloatArray(),
                    To     = to.ToRecast().ToFloatArray(),
                    Radius = 5,
                    Type   = ConnectionType.BiDirectional,
                    UserID = 1,
                };
                connections.Add(conn);
                from = new SlimDX.Vector3(1661.302f, 4727.02f, 139.4618f);
                to   = new SlimDX.Vector3(1669.568f, 4733.722f, 138.7087f);
                conn = new OffMeshConnection
                {
                    AreaId = PolyArea.Road,
                    Flags  = PolyFlag.Walk,
                    From   = from.ToRecast().ToFloatArray(),
                    To     = to.ToRecast().ToFloatArray(),
                    Radius = 5f,
                    Type   = ConnectionType.BiDirectional,
                    UserID = 2,
                };
                connections.Add(conn);
            }

            /*if (MapId == 0 && X == 31 && Y == 58)
             * {
             *  //<X>-14250.71</X>
             *  //<Y>329.7578</Y>
             *  //<Z>24.17678</Z>
             *
             *  //<X>-14254.86</X>
             *  //<Y>336.1039</Y>
             *  //<Z>26.79213</Z>
             *
             *  SlimDX.Vector3 from = new SlimDX.Vector3(1.0f, 1.0f, 1.0f);
             *  SlimDX.Vector3 to   = new SlimDX.Vector3(1.0f, 1.0f, 1.0f);
             *  OffMeshConnection conn = new OffMeshConnection
             *          {
             *              From = from.ToRecast().ToFloatArray(),
             *              To = to.ToRecast().ToFloatArray(),
             *              Radius = 2.5f,
             *              Type = ConnectionType.BiDirectional,
             *              UserID = 88,
             *          };
             *  connections.Add(conn);
             * }*/

            /* // Disable mesh connexion RIVAL
             * var taxis = TaxiHelper.GetNodesInBBox(MapId, tilebMax.ToWoW(), tilebMin.ToWoW());
             * foreach (var taxi in taxis)
             * {
             *  Log.Log(LogCategory.Warning,
             *          "Flightmaster \"" + taxi.Name + "\", Id: " + taxi.Id + " Horde: " + taxi.IsHorde + " Alliance: " +
             *          taxi.IsAlliance);
             *
             *  var data = TaxiHelper.GetTaxiData(taxi);
             *  var from = taxi.Location.ToRecast().ToFloatArray();
             *  connections.AddRange(data.To.Select(to => new OffMeshConnection
             *                                                {
             *                                                    AreaId = PolyArea.Road,
             *                                                    Flags = PolyFlag.FlightMaster,
             *                                                    From = from,
             *                                                    To = to.Value.Location.ToRecast().ToFloatArray(),
             *                                                    Radius = Config.WorldWalkableRadius,
             *                                                    Type = ConnectionType.OneWay,
             *                                                    UserID = (uint) to.Key
             *                                                }));
             *
             *  foreach (var target in data.To)
             *      Log.Log(LogCategory.Warning,
             *              "\tPath to: \"" + target.Value.Name + "\" Id: " + target.Value.Id + " Path Id: " +
             *              target.Key);
             * }*/


            byte[] tileData;
            if (!Detour.CreateNavMeshData(out tileData, pmesh, dmesh,
                                          X * Constant.Division + i, Y * Constant.Division + j, tilebMin, tilebMax,
                                          Config.WorldWalkableHeight, Config.WorldWalkableRadius,
                                          Config.WorldWalkableClimb, Config.CellSize,
                                          Config.CellHeight, Config.BuildBvTree,
                                          connections.ToArray()))
            {
                pmesh.Delete();
                //dmesh.Delete();
                return(null);
            }
#if (TIMETHIS)
            Console.WriteLine("CreateNavMeshData: " + (stopWatch.ElapsedMilliseconds - cur) + ", total: " + stopWatch.ElapsedMilliseconds);
            #endif
            pmesh.Delete();
            //dmesh.Delete();
            GC.Collect();

            return(tileData);
        }
예제 #3
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]           = new Poly();
                    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] = new OffMeshConnection();

                    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++;
                }
            }
        }
예제 #4
0
        public static bool CreateNavMeshData(NavMeshCreateParams param, out MeshData outData)
        {
            outData = null;

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

            int nvp = param.nvp;

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

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

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

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

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

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

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

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

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

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

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

                    edgeCount++;

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

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

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

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

            MeshData data = new MeshData
            {
                // Store header
                Header = new MeshHeader
                {
                    Magic           = DT_NAVMESH_MAGIC,
                    Version         = DT_NAVMESH_VERSION,
                    X               = param.tileX,
                    Y               = param.tileY,
                    Layer           = param.tileLayer,
                    UserId          = param.userId,
                    PolyCount       = totPolyCount,
                    VertCount       = totVertCount,
                    MaxLinkCount    = maxLinkCount,
                    BMin            = param.bmin,
                    BMax            = param.bmax,
                    DetailMeshCount = param.polyCount,
                    DetailVertCount = uniqueDetailVertCount,
                    DetailTriCount  = detailTriCount,
                    BvQuantFactor   = 1.0f / param.cs,
                    OffMeshBase     = param.polyCount,
                    WalkableHeight  = param.walkableHeight,
                    WalkableRadius  = param.walkableRadius,
                    WalkableClimb   = param.walkableClimb,
                    OffMeshConCount = storedOffMeshConCount,
                    BvNodeCount     = param.buildBvTree ? param.polyCount * 2 : 0
                }
            };

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

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

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

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

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

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

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

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

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

                    p.VertCount++;
                }

                data.NavPolys.Add(p);

                srcIndex++;
            }

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

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

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

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

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

                data.NavBvtree.AddRange(nodes);
            }

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

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

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

            outData = data;

            return(true);
        }