private void Subdivide(BVNode[] items, int nitems, int imin, int imax, ref int curNode, ref BVNode[] nodes)
        {
            int inum = imax - imin;
            int icur = curNode;

            BVNode node = nodes[curNode++];

            if (inum == 1)
            {
                node.BMin[0] = items[imin].BMin[0];
                node.BMin[1] = items[imin].BMin[1];
                node.BMin[2] = items[imin].BMin[2];

                node.BMax[0] = items[imin].BMax[0];
                node.BMax[1] = items[imin].BMax[1];
                node.BMax[2] = items[imin].BMax[2];

                node.I = items[imin].I;
            }
            else
            {
                int[] tempBMin = new int[3], tempBMax = new int[3];
                CalcExtends(items, nitems, imin, imax, ref tempBMin, ref tempBMax);

                Array.Copy(tempBMin, node.BMin, 3);
                Array.Copy(tempBMax, node.BMax, 3);

                int axis = LongestAxis(node.BMax[0] - node.BMin[0],
                                       node.BMax[1] - node.BMin[1],
                                       node.BMax[2] - node.BMin[2]);
                if (axis == 0)
                {
                    Array.Sort(items, imin, inum, new CompareBVNodeX());
                }
                else if (axis == 1)
                {
                    Array.Sort(items, imin, inum, new CompareBVNodeY());
                }
                else
                {
                    Array.Sort(items, imin, inum, new CompareBVNodeZ());
                }

                int isplit = imin + inum / 2;

                Subdivide(items, nitems, imin, isplit, ref curNode, ref nodes);
                Subdivide(items, nitems, isplit, imax, ref curNode, ref nodes);

                int iescape = curNode - icur;
                node.I = -iescape;
            }
        }
Esempio n. 2
0
        int IComparer.Compare(object va, object vb)
        {
            BVNode a = va as BVNode;
            BVNode b = va as BVNode;

            if (a != null && b != null)
            {
                if (a.BMin[0] < b.BMin[0])
                {
                    return(-1);
                }
                if (a.BMin[0] > b.BMin[0])
                {
                    return(1);
                }
            }
            return(0);
        }
        private void CalcExtends(BVNode[] items, int nitems, int imin, int imax, ref int[] bmin, ref int[] bmax)
        {
            bmin[0] = items[imin].BMin[0];
            bmin[1] = items[imin].BMin[1];
            bmin[2] = items[imin].BMin[2];

            bmax[0] = items[imin].BMax[0];
            bmax[1] = items[imin].BMax[1];
            bmax[2] = items[imin].BMax[2];

            for (int i = imin + 1; i < imax; ++i)
            {
                BVNode it = items[i];
                if (it.BMin[0] < bmin[0])
                {
                    bmin[0] = it.BMin[0];
                }
                if (it.BMin[1] < bmin[1])
                {
                    bmin[1] = it.BMin[1];
                }
                if (it.BMin[2] < bmin[2])
                {
                    bmin[2] = it.BMin[2];
                }

                if (it.BMax[0] > bmax[0])
                {
                    bmax[0] = it.BMax[0];
                }
                if (it.BMax[1] > bmax[1])
                {
                    bmax[1] = it.BMax[1];
                }
                if (it.BMax[2] > bmax[2])
                {
                    bmax[2] = it.BMax[2];
                }
            }
        }
        public NavMeshBuilder(NavMeshCreateParams param)
        {
            if (param.Nvp > VertsPerPoly)
            {
                throw new ArgumentException("Too many Verts per Poly for NavMeshBuilder");
            }
            if (param.VertCount >= 0xffff)
            {
                throw new ArgumentException("Too many total verticies for NavMeshBuilder");
            }
            if (param.VertCount == 0 || param.Verts == null)
            {
                throw new ArgumentException("No vertices, cannot generate nav mesh");
            }
            if (param.PolyCount == 0 || param.Polys == null)
            {
                throw new ArgumentException("No Polygons, cannot generate nav mesh");
            }

            int nvp = param.Nvp;

            short[] offMeshConClass       = new short[0];
            int     storedOffMeshConCount = 0;
            int     offMeshConLinkCount   = 0;

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

                float hmin = float.MaxValue;
                float hmax = float.MinValue;

                if (param.DetailVerts != null && param.DetailVertsCount > 0)
                {
                    for (int i = 0; i < param.DetailVertsCount; i++)
                    {
                        int h = i * 3 + 1;
                        hmin = Math.Min(hmin, param.DetailVerts[h]);
                        hmax = Math.Max(hmax, param.DetailVerts[h]);
                    }
                }
                else
                {
                    for (int i = 0; i < param.VertCount; i++)
                    {
                        int   iv = i * 3;
                        float h  = param.BMin[1] + param.Verts[iv + 1] * param.Ch;
                        hmin = Math.Min(hmin, h);
                        hmax = Math.Max(hmax, h);
                    }
                }
                hmin -= param.WalkableClimb;
                hmax += param.WalkableClimb;
                float[] bmin = new float[3], bmax = new float[3];
                Array.Copy(param.BMin, bmin, 3);
                Array.Copy(param.BMax, bmax, 3);

                bmin[1] = hmin;
                bmax[1] = hmax;

                for (int i = 0; i < param.OffMeshConCount; i++)
                {
                    int p0 = (i * 2 + 0) * 3;
                    int p1 = (i * 2 + 1) * 3;
                    offMeshConClass[i * 2 + 0] = ClassifyOffMeshPoint(param.OffMeshConVerts[p0 + 0],
                                                                      param.OffMeshConVerts[p0 + 1],
                                                                      param.OffMeshConVerts[p0 + 2], bmin, bmax);
                    offMeshConClass[i * 2 + 1] = ClassifyOffMeshPoint(param.OffMeshConVerts[p1 + 0],
                                                                      param.OffMeshConVerts[p1 + 1],
                                                                      param.OffMeshConVerts[p1 + 2], bmin, bmax);

                    if (offMeshConClass[i * 2 + 0] == 0xff)
                    {
                        if (param.OffMeshConVerts[p0 + 1] < bmin[1] || param.OffMeshConVerts[p0 + 1] > bmax[1])
                        {
                            offMeshConClass[i * 2 + 0] = 0;
                        }
                    }

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

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

            int totPolyCount = param.PolyCount + storedOffMeshConCount;
            int totVertCount = param.VertCount + storedOffMeshConCount * 2;

            int edgeCount   = 0;
            int portalCount = 0;

            for (int i = 0; i < param.PolyCount; i++)
            {
                int p = i * 2 * nvp;
                for (int j = 0; j < nvp; j++)
                {
                    if (param.Polys[p + j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    edgeCount++;

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

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

            int uniqueDetailVertCount = 0;
            int detailTryCount        = 0;

            if (param.DetailMeshes != null)
            {
                detailTryCount = param.DetailTriCount;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    int p   = i * nvp * 2;
                    int ndv = (int)param.DetailMeshes[i * 4 + 1];
                    int nv  = 0;
                    for (int j = 0; j < nvp; j++)
                    {
                        if (param.Polys[p + j] == PolyMesh.MeshNullIdx)
                        {
                            break;
                        }
                        nv++;
                    }
                    ndv -= nv;
                    uniqueDetailVertCount += ndv;
                }
            }
            else
            {
                uniqueDetailVertCount = 0;
                detailTryCount        = 0;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    int p  = i * nvp * 2;
                    int nv = 0;
                    for (int j = 0; j < nvp; j++)
                    {
                        if (param.Polys[p + j] == PolyMesh.MeshNullIdx)
                        {
                            break;
                        }
                        nv++;
                    }
                    detailTryCount += nv - 2;
                }
            }

            // Initialize the header and all nav data.
            Header = new MeshHeader
            {
                Magic           = Helper.NavMeshMagic,
                Version         = Helper.NavMeshVersion,
                X               = param.TileX,
                Y               = param.TileY,
                Layer           = param.TileLayer,
                UserId          = param.UserId,
                PolyCount       = totPolyCount,
                VertCount       = totVertCount,
                MaxLinkCount    = maxLinkCount,
                DetailMeshCount = param.PolyCount,
                DetailVertCount = uniqueDetailVertCount,
                DetailTriCount  = detailTryCount,
                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,
                BMin            = new float[3],
                BMax            = new float[3]
            };
            Array.Copy(param.BMin, Header.BMin, 3);
            Array.Copy(param.BMax, Header.BMax, 3);

            NavVerts = new float[totVertCount * 3];
            NavPolys = new Poly[totPolyCount];
            for (int i = 0; i < totPolyCount; i++)
            {
                NavPolys[i] = new Poly();
            }
            NavLinks = new Link[maxLinkCount];
            for (int i = 0; i < maxLinkCount; i++)
            {
                NavLinks[i] = new Link();
            }
            NavDMeshes = new PolyDetail[param.PolyCount];
            for (int i = 0; i < param.PolyCount; i++)
            {
                NavDMeshes[i] = new PolyDetail();
            }
            NavDVerts = new float[3 * uniqueDetailVertCount];
            NavDTris  = new short[4 * detailTryCount];
            NavBvTree = param.BuildBvTree ? new BVNode[param.PolyCount * 2] : new BVNode[0];
            if (param.BuildBvTree)
            {
                for (int i = 0; i < param.PolyCount * 2; i++)
                {
                    NavBvTree[i] = new BVNode();
                }
            }
            OffMeshCons = new OffMeshConnection[storedOffMeshConCount];
            for (int i = 0; i < storedOffMeshConCount; i++)
            {
                OffMeshCons[i] = new OffMeshConnection();
            }


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

            // store vertices
            // Mesh
            for (int i = 0; i < param.VertCount; i++)
            {
                int iv = i * 3;
                int v  = i * 3;
                NavVerts[v + 0] = param.BMin[0] + param.Verts[iv + 0] * param.Cs;
                NavVerts[v + 1] = param.BMin[1] + param.Verts[iv + 1] * param.Ch;
                NavVerts[v + 2] = param.BMin[2] + param.Verts[iv + 2] * param.Cs;
            }
            // off-link
            int n = 0;

            for (int i = 0; i < param.OffMeshConCount; i++)
            {
                if (offMeshConClass[i * 2 + 0] == 0xff)
                {
                    int linkv = i * 2 * 3;
                    int v     = (offMeshVertsBase + n * 2) * 3;
                    Array.Copy(param.OffMeshConVerts, linkv, NavVerts, v, 3);
                    Array.Copy(param.OffMeshConVerts, linkv + 3, NavVerts, v + 3, 3);
                    n++;
                }
            }

            // store polygons
            // mesh
            int src = 0;

            for (int i = 0; i < param.PolyCount; i++)
            {
                Poly p = NavPolys[i];
                p.VertCount = 0;
                p.Flags     = param.PolyFlags[i];
                p.Area      = param.PolyAreas[i];
                p.Type      = PolyTypeGround;
                for (int j = 0; j < nvp; j++)
                {
                    if (param.Polys[src + j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    p.Verts[j] = param.Polys[src + j];
                    if ((param.Polys[src + nvp + j] & 0x8000) != 0)
                    {
                        int dir = param.Polys[src + nvp + j] & 0xf;
                        if (dir == 0xf)
                        {
                            p.Neis[j] = 0;
                        }
                        else if (dir == 0)
                        {
                            p.Neis[j] = ExtLink | 4;
                        }
                        else if (dir == 1)
                        {
                            p.Neis[j] = ExtLink | 2;
                        }
                        else if (dir == 2)
                        {
                            p.Neis[j] = ExtLink | 0;
                        }
                        else if (dir == 3)
                        {
                            p.Neis[j] = ExtLink | 6;
                        }
                    }
                    else
                    {
                        p.Neis[j] = param.Polys[src + nvp + j] + 1;
                    }
                    p.VertCount++;
                }
                src += nvp * 2;
            }
            // off mesh
            n = 0;
            for (int i = 0; i < param.OffMeshConCount; i++)
            {
                if (offMeshConClass[i * 2 + 0] == 0xff)
                {
                    Poly p = NavPolys[offMeshPolyBase + n];
                    p.VertCount = 2;
                    p.Verts[0]  = (offMeshVertsBase + n * 2 + 0);
                    p.Verts[1]  = (offMeshVertsBase + n * 2 + 1);
                    p.Flags     = param.OffMeshConFlags[i];
                    p.Area      = (short)param.OffMeshConAreas[i];
                    p.Type      = PolyTypeOffMeshConnection;
                    n++;
                }
            }

            // Store detail meshes and verts
            if (param.DetailMeshes != null)
            {
                int vbase = 0;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    PolyDetail dtl = NavDMeshes[i];
                    int        vb  = (int)param.DetailMeshes[i * 4 + 0];
                    int        ndv = (int)param.DetailMeshes[i * 4 + 1];
                    int        nv  = NavPolys[i].VertCount;
                    dtl.VertBase  = vbase;
                    dtl.VertCount = (short)(ndv - nv);
                    dtl.TriBase   = param.DetailMeshes[i * 4 + 2];
                    dtl.TriCount  = (short)param.DetailMeshes[i * 4 + 3];
                    if (ndv - nv > 0)
                    {
                        Array.Copy(param.DetailVerts, (vb + nv) * 3, NavDVerts, vbase * 3, (ndv - nv) * 3);
                        vbase += (short)(ndv - nv);
                    }
                }
                Array.Copy(param.DetailTris, NavDTris, param.DetailTriCount * 4);
            }
            else
            {
                // Create dummy detail mesh
                int tbase = 0;
                for (int i = 0; i < param.PolyCount; i++)
                {
                    PolyDetail dtl = NavDMeshes[i];
                    int        nv  = NavPolys[i].VertCount;
                    dtl.VertBase  = 0;
                    dtl.VertCount = 0;
                    dtl.TriBase   = tbase;
                    dtl.TriCount  = (short)(nv - 2);
                    for (int j = 2; j < nv; j++)
                    {
                        int t = tbase * 4;
                        NavDTris[t + 0] = 0;
                        NavDTris[t + 1] = (short)(j - 1);
                        NavDTris[t + 2] = (short)j;
                        NavDTris[t + 3] = (1 << 2);
                        if (j == 2)
                        {
                            NavDTris[t + 3] |= (1 << 0);
                        }
                        if (j == nv - 1)
                        {
                            NavDTris[t + 3] |= (1 << 4);
                        }
                        tbase++;
                    }
                }
            }

            // Store and create BVTree
            if (param.BuildBvTree)
            {
                CreateBVTree(param.Verts, param.VertCount, param.Polys, param.PolyCount, nvp, param.Cs, param.Ch, param.PolyCount * 2);
            }

            // store off-mesh connections
            n = 0;
            for (int i = 0; i < param.OffMeshConCount; i++)
            {
                if (offMeshConClass[i * 2 + 0] == 0xff)
                {
                    OffMeshConnection con = OffMeshCons[n];
                    con.Poly = offMeshPolyBase + n;
                    int endPts = i * 2 * 3;
                    Array.Copy(param.OffMeshConVerts, endPts, con.Pos, 0, 3);
                    Array.Copy(param.OffMeshConVerts, endPts + 3, con.Pos, 3, 3);
                    con.Rad   = param.OffMeshConRad[i];
                    con.Flags = param.OffMeshConDir[i] > 0 ? OffMeshConBiDir : (short)0;
                    con.Side  = offMeshConClass[i * 2 + 1];
                    if (param.OffMeshConUserId != null)
                    {
                        con.UserId = param.OffMeshConUserId[i];
                    }
                    n++;
                }
            }
        }
        private int CreateBVTree(int[] verts, int nverts, int[] polys, int npolys, int nvp, float cs, float ch, int nnodes)
        {
            BVNode[] items = new BVNode[npolys];
            for (int i = 0; i < npolys; i++)
            {
                items[i] = new BVNode();
            }
            for (int i = 0; i < npolys; i++)
            {
                BVNode it = items[i];
                it.I = i;

                int[] p = new int[polys.Length - (i * nvp * 2)];
                Array.Copy(polys, i * nvp * 2, p, 0, p.Length);

                it.BMin[0] = it.BMax[0] = verts[p[0] * 3 + 0];
                it.BMin[1] = it.BMax[1] = verts[p[0] * 3 + 1];
                it.BMin[2] = it.BMax[2] = verts[p[0] * 3 + 2];

                for (int j = 1; j < nvp; j++)
                {
                    if (p[j] == PolyMesh.MeshNullIdx)
                    {
                        break;
                    }
                    int x = verts[p[j] * 3 + 0];
                    int y = verts[p[j] * 3 + 1];
                    int z = verts[p[j] * 3 + 2];

                    if (x < it.BMin[0])
                    {
                        it.BMin[0] = x;
                    }
                    if (y < it.BMin[1])
                    {
                        it.BMin[1] = y;
                    }
                    if (z < it.BMin[2])
                    {
                        it.BMin[2] = z;
                    }

                    if (x > it.BMax[0])
                    {
                        it.BMax[0] = x;
                    }
                    if (y > it.BMax[1])
                    {
                        it.BMax[1] = y;
                    }
                    if (z > it.BMax[2])
                    {
                        it.BMax[2] = z;
                    }
                }
                it.BMin[1] = (int)Math.Floor(it.BMin[1] * ch / cs);
                it.BMax[1] = (int)Math.Ceiling(it.BMax[1] * ch / cs);
            }
            int curNode = 0;

            BVNode[] temp = NavBvTree;
            Subdivide(items, npolys, 0, npolys, ref curNode, ref temp);
            NavBvTree = temp;
            return(curNode);
        }