csharp/AnotherEnd15/NetCodeDemo/Unity/Assets/Editor/RecastNavDataExporter/NavMeshExporter.cs

RecastNavDataExporter
NavMeshExporter.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using ET;
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

namespace ETEditor
{
    /// 
    /// 从Unity的NavMesh组件里导出地图数据,供服务器来使用
    /// https://blog.csdn.net/huutu/article/details/52672505
    /// 
    public clast NavMeshExporter: Editor
    {
        public const byte VERSION = 1;

        private clast Vert
        {
            public int id;
            public float x;
            public float y;
            public float z;

            public UnityEngine.Vector3 ToVector3()
            {
                return new UnityEngine.Vector3(x, y, z);
            }
        }

        private clast Face
        {
            public int id;
            public int area;
            public float centerX;
            public float centerZ;
            public float normalX;
            public float normalZ;
            public double normalA;
            public double normalB;
            public double normalC;
            public double normalD;
            public uint sortValue;
            public List verts = new List();
        }

        private clast Pair
        {
            public float centerX;
            public float centerZ;
            public float distance;
            public Face firstEdgeFace;
            public int firstEdgeIndex;
            public Face secondEdgeFace;
            public int secondEdgeIndex;
        }

        private static List vertList = new List();
        private static List faceList = new List();
        private static List pairList = new List();
        private static Dictionary vertFaceDict = new Dictionary();
        private static Dictionary vertPairDict = new Dictionary();
        private static Dictionary pointVertDict = new Dictionary();
        private static Dictionary indexVertDict = new Dictionary();
        private static string outputClientFolder = "../RecastNavMesh/Meshes/";
        private static string outputServerFolder = "../Config/RecastNavData/ExportedObj/";

        #region 菜单主函数
        [MenuItem("Tools/NavMesh/ExportSceneObj")]
        public static void ExportScene()
        {
            var triangulation = UnityEngine.AI.NavMesh.CalculateTriangulation();
            if (triangulation.indices.Length < 3)
            {
                Debug.LogError($"NavMeshExporter ExportScene Error - 场景里没有需要被导出的物体,请先用NavMesh进行Bake。");
                return;
            }

            vertList.Clear();
            faceList.Clear();
            pairList.Clear();
            vertFaceDict.Clear();
            vertPairDict.Clear();
            pointVertDict.Clear();
            indexVertDict.Clear();
            InputVertices(triangulation.vertices);
            InputTriangles(triangulation.indices, triangulation.areas);
            IndexVertsAndFaces();
            //WriteFile();

            // 导出*_internal.Obj,仅供Unity编辑器自己查看
            //WriteUnityObjFile();
            // 导出Recast可用的*.Obj文件
            WriteRecastObjFile();
            // 拷贝Obj和Bytes文件到服务器目录下 TODO 暂不需要
            //CopyObjFiles();

            Debug.Log($"NavMesh Output Info - Vertices:[{vertList.Count}] - Faces:[{faceList.Count}]");
        }

        #endregion

        #region 导出Bytes

        private static void InputVertices(Vector3[] vertices)
        {
            for (int i = 0, n = vertices.Length - 1; i = 3? areas[i] - 2 : 0;
                if (face != null && face.area == area)
                {
                    for (var j = 0; j < 3; j++)
                    {
                        if (faceIndices.Contains(triangleIndexList[j]))
                        {
                            newFace = false;
                            break;
                        }
                    }
                }

                if (newFace)
                {
                    if (face != null)
                    {
                        InitFace(face);
                        faceIndices.Clear();
                    }

                    face = new Face();
                    face.area = area;
                }

                double x1 = vert1.x - vert0.x;
                double y1 = vert1.y - vert0.y;
                double z1 = vert1.z - vert0.z;
                double x2 = vert2.x - vert0.x;
                double y2 = vert2.y - vert0.y;
                double z2 = vert2.z - vert0.z;
                double normalA = y1 * z2 - z1 * y2;
                double normalB = z1 * x2 - x1 * z2;
                double normalC = x1 * y2 - y1 * x2;
                if (normalB < -0.000001 || 0.000001 < normalB)
                {
                    var normalD = normalA + normalB + normalC;
                    if (normalD > face.normalD)
                    {
                        face.normalA = normalA;
                        face.normalB = normalB;
                        face.normalC = normalC;
                        face.normalD = normalD;
                    }
                }

                for (var j = 0; j < 3; j++)
                {
                    if (!faceIndices.Contains(triangleIndexList[j]))
                    {
                        faceIndices.Add(triangleIndexList[j]);
                        face.verts.Add(triangleVertList[j]);
                    }
                }
            }

            if (face != null)
            {
                InitFace(face);
            }

            foreach (var pair in pairList)
            {
                var firstFace = pair.firstEdgeFace;
                var secondFace = pair.secondEdgeFace;
                var firstDistance = GetDistance(firstFace.centerX - pair.centerX, firstFace.centerZ - pair.centerZ);
                var secondDistance = GetDistance(secondFace.centerX - pair.centerX, secondFace.centerZ - pair.centerZ);
                pair.distance = firstDistance + secondDistance;
            }
        }

        private static float GetDistance(float deltaX, float deltaZ)
        {
            return (float) Math.Round(Math.Sqrt((double) deltaX * (double) deltaX + (double) deltaZ * (double) deltaZ), 2);
        }

        private static void InitFace(Face face)
        {
            face.centerX = 0;
            face.centerZ = 0;
            var vertCount = face.verts.Count;
            foreach (var vert in face.verts)
            {
                face.centerX += vert.x;
                face.centerZ += vert.z;
                if (!vertFaceDict.ContainsKey(vert))
                {
                    vertFaceDict.Add(vert, face);
                    vertList.Add(vert);
                }
            }

            face.centerX /= vertCount;
            face.centerZ /= vertCount;
            if (face.normalB != 0)
            {
                face.normalX = (float) Math.Round(face.normalA / face.normalB, 6);
                face.normalZ = (float) Math.Round(face.normalC / face.normalB, 6);
            }

            for (int i = 0, n = vertCount - 1; i  vert.x)
                {
                    minX = vert.x;
                }

                if (maxX < vert.x)
                {
                    maxX = vert.x;
                }

                if (minZ > vert.z)
                {
                    minZ = vert.z;
                }

                if (maxZ < vert.x)
                {
                    maxZ = vert.x;
                }
            }

            var hilbertX = 65535f / (maxX - minX);
            var hilbertZ = 65535f / (maxZ - minZ);
            foreach (var face in faceList)
            {
                var X = (uint) Math.Round((face.centerX - minX) * hilbertX);
                var Z = (uint) Math.Round((face.centerZ - minZ) * hilbertZ);
                var a = X ^ Z;
                var b = 0xFFFF ^ a;
                var c = 0xFFFF ^ (X | Z);
                var d = X & (Z ^ 0xFFFF);
                var A = a | (b >> 1);
                var B = (a >> 1) ^ a;
                var C = ((c >> 1) ^ (b & (d >> 1))) ^ c;
                var D = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
                a = A;
                b = B;
                c = C;
                d = D;
                A = (a & (a >> 2)) ^ (b & (b >> 2));
                B = (a & (b >> 2)) ^ (b & ((a ^ b) >> 2));
                C ^= (a & (c >> 2)) ^ (b & (d >> 2));
                D ^= (b & (c >> 2)) ^ ((a ^ b) & (d >> 2));
                a = A;
                b = B;
                c = C;
                d = D;
                A = (a & (a >> 4)) ^ (b & (b >> 4));
                B = (a & (b >> 4)) ^ (b & ((a ^ b) >> 4));
                C ^= (a & (c >> 4)) ^ (b & (d >> 4));
                D ^= (b & (c >> 4)) ^ ((a ^ b) & (d >> 4));
                a = A;
                b = B;
                c = C;
                d = D;
                C ^= (a & (c >> 8)) ^ (b & (d >> 8));
                D ^= (b & (c >> 8)) ^ ((a ^ b) & (d >> 8));
                C ^= C >> 1;
                D ^= D >> 1;
                c = X ^ Z;
                d = D | (0xFFFF ^ (c | C));
                c = (c | (c