Ejemplo n.º 1
0
 /// <summary>
 /// Patch header pointers
 /// </summary>
 /// <param name="header">Header</param>
 public void Patch(MeshHeader header)
 {
     Verts        = new Vector3[header.VertCount];
     Polys        = new Poly[header.PolyCount];
     Links        = new Link[header.MaxLinkCount];
     DetailMeshes = new PolyDetail[header.DetailMeshCount];
     DetailVerts  = new Vector3[header.DetailVertCount];
     DetailTris   = new Int4[header.DetailTriCount];
     BvTree       = new BVNode[header.BvNodeCount];
     OffMeshCons  = new OffMeshConnection[header.OffMeshConCount];
 }
Ejemplo n.º 2
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);
        }