RhinoFreak 发表于 2022-4-28 17:53

Unity:一键提取fbx文件中的材质和动画片段

做项目的时候遇到一个问题,美术同学把fbx文件导入Unity后,模型的材质球和动画片段都包含在fbx文件中。如果要重新给模型增加材质球,需要对每个模型部件分别添加,非常麻烦。


查找资料后发现在fpx Import Settings里可以设置材质球的映射,但如果一个个材质球都靠导入时手动设置,工作量也很大。在网上翻了一圈以后参考别人的插件自己写了一版提取材质球和动画片段的功能。
一键提取材质球

脚本需要放在Editor文件夹内
using System.IO;
using UnityEngine;
using UnityEditor;

public class ExtractMaterials : EditorWindow
{
   
    static void CreateMaterialsFromFBX()
    {
      UnityEngine.Object[] gameObjects = Selection.objects;
      string[] strs = Selection.assetGUIDs;

      if (gameObjects.Length > 0)
      {
            int gameNum = gameObjects.Length;
            for(int i = 0; i < gameNum; i++)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(strs);
                //Debug.Log(assetPath); //具体到fbx的路径
                string materialFolder = Path.GetDirectoryName(assetPath) + "/Materials";
                // 如果不存在该文件夹则创建一个新的
                if (!AssetDatabase.IsValidFolder(materialFolder))
                {
                  AssetDatabase.CreateFolder(Path.GetDirectoryName(assetPath), "Materials");
                }
                // 获取assetPath下所有资源
                Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
                bool isCreate = false;
                foreach (Object item in assets)
                {
                  if (typeof(Material) == item?.GetType())//找到fbx里面的材质
                  {
                        Debug.Log("找到材质文件:" + item);
                        string path = System.IO.Path.Combine(materialFolder, item.name) + ".mat";//提取后的名字
                        if(System.IO.File.Exists(path)){
                            Debug.Log("该材质已存在");

                            var assetImporter = AssetImporter.GetAtPath(assetPath);
                            var clone = AssetDatabase.LoadAssetAtPath(path, typeof(Object));
                            assetImporter.AddRemap(new AssetImporter.SourceAssetIdentifier(item), clone);
                            AssetDatabase.WriteImportSettingsIfDirty(assetPath);
                            AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
                        }
                        else{
                            path = AssetDatabase.GenerateUniqueAssetPath(path);
                            string value = AssetDatabase.ExtractAsset(item, path);
                            if (string.IsNullOrEmpty(value))
                            {
                              AssetDatabase.WriteImportSettingsIfDirty(assetPath);
                              AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
                              isCreate = true;
                            }
                        }
                  }
                }

                AssetDatabase.Refresh();
                if (isCreate)
                  Debug.Log("自动创建材质球成功:" + materialFolder);
            }
      }
      else
      {
            Debug.LogError("请选中需要一键生成材质球的模型");
      }
    }
}
使用方法:右键选中需要提取材质球的fbx文件(可多选),选择”一键生成材质球“,在同个文件夹中会出现Material文件夹,包含提取出来的材质球文件。名字相同的材质球会自动匹配到,不会重复生成。


一键提取动画片段

脚本需要放在Editor文件夹内
using System.IO;
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;

public class ExtractAnim : MonoBehaviour
{
   
    static void CreateAnimFromFBX()
    {
      UnityEngine.Object[] gameObjects = Selection.objects;
      string[] strs = Selection.assetGUIDs;

      if (gameObjects.Length > 0)
      {
            int gameNum = gameObjects.Length;
            for(int i = 0; i < gameNum; i++)
            {
                string fbxName = gameObjects.name;
                string assetPath = AssetDatabase.GUIDToAssetPath(strs);
                //Debug.Log(assetPath); //具体到fbx的路径
                string animFolder = Path.GetDirectoryName(assetPath) + "/Anim";
                // 如果不存在该文件夹则创建一个新的
                if (!AssetDatabase.IsValidFolder(animFolder))
                {
                  AssetDatabase.CreateFolder(Path.GetDirectoryName(assetPath), "Anim");
                }
                // 获取assetPath下所有资源
                Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
                bool isCreate = false;
                List<Object> animation_clip_list = new List<Object>();
                foreach (Object item in assets)
                {
                  if (typeof(AnimationClip) == item?.GetType())//找到fbx里面的动画
                  {
                        Debug.Log("找到动画片段:" + item);
                        if(!item.name.StartsWith("__preview")){
                            animation_clip_list.Add(item);
                        }
                  }
                }
                foreach(AnimationClip animation_clip in animation_clip_list){
                  Object new_animation_clip = new AnimationClip();
                  EditorUtility.CopySerialized(animation_clip, new_animation_clip);
                  new_animation_clip.name = Path.GetFileNameWithoutExtension(assetPath);
                  string animation_path = Path.Combine(animFolder, new_animation_clip.name + ".anim");
                  Debug.Log(animation_path);
                  AssetDatabase.CreateAsset(new_animation_clip, animation_path);
                  
                  isCreate = true;
                }
                //AssetDatabase.DeleteAsset(assetPath);
                AssetDatabase.Refresh();
                if (isCreate)
                  Debug.Log("自动创建动画片段成功:" + animFolder);
                else
                  Debug.Log("未自动创建动画片段。");

            }
      }
      else
      {
            Debug.LogError("请选中需要一键提取动画片段的模型");
      }
    }
}
使用方法:右键选中需要提取材质球的fbx文件(可多选),选择”一键生成动画片段“,在同个文件夹中会出现Anim文件夹,包含提取出来的动画片段文件。


参考资料

Unity官方文档:AssetImporter.AddRemap
Unity Editor - 一键导出模型内置材质、设置模型设置、修改Shader
Unity动画TA:导入fbx时直接分解出AnimationClip

kyuskoj 发表于 2022-4-28 17:58

请问,能把anim文件改回fbx文件么
页: [1]
查看完整版本: Unity:一键提取fbx文件中的材质和动画片段