148 lines
5.2 KiB
C#
148 lines
5.2 KiB
C#
#if UNITY_EDITOR
|
|
using System.IO;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
public static class FixFbxClipNames
|
|
{
|
|
// -------- Helpers --------
|
|
static string Sanitize(string s)
|
|
{
|
|
foreach (var c in Path.GetInvalidFileNameChars()) s = s.Replace(c, '_');
|
|
return s.Replace(' ', '_');
|
|
}
|
|
|
|
static string GetFBXName(string assetPath) =>
|
|
Path.GetFileNameWithoutExtension(assetPath);
|
|
|
|
static ModelImporterClipAnimation CloneClip(ModelImporterClipAnimation src)
|
|
{
|
|
return new ModelImporterClipAnimation
|
|
{
|
|
name = src.name,
|
|
firstFrame = src.firstFrame,
|
|
lastFrame = src.lastFrame,
|
|
loopTime = src.loopTime,
|
|
loopPose = src.loopPose,
|
|
lockRootRotation = src.lockRootRotation,
|
|
lockRootHeightY = src.lockRootHeightY,
|
|
lockRootPositionXZ = src.lockRootPositionXZ,
|
|
keepOriginalOrientation = src.keepOriginalOrientation,
|
|
keepOriginalPositionXZ = src.keepOriginalPositionXZ,
|
|
keepOriginalPositionY = src.keepOriginalPositionY,
|
|
heightFromFeet = src.heightFromFeet,
|
|
mirror = src.mirror,
|
|
additiveReferencePoseFrame = src.additiveReferencePoseFrame,
|
|
maskType = src.maskType,
|
|
hasAdditiveReferencePose = src.hasAdditiveReferencePose,
|
|
};
|
|
}
|
|
|
|
// -------- 1) Rename in importer --------
|
|
[MenuItem("Tools/Animations/Fix FBX Clip Names (Rename in Importer)")]
|
|
public static void FixInImporter()
|
|
{
|
|
var guids = Selection.assetGUIDs.Length > 0
|
|
? Selection.assetGUIDs
|
|
: AssetDatabase.FindAssets("t:Model");
|
|
|
|
int changedAssets = 0, changedClips = 0;
|
|
|
|
foreach (var guid in guids)
|
|
{
|
|
var path = AssetDatabase.GUIDToAssetPath(guid);
|
|
if (!path.EndsWith(".fbx", System.StringComparison.OrdinalIgnoreCase)) continue;
|
|
|
|
var importer = AssetImporter.GetAtPath(path) as ModelImporter;
|
|
if (importer == null) continue;
|
|
|
|
var clips = importer.clipAnimations;
|
|
if (clips == null || clips.Length == 0)
|
|
clips = importer.defaultClipAnimations;
|
|
|
|
if (clips == null || clips.Length == 0) continue;
|
|
|
|
string fbxName = Sanitize(GetFBXName(path));
|
|
bool anyChange = false;
|
|
|
|
var newClips = clips.Select(c =>
|
|
{
|
|
var nc = CloneClip(c);
|
|
string old = string.IsNullOrEmpty(c.name) ? "Clip" : c.name;
|
|
string newName = (clips.Length == 1) ? fbxName : $"{fbxName}_{Sanitize(old)}";
|
|
if (nc.name != newName)
|
|
{
|
|
nc.name = newName;
|
|
anyChange = true;
|
|
changedClips++;
|
|
}
|
|
return nc;
|
|
}).ToArray();
|
|
|
|
if (anyChange)
|
|
{
|
|
importer.clipAnimations = newClips; // write custom list
|
|
try
|
|
{
|
|
importer.SaveAndReimport();
|
|
changedAssets++;
|
|
Debug.Log($"[FixFbxClipNames] Renamed clips in: {path}");
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"[FixFbxClipNames] Failed to reimport {path}: {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
Debug.Log($"[FixFbxClipNames] Done. Assets changed: {changedAssets}, Clips renamed: {changedClips}");
|
|
}
|
|
|
|
// -------- 2) Extract .anim copies (optional) --------
|
|
[MenuItem("Tools/Animations/Extract .anim Copies With Clean Names")]
|
|
public static void ExtractAnimCopies()
|
|
{
|
|
var guids = Selection.assetGUIDs.Length > 0
|
|
? Selection.assetGUIDs
|
|
: AssetDatabase.FindAssets("t:Model");
|
|
|
|
int created = 0;
|
|
|
|
foreach (var guid in guids)
|
|
{
|
|
var path = AssetDatabase.GUIDToAssetPath(guid);
|
|
if (!path.EndsWith(".fbx", System.StringComparison.OrdinalIgnoreCase)) continue;
|
|
|
|
string fbxName = Sanitize(GetFBXName(path));
|
|
string folder = Path.GetDirectoryName(path).Replace('\\', '/');
|
|
string outDir = folder + "/Animations_Extracted";
|
|
if (!AssetDatabase.IsValidFolder(outDir))
|
|
AssetDatabase.CreateFolder(folder, "Animations_Extracted");
|
|
|
|
var subAssets = AssetDatabase.LoadAllAssetsAtPath(path);
|
|
var clips = subAssets.OfType<AnimationClip>()
|
|
.Where(c => c != null && c.name != "__preview__")
|
|
.ToArray();
|
|
|
|
foreach (var clip in clips)
|
|
{
|
|
string baseName = (clips.Length == 1) ? fbxName : $"{fbxName}_{Sanitize(clip.name)}";
|
|
string outPath = $"{outDir}/{baseName}.anim";
|
|
|
|
if (File.Exists(outPath)) continue;
|
|
|
|
var dup = new AnimationClip();
|
|
EditorUtility.CopySerialized(clip, dup);
|
|
AssetDatabase.CreateAsset(dup, outPath);
|
|
created++;
|
|
Debug.Log($"[Extract] {outPath}");
|
|
}
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
Debug.Log($"[FixFbxClipNames] Extracted {created} .anim files.");
|
|
}
|
|
}
|
|
#endif
|