obsidian/笔记文件/2.笔记/SkinToolEditor.md
2025-03-26 00:02:56 +08:00

78 KiB
Raw Permalink Blame History

#unity/白日梦/代码缓存

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System.Linq;
using System.Text.RegularExpressions;
using ResourcesModule.LMNA;

namespace SkinToolModule.M.Editor
{
    public struct LODDynamicColliderData
    {
        public Transform colTrans;
        public string name;
        public float radius;
        public string rootName;

        public LODDynamicColliderData(string n, float r = 0.3f, string rn = "", Transform c = null)
        {
            name = n;
            radius = r;
            rootName = rn;
            colTrans = c;
        }
    }

    public struct LODDynamicBoneParams
    {
        public Transform root;
        public float Damping;
        public float Elasticty;
        public float Stiffness;
        public float Inert;
        public float Friction;
        public float Radius;
        public List<LODDynamicColliderData> colliderDataList;
        public LODDynamicBoneParams(Transform t, float d = 0.02f, float e = 0.08f, float s = 0.06f, float i = 0.8f, float f = 0.6f, float r = 0.0f, List<LODDynamicColliderData> colliders = null)
        {
            root = t;
            Damping = d;
            Elasticty = e;
            Stiffness = s;
            Inert = i;
            Friction = f;
            Radius = r;
            colliderDataList = colliders;
        }
    }

    public class SkinToolEditor : EditorWindow
    {
        private enum MatType
        {
            Character,
            Transparent,
            Fakeskin,
            Fur,
            Error,
        }

        private static SkinToolEditor m_Window;
        public const string m_SuiteLevelPath = "Assets/Editor/ArtResources/Tool/Config/SuiteLevel.txt";
        private const string m_DefaultMatPath = "Assets/Editor/ArtResources/Tool/Config/DefaultMaterial.mat";
        private const string m_FbxDirIng = "/ResourcesRaw/CommonRegion/InGame/Skins/Characters/";
        private const string m_FbxDirLob = "/ResourcesRaw/CommonRegion/OutsideGame/Skins/Characters/";
        private const string m_PrefabDirIng = "Assets/BundleResources/CommonRegion/InGame/Skins/Characters/";
        private const string m_PrefabDirLob = "Assets/BundleResources/CommonRegion/OutsideGame/Skins/Characters/";
        private const string m_DynamicBoneParamDir = "Assets/Editor/ArtResources/Tool/LODDynamicBoneParams";
        private string m_OldFBXPath = "Assets/ResourcesRaw/CommonRegion/Skins/Characters/Models/";
        

        private bool m_IsLobby;
        private bool m_IsOld; // 是否是重导的减面资源
        private GameObject m_OldPrefab;
        private FileInfo m_FbxFile;
        private FileInfo[] m_TgaFiles;
        private string m_FolderPath = "";
        private string m_FbxPath = "";
        private string m_PrefabPath = "";
        private string LOD_m_PrefabPath = "";
        private string m_DynamicBonePrefabPath;

        

        private Dictionary<string,eAssetLodLevel> levelDic = new Dictionary<string, eAssetLodLevel>
        {
            ["lv0"] = eAssetLodLevel.Lv0,
            ["lv1"] = eAssetLodLevel.Lv1,
            ["lv2"] = eAssetLodLevel.Lv2,
            ["lv3"] = eAssetLodLevel.Lv3,
            ["lv4"] = eAssetLodLevel.Lv4
        };

        private Dictionary<string, eAssetSceneType> sceneDic = new Dictionary<string, eAssetSceneType>
        {
            ["lob"] = eAssetSceneType.Lobby,
            ["ing"] = eAssetSceneType.InGame,
        };

        private eAssetSceneType currentAssetSceneType = eAssetSceneType.Unknown;
        private eAssetLodLevel currentAssetLodLevel = eAssetLodLevel.None;
        
        private Dictionary<int,FileInfo>  mFbxFileList = new Dictionary<int,FileInfo>();
        private Dictionary<int, string> mFolderPathList = new Dictionary<int, string>();
        private Dictionary<int,string> mFbxPathList = new Dictionary<int,string>();
        private Dictionary<int,string> mPrefabPathList = new Dictionary<int,string>();
        
        
        // 品质相关
        public enum SuiteLevel { 
            white = 0, 
            blue = 1, 
            purple = 2, 
            orangee = 3, 
            pink = 4,
        };
        private SuiteLevel m_SuiteLevel = SuiteLevel.white;
        private GameObject m_FbxSenceObject;

        // Dynamic bone相关
        private GameObject m_DynamicBoneSenceObject;
        Dictionary<Transform, string> m_TaggedTransformDict = new Dictionary<Transform, string>(); // 被标记的骨骼节点 - 对应父节点名字
        Dictionary<string, LODDynamicBoneParams> m_TagToDynamicBoneParamDict = new Dictionary<string, LODDynamicBoneParams>(); // 被标记的骨骼节点名字 - 对应DynamicBone参数组
        List<Transform> m_SkinPartTransformList = new List<Transform>(); // 部位
        // 眨眼相关
        string BlinkFaceTag = "head_fx_01";
        Dictionary<string, string[]> m_BlinkFaceDict = new Dictionary<string, string[]> {
            { "Thief_08", new string[]{"Thief_08_head_OpenEyes_01_height", "Thief_08_head_CloseEyes_01_height"} },
            { "Police_05", new string[]{ "Police_05_head_OpenEyes_01_height", "Police_05_head_CloseEyes_01_height" } },
        };
        // 界面相关
        Color m_BlueColor = new Color(129 / 255f, 209 / 255f, 248 / 255f, 1);
        Color m_PinkColor = new Color(253 / 255f, 184 / 255f, 225 / 255f, 1);
        Color m_RedColor = new Color(233 / 255f, 74 / 255f, 110 / 255f, 1);
        Color m_PurpColor = new Color(125 / 255f, 128 / 255f, 227 / 255f, 1);
        float m_RowHeight = 20f;
        int m_RowFontSize = 12;
        GUIStyle m_MainHeaderStyle;
        GUIStyle m_SubHeaderStyle;
        GUIStyle m_ButtonStyle;
        GUIStyle m_TipsStyle;
        GUIStyle m_ErrorTipsStyle;
        GUIStyle m_LabelStyle;
        GUIStyle m_TagLabelStyle;
        GUIStyle m_ParamsLabelStyle;

        private string[] ModelName = new[]
        {
            "clothes",
            "hand",
            "head",
            "shoes",
            "trousers",
        };

        private string[] DynamicBoneTag = new[]
        {
            "CLOTHES_",
            "HEAD_",
            "HAND_",
            "SHOES_",
            "TROUSERS_"
        };

        [UnityEditor.MenuItem("美术工具/导入时装资源", false, 1)]
        static void ShowWindow()
        {
            if (m_Window == null)
            {
                m_Window = (SkinToolEditor)EditorWindow.GetWindow(typeof(SkinToolEditor), true, "SkinToolEditor");
                m_Window.Show();
            }
            else
            {
                m_Window.Close();
                m_Window = null;
            }
        }
        

        #region GUI

        void OnGUI()
        {
            InitStyles();
            EditorGUILayout.BeginVertical();
            // Title
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Bunny Editor", m_MainHeaderStyle, GUILayout.Height(30));
            if (GUILayout.Button("清除", m_ButtonStyle, GUILayout.Width(150), GUILayout.Height(m_RowHeight)))
            {
                if (EditorUtility.DisplayDialog("警告", "确定要清除本次生成的所有资源么?", "删就完事儿了"))
                {
                    OnClickClearButton();
                }
            }
            // if (GUILayout.Button(m_IsOld?"当前:老资源合入": "当前:新资源合入", m_ButtonStyle, GUILayout.Width(250), GUILayout.Height(m_RowHeight)))
            // {
            //     m_IsOld = !m_IsOld;
            // }
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.Space();
            DrawSplitLine(25);

            // Load fbx
            if (GUILayout.Button("选fbx所在文件夹", m_ButtonStyle, GUILayout.Width(150), GUILayout.Height(m_RowHeight)))
            {
                OnClickCreateButton(OnClickLoadButton());
            }

            EditorGUILayout.Space();
            // Show FolderPath
            // if (!string.IsNullOrEmpty(m_FolderPath))
            // {
            //     if (!Directory.Exists(m_FolderPath)&&!m_IsOld)
            //     {
            //         EditorGUILayout.TextArea("地址已存在:\n" + m_FolderPath, m_TipsStyle);
            //     }
            //     else if(!Directory.Exists(m_FolderPath)&&m_IsOld)
            //     {
            //         EditorGUILayout.TextArea("导入完毕,资源地址为:\n" + m_OldFBXPath, m_TipsStyle);
            //     }
            //     else
            //     {
            //         EditorGUILayout.TextArea("导入完毕,资源地址为:\n" + m_FolderPath, m_TipsStyle);
            //     }
            // }
            // else
            // {
            //     EditorGUILayout.TextArea("无资源导入...", m_TipsStyle);
            // }

            EditorGUILayout.Space();

            // Select Suite Level
            // m_SuiteLevel = (SuiteLevel)EditorGUILayout.EnumPopup("选择时装品质", m_SuiteLevel, GUILayout.Width(300));

            // EditorGUILayout.Space();
            // Create Prefab without Dynamic bone
            // if (GUILayout.Button("生成模型", m_ButtonStyle, GUILayout.Width(150), GUILayout.Height(m_RowHeight)))
            // {
            //     OnClickCreateButton();
            // }

            EditorGUILayout.Space();
            // show prefab path
            if (!string.IsNullOrEmpty(LOD_m_PrefabPath))
            {
                EditorGUILayout.TextArea("创建Prefab完毕资源地址为\n" + LOD_m_PrefabPath, m_TipsStyle);
            }
            else
            {
                EditorGUILayout.TextArea("无Prefab创建...", m_TipsStyle);
            }

            EditorGUILayout.Space();

            // Put prefab in
            // EditorGUILayout.TextArea("部件模型路径", m_LabelStyle);
            // Rect modelRect = EditorGUILayout.GetControlRect(GUILayout.Width(500));
            // m_DynamicBonePrefabPath = EditorGUI.TextField(modelRect, m_DynamicBonePrefabPath);
            // EditorGUILayout.Space();
            // if ((Event.current.type == EventType.DragUpdated)
            //     && modelRect.Contains(Event.current.mousePosition))
            // {
            //     DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
            // }
            //
            // if ((Event.current.type == EventType.DragPerform) && modelRect.Contains(Event.current.mousePosition))
            // {
            //     if (DragAndDrop.paths != null && DragAndDrop.paths.Length > 0)
            //     {
            //         m_DynamicBonePrefabPath = DragAndDrop.paths[0];
            //     }
            // }
            // // Analysis Dynamic bone params
            // if (GUILayout.Button("生成DynamicBone参数", m_ButtonStyle, GUILayout.Width(150),
            //     GUILayout.Height(m_RowHeight)))
            // {
            // }
            //     OnClickCreateLODDynamicBoneParams();

            // EditorGUILayout.Space();
            // if (m_TagToDynamicBoneParamDict.Count > 0)
            // {
            //     //在 foreach 中修改Dictionary中的值是不允许的可以将key 先放在List中foreach 这个list 找到需要修改的项后再修改原Dic中的内容
            //     OnDrawListView();
            //     EditorGUILayout.Space();
            //     EditorGUILayout.BeginHorizontal();
            //     // Create Prefab with Dynamic bone
            //     if (GUILayout.Button("挂DynamicBone脚本", m_ButtonStyle, GUILayout.Width(200),
            //         GUILayout.Height(m_RowHeight)))
            //     {
            //         OnClickDynamicBoneButton();
            //     }
            //
            //     if (GUILayout.Button("导入DynamicBone文件", m_ButtonStyle, GUILayout.Width(200),
            //         GUILayout.Height(m_RowHeight)))
            //     {
            //         OnClickImportDynamicBoneFile();
            //     }
            //
            //     if (GUILayout.Button("导出DynamicBone文件", m_ButtonStyle, GUILayout.Width(200),
            //         GUILayout.Height(m_RowHeight)))
            //     {
            //         OnClickExportDynamicBoneFile();
            //     }
            //
            //     EditorGUILayout.EndHorizontal();
            //
            // }
        }

        /// <summary>
        /// 初始化,清空数据
        /// </summary>
        void ClearData()
        {
            mFbxFileList.Clear();
            mFolderPathList.Clear();
            mFbxFileList.Clear();
            mPrefabPathList.Clear();
        }

        void OnDestroy()
        {
            ClearScenePrefabs();
        }

        void InitStyles()
        {
            if (m_MainHeaderStyle == null)
            {
                m_MainHeaderStyle = new GUIStyle(GUI.skin.label);
                m_MainHeaderStyle.fontSize = 20;
                m_MainHeaderStyle.fontStyle = FontStyle.Bold;
                m_MainHeaderStyle.normal.textColor = m_BlueColor;
            }

            if (m_SubHeaderStyle == null)
            {
                m_SubHeaderStyle = new GUIStyle(GUI.skin.label);
                m_SubHeaderStyle.fontSize = 14;
                m_SubHeaderStyle.fontStyle = FontStyle.Bold;
                m_SubHeaderStyle.normal.textColor = m_BlueColor;
            }

            if (m_ButtonStyle == null)
            {
                m_ButtonStyle = new GUIStyle(GUI.skin.button);
                m_ButtonStyle.fontSize = 14;
            }

            if (m_TipsStyle == null)
            {
                m_TipsStyle = new GUIStyle(GUI.skin.label);
                m_TipsStyle.normal.textColor = m_PinkColor;
                m_TipsStyle.fontSize = m_RowFontSize;
            }

            if (m_ErrorTipsStyle == null)
            {
                m_ErrorTipsStyle = new GUIStyle(GUI.skin.label);
                m_ErrorTipsStyle.normal.textColor = m_RedColor;
                m_ErrorTipsStyle.fontSize = m_RowFontSize;
            }

            if (m_LabelStyle == null)
            {
                m_LabelStyle = new GUIStyle(GUI.skin.label);
                m_LabelStyle.normal.textColor = m_BlueColor;
                m_LabelStyle.fontSize = 18;
            }

            if (m_TagLabelStyle == null)
            {
                m_TagLabelStyle = new GUIStyle(GUI.skin.label);
                m_TagLabelStyle.normal.textColor = m_PinkColor;
                m_TagLabelStyle.fontSize = 16;
            }

            if (m_ParamsLabelStyle == null)
            {
                m_ParamsLabelStyle = new GUIStyle(GUI.skin.label);
                m_ParamsLabelStyle.normal.textColor = m_PurpColor;
                m_ParamsLabelStyle.fontSize = 13;
            }
        }

        void DrawSplitLine(float height)
        {
            GUI.Box(new Rect(0, height, position.width, 1), string.Empty);
        }

        // 这里是scrollview
        Vector2 _scrollPos;

        void OnDrawListView()
        {
            _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, false, false, GUILayout.Width(500), GUILayout.Height(400));
            List<string> keyList = new List<string>();
            foreach (KeyValuePair<string, LODDynamicBoneParams> pair in m_TagToDynamicBoneParamDict)
            {
                keyList.Add(pair.Key);
            }
            foreach (string key in keyList)
            {
                EditorGUILayout.TextArea(key, m_TagLabelStyle, GUILayout.Height(20));
                LODDynamicBoneParams inputValue;
        
                float Damping = EditorGUILayout.FloatField("Damping", m_TagToDynamicBoneParamDict[key].Damping, m_ParamsLabelStyle);
                float Elasticty = EditorGUILayout.FloatField("Elasticty",
                    m_TagToDynamicBoneParamDict[key].Elasticty, m_ParamsLabelStyle);
                float Stiffness = EditorGUILayout.FloatField("Stiffness",
                    m_TagToDynamicBoneParamDict[key].Stiffness, m_ParamsLabelStyle);
                float Inert =
                    EditorGUILayout.FloatField("Inert", m_TagToDynamicBoneParamDict[key].Inert, m_ParamsLabelStyle);
                float Friction =
                    EditorGUILayout.FloatField("Friction", m_TagToDynamicBoneParamDict[key].Friction, m_ParamsLabelStyle);
                float Radius =
                    EditorGUILayout.FloatField("Radius", m_TagToDynamicBoneParamDict[key].Friction, m_ParamsLabelStyle);
                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("+", m_ButtonStyle, GUILayout.Width(50), GUILayout.Height(m_RowHeight)))
                {
                    OnClickAddCollider(key);
                }
                if (GUILayout.Button("-", m_ButtonStyle, GUILayout.Width(50), GUILayout.Height(m_RowHeight)))
                {
                    OnClickRemoveCollider(key);
                }
                EditorGUILayout.EndHorizontal();
        
                if (m_TagToDynamicBoneParamDict[key].colliderDataList != null)
                {
                    for (int i = 0; i < m_TagToDynamicBoneParamDict[key].colliderDataList.Count; i++)
                    {
                        EditorGUILayout.BeginHorizontal();
                        LODDynamicColliderData inputColliderValue;
                        // 列出这个标记位下绑到的所有collider信息
                        List<LODDynamicColliderData> colliderList = m_TagToDynamicBoneParamDict[key].colliderDataList;
                        LODDynamicColliderData collider = colliderList[i];
                        EditorGUILayout.LabelField(collider.name, GUILayout.MaxWidth(80));
                        float collRadius = EditorGUILayout.FloatField("碰撞体半径:", collider.radius, m_ParamsLabelStyle);
                        EditorGUILayout.LabelField("关联骨骼名字:", GUILayout.MaxWidth(80));
                        GUI.SetNextControlName("RootNameText");
                        string collAttachName = EditorGUILayout.TextField(collider.rootName, m_ParamsLabelStyle,
                            GUILayout.MaxWidth(200), GUILayout.Height(m_RowHeight));
                        EditorGUILayout.EndHorizontal();
                        // 改碰撞体list
                        if (collRadius != collider.radius || collAttachName != collider.rootName)
                        {
                            inputColliderValue = new LODDynamicColliderData(collider.name, collRadius, collAttachName, collider.colTrans);
                            colliderList[i] = inputColliderValue;
                            inputValue = new LODDynamicBoneParams(null, Damping, Elasticty, Stiffness, // TODO 这些地方要把null替换掉
                                Inert,
                                Friction, Radius, colliderList);
                            m_TagToDynamicBoneParamDict[key] = inputValue;
                        }
                    }
                }
                // 只改Dynamicbone的数字参数不涉及碰撞体list
                if (Damping != m_TagToDynamicBoneParamDict[key].Damping ||
                    Elasticty != m_TagToDynamicBoneParamDict[key].Elasticty ||
                    Stiffness != m_TagToDynamicBoneParamDict[key].Stiffness ||
                    Inert != m_TagToDynamicBoneParamDict[key].Inert ||
                    Friction != m_TagToDynamicBoneParamDict[key].Friction ||
                    Radius != m_TagToDynamicBoneParamDict[key].Radius)
                {
                    List<LODDynamicColliderData> colliderList = m_TagToDynamicBoneParamDict[key].colliderDataList;
                    inputValue = new LODDynamicBoneParams(null, Damping, Elasticty, Stiffness, Inert,// TODO 这些地方要把null替换掉
                        Friction, Radius, colliderList);
                    m_TagToDynamicBoneParamDict[key] = inputValue;
                }
                EditorGUILayout.Space();
            }
            EditorGUILayout.EndScrollView(); // 组结束
        }
        #endregion

        #region LoadFile
        

        bool OnClickLoadButton()
        {
            ClearData();
            
            bool success = true;
            m_OldPrefab = null;
            string defaultFolder = PlayerPrefs.GetString("BunnyflopFilesDefalutFolder", "");
            string folder = EditorUtility.OpenFolderPanel("Select Versions Folder", defaultFolder, "");
            if (string.IsNullOrEmpty(folder))
            {
                return false;
            }

            m_SuiteLevel = SuiteLevel.white;

            PlayerPrefs.SetString("BunnyflopFilesDefalutFolder", folder);
            DirectoryInfo direction = new DirectoryInfo(folder);
            FileInfo[] fbxFiles = direction.GetFiles("*.fbx", SearchOption.AllDirectories);
            if (fbxFiles.Length == 0)
            {
                NamingChecker.PopupErrorMessage("未检测到有fbx导入");
                return false;
            }

            for (int i = 0; i < fbxFiles.Length; i++)
            {
                
                m_FbxFile = fbxFiles[i];// 只允许载入一个
                mFbxFileList.Add(i,m_FbxFile);
                // if(m_IsOld)
                // {
                //     // 重新导入性能优化使用的低模不检查命名规范但要找到老的fbx不然就报错退出
                //     m_OldPrefab = FindOldPrefabByFbxFile(m_FbxFile.Name);
                //     if(m_OldPrefab==null)
                //     {
                //         EditorUtility.DisplayDialog("警告", "项目内找不到名为"+m_FbxFile.Name+"的模型文件,请确认减面优化资源命名是否与老资源完全一致!", "我知道了");
                //         return;
                //     }
                // }
                // else
                // {
                //     bool isPassCheck = NamingChecker.PreCheck(m_FbxFile);
                //     if (!isPassCheck)
                //     {
                //         return;
                //     }
                // }
                
                m_IsLobby = !m_FbxFile.Name.Contains("Low") && !m_FbxFile.Name.Contains("low")&&!m_FbxFile.Name.Contains("&ing");
                success = LoadFbxFile(i);

                if (success == false)
                {
                    break;
                }
            }

            return success;
        }

        GameObject FindOldPrefabByFbxFile(string fileName)
        {
            string fbxName = fileName.Split('.')[0];
            string[] allPath = AssetDatabase.FindAssets("t:Model", new string[] { "Assets/ResourcesRaw/CommonRegion/Skins/Characters/Models" });
            foreach(string singlePath in allPath)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(singlePath);
                if(assetPath.Contains(fbxName+".FBX"))
                    m_OldFBXPath = assetPath.Replace(fbxName+".FBX","");
                if(assetPath.Contains(fbxName+".fbx"))
                    m_OldFBXPath = assetPath.Replace(fbxName+".fbx","");
                if(!GetFbxName(assetPath).Equals(fbxName))
                {
                    continue;
                }
                // 找到同名的fbx之后再找依赖fbx的prefab
                GameObject fbx = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject;
                GameObject dependPrefab = GetPrefabDependByThisFbx(singlePath,fbxName);
                return dependPrefab;
            }
            return null;
        }


        GameObject GetPrefabDependByThisFbx(string fbxGuid,string fbxName)
        {
            List<string> prefabExtension = new List<string>() { ".prefab" };
            string[] files = new string[10000];
            if(fbxName.Contains("low")||fbxName.Contains("&ing"))
                files = Directory.GetFiles(Application.dataPath + "/BundleResources/CommonRegion/InGame/Skins/Characters/", "*.*", SearchOption.AllDirectories)
                .Where(s => prefabExtension.Contains(Path.GetExtension(s).ToLower())).ToArray();
            if(fbxName.Contains("&lob")||fbxName.Contains("height"))
                files = Directory.GetFiles(Application.dataPath + "/BundleResources/CommonRegion/OutsideGame/Skins/Characters/", "*.*", SearchOption.AllDirectories)
                    .Where(s => prefabExtension.Contains(Path.GetExtension(s).ToLower())).ToArray();
            string prefabFileName = "";
            foreach (string file in files)
            {
                if (Regex.IsMatch(File.ReadAllText(file), fbxGuid)) 
                {
                    prefabFileName = file;
                }
            }
            if (string.IsNullOrEmpty(prefabFileName))
            {
                return null;
            }
            else
            { 
                // 得到的filename是类似【G:/DMM_Art_B/Assets/BundleResources/Prefabs/Characters/SkinParts\Police_03_01_height_skin.prefab】这样的
                string[] words_1 = prefabFileName.Split('/');
                string[] words_2 = words_1[words_1.Length - 1].Split('\\'); ; // SkinParts\Police_03_01_height_skin.prefab
                string prefabName = words_2[words_2.Length-1];
                string prefabPath = "";
                string pathname = CombinePath(prefabName)+"/Prefabs/";
                if(fbxName.Contains("low")||fbxName.Contains("&ing"))
                    prefabPath = Application.dataPath + "/BundleResources/CommonRegion/InGame/Skins/Characters/" +pathname+ prefabName;
                if(fbxName.Contains("&lob")||fbxName.Contains("height"))
                    prefabPath = Application.dataPath + "/BundleResources/CommonRegion/OutsideGame/Skins/Characters/"  +pathname+ prefabName;
                return AssetDatabase.LoadAssetAtPath(NamingChecker.GetRelativePath(prefabPath), typeof(GameObject)) as GameObject;
            }
        }
        
        string RenameFBX(string fbxName)
        {
            string newName = "";
            
            
            
            return newName;

        }
        bool LoadFbxFile(int index)
        {
            string fbxName = m_FbxFile.Name;
            // if (fbxName.Contains("height_") && !fbxName.Contains("lob"))
            // {
            //     EditorUtility.DisplayDialog("错误", fbxName+"的模型文件命名不正确缺少前缀lob或者多了height后缀", "我知道了");
            //     return false;
            //     // fbxName = fbxName.Replace("height_", "").Replace("&", "&lob");
            // }
            // else if (fbxName.Contains("low_") && !fbxName.Contains("ing"))
            // {
            //     EditorUtility.DisplayDialog("错误", fbxName+"的模型文件命名不正确缺少前缀ing或者多了low后缀", "我知道了");
            //     return false;
            //     // fbxName = fbxName.Replace("low_", "").Replace("&", "&ing");
            // }
            // if (!fbxName.Contains("mdl&"))
            // {
            //     EditorUtility.DisplayDialog("警告", fbxName+"的模型文件命名不正确缺少前缀!", "我知道了");
            //     return;
            // }

            if(!m_IsOld)
            {
                
                m_FolderPath = CreateFolder(fbxName);
                m_FbxPath = m_FolderPath + fbxName;
                
                
                mFolderPathList.Add(index, m_FolderPath);
                mFbxPathList.Add(index,m_FbxPath);
                
                
                if (File.Exists(m_FbxPath))
                {
                    File.Delete(m_FbxPath);
                }
                
                m_FbxFile.CopyTo(m_FbxPath);
                
            }
            else
            {
                m_FolderPath = m_OldFBXPath;
                m_FbxPath = m_FolderPath+fbxName;
                if (File.Exists(m_FbxPath))
                {
                    File.Delete(m_FbxPath);
                }
                m_FbxFile.CopyTo(m_FbxPath);
            }
            /*if (!Directory.Exists(m_FolderPath))
            {
               // NamingChecker.PopupErrorMessage("创建文件夹失败:" + m_FolderPath);
            }*/
            AssetDatabase.Refresh();
            // 如果是AnimPrefab则需要reimport一次否则无法自动挂上Animation脚本刚导入只能是Animator
            if (fbxName.Contains(NamingChecker.AnimPattern))
            {
                ModelImporter mi = AssetImporter.GetAtPath(NamingChecker.GetRelativePath(m_FbxPath)) as ModelImporter;
                mi.SaveAndReimport();
            }
            // 这里就可以开始分析skinpart命名是否规范
            // if(m_IsOld)
            // {
            //     
            // }
            // else
            // {
            //     if (!NamingChecker.SkinpartCheck(GetFbxGameobject()))
            //     {
            //         Clear();
            //         return;
            //     }
            // }

            return true;
        }

        string LODCombinePath(string prefabName)
        {
            string realpath = String.Empty;
            string path = "SkinParts";
            string feature = "";
            string config = "";
            string[] splitname = prefabName.Split('_');
            

            if (prefabName.Contains("&ing") || prefabName.Contains("&lob"))
            {
                if (splitname.Length >= 4)
                {
                    string[] nameList = splitname[0].Split('&');
                    
                    feature = $"{nameList[1].Replace("lob","").Replace("ing","")}_{splitname[1]}";
                    config = splitname[2];
                    realpath = $"{feature}/{path}/{config}";
                    return realpath;
                }
            }
            else
            {
                //兼容原版fbx导入
                if (splitname.Length > 4)
                {
                    feature = $"{splitname[0].Replace("mdl&","")}_{splitname[1]}";
                    config = splitname[2];
                }
                realpath = $"{feature}/{path}/{config}";
            }

            return realpath;
        }

        string CombinePath(string prefabName)
        {
            string realpath = String.Empty;
            string path = "SkinParts";
            string feature = "";
            string config = "";
            string[] splitname = prefabName.Split('_');
            if (prefabName.Contains("&ing") || prefabName.Contains("&lob"))
            {
                if (splitname.Length >= 4)
                {
                    // feature = $"{splitname[1]}_{splitname[2]}";
                    // config = splitname[3];

                    string[] nameList = splitname[0].Split('&');
                    string frontDelete = nameList[0] + "&";

                    feature =
                        $"{splitname[0].Replace("mdl&", "").Replace("pfb&", "").Replace(frontDelete, "").Replace("lob", "").Replace("ing", "")}_{splitname[1]}";
                    config = splitname[2];
                }
                realpath = $"{feature}/{path}/{config}";
            }
            else
            {
                //兼容原版prefab导入
                if (splitname.Length >= 4)
                {
                    feature = $"{splitname[0].Replace("mdl&","").Replace("pfb&","")}_{splitname[1]}";
                    config = splitname[2];
                }
                realpath = $"{feature}/{path}/{config}";
            }
            
            return realpath;

        }

        void CreatePrefabFodler(string path)
        {
            string dirPath = Application.dataPath + path.Replace("Assets/", "/");
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            if (!Directory.Exists(m_FolderPath))
            {
                NamingChecker.PopupErrorMessage("创建文件夹失败:" + m_FolderPath);
            }
        }
        string CreateFolder(string fbxName)
        {
            string path = "";
            if (fbxName.Contains("&ing")||fbxName.Contains("low"))
            {
                path = m_FbxDirIng + LODCombinePath(fbxName)+"/Models/";
            }else if (fbxName.Contains("&lob")||fbxName.Contains("height"))
            {
                path = m_FbxDirLob + LODCombinePath(fbxName)+"/Models/";
            }else
                Debug.LogError(fbxName+"FBX命名不规范未包含前缀lob or ing");
            string dirPath = Application.dataPath + path;
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            return dirPath;
        }

        string GetFbxDirectry(string fbxName)
        {
            string rst = "";
            string[] wordList = fbxName.Split('_');
            rst = wordList[0] + wordList[1] + "/SkinParts/" + wordList[2] + "/";
            return rst;
        }

        string GetFbxName(string assetPath)
        {
            string[] wordList = assetPath.Split('/');
            return wordList[wordList.Length - 1].Split('.')[0];//TODO
        }

        #endregion

        #region CreatePrefab

        void OnClickCreateButton(bool isSuccess = true)
        {
            if (isSuccess == false)
            {
                return;
            }
            
            
            m_PrefabPath = "";
            if (m_FolderPath == "")
            {
                return;
            }

            for (int i = 0; i < mFolderPathList.Count; i++)
            {
                
                GameObject fbxObject = GetFbxGameobject(mFolderPathList[i],mFbxFileList[i]);
                // WriteSuiteLevel();
                // 性能检查分析
                // if (!DataAnalysisUtility.IsInIgnoreList(fbxObject.name) && !DataAnalysisUtility.ResourcesDataCheck(fbxObject))
                // {
                //     //此处先忽略
                //      Clear();
                //     return;
                // }
                CreatePrefab(ref fbxObject,i);
            }
            
        }

        void WriteSuiteLevel()
        {
            string fbxName = m_FbxFile.Name.Replace(".FBX","");
            string writeLine = fbxName + "*" + (int)m_SuiteLevel;
            StreamReader sr = null;
            if (!File.Exists(m_SuiteLevelPath))
            {
                File.Create(m_SuiteLevelPath).Dispose();
            }

            sr = new StreamReader(m_SuiteLevelPath, Encoding.Default);
            string readStr = "";
            List<string> content = new List<string>();
            // 先缓存下所有除了本次处理fbx信息以外
            while (sr != null && (readStr = sr.ReadLine()) != null)
            {
                if (!readStr.Contains(fbxName))
                {
                    content.Add(readStr);            
                }
            }
            sr.Close();
            // 重写文件内容
            StreamWriter sw = new StreamWriter(m_SuiteLevelPath, false);
            foreach(string str in content)
            {
                sw.WriteLine(str);
            }
            sw.Close();
            // 末尾补上新改的一段
            FileInfo fi = new FileInfo(m_SuiteLevelPath);
            sw = fi.AppendText();
            sw.WriteLine(writeLine);
            sw.Close();
            sw.Dispose();
        }

        GameObject GetFbxGameobject(string m_FolderPath,FileInfo m_FbxFile)
        {
            DirectoryInfo direction = new DirectoryInfo(m_FolderPath);
            FileInfo[] fbxFiles = direction.GetFiles("*.fbx", SearchOption.AllDirectories);
            string fbxFileChoosed = "";
            foreach (FileInfo fbxfile in fbxFiles)
            {
                if (fbxfile.Name.Equals(m_FbxFile.Name))
                {
                    fbxFileChoosed = fbxfile.FullName;
                    break;
                }
            }
            string relativePath = NamingChecker.GetRelativePath(fbxFileChoosed);
            Transform fbxTrans = AssetDatabase.LoadAssetAtPath(relativePath, typeof(Transform)) as Transform;
            if (m_FbxSenceObject != null) // 不要重复生成,关闭的时候清不掉
            {
                DestroyImmediate(m_FbxSenceObject.gameObject);
            }
            m_FbxSenceObject = Instantiate(fbxTrans).gameObject;
            return m_FbxSenceObject;
        }

        void CreatePrefab(ref GameObject fbxGameObject,int index)
        {
            string prefabName = "";
            string realPrefabName = "";
            if (m_IsOld)
            {
                prefabName = m_OldPrefab.name;
            }
            else
            {
                prefabName = fbxGameObject.name.Split('(')[0];
                realPrefabName = GetPrefabName(prefabName);
                prefabName = DropPostfix(prefabName);
            }
            string pathname = "";
            string instPrefabPath = "";
            pathname = CombinePath(prefabName)+"/Prefabs/";
            if (prefabName.Contains("&ing")||prefabName.Contains("low"))
            {
                instPrefabPath = m_PrefabDirIng + pathname;
                CreatePrefabFodler(instPrefabPath);
                m_PrefabPath = instPrefabPath + realPrefabName + ".prefab";
                LOD_m_PrefabPath = LOD_m_PrefabPath + instPrefabPath + realPrefabName + ".prefab\n";
                mPrefabPathList.Add(index, m_PrefabPath);
            }
            else if(prefabName.Contains("&lob")||prefabName.Contains("height"))
            {
                instPrefabPath = m_PrefabDirLob + pathname;
                CreatePrefabFodler(instPrefabPath);
                m_PrefabPath =  instPrefabPath+ realPrefabName + ".prefab";
                LOD_m_PrefabPath = LOD_m_PrefabPath + instPrefabPath + realPrefabName + ".prefab\n";
                mPrefabPathList.Add(index,m_PrefabPath);
            }
            else
            {
                Debug.LogError(prefabName+"资源命名缺少前缀ing or lob!");
            }
            
            // 【注意】不能使用绝对路径https://blog.csdn.net/MAOMAOXIAOHUO/article/details/51204230
            Material defaultMat = AssetDatabase.LoadAssetAtPath(m_DefaultMatPath, typeof(Material)) as Material;
            // Dictionary<string, string> dict = new Dictionary<string, string>();
            // if (m_IsOld)
            // {
            //     // 拿到当前项目里已经存在的prefab中的材质与mesh映射关系
            //     dict = GetMaterialRelationFromOldPrefab();
            // }
            
            // fbxGameObject.name =  fbxGameObject.name.Replace("mdl&", "").Replace("(Clone)", "");
            
            
            // if (m_IsOld)
            // {
            //     // 赋予新生成的go材质
            //     foreach (Transform temp in fbxGameObject.transform)
            //     {
            //         if (temp.GetComponent<SkinnedMeshRenderer>() != null)
            //         {
            //             string matPath = "";
            //             if (dict.TryGetValue(temp.name, out matPath))
            //             {
            //                 temp.GetComponent<SkinnedMeshRenderer>().sharedMaterial = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material;
            //             }
            //         }
            //     }
            // }
            // else
            // {
            DMMCharacterRevisionWindow.LODLoadPresetToGameObject(ref fbxGameObject,currentAssetLodLevel,currentAssetSceneType,realPrefabName);
            
            // }

            // 这一段是试图挂blink eye脚本
            foreach (Transform temp in fbxGameObject.transform)
            {
                foreach (string m_name in ModelName)
                {
                    if (temp.gameObject.name.Contains(m_name) && temp.GetComponent<SkinnedMeshRenderer>() != null)
                    {
                        if (m_IsLobby && temp.gameObject.name.Contains(BlinkFaceTag) && temp.gameObject.GetComponent<BlinkController>() == null)
                        {
                            string characterTag = prefabName.Split('_')[0] + "_" + prefabName.Split('_')[1];
                            string[] faceNames;
                            bool hasBlinkface = m_BlinkFaceDict.TryGetValue(characterTag, out faceNames);
                            if (hasBlinkface)
                            {
                                // 挂脚本
                                temp.GetComponent<SkinnedMeshRenderer>().sharedMaterial = defaultMat;
                                BlinkController ctrl = temp.gameObject.AddComponent<BlinkController>();
                                ctrl.m_OpenEyesMatName = faceNames[0];
                                ctrl.mCloseEyesMatName = faceNames[1];
                            }
                        }
                        break;
                    }
                }
            
            }

            
            string DynaMicPath =  AddDynamicBoneData(Path.GetFileNameWithoutExtension(mPrefabPathList[index]));
            PrefabUtility.SaveAsPrefabAssetAndConnect(fbxGameObject, mPrefabPathList[index], InteractionMode.AutomatedAction);
            // GameObject m_prefabRoot = PrefabUtility.GetCorrespondingObjectFromOriginalSource(fbxGameObject) as GameObject;
            
            AssetDatabase.SaveAssets();
            DestroyImmediate(fbxGameObject.gameObject);
            
            // 自动生成动态骨骼
            
            // 读取prefab
            Transform model = AssetDatabase.LoadAssetAtPath(mPrefabPathList[index], typeof(Transform)) as Transform;
            if (m_DynamicBoneSenceObject != null) // 不要重复生成,关闭的时候清不掉
            {
                DestroyImmediate(m_DynamicBoneSenceObject.gameObject);
            }
            m_DynamicBoneSenceObject = Instantiate(model).gameObject;

            // 找出标记父节点并初始化一系列成员变量
            ProcessPrefabToInit(ref m_DynamicBoneSenceObject);
            //如果存在配置文件,就按照标准保存
            if (DynaMicPath.Length > 0)
            {
                OnClickImportDynamicBoneFileByName(DynaMicPath);
            }

            // 根据m_TagToDynamicBoneParamDict动态生成可调节Dynamic Bone参数
            RePaintDynamicBoneParamsPanel();
            
            // 在对应部位挂HeadBoneAttach
            AddHeadBoneAttach();
            
            
            
            // 保存prefab
            SaveDynamicBonePrefab();

        }

        /// <summary>
        /// 缓存导入已有的dynamic配置参数
        /// </summary>
        /// <param name="DynamicBoneName"></param>
        string AddDynamicBoneData(string DynamicBoneName)
        {
            string objectName = DynamicBoneName.Split('(')[0];
            string pathName;

            string DynamicPath = "";
            
            pathName = CombinePath(objectName) + "/Prefabs/";
            string assetPath = "";
            if (objectName.Contains("&ing") || objectName.Contains("low"))
            {
                assetPath = m_PrefabDirIng + pathName + objectName + ".prefab";
            }
            else if (objectName.Contains("&lob") || objectName.Contains("height"))
            {
                assetPath = m_PrefabDirLob + pathName + objectName + ".prefab";
            }

            Transform model = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Transform)) as Transform;
            
            if (model != null)
            {
                GameObject DynamicObj = Instantiate(model).gameObject;
                ProcessPrefabToInit(ref DynamicObj);
                DynamicPath = OnClickExportDynamicBoneFileByName(DynamicObj.name);
                DestroyImmediate(DynamicObj);

            }

            return DynamicPath;

        }
        
        void RePaintDynamicBoneParamsPanel()
        {
            this.Repaint();
        }

        Dictionary<string, string> GetMaterialRelationFromOldPrefab()
        {
            Dictionary<string, string> rst = new Dictionary<string, string>();
            foreach (Transform temp in m_OldPrefab.transform)
            {
                if (temp.GetComponent<SkinnedMeshRenderer>() != null)
                {
                    Material mat = temp.GetComponent<SkinnedMeshRenderer>().sharedMaterial;
                    rst.Add(temp.name, AssetDatabase.GetAssetPath(mat));
                }
            }
            return rst;
        }

        // 去掉_SRN后缀
        string DropPostfix(string name)
        {

            return name.Replace("mdl", "pfb");
            // string postfix = NamingChecker.GetPostfix(name);
            // bool isValid = NamingChecker.IsValidPostfix(postfix);
            // if (isValid)
            // {
            //     return name.Replace("_" + postfix, "").Replace("mdl", "pfb");
            // }
            // else
            // {
            //     return name.Replace("mdl", "pfb");
            // }
        }

        /// <summary>
        /// 获取fbx对应的prefab名字
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        string GetPrefabName(string name)
        {
            string[] nameList = name.Split('&');
            eAssetLodLevel level = eAssetLodLevel.None;
            string finalName = String.Empty;

            foreach (KeyValuePair<string,eAssetLodLevel> keyValuePair in levelDic)
            {
                if (nameList[0].Contains(keyValuePair.Key))
                {
                    level = keyValuePair.Value;
                    currentAssetLodLevel = level;
                    break;
                }
            }

            foreach (KeyValuePair<string,eAssetSceneType> keyValuePair in sceneDic)
            {
                if (nameList[1].Contains(keyValuePair.Key))
                {
                    currentAssetSceneType = keyValuePair.Value;
                }
            }
            


            if (nameList.Length == 2 )
            {
                string[] logicNamelist = nameList[1].Split('_');
                if (logicNamelist.Length >= 4)
                {
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.Clear();

                    for (int i = 0; i < 3; i++)
                    {
                        stringBuilder.Append(logicNamelist[i] + "_");
                    }

                    stringBuilder.Append(logicNamelist[3]);
                    
                    string prefabName = stringBuilder.ToString();

                    //识别补充后缀
                    if (!prefabName.Contains("_skin"))
                    {
                        prefabName = prefabName + "_skin";
                    }

                    stringBuilder.Clear();
                    finalName = LMNAResMapUtil.AssembleAssetName(prefabName, eAssetFunctionType.GameObject,
                        level);

                }
            }

            return finalName;
        }

        // 处理需要蒙皮的特效部位
        void ProcessEffectPart(ref GameObject prefab)
        {
            foreach(Transform temp in prefab.transform)
            {
                if (!temp.name.Contains(NamingChecker.AttachmentPatterns[2])) continue;
                // 新建一个子go
                GameObject go = new GameObject();
                go.transform.SetParent(temp);
                // 把自己挂着的skinmeshrenderer剪切到子go上
                SkinnedMeshRenderer smr = temp.GetComponent<SkinnedMeshRenderer>();
                UnityEditorInternal.ComponentUtility.CopyComponent(smr);
                UnityEditorInternal.ComponentUtility.PasteComponentAsNew(go);
                // 挂上PlayerEffectController
                PlayerEffectController pec = temp.gameObject.AddComponent<PlayerEffectController>();
                pec.m_Effect = go;
            }
        }

        #endregion

        #region Clear
        void OnClickClearButton()
        {
            ClearFbxAndTextures(() =>
            {
                NamingChecker.PopupErrorMessage("成功清除" + m_FolderPath + "下新导入的资源!");
            });
            ClearPrefabFolder(() =>
            {
                NamingChecker.PopupErrorMessage("成功清除" + m_PrefabPath + "");
            });
            AssetDatabase.Refresh();
            this.Repaint();
        }

        void Clear(System.Action onFbxSucc = null, System.Action onPrefabSucc = null)
        {
            ClearFbxAndTextures(onFbxSucc);
            ClearPrefabFolder(onPrefabSucc);
            m_OldPrefab = null; ;
            AssetDatabase.Refresh();
            this.Repaint();
        }

        void ClearFbxAndTextures(System.Action onSucc = null)
        {
            if (!Directory.Exists(m_FolderPath))
                return;
            // 删除fbx同目录下所有文件
            string tempMatDir = m_FolderPath;
            DirectoryInfo tempDirection = new DirectoryInfo(tempMatDir);
            FileInfo[] files = tempDirection.GetFiles("*.*", SearchOption.TopDirectoryOnly);
            File.Delete(m_FbxPath);
            m_FbxPath = "";
            m_FolderPath = "";
            if (onSucc != null) onSucc();
        }

        void ClearPrefabFolder(System.Action onSucc = null)
        {
            if (!File.Exists(m_PrefabPath))
                return;
            File.Delete(m_PrefabPath);
            m_PrefabPath = "";
            if (onSucc != null) onSucc();
        }

        #endregion

        #region DynamicBone
        void OnClickCreateLODDynamicBoneParams()
        {
            if (string.IsNullOrEmpty(m_DynamicBonePrefabPath))
            {
                return;
            }
            // 读取prefab
            Transform model = AssetDatabase.LoadAssetAtPath(m_DynamicBonePrefabPath, typeof(Transform)) as Transform;
            if (m_DynamicBoneSenceObject != null) // 不要重复生成,关闭的时候清不掉
            {
                DestroyImmediate(m_DynamicBoneSenceObject.gameObject);
            }
            m_DynamicBoneSenceObject = Instantiate(model).gameObject;

            // 找出标记父节点并初始化一系列成员变量
            ProcessPrefabToInit(ref m_DynamicBoneSenceObject);

            // 根据m_TagToDynamicBoneParamDict动态生成可调节Dynamic Bone参数
            RePaintLODDynamicBoneParamsPanel();
        }

        void OnClickDynamicBoneButton()
        {
            // 在对应部位挂HeadBoneAttach
            AddHeadBoneAttach();
            // 保存prefab
            SaveDynamicBonePrefab();
        }

        void OnClickAddCollider(string key)
        {
            int idx = 0;
            LODDynamicColliderData newItem;
            List<LODDynamicColliderData> tempList = new List<LODDynamicColliderData>();
            LODDynamicBoneParams tempDictData = m_TagToDynamicBoneParamDict[key]; // 用于给字典赋值
            // 添加碰撞体
            if (m_TagToDynamicBoneParamDict[key].colliderDataList != null)
            {
                tempList = m_TagToDynamicBoneParamDict[key].colliderDataList;
                idx = tempList.Count; // 目前有多少个collider了
        
            }
            // 点击添加键时就需要创建并挂上transform
            string name = "Collider_" + idx;
            GameObject collider = new GameObject();
            collider.name = name;
            collider.transform.SetParent(GetTransformByName(key).GetChild(0));
            DynamicBoneCollider dc = collider.AddComponent<DynamicBoneCollider>();
            newItem = new LODDynamicColliderData(name, 0.3f, null, collider.transform);
            dc.m_Radius = newItem.radius;
            tempList.Add(newItem);
            tempDictData = new LODDynamicBoneParams(tempDictData.root, tempDictData.Damping, tempDictData.Elasticty,
                tempDictData.Stiffness, tempDictData.Inert, tempDictData.Friction, tempDictData.Radius,
                tempList);
            m_TagToDynamicBoneParamDict[key] = tempDictData;
            RePaintLODDynamicBoneParamsPanel();
        }

        void OnClickRemoveCollider(string key)
        {
            if (m_TagToDynamicBoneParamDict[key].colliderDataList == null) return;
            int idx = m_TagToDynamicBoneParamDict[key].colliderDataList.Count;
            List<LODDynamicColliderData> tempList = m_TagToDynamicBoneParamDict[key].colliderDataList;
            LODDynamicBoneParams tempDictData = m_TagToDynamicBoneParamDict[key]; // 用于给字典赋值
            DestroyImmediate(tempList[idx - 1].colTrans.gameObject);
            tempList.Remove(tempList[idx - 1]);
            tempDictData = new LODDynamicBoneParams(tempDictData.root, tempDictData.Damping, tempDictData.Elasticty,
                tempDictData.Stiffness, tempDictData.Inert, tempDictData.Friction, tempDictData.Radius,
                tempList);
            m_TagToDynamicBoneParamDict[key] = tempDictData;
            RePaintLODDynamicBoneParamsPanel();
        }

        // 导入Dynamicbone配置文件
        void OnClickImportDynamicBoneFile()
        {
            TryReadSBFile();
        }

        void OnClickImportDynamicBoneFileByName(string path)
        {
            TryReadSBFileByName(path);
        }

        // 导出Dynamicbone配置文件
        void OnClickExportDynamicBoneFile()
        {
            WriteSBInfo();
            EditorUtility.DisplayDialog("成功", "导出完毕", "确定");
            AssetDatabase.Refresh();
        }


        string OnClickExportDynamicBoneFileByName(string name)
        {
            string path = WriteSBInfoByName(name);
            // EditorUtility.DisplayDialog("成功", "导出完毕", "确定");
            AssetDatabase.Refresh();
            return path;
        }

        void SaveDynamicBonePrefab()
        {
            string senceObjectName = m_DynamicBoneSenceObject.name.Split('(')[0];
            string pathname = "";
            pathname = CombinePath(senceObjectName)+"/Prefabs/";

            // string dirPath = Application.dataPath + m_PrefabDirLob + pathname.Replace("Assets/", "/");
            // if (!Directory.Exists(dirPath))
            // {
            //     Directory.CreateDirectory(dirPath);
            // }
            
            if (senceObjectName.Contains("&ing")||senceObjectName.Contains("low"))
                PrefabUtility.SaveAsPrefabAsset(m_DynamicBoneSenceObject, m_PrefabDirIng +pathname+ senceObjectName + ".prefab");
            else if(senceObjectName.Contains("&lob")||senceObjectName.Contains("height"))
                PrefabUtility.SaveAsPrefabAsset(m_DynamicBoneSenceObject, m_PrefabDirLob +pathname+ senceObjectName + ".prefab");
            
            AssetDatabase.SaveAssets();
        }

        void ProcessPrefabToInit(ref GameObject senceObject)
        {
            m_SkinPartTransformList.Clear();
            m_TaggedTransformDict.Clear();
            m_TagToDynamicBoneParamDict.Clear();
        
            // 初始化m_SkinPartTransformList
            for (int i = 0; i < senceObject.transform.childCount; i++)
            {
                foreach (string part in ModelName)
                {
                    if (senceObject.transform.GetChild(i).name.Contains(part) &&
                        !senceObject.transform.GetChild(i).name.Contains(part + "_ex") &&
                        !senceObject.transform.GetChild(i).name.Contains(part + "_fx"))
                    {
                        m_SkinPartTransformList.Add(senceObject.transform.GetChild(i));
                        break;
                    }
                }
            }
        
            // 初始化m_TaggedTransformDict和m_TagToDynamicBoneParamDict
            foreach (Transform child in senceObject.GetComponentsInChildren<Transform>())
            {

                
                foreach (string tag in DynamicBoneTag)
                {
                    if (child.name.Contains(tag))
                    {
                        m_TaggedTransformDict.Add(child, child.parent.name);
                        LODDynamicBoneParams param = GetLODDynamicBoneParams(child);
                        m_TagToDynamicBoneParamDict.Add(child.name, param);
                        break;
                    }
                }
            }
        }

        void RePaintLODDynamicBoneParamsPanel()
        {
            this.Repaint();
        }

        void AddHeadBoneAttach()
        {
            foreach (KeyValuePair<Transform, string> part in m_TaggedTransformDict)
            {
                Transform targetTrans = null;
                if (part.Key.name.Contains("CLOTHES_"))
                {
                    targetTrans = m_SkinPartTransformList.Find(x => x.name.Contains("clothes"));

                }
                else if (part.Key.name.Contains("HEAD_"))
                {
                    targetTrans = m_SkinPartTransformList.Find(x => x.name.Contains("head"));
                }
                else if (part.Key.name.Contains("HAND_"))
                {
                    targetTrans = m_SkinPartTransformList.Find(x => x.name.Contains("hand"));
                }
                else if (part.Key.name.Contains("SHOES_"))
                {
                    targetTrans = m_SkinPartTransformList.Find(x => x.name.Contains("shoes"));
                }
                else if (part.Key.name.Contains("TROUSERS_"))
                {
                    targetTrans = m_SkinPartTransformList.Find(x => x.name.Contains("trousers"));
                }
                if (targetTrans == null)
                {
                    Debug.LogError("在绑DynamicBone时未找到" + part.Key.name + "对应的部位Transform!");
                }
                else
                {
                    bool isShouldAdd = true; // 检查一下是否已经挂了一模一样的attachment脚本
                    HeadBoneAttach[] comps = targetTrans.gameObject.GetComponents<HeadBoneAttach>();
                    HeadBoneAttach comp;
                    Transform attachment = part.Key.GetChild(0);
                    for (int i = 0; i < comps.Length; i++)
                    {
                        if (comps[i].m_Attachment == attachment && comps[i].m_HeadBoneName.Equals(part.Value))
                        {
                            isShouldAdd = false;
                            break;
                        }
                    }
                    if (isShouldAdd)
                    {
                        comp = targetTrans.gameObject.AddComponent<HeadBoneAttach>();
                        comp.m_Attachment = attachment;
                        comp.m_HeadBoneName = part.Value;
                    }

                    part.Key.SetParent(targetTrans);
                    // 骨骼串的跟节点挂DynamicBone脚本
                    SetLODDynamicBoneParams(ref attachment, m_TagToDynamicBoneParamDict[part.Key.name]);

                    // collider的attachment
                    // TODO我忘了。。DB需不需要和SB一样都要对collider的transform也挂上HeadBoneAttach脚本先注释掉
                    /*
                    LODDynamicBoneParams dbParams = m_TagToDynamicBoneParamDict[part.Key.name];
                    List<LODDynamicColliderData> collParams = dbParams.colliderDataList;
                    HeadBoneAttach collComp;
                    if (collParams != null)
                    {
                        for (int i = 0; i < collParams.Count; i++)
                        {
                            bool isShouldAddCollAttach = !string.IsNullOrEmpty(collParams[i].rootName);
                            for (int j = 0; j < comps.Length; j++)
                            {
                                // 检查一下是否已经挂了一模一样的attachment脚本
                                if (comps[j].m_Attachment == null || string.IsNullOrEmpty(comps[j].m_HeadBoneName))
                                    continue;
                                if (comps[j].m_Attachment.name.Equals(collParams[i].name) &&
                                    comps[j].m_HeadBoneName.Equals(collParams[i].rootName))
                                {
                                    isShouldAddCollAttach = false;
                                    break;
                                }
                            }
                            if (isShouldAddCollAttach)
                            {
                                collComp = targetTrans.gameObject.AddComponent<HeadBoneAttach>();
                                collComp.m_Attachment = collParams[i].colTrans;
                                collComp.m_HeadBoneName = collParams[i].rootName;
                            }
                        }
                        
                    }
                    */
                }
            }
        }

        Transform GetTransformByName(string name)
        {
            foreach (Transform VARIABLE in m_DynamicBoneSenceObject.transform.GetComponentsInChildren<Transform>())
            {
                if (VARIABLE.name.Equals(name))
                    return VARIABLE;
            }

            return null;
        }

        LODDynamicBoneParams GetLODDynamicBoneParams(Transform trans)
        {
            LODDynamicBoneParams rst = new LODDynamicBoneParams(trans.GetChild(0), 0.02f, 0.08f, 0.06f, 0.8f, 0.6f, 0.02f, null); // TODO: 这个经验数值应该也用不了
            if (trans.childCount == 0 || trans.GetChild(0).childCount == 0)
                return rst;
            DynamicBone root = trans.GetChild(0).gameObject.GetComponent<DynamicBone>();
            if (root == null)
            {
                root = trans.GetChild(0).gameObject.AddComponent<DynamicBone>();
            }
            rst.root = trans.GetChild(0);
            rst.Damping = root.m_Damping;
            rst.Elasticty = root.m_Elasticity;
            rst.Inert = root.m_Inert;
            rst.Friction = root.m_Friction;
            rst.Stiffness = root.m_Stiffness;
            rst.Radius = root.m_Radius;
            rst.colliderDataList = new List<LODDynamicColliderData>();
            if(root.m_Colliders!=null)
            {
                for (int i = 0; i < root.m_Colliders.Count; i++)
                {
                    DynamicBoneCollider dc = root.m_Colliders[i] as DynamicBoneCollider;
                    string rootName = GetAttachmentRootName(dc.transform);
                    LODDynamicColliderData collData = new LODDynamicColliderData(dc.name, dc.m_Radius, rootName, dc.transform);
                    rst.colliderDataList.Add(collData);
                }
            }
            return rst;
        }

        void SetLODDynamicBoneParams(ref Transform trans, LODDynamicBoneParams param)
        {
            if (trans == null || param.root == null)
                return;
            DynamicBone db = trans.GetComponent<DynamicBone>();
            if (db == null)
            {
                db = trans.gameObject.AddComponent<DynamicBone>();
            }
            db.m_Root = trans;
            db.m_Damping = param.Damping;
            db.m_Elasticity = param.Elasticty;
            db.m_Stiffness = param.Stiffness;
            db.m_Inert = param.Inert;
            db.m_Friction = param.Friction;
            db.m_Radius = param.Radius;
        }

        string GetAttachmentRootName(Transform collider)
        {
            string name = "";
            HeadBoneAttach[] partAttaches = collider.GetComponentsInParent<HeadBoneAttach>();
            if (partAttaches != null)
            {
                for (int i = 0; i < partAttaches.Length; i++)
                {
                    if (partAttaches[i].m_Attachment == collider)
                    {
                        name = partAttaches[i].m_HeadBoneName;
                    }
                }
            }
            return name;
        }

        Transform GetCollider(Transform parent, string name)
        {
            for (int i = 0; i < parent.childCount; i++)
            {
                if (parent.GetChild(i).name.Equals(name))
                    return parent.GetChild(i);
            }

            return null;
        }

        void ClearScenePrefabs()
        {
            if (m_FbxSenceObject != null && m_FbxSenceObject.gameObject != null)
            {
                GameObject.DestroyImmediate(m_FbxSenceObject.gameObject);
            }
            if (m_DynamicBoneSenceObject != null && m_DynamicBoneSenceObject.gameObject != null)
            {
                GameObject.DestroyImmediate(m_DynamicBoneSenceObject.gameObject);
            }
        }

        #endregion

        #region Export/ImportDynamicBoneInfo

        string GetDynamicBoneInfoFilePath()
        {
            if (m_DynamicBoneSenceObject != null && m_DynamicBoneSenceObject.gameObject != null)
            {
                string fileName = m_DynamicBoneSenceObject.transform.name.Replace("(Clone)", ""); // 就是prefab同名
                return m_DynamicBoneParamDir + fileName + ".txt";
            }
            else
            {
                EditorUtility.DisplayDialog("警告", "先生成Dynamicbone参数才能正确取到文件名字", "好吧");
                return "";
            }
        }

        /// <summary>
        /// 通过名字,拿到对应的动态骨骼配置文件
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        string GetDynamicBoneInfoFilePathByName(string name)
        {
            string fileName = name.Replace("(Clone)", ""); // 就是prefab同名
            return m_DynamicBoneParamDir + fileName + ".txt";
        }

        /// <summary>
        /// 通过名字写入动态骨骼配置文件
        /// </summary>
        /// <param name="name"></param>
        string WriteSBInfoByName(string name)
        {
            string path = GetDynamicBoneInfoFilePathByName(name);
            StreamWriter sw;
            if (!File.Exists(path))
            {
                File.Create(path).Dispose();
            }
            else
            {
                // 清空
                File.WriteAllText(path, string.Empty);
            }
            FileInfo fi = new FileInfo(path);
            sw = fi.AppendText();
            // 写入m_TagToDynamicBoneParamDict
            WriteSBParamInfo(sw);
            sw.Close();
            sw.Dispose();
            
            AssetDatabase.Refresh();

            return path;
        }
        


        void WriteSBInfo()
        {
            string path = GetDynamicBoneInfoFilePath();
            StreamWriter sw;
            if (!File.Exists(path))
            {
                File.Create(path).Dispose();
            }
            else
            {
                // 清空
                File.WriteAllText(path, string.Empty);
            }
            FileInfo fi = new FileInfo(path);
            sw = fi.AppendText();
            // 写入m_TagToDynamicBoneParamDict
            WriteSBParamInfo(sw);
            sw.Close();
            sw.Dispose();
            
        }

        
        

        void TryReadSBFile()
        {
            string path = GetDynamicBoneInfoFilePath();
            if (!File.Exists(path))
            {
                EditorUtility.DisplayDialog("警告", "找不到对应的DynamicBone参数文件", "好吧");
            }
            if (EditorUtility.DisplayDialog("警告", "确定要覆盖DynamicBone参数么", "确定"))
            {
                LoadSBFile();
            }
        }

        /// <summary>
        /// 加载动态骨骼配置文件(通过名字)
        /// </summary>
        /// <param name="path"></param>
        void TryReadSBFileByName(string path)
        {
            // string path = GetDynamicBoneInfoFilePathByName(name);
            // if (!File.Exists(path))
            // {
            //     EditorUtility.DisplayDialog("警告", "找不到对应的DynamicBone参数文件", "好吧");
            // }
            // if (EditorUtility.DisplayDialog("警告", "确定要覆盖DynamicBone参数么", "确定"))
            // {
            //     LoadSBFileByName(name);
            // }

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

        void LoadSBFile()
        {
            string path = GetDynamicBoneInfoFilePath();
            StreamReader sr = new StreamReader(path, Encoding.Default);
            string readStr = "";
            string fileInfoString = ""; // 整个文件的内容都存入
            while (sr != null && (readStr = sr.ReadLine()) != null)
            {
                fileInfoString += readStr;
            }
            sr.Close();
            SetLODDynamicBoneParams(fileInfoString);
            Repaint();
            EditorUtility.DisplayDialog("成功", "导入完毕", "确定");
        }

        void LoadSBFileByName(string path)
        {
            // string path = GetDynamicBoneInfoFilePathByName(name);
            StreamReader sr = new StreamReader(path, Encoding.Default);
            string readStr = "";
            string fileInfoString = ""; // 整个文件的内容都存入
            while (sr != null && (readStr = sr.ReadLine()) != null)
            {
                fileInfoString += readStr;
            }
            sr.Close();
            SetLODDynamicBoneParams(fileInfoString);
            Repaint();
            // EditorUtility.DisplayDialog("成功", "导入完毕", "确定");
        }

        void SetLODDynamicBoneParams(string fileInfoString)
        {
            string[] tags = fileInfoString.Split('!');
            string currTagKey;
            LODDynamicBoneParams currDBParam;
            foreach (string tagStr in tags)
            {
                currTagKey = "";
                currDBParam = new LODDynamicBoneParams(null, 0.02f, 0.08f, 0.06f, 0.8f, 0.6f, 0.02f, null);
                if (string.IsNullOrEmpty(tagStr)) continue;
                string[] pairStrs = tagStr.Split('%');
        
                foreach (string pairStr in pairStrs)
                {
                    if (string.IsNullOrEmpty(pairStr)) continue;
                    string[] pair = pairStr.Split(':');
                    string key = "";
                    string value = "";
                    if (pair.Length != 2)
                    {
                        Debug.LogError("数据对" + pairStr + "解析失败!");
                    }
                    if (!string.IsNullOrEmpty(pair[0])) key = pair[0];
                    if (!string.IsNullOrEmpty(pair[1])) value = pair[1];
                    // 简陋的字符匹配
                    if (key.Contains("TagName"))
                    {
                        currTagKey = value;
                        if (m_TagToDynamicBoneParamDict.ContainsKey(currTagKey))
                        {
                            currDBParam = m_TagToDynamicBoneParamDict[currTagKey]; // 应该是最先被赋值的保证后面参数修改的都是拿到了有值的currDBParam
                        }
                    }
                    else if (key.Contains("DB_Root"))
                    {
                        currDBParam.root = GetRootFromPath(value);
                    }
                    else if (key.Contains("DB_Damping"))
                    {
                        currDBParam.Damping = float.Parse(value);
                    }
                    else if (key.Contains("DB_Elasticty"))
                    {
                        currDBParam.Elasticty = float.Parse(value);
                    }
                    else if (key.Contains("DB_Stiffness"))
                    {
                        currDBParam.Stiffness = float.Parse(value);
                    }
                    else if (key.Contains("DB_Inert"))
                    {
                        currDBParam.Inert = float.Parse(value);
                    }
                    else if (key.Contains("DB_Friction"))
                    {
                        currDBParam.Friction = float.Parse(value);
                    }
                    else if (key.Contains("DB_Radius"))
                    {
                        currDBParam.Radius = float.Parse(value);
                    }
                    else if (key.Contains("SBC_List"))
                    {
                        SetColliders(currTagKey, ref currDBParam.colliderDataList, value);
                    }
                    else
                    {
                        Debug.LogError("读取文件发现异常内容:" + pairStr);
                    }
                }
                m_TagToDynamicBoneParamDict[currTagKey] = currDBParam;
            }
        }

        void SetColliders(string tagName, ref List<LODDynamicColliderData> colliderDataList, string colliderInfoStr)
        {
            string[] collInfoStrs = colliderInfoStr.Split('$');
            foreach (string infoStrs in collInfoStrs)
            {
                if (string.IsNullOrEmpty(infoStrs)) continue;
                string[] infoStrArray = infoStrs.Split('@');
                string name = infoStrArray[1];
                float radius = float.Parse(infoStrArray[2]);
                string rootName = infoStrArray[3];
                string[] posArray = infoStrArray[4].Split('#');
                float x, y, z;
                if (posArray.Length != 3)
                {
                    Debug.LogError("读取" + name + "collider坐标失败!");
                    x = y = z = 0f;
                }
                else
                {
                    x = float.Parse(posArray[0]);
                    y = float.Parse(posArray[1]);
                    z = float.Parse(posArray[2]);
                }
                // 查找colliderDataList是否存在同名Collider存在则覆盖不存在则新建
                bool isNeedAdd = true;
                LODDynamicColliderData scData;
                for (int i = 0; i < colliderDataList.Count; i++)
                {
                    scData = colliderDataList[i];
                    if (scData.name.Equals(name))
                    {
                        isNeedAdd = false;
                        // 存在,覆盖
                        scData.radius = radius;
                        scData.rootName = rootName;
                        scData.colTrans.localPosition = new Vector3(x, y, z);
                        break;
                    }
                }
                // 新建并赋值
                if (isNeedAdd)
                {
                    GameObject collider = new GameObject();
                    collider.name = name;
                    collider.transform.SetParent(GetTransformByName(tagName).GetChild(0));
                    collider.transform.localPosition = new Vector3(x, y, z);
                    DynamicBoneCollider dc = collider.AddComponent<DynamicBoneCollider>();
                    dc.m_Radius = radius;
                    scData = new LODDynamicColliderData(name, radius, rootName, collider.transform);
                    colliderDataList.Add(scData);
                }
            }
        }

        void WriteSBParamInfo(StreamWriter sw)
        {
            Dictionary<string, string> TagInfo = new Dictionary<string, string>();
            foreach (KeyValuePair<string, LODDynamicBoneParams> pair in m_TagToDynamicBoneParamDict)
            {
                LODDynamicBoneParams param = pair.Value;
                TagInfo = new Dictionary<string, string>
                {
                    {"TagName:", pair.Key},
                    {"DB_Root:", CreateDynamicBoneRoot(pair.Key, param.root==null?"":param.root.name)},
                    {"DB_Damping:", param.Damping.ToString()},
                    {"DB_Elasticty:", param.Elasticty.ToString()},
                    {"DB_Stiffness:", param.Stiffness.ToString()},
                    {"DB_Inert:", param.Inert.ToString()},
                    {"DB_Friction:", param.Friction.ToString()},
                    {"DB_Radius:", param.Radius.ToString()},
                    {"SBC_List:", GetColliderListString(pair.Key, param.colliderDataList)}
                };
                sw.WriteLine('!');
                foreach (KeyValuePair<string, string> tag in TagInfo)
                {
                    sw.WriteLine('%' + tag.Key + tag.Value);
                }
            }
        }

        string GetColliderListString(string tagName, List<LODDynamicColliderData> colliderDataList)
        {
            string rst = "";
            string singleCollDataStr = "";
            if (colliderDataList == null) return "";
            for (int i = 0; i < colliderDataList.Count; i++)
            {
                singleCollDataStr = "";
                // 普通的参数普通地获取
                singleCollDataStr += "@" + colliderDataList[i].name;
                singleCollDataStr += "@" + colliderDataList[i].radius;
                singleCollDataStr += "@" + colliderDataList[i].rootName;
                // 厉害的参数遍历获取(?)
                foreach (Transform trans in m_DynamicBoneSenceObject.transform.GetComponentsInChildren<Transform>())
                {
                    if (!trans.name.Equals(tagName)) continue;
                    // 找到标记位下面同名的collider拿到position
                    foreach (DynamicBoneCollider collider in trans.GetComponentsInChildren<DynamicBoneCollider>())
                    {
                        if (!collider.transform.name.Equals(colliderDataList[i].name)) continue;
                        Vector3 pos = collider.transform.localPosition;
                        singleCollDataStr += "@" + pos.x + "#" + pos.y + "#" + pos.z; // @-0.21#0#0
                    }
                }
                rst += singleCollDataStr + "$"; // collider单条信息以$结尾
            }
            return rst;
        }

        string CreateDynamicBoneRoot(string parentName, string childName)
        {
            return parentName + "/" + childName;
        }

        Transform GetRootFromPath(string path)
        {
            DynamicBone[] transList = m_DynamicBoneSenceObject.transform.GetComponentsInChildren<DynamicBone>();
            foreach(DynamicBone trans in transList)
            {
                if(path.Contains(trans.name) &&path.Contains(trans.transform.parent.name))
                {
                    return trans.transform;
                }
            }
            return null;
        }

        #endregion

    }

}