分解FBX文件
作者:追风剑情 发布于:2018-12-7 18:33 分类:Unity3d
一、导入一个FBX文件
二、分解FBX文件
示例代码
using UnityEditor; using UnityEngine; using System.IO; using System.Collections; using System.Collections.Generic; public class TestEditor { public const string PREFIX_BONE = "bone"; public const string PREFIX_M2B = "smb"; public const string PREFIX_MODEL = "model"; [MenuItem("Assets/分解FBX", false, 100)] public static void DetachFBX() { GameObject go = Selection.activeGameObject; if (go == null){ EditorUtility.DisplayDialog("提示", "请选择FBX文件", "OK"); return; } string asset_path = AssetDatabase.GetAssetPath(go); string postfix = Path.GetExtension(asset_path).ToLower(); if (postfix != ".fbx") { EditorUtility.DisplayDialog("错误", "选中的不是FBX文件", "OK"); return; } //创建导出目录 string asset_folder = Path.GetDirectoryName(asset_path); string export_folder = Path.Combine(asset_folder, go.name).Replace("\\","/"); if (!Directory.Exists(export_folder)) { AssetDatabase.CreateFolder(asset_folder, go.name); } Object[] assets = AssetDatabase.LoadAllAssetsAtPath(asset_path); //遍历FBX内部所有资源 for (int i = 0; i < assets.Length; i++) { if (!(assets[i] is GameObject)) continue; GameObject asset = assets[i] as GameObject; asset_path = Path.Combine(export_folder, asset.name); //导出带动画的模型 SkinnedMeshRenderer smr = asset.GetComponent<SkinnedMeshRenderer>(); if (smr != null) { ExportSkinnedBone(smr, export_folder); ExportModel(asset, export_folder); } //导出无动画的模型 MeshFilter mf = asset.GetComponent<MeshFilter>(); if (mf != null) { ExportModel(asset, export_folder); } } //导出骨架 ExportSkeleton(go, export_folder); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } [MenuItem("Assets/导出选中", false, 200)] public static void ExportSelected() { Object[] objects = Selection.objects; for(int i=0; i<objects.Length; i++) { Object obj = objects[i]; if (obj == null) continue; string name = obj.name; if (name.StartsWith(PREFIX_BONE) || name.StartsWith(PREFIX_M2B) || name.StartsWith(PREFIX_MODEL)) BuildAsset(obj); } } // 导出骨架 public static void ExportSkeleton(GameObject asset, string export_folder) { GameObject go = GameObject.Instantiate(asset); //去掉模型资源 foreach (var smr in go.GetComponentsInChildren<SkinnedMeshRenderer>()) GameObject.DestroyImmediate(smr.gameObject); foreach (var mf in go.GetComponentsInChildren<MeshFilter>()) GameObject.DestroyImmediate(mf.gameObject); string prefab_path = string.Format("{0}/{1}_{2}.prefab", export_folder, PREFIX_BONE, asset.name); CreatePrefab(prefab_path, go); //设置bundle信息 string bundle_name = Path.GetFileNameWithoutExtension(prefab_path); SetAssetBundleNameAndVariant(prefab_path, bundle_name, PREFIX_BONE); //生成AssetBundle资源 BuildAsset(prefab_path, export_folder, bundle_name, PREFIX_BONE); GameObject.DestroyImmediate(go); } // 导出蒙皮骨骼信息 public static void ExportSkinnedBone(SkinnedMeshRenderer smr, string export_folder) { string asset_path = string.Format("{0}/{1}_{2}.asset", export_folder, PREFIX_M2B, smr.name); SkinnedMeshBone obj = SkinnedMeshBone.Create(smr); AssetDatabase.CreateAsset(obj, asset_path); //设置bundle信息 string bundle_name = Path.GetFileNameWithoutExtension(asset_path); SetAssetBundleNameAndVariant(asset_path, bundle_name, PREFIX_M2B); //生成AssetBundle资源 BuildAsset(asset_path, export_folder, bundle_name, PREFIX_M2B, false); } // 导出模型 public static void ExportModel(GameObject asset, string export_folder) { GameObject go = GameObject.Instantiate(asset); string prefab_path = string.Format("{0}/{1}_{2}.prefab", export_folder, PREFIX_MODEL, asset.name); CreatePrefab(prefab_path, go); //设置bundle信息 string bundle_name = Path.GetFileNameWithoutExtension(prefab_path); SetAssetBundleNameAndVariant(prefab_path, bundle_name, PREFIX_MODEL); //生成AssetBundle资源 BuildAsset(prefab_path, export_folder, bundle_name, PREFIX_MODEL); GameObject.DestroyImmediate(go); } // 创建Prefab public static void CreatePrefab(string asset_path, GameObject go) { Object prefab = PrefabUtility.CreateEmptyPrefab(asset_path); PrefabUtility.ReplacePrefab(go, prefab); } // 设置资源的AssetBundle信息 static void SetAssetBundleNameAndVariant(Object asset, string bundle_name, string bundle_variant) { AssetImporter importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(asset)); if (importer == null) return; importer.SetAssetBundleNameAndVariant(bundle_name, bundle_variant); } // 设置资源的AssetBundle信息 static void SetAssetBundleNameAndVariant(string asset_path, string bundle_name, string bundle_variant) { AssetImporter importer = AssetImporter.GetAtPath(asset_path); if (importer == null) return; importer.SetAssetBundleNameAndVariant(bundle_name, bundle_variant); } //打包AssetBundle资源 public static void BuildAsset(Object asset, string export_folder=null, string bundle_name=null, string bundle_variant=null, bool includeDependencies=true, bool compress=true) { if (asset is ScriptableObject) { includeDependencies = false; } string asset_path = AssetDatabase.GetAssetPath(asset); BuildAsset(asset_path, export_folder, bundle_name, bundle_variant, includeDependencies, compress); } public static void BuildAsset(string asset_path, string export_folder=null, string bundle_name=null, string bundle_variant=null, bool includeDependencies=true, bool compress=true) { if (string.IsNullOrEmpty(export_folder)) { export_folder = Path.GetDirectoryName(asset_path); } if (string.IsNullOrEmpty(bundle_name)) { AssetImporter importer = AssetImporter.GetAtPath(asset_path); if (!string.IsNullOrEmpty(importer.assetBundleName)) { bundle_name = importer.assetBundleName; } } if (string.IsNullOrEmpty(bundle_variant)) { AssetImporter importer = AssetImporter.GetAtPath(asset_path); if (!string.IsNullOrEmpty(importer.assetBundleVariant)) { bundle_variant = importer.assetBundleVariant; } } //-----检查bundle信息----- if (string.IsNullOrEmpty(bundle_name)) { string msg = string.Format("未设置BundleName\n{0}", Path.GetFileName(asset_path)); EditorUtility.DisplayDialog("Error", msg, "OK"); return; } if (string.IsNullOrEmpty(bundle_variant)) { string msg = string.Format("未设置BundleVariant\n{0}", Path.GetFileName(asset_path)); EditorUtility.DisplayDialog("Error", msg, "OK"); return; } //------------------------ string[] assetNames; if (includeDependencies){ /************************************************************* 这里返回的依赖资源包含了自己 注意: 如果依赖资源未设置bundle信息,会全部打进ab文件中, 这里必须把依赖资源的path也放进assetNames数组中,否则无法从AssetBundle中读取依赖资源 如果未设置addressableNames,用资源名从AssetBundle从读取资源 示例: ab.LoadAsset("资源名") 如果设置了addressableNames,则只能用addressableName读取资源 示例: ab.LoadAsset("addressableName") addressableNames数组长度必须和assetNames数组长度相同 **************************************************************/ assetNames = AssetDatabase.GetDependencies(asset_path); }else{ assetNames = new string[] { asset_path }; } AssetBundleBuild abb = new AssetBundleBuild(); abb.assetBundleName = bundle_name; abb.assetBundleVariant = bundle_variant; abb.assetNames = assetNames; //abb.addressableNames = addressableNames;//自定义资源名称 BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression; if (!compress) options = BuildAssetBundleOptions.UncompressedAssetBundle; options |= BuildAssetBundleOptions.StrictMode; BuildPipeline.BuildAssetBundles(export_folder, new AssetBundleBuild[] { abb }, options, EditorUserBuildSettings.activeBuildTarget); AssetDatabase.Refresh(); //删除生成的manifest文件 AssetDatabase.DeleteAsset(string.Format("{0}/{1}.{2}.manifest", export_folder, bundle_name, bundle_variant)); string folder_name = export_folder.Substring(export_folder.LastIndexOf("/") + 1); AssetDatabase.DeleteAsset(string.Format("{0}/{1}.manifest", export_folder, folder_name)); AssetDatabase.DeleteAsset(string.Format("{0}/{1}", export_folder, folder_name)); } }
三、将生成的AssetBundle文件放到StreamingAssets目录下
测试代码 Test.cs
using UnityEngine; using System.Collections; using System.Collections.Generic; /// <summary> /// 测试: 重新将骨架、模型、蒙皮骨骼配置信息组装成角色 /// </summary> public class Test : MonoBehaviour { CRoleObject ro; void Start() { ModelConfig cfg = new ModelConfig(); cfg.skeletonUrl = string.Format("file://{0}/bone_lady01.bone", Application.streamingAssetsPath); cfg.smbUrl = string.Format("file://{0}/smb_lady01.smb", Application.streamingAssetsPath); cfg.modelUrl = string.Format("file://{0}/model_lady01.model", Application.streamingAssetsPath); ro = new CRoleObject(cfg); } void Update() { if (ro != null) ro.Update(); } } public abstract class IRenderObject { public bool completed { get; protected set; } public void Update() { OnUpdate(); } public void Destroy() { OnDestroy(); } protected virtual void OnUpdate() { } protected virtual void OnDestroy() { } } public class CRoleObject : IRenderObject { protected CSkeleton mSkeleton = null; protected CSkinnedMeshBone mSkinnedMeshBone = null; protected CSkinnedMesh mSkinnedMesh = null; //构建状态 public enum BuildState { None, SkeletonLoading, MeshToBoneMapLoading, SkinnedMeshLoading, LoadCompleted, BuildCompleted } public BuildState buildState { get; protected set; } public CRoleObject() { } public CRoleObject(ModelConfig cfg) { Load(cfg); } public void Load(ModelConfig cfg) { this.completed = false; mSkeleton = new CSkeleton(); mSkeleton.Load(cfg); mSkinnedMeshBone = new CSkinnedMeshBone(); mSkinnedMeshBone.Load(cfg); mSkinnedMesh = new CSkinnedMesh(); mSkinnedMesh.Load(cfg); buildState = BuildState.SkeletonLoading; } protected override void OnUpdate() { if (mSkeleton != null) mSkeleton.Update(); if (mSkinnedMeshBone != null) mSkinnedMeshBone.Update(); if (mSkinnedMesh != null) mSkinnedMesh.Update(); switch(buildState) { case BuildState.SkeletonLoading: if (mSkeleton.completed) buildState = BuildState.MeshToBoneMapLoading; break; case BuildState.MeshToBoneMapLoading: if (mSkinnedMeshBone.completed) buildState = BuildState.SkinnedMeshLoading; break; case BuildState.SkinnedMeshLoading: if (mSkinnedMesh.completed) buildState = BuildState.LoadCompleted; break; case BuildState.LoadCompleted: mSkinnedMesh.SetBones(mSkinnedMeshBone.rootBone, mSkinnedMeshBone.bones); mSkinnedMesh.AttachSkeleton(mSkeleton); buildState = BuildState.BuildCompleted; this.completed = true; Debug.Log("Role build completed."); break; } Debug.Log("build state: " + buildState); } protected override void OnDestroy() { if (mSkeleton != null) { mSkeleton.Destroy(); mSkeleton = null; } if (mSkinnedMeshBone != null) { mSkinnedMeshBone.Destroy(); mSkinnedMeshBone = null; } if (mSkinnedMesh != null) { mSkinnedMesh.Destroy(); mSkinnedMesh = null; } } } public class CSkeleton : IRenderObject { protected Dictionary<string, Transform> mBoneDic = null; protected WWW www = null; protected Animation animation = null; protected override void OnUpdate() { if (this.www == null) return; if (!string.IsNullOrEmpty(this.www.error)) { Debug.LogErrorFormat("load skeleton failed. url={0}", this.www.url); return; } if (!this.www.isDone) return; AssetBundle ab = this.www.assetBundle; Object[] assets = ab.LoadAllAssets(); if (assets == null || assets.Length <= 0) { Debug.LogErrorFormat("load skeleton asset failed. url={0}", this.www.url); return; } GameObject mainAsset = null; //找到骨架资源作为主资源 for(int i=0; i < assets.Length; i++) { if (assets[i].name.StartsWith("bone_")) { mainAsset = assets[i] as GameObject; break; } } if (mainAsset == null) { Debug.LogErrorFormat("not skeleton asset. url={0}", this.www.url); return; } GameObject skeleton = GameObject.Instantiate(mainAsset); skeleton.name = mainAsset.name; //是否挂有动画组件 animation = skeleton.GetComponent<Animation>(); //建立 骨骼名-Transform 映射表 mBoneDic = new Dictionary<string, Transform>(); Transform[] transforms = skeleton.GetComponentsInChildren<Transform>(); int length = transforms.Length; for (int i = 0; i < length; ++i) { string bone_name = transforms[i].name; if (!this.mBoneDic.ContainsKey(bone_name)) { this.mBoneDic[bone_name] = transforms[i]; } else { Debug.LogErrorFormat("bone name '{0}' repeated", bone_name); } } ab.Unload(false); this.www.Dispose(); this.www = null; this.completed = true; Debug.Log("load skeleton completed."); } public void Load(ModelConfig cfg) { this.Dispose(); this.completed = false; this.www = WWW.LoadFromCacheOrDownload(cfg.skeletonUrl, cfg.version); } public Transform FindBoneByName(string boneName) { if (this.mBoneDic == null || string.IsNullOrEmpty(boneName) || !this.mBoneDic.ContainsKey(boneName)) return null; return this.mBoneDic[boneName]; } public Transform[] FindBonesByName(string[] boneNames) { if (this.mBoneDic == null || boneNames == null) return null; List<Transform> list = new List<Transform>(); string bone_name; for(int i=0; i<boneNames.Length; i++){ bone_name = boneNames[i]; if (string.IsNullOrEmpty(bone_name)) continue; if (this.mBoneDic.ContainsKey(bone_name)) { list.Add(this.mBoneDic[bone_name]); }else{ Debug.LogWarningFormat("can't find bone transform:{0}", bone_name); } } return list.ToArray(); } public void Dispose() { if (www != null) { www.Dispose(); www = null; } } protected override void OnDestroy() { Dispose(); if (mBoneDic != null) { mBoneDic.Clear(); mBoneDic = null; } } } public class CSkinnedMesh : IRenderObject { protected WWW www; protected SkinnedMeshRenderer smr; protected string rootBone; protected string[] bones; protected override void OnUpdate() { if (this.www == null) return; if (!string.IsNullOrEmpty(this.www.error)) { Debug.LogErrorFormat("load model failed. url={0}", this.www.url); return; } if (!this.www.isDone) return; AssetBundle ab = this.www.assetBundle; Object[] assets = ab.LoadAllAssets(); if (assets == null || assets.Length <= 0) { Debug.LogErrorFormat("load model asset failed. url={0}", this.www.url); return; } GameObject mainAsset = null; //找到模型资源作为主资源 for(int i=0; i < assets.Length; i++) { if (assets[i].name.StartsWith("model_")) { mainAsset = assets[i] as GameObject; break; } } if (mainAsset == null) { Debug.LogErrorFormat("not model asset. url={0}", this.www.url); return; } GameObject model = GameObject.Instantiate(mainAsset); model.name = mainAsset.name; this.smr = model.GetComponent<SkinnedMeshRenderer>(); ab.Unload(false); this.www.Dispose(); this.www = null; this.completed = true; Debug.Log("load model completed."); } public void Load(ModelConfig cfg) { Dispose(); this.completed = false; this.www = WWW.LoadFromCacheOrDownload(cfg.modelUrl, cfg.version); } public void SetBones(string rootBone, string[] bones) { this.rootBone = rootBone; this.bones = bones; } public void AttachSkeleton(CSkeleton skeleton) { if (skeleton == null || smr == null) return; Transform rootTran = skeleton.FindBoneByName(rootBone); smr.transform.parent = rootTran.parent != null ? rootTran.parent : rootTran; smr.rootBone = rootTran; smr.bones = skeleton.FindBonesByName(bones); smr.enabled = true; } public void Dispose() { if (www != null) { www.Dispose(); www = null; } } protected override void OnDestroy() { Dispose(); if (smr != null) { smr.rootBone = null; smr.bones = null; smr = null; } bones = null; } } public class CSkinnedMeshBone : IRenderObject { public string rootBone; public string[] bones; protected WWW www; protected override void OnUpdate() { if (this.www == null) return; if (!string.IsNullOrEmpty(this.www.error)) { Debug.LogErrorFormat("load smb failed. url={0}", this.www.url); return; } if (!this.www.isDone) return; AssetBundle ab = this.www.assetBundle; Object[] assets = ab.LoadAllAssets(); if (assets == null || assets.Length <= 0) { Debug.LogErrorFormat("load smb asset failed. url={0}", this.www.url); return; } SkinnedMeshBone smb = assets[0] as SkinnedMeshBone; if (smb == null) { Debug.LogErrorFormat("not smb asset. url={0}", this.www.url); return; } this.rootBone = smb.rootBone; this.bones = smb.bones.Clone() as string[]; ab.Unload(false); this.www.Dispose(); this.www = null; this.completed = true; Debug.Log("load smb completed."); } public void Load(ModelConfig cfg) { this.Dispose(); this.completed = false; this.www = WWW.LoadFromCacheOrDownload(cfg.smbUrl, cfg.version); } public void Dispose() { if (www != null) { www.Dispose(); www = null; } } protected override void OnDestroy() { Dispose(); bones = null; } } public class ModelConfig { public int version = 6; public string skeletonUrl; public string modelUrl; public string smbUrl; }
运行效果
Hierarchy窗口
Console日志
标签: Unity3d
日历
最新文章
随机文章
热门文章
分类
存档
- 2024年11月(3)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号