码迷,mamicode.com
首页 > 其他好文 > 详细

自动切割fbx中的动画

时间:2016-05-04 17:13:29      阅读:333      评论:0      收藏:0      [点我收藏+]

标签:

自动切割FBX中的动画

最近写了一个有意思的工具,能够自动切割fbx中的动画信息,如下图所示:

技术分享

本来不想造轮子,从晚上查找一番,发现并没有自己满意的工具,只好自己出手啦。
核心代码如下:

  1. [MenuItem("liubo/自动切割骨骼动画2")]
  2. public static void AutoClip2()
  3. {
  4. AutoClip2Impl("");
  5. }
  6. static void AutoClip2Impl(string spec)
  7. {
  8. /*
  9. * 读取excel表,获取动画的信息:名字,起始帧,结束帧
  10. * 再读取fbx的动画信息,对名字相同的进行信息merge
  11. * merge:起始帧,结束帧
  12. * 保留:event信息,mask信息,curve信息
  13. * 如果有新加的,那么加入新的信息
  14. * **/
  15. // 匿名函数
  16. System.Func<bool, ConfigLine, SerializedProperty, int> SetPropertyFunction = delegate(bool IsHumanClip, ConfigLine csv_line, SerializedProperty sp)
  17. {
  18. sp.FindPropertyRelative("firstFrame").floatValue = csv_line.GetColumnUnsafe<float>("firstFrame");
  19. sp.FindPropertyRelative("lastFrame").floatValue = csv_line.GetColumnUnsafe<float>("lastFrame");
  20. // LoopTime, LoopPose
  21. sp.FindPropertyRelative("loopTime").boolValue = csv_line.GetColumnUnsafe<int>("loopTime") > 0;
  22. sp.FindPropertyRelative("loopBlend").boolValue = csv_line.GetColumnUnsafe<int>("loopBlend") > 0;
  23. // Rotation_BakeIntoPose, Rotation_BaseUpon
  24. sp.FindPropertyRelative("loopBlendOrientation").boolValue = csv_line.GetColumnUnsafe<int>("loopBlendOrientation") > 0;
  25. sp.FindPropertyRelative("keepOriginalOrientation").boolValue = csv_line.GetColumnUnsafe<int>("keepOriginalOrientation") == 0;
  26. // PositionY_BakeIntoPose, PositionY_BaseUpon
  27. sp.FindPropertyRelative("loopBlendPositionY").boolValue = csv_line.GetColumnUnsafe<int>("loopBlendPositionY") > 0;
  28. if (IsHumanClip)
  29. {
  30. // 如果是humanoid类型的动画,那么keepOriginalPositionY的取值为0,1,2三种。
  31. sp.FindPropertyRelative("keepOriginalPositionY").boolValue = csv_line.GetColumnUnsafe<int>("keepOriginalPositionY") == 0;
  32. sp.FindPropertyRelative("heightFromFeet").boolValue = csv_line.GetColumnUnsafe<int>("keepOriginalPositionY") == 2;
  33. }
  34. else
  35. {
  36. sp.FindPropertyRelative("keepOriginalPositionY").boolValue = csv_line.GetColumnUnsafe<int>("keepOriginalPositionY") == 0;
  37. }
  38. // PositionXZ_BakeIntoPose, PositionXZ_BaseUpon
  39. sp.FindPropertyRelative("loopBlendPositionXZ").boolValue = csv_line.GetColumnUnsafe<int>("loopBlendPositionXZ") > 0;
  40. sp.FindPropertyRelative("keepOriginalPositionXZ").boolValue = csv_line.GetColumnUnsafe<int>("keepOriginalPositionXZ") == 0;
  41. return 0;
  42. };
  43. // 读取excel
  44. string[] files = Directory.GetFiles(Application.dataPath, "animations*.xls", SearchOption.AllDirectories);
  45. foreach (var fileName in files)
  46. {
  47. DateTime excelTime = File.GetLastWriteTime(fileName);
  48. Debug.Log("读取xls文件:" + fileName + ", file-write-time=" + excelTime.ToString());
  49. Dictionary<string, ConfigFile> excels = ReadXML(fileName);// ReadXML(Application.dataPath + "/Art/animations.xls");
  50. foreach (var it in excels)
  51. {
  52. if (string.IsNullOrEmpty(spec) || spec == it.Key)
  53. {
  54. }
  55. else
  56. {
  57. // 不处理啦。
  58. continue;
  59. }
  60. Debug.Log("处理:" + it.Key);
  61. ConfigFile csv = it.Value;
  62. string[] assets = AssetDatabase.FindAssets(it.Key, new string[] { "Assets/Art" });
  63. if (assets.Length > 0)
  64. {
  65. string asset = AssetDatabase.GUIDToAssetPath(assets[0]);
  66. // 如果.meta时间和excel时间不一样,说明有变化,否则没变化的就直接跳过
  67. DateTime assetMetaTime = File.GetLastWriteTime(asset + ".meta");
  68. if (DateTime.Equals(assetMetaTime, excelTime))
  69. {
  70. // 文件没有变化,不处理
  71. Debug.Log("文件没有变化,不处理啦:" + asset);
  72. continue;
  73. }
  74. File.SetLastWriteTime(asset + ".meta", excelTime);
  75. ModelImporter modelImporter = (ModelImporter)AssetImporter.GetAtPath(asset);
  76. SerializedObject serializedObject = modelImporter == null ? null : new SerializedObject(modelImporter);
  77. ModelImporterAnimationType m_AnimationType = (ModelImporterAnimationType)serializedObject.FindProperty("m_AnimationType").intValue;
  78. bool IsHumanClip = m_AnimationType == ModelImporterAnimationType.Human;
  79. HashSet<string> processed_clip = new HashSet<string>();
  80. if (serializedObject == null)
  81. {
  82. Debug.Log("无法处理该文件:" + asset);
  83. }
  84. else
  85. {
  86. // 压缩方式
  87. serializedObject.FindProperty("m_AnimationCompression").intValue = (int)ModelImporterAnimationCompression.Optimal;
  88. // 切割或者merge动画
  89. SerializedProperty m_ClipAnimations = serializedObject.FindProperty("m_ClipAnimations");
  90. for (int i = 0; i < m_ClipAnimations.arraySize; i++)
  91. {
  92. SerializedProperty sp = m_ClipAnimations.GetArrayElementAtIndex(i);
  93. string clip_name = sp.FindPropertyRelative("name").stringValue;
  94. if (clip_name.Contains("@"))
  95. {
  96. clip_name = clip_name.Substring(clip_name.LastIndexOf("@") + 1);
  97. }
  98. ConfigLine csv_line = csv.FindData(clip_name);
  99. if (csv_line != null)
  100. {
  101. processed_clip.Add(clip_name);
  102. // merge基本属性
  103. SetPropertyFunction(IsHumanClip, csv_line, sp);
  104. // 其他属性,如event,mask,都不处理,保持原来信息
  105. }
  106. }
  107. // 把剩下没处理的内容,作为新的动画,分割到fbx中
  108. Dictionary<string, ConfigLine> csv_data = csv.GetLines();
  109. foreach (var kt in csv_data)
  110. {
  111. if (processed_clip.Contains(kt.Key))
  112. {
  113. continue;
  114. }
  115. processed_clip.Add(kt.Key);
  116. ConfigLine csv_line = kt.Value;
  117. m_ClipAnimations.InsertArrayElementAtIndex(m_ClipAnimations.arraySize);
  118. SerializedProperty sp = m_ClipAnimations.GetArrayElementAtIndex(m_ClipAnimations.arraySize - 1);
  119. sp.FindPropertyRelative("name").stringValue = it.Key + "@" + kt.Key;
  120. sp.FindPropertyRelative("takeName").stringValue = "Take 001";
  121. // 处理基本属性
  122. SetPropertyFunction(IsHumanClip, csv_line, sp);
  123. // 设置mask
  124. UnityEditorInternal.AvatarMask mask = new UnityEditorInternal.AvatarMask();
  125. mask.transformCount = modelImporter.transformPaths.Length;
  126. for (int i = 0; i < modelImporter.transformPaths.Length; i++)
  127. {
  128. mask.SetTransformPath(i, modelImporter.transformPaths[i]);
  129. mask.SetTransformActive(i, true);
  130. }
  131. SerializedProperty bodyMask = sp.FindPropertyRelative("bodyMask");
  132. if (bodyMask != null && bodyMask.isArray)
  133. {
  134. for (int i = 0; i < mask.humanoidBodyPartCount; i++)
  135. {
  136. if (i >= bodyMask.arraySize) bodyMask.InsertArrayElementAtIndex(i);
  137. bodyMask.GetArrayElementAtIndex(i).intValue = mask.GetHumanoidBodyPartActive(i) ? 1 : 0;
  138. }
  139. }
  140. SerializedProperty transformMask = sp.FindPropertyRelative("transformMask");
  141. //ModelImporter.UpdateTransformMask(mask, transformMask);
  142. Type ty = typeof(ModelImporter);
  143. MethodInfo mi = ty.GetMethod("UpdateTransformMask", BindingFlags.Static | BindingFlags.NonPublic);
  144. if (mi != null)
  145. {
  146. mi.Invoke(null, new object[] { mask, transformMask });
  147. }
  148. else
  149. {
  150. Debug.LogError("无法找到此方法!");
  151. }
  152. }
  153. serializedObject.ApplyModifiedProperties();
  154. AssetDatabase.WriteImportSettingsIfDirty(asset);
  155. AssetDatabase.ImportAsset(asset);
  156. }
  157. }
  158. else
  159. {
  160. Debug.LogError("无法找到这个FBX文件:" + it.Key);
  161. }
  162. }
  163. }
  164. }
  165. static void ShowProperties(SerializedProperty sp)
  166. {
  167. SerializedProperty backup = sp.Copy();
  168. {
  169. sp = backup.Copy();
  170. Debug.Log("[显示属性] sp.name=" + sp.displayName + ", " + sp.name);
  171. sp.Next(true);
  172. do
  173. {
  174. Debug.Log("[显示属性] sp.name=" + sp.displayName + ", " + sp.name);
  175. } while (sp.Next(false));
  176. }
  177. }

实现原理

    简单说明一下,需要攻克的问题就是,找到ModelImporter的属性信息,我是咋找到的呢,看对应的meta文件,以及...(次数省略三个字)。不过话说回来,meta文件中的信息很丰富,再加上自己写的ShowProperties函数,基本上都能猜到,有点站着说话不腰疼的感觉,哈哈。

核心的属性

其实,这些属性在meta中都能找到

  • name
  • firstFrame
  • lastFrame
  • loopTime
  • loopBlend
  • loopBlendOrientation
  • keepOriginalOrientation
  • loopBlendPositionY
  • keepOriginalPositionY
  • loopBlendPositionXZ
  • keepOriginalPositionXZ
  • bodyMask
  • transformMask
  • event

有人可能会问ModelImporter已经包含了很多属性,比如animationCompression,为什么不直接设置,非得用修改Property的形式呢?

我只能说,我设置过属性,但是不知道怎么,他并没有导出到meta中,所以就用了这段代码
  1. serializedObject.FindProperty("m_AnimationCompression").intValue = (int)ModelImporterAnimationCompression.Optimal;

错误的方法

最开始,没有研究Property的时候,采用了很直接的方法,如下所示。这段代码带来的危害就是,无法进行merge旧的meta文件,会导致event和mask丢失。
  1. [MenuItem("liubo/自测/自动切割骨骼动画")]
  2. public static void AutoClip()
  3. {
  4. string path = Selection.activeObject == null ? "" : AssetDatabase.GetAssetPath(Selection.activeObject);
  5. Debug.Log("[切割动画] path=" + path);
  6. ModelImporter mi = AssetImporter.GetAtPath(path) as ModelImporter;
  7. if (mi == null)
  8. {
  9. Debug.Log("[切割动画] 没找到FBX文件! path=" + path);
  10. }
  11. else
  12. {
  13. Debug.Log("[切割动画] 开始切割:path=" + path);
  14. // 注意保留就动画的一些数据,比如绑定的event
  15. List<ModelImporterClipAnimation> oldClips = new List<ModelImporterClipAnimation>();
  16. List<ModelImporterClipAnimation> newClips = new List<ModelImporterClipAnimation>();
  17. if (mi.clipAnimations != null)
  18. {
  19. oldClips.AddRange(mi.clipAnimations);
  20. }
  21. #if true//TEST
  22. //AnimationUtility.GetAnimationClipSettings(null);
  23. ModelImporterClipAnimation test = new ModelImporterClipAnimation();
  24. test.name = "test";
  25. test.takeName = "auto";
  26. test.firstFrame = 0;
  27. test.lastFrame = 30;
  28. test.wrapMode = WrapMode.Default;
  29. test.keepOriginalPositionY = true;
  30. test.loop = false;
  31. test.loopTime = false;
  32. test.loopPose = false;
  33. test.keepOriginalOrientation = false;
  34. test.keepOriginalPositionY = false;
  35. test.keepOriginalPositionXZ = false;
  36. test.cycleOffset = 0;
  37. test.heightOffset = 0;
  38. test.rotationOffset = 0;
  39. newClips.Add(test);
  40. newClips.AddRange(oldClips);
  41. #endif
  42. mi.clipAnimations = newClips.ToArray();
  43. EditorUtility.SetDirty(Selection.activeObject);
  44. }
  45. }

链接

unity工程链接:https://github.com/badforlabor/test2 中的auto_split_animation





自动切割fbx中的动画

标签:

原文地址:http://www.cnblogs.com/badforlabor/p/5458814.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!