mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-11-12 23:32:42 -05:00
[CLI] Add Animator export
This commit is contained in:
@ -79,6 +79,18 @@ namespace AssetStudio
|
|||||||
{
|
{
|
||||||
filteredAssetTypesList.Add(ClassIDType.AnimatorController);
|
filteredAssetTypesList.Add(ClassIDType.AnimatorController);
|
||||||
filteredAssetTypesList.Add(ClassIDType.AnimatorOverrideController);
|
filteredAssetTypesList.Add(ClassIDType.AnimatorOverrideController);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.Animation);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.AnimationClip);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.Avatar);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.Material);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.MeshFilter);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.MeshRenderer);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.SkinnedMeshRenderer);
|
||||||
|
}
|
||||||
|
if (classIDTypes.Contains(ClassIDType.AnimatorController))
|
||||||
|
{
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.Animator);
|
||||||
|
filteredAssetTypesList.Add(ClassIDType.AnimatorOverrideController);
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredAssetTypesList.UnionWith(classIDTypes);
|
filteredAssetTypesList.UnionWith(classIDTypes);
|
||||||
|
|||||||
@ -10,199 +10,6 @@ namespace AssetStudioCLI
|
|||||||
{
|
{
|
||||||
internal static class Exporter
|
internal static class Exporter
|
||||||
{
|
{
|
||||||
public static bool ExportVideoClip(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
var m_VideoClip = (VideoClip)item.Asset;
|
|
||||||
if (m_VideoClip.m_ExternalResources.m_Size > 0)
|
|
||||||
{
|
|
||||||
if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}");
|
|
||||||
sb.AppendLine($"VideoClip width: {m_VideoClip.Width}");
|
|
||||||
sb.AppendLine($"VideoClip height: {m_VideoClip.Height}");
|
|
||||||
sb.AppendLine($"VideoClip frame rate: {m_VideoClip.m_FrameRate:.0##}");
|
|
||||||
sb.AppendLine($"VideoClip split alpha: {m_VideoClip.m_HasSplitAlpha}");
|
|
||||||
Logger.Debug(sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_VideoClip.m_VideoData.WriteData(exportFullPath);
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportMovieTexture(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
var m_MovieTexture = (MovieTexture)item.Asset;
|
|
||||||
if (!TryExportFile(exportPath, item, ".ogv", out var exportFullPath))
|
|
||||||
return false;
|
|
||||||
File.WriteAllBytes(exportFullPath, m_MovieTexture.m_MovieData);
|
|
||||||
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportShader(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath))
|
|
||||||
return false;
|
|
||||||
var m_Shader = (Shader)item.Asset;
|
|
||||||
var str = m_Shader.Convert();
|
|
||||||
File.WriteAllText(exportFullPath, str);
|
|
||||||
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportTextAsset(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
var m_TextAsset = (TextAsset)item.Asset;
|
|
||||||
var extension = ".txt";
|
|
||||||
var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
|
|
||||||
if (!CLIOptions.f_notRestoreExtensionName.Value)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(assetExtension))
|
|
||||||
{
|
|
||||||
extension = "";
|
|
||||||
}
|
|
||||||
else if (!string.IsNullOrEmpty(item.Container))
|
|
||||||
{
|
|
||||||
var ext = Path.GetExtension(item.Container);
|
|
||||||
if (!string.IsNullOrEmpty(item.Container))
|
|
||||||
{
|
|
||||||
extension = ext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
|
|
||||||
return false;
|
|
||||||
File.WriteAllBytes(exportFullPath, m_TextAsset.m_Script);
|
|
||||||
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportMonoBehaviour(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
|
|
||||||
return false;
|
|
||||||
var m_MonoBehaviour = (MonoBehaviour)item.Asset;
|
|
||||||
var type = m_MonoBehaviour.ToType();
|
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
|
|
||||||
type = m_MonoBehaviour.ToType(m_Type);
|
|
||||||
}
|
|
||||||
if (type != null)
|
|
||||||
{
|
|
||||||
var str = JsonConvert.SerializeObject(type, Formatting.Indented);
|
|
||||||
File.WriteAllText(exportFullPath, str);
|
|
||||||
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportFont(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
var m_Font = (Font)item.Asset;
|
|
||||||
if (m_Font.m_FontData != null)
|
|
||||||
{
|
|
||||||
var extension = ".ttf";
|
|
||||||
if (m_Font.m_FontData[0] == 79 && m_Font.m_FontData[1] == 84 && m_Font.m_FontData[2] == 84 && m_Font.m_FontData[3] == 79)
|
|
||||||
{
|
|
||||||
extension = ".otf";
|
|
||||||
}
|
|
||||||
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
|
|
||||||
return false;
|
|
||||||
File.WriteAllBytes(exportFullPath, m_Font.m_FontData);
|
|
||||||
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
|
|
||||||
{
|
|
||||||
var convert = animationList != null
|
|
||||||
? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
|
|
||||||
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
|
|
||||||
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
|
|
||||||
ExportFbx(convert, exportPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ExportFbx(IImported convert, string exportPath)
|
|
||||||
{
|
|
||||||
var fbxSettings = new Fbx.Settings
|
|
||||||
{
|
|
||||||
BoneSize = CLIOptions.o_fbxBoneSize.Value,
|
|
||||||
ScaleFactor = CLIOptions.o_fbxScaleFactor.Value,
|
|
||||||
ExportAllUvsAsDiffuseMaps = CLIOptions.f_fbxUvsAsDiffuseMaps.Value,
|
|
||||||
};
|
|
||||||
ModelExporter.ExportFbx(exportPath, convert, fbxSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportRawFile(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath, mode: "ExportRaw"))
|
|
||||||
return false;
|
|
||||||
switch (item.Asset)
|
|
||||||
{
|
|
||||||
case Texture2D m_Texture2D:
|
|
||||||
if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path))
|
|
||||||
{
|
|
||||||
m_Texture2D.image_data.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AudioClip m_AudioClip:
|
|
||||||
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
|
|
||||||
{
|
|
||||||
m_AudioClip.m_AudioData.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VideoClip m_VideoClip:
|
|
||||||
if (!string.IsNullOrEmpty(m_VideoClip.m_ExternalResources.m_Source))
|
|
||||||
{
|
|
||||||
m_VideoClip.m_VideoData.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
|
|
||||||
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ExportDumpFile(AssetItem item, string exportPath)
|
|
||||||
{
|
|
||||||
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath, mode: "Dump"))
|
|
||||||
return false;
|
|
||||||
var str = item.Asset.Dump();
|
|
||||||
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour)
|
|
||||||
{
|
|
||||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
|
|
||||||
str = m_MonoBehaviour.Dump(m_Type);
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(str))
|
|
||||||
{
|
|
||||||
str = item.Asset.DumpObject();
|
|
||||||
}
|
|
||||||
if (str != null)
|
|
||||||
{
|
|
||||||
File.WriteAllText(exportFullPath, str);
|
|
||||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" saved to \"{exportFullPath}\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string mode = "Export")
|
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string mode = "Export")
|
||||||
{
|
{
|
||||||
var fileName = FixFileName(item.Text);
|
var fileName = FixFileName(item.Text);
|
||||||
@ -235,6 +42,125 @@ namespace AssetStudioCLI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool ExportVideoClip(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
var m_VideoClip = (VideoClip)item.Asset;
|
||||||
|
if (m_VideoClip.m_ExternalResources.m_Size > 0)
|
||||||
|
{
|
||||||
|
if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}");
|
||||||
|
sb.AppendLine($"VideoClip width: {m_VideoClip.Width}");
|
||||||
|
sb.AppendLine($"VideoClip height: {m_VideoClip.Height}");
|
||||||
|
sb.AppendLine($"VideoClip frame rate: {m_VideoClip.m_FrameRate:.0##}");
|
||||||
|
sb.AppendLine($"VideoClip split alpha: {m_VideoClip.m_HasSplitAlpha}");
|
||||||
|
Logger.Debug(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_VideoClip.m_VideoData.WriteData(exportFullPath);
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ExportMovieTexture(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
var m_MovieTexture = (MovieTexture)item.Asset;
|
||||||
|
if (!TryExportFile(exportPath, item, ".ogv", out var exportFullPath))
|
||||||
|
return false;
|
||||||
|
File.WriteAllBytes(exportFullPath, m_MovieTexture.m_MovieData);
|
||||||
|
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ExportShader(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath))
|
||||||
|
return false;
|
||||||
|
var m_Shader = (Shader)item.Asset;
|
||||||
|
var str = m_Shader.Convert();
|
||||||
|
File.WriteAllText(exportFullPath, str);
|
||||||
|
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ExportTextAsset(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
var m_TextAsset = (TextAsset)item.Asset;
|
||||||
|
var extension = ".txt";
|
||||||
|
var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
|
||||||
|
if (!CLIOptions.f_notRestoreExtensionName.Value)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(assetExtension))
|
||||||
|
{
|
||||||
|
extension = "";
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(item.Container))
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension(item.Container);
|
||||||
|
if (!string.IsNullOrEmpty(item.Container))
|
||||||
|
{
|
||||||
|
extension = ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
|
||||||
|
return false;
|
||||||
|
File.WriteAllBytes(exportFullPath, m_TextAsset.m_Script);
|
||||||
|
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ExportMonoBehaviour(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
|
||||||
|
return false;
|
||||||
|
var m_MonoBehaviour = (MonoBehaviour)item.Asset;
|
||||||
|
var type = m_MonoBehaviour.ToType();
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
|
||||||
|
type = m_MonoBehaviour.ToType(m_Type);
|
||||||
|
}
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
var str = JsonConvert.SerializeObject(type, Formatting.Indented);
|
||||||
|
File.WriteAllText(exportFullPath, str);
|
||||||
|
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ExportFont(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
var m_Font = (Font)item.Asset;
|
||||||
|
if (m_Font.m_FontData != null)
|
||||||
|
{
|
||||||
|
var extension = ".ttf";
|
||||||
|
if (m_Font.m_FontData[0] == 79 && m_Font.m_FontData[1] == 84 && m_Font.m_FontData[2] == 84 && m_Font.m_FontData[3] == 79)
|
||||||
|
{
|
||||||
|
extension = ".otf";
|
||||||
|
}
|
||||||
|
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
|
||||||
|
return false;
|
||||||
|
File.WriteAllBytes(exportFullPath, m_Font.m_FontData);
|
||||||
|
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ExportMesh(AssetItem item, string exportPath)
|
private static bool ExportMesh(AssetItem item, string exportPath)
|
||||||
{
|
{
|
||||||
var m_Mesh = (Mesh)item.Asset;
|
var m_Mesh = (Mesh)item.Asset;
|
||||||
@ -332,6 +258,87 @@ namespace AssetStudioCLI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null)
|
||||||
|
{
|
||||||
|
var exportFullPath = Path.Combine(exportPath, "FBX_Animator", item.Text, item.Text + ".fbx");
|
||||||
|
if (File.Exists(exportFullPath))
|
||||||
|
{
|
||||||
|
exportFullPath = Path.Combine(exportPath, item.Text + item.UniqueID, item.Text + ".fbx");
|
||||||
|
}
|
||||||
|
var m_Animator = (Animator)item.Asset;
|
||||||
|
var convert = animationList != null
|
||||||
|
? new ModelConverter(m_Animator, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
|
||||||
|
: new ModelConverter(m_Animator, CLIOptions.o_imageFormat.Value);
|
||||||
|
ExportFbx(convert, exportFullPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExportFbx(IImported convert, string exportPath)
|
||||||
|
{
|
||||||
|
var fbxSettings = new Fbx.Settings
|
||||||
|
{
|
||||||
|
BoneSize = CLIOptions.o_fbxBoneSize.Value,
|
||||||
|
ScaleFactor = CLIOptions.o_fbxScaleFactor.Value,
|
||||||
|
ExportAllUvsAsDiffuseMaps = CLIOptions.f_fbxUvsAsDiffuseMaps.Value,
|
||||||
|
ExportAnimations = CLIOptions.o_fbxAnimMode.Value != AnimationExportMode.Skip,
|
||||||
|
};
|
||||||
|
ModelExporter.ExportFbx(exportPath, convert, fbxSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ExportRawFile(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath, mode: "ExportRaw"))
|
||||||
|
return false;
|
||||||
|
switch (item.Asset)
|
||||||
|
{
|
||||||
|
case Texture2D m_Texture2D:
|
||||||
|
if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path))
|
||||||
|
{
|
||||||
|
m_Texture2D.image_data.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AudioClip m_AudioClip:
|
||||||
|
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
|
||||||
|
{
|
||||||
|
m_AudioClip.m_AudioData.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VideoClip m_VideoClip:
|
||||||
|
if (!string.IsNullOrEmpty(m_VideoClip.m_ExternalResources.m_Source))
|
||||||
|
{
|
||||||
|
m_VideoClip.m_VideoData.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
|
||||||
|
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ExportDumpFile(AssetItem item, string exportPath)
|
||||||
|
{
|
||||||
|
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath, mode: "Dump"))
|
||||||
|
return false;
|
||||||
|
var str = item.Asset.Dump();
|
||||||
|
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour)
|
||||||
|
{
|
||||||
|
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
|
||||||
|
str = m_MonoBehaviour.Dump(m_Type);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
{
|
||||||
|
str = item.Asset.DumpObject();
|
||||||
|
}
|
||||||
|
if (str != null)
|
||||||
|
{
|
||||||
|
File.WriteAllText(exportFullPath, str);
|
||||||
|
Logger.Debug($"{item.TypeString} \"{item.Text}\" saved to \"{exportFullPath}\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool ExportConvertFile(AssetItem item, string exportPath)
|
public static bool ExportConvertFile(AssetItem item, string exportPath)
|
||||||
{
|
{
|
||||||
switch (item.Type)
|
switch (item.Type)
|
||||||
@ -360,6 +367,15 @@ namespace AssetStudioCLI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
|
||||||
|
{
|
||||||
|
var convert = animationList != null
|
||||||
|
? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
|
||||||
|
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
|
||||||
|
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
|
||||||
|
ExportFbx(convert, exportPath);
|
||||||
|
}
|
||||||
|
|
||||||
public static string FixFileName(string str)
|
public static string FixFileName(string str)
|
||||||
{
|
{
|
||||||
return str.Length >= 260
|
return str.Length >= 260
|
||||||
|
|||||||
@ -27,6 +27,7 @@ namespace AssetStudioCLI.Options
|
|||||||
Info,
|
Info,
|
||||||
Live2D,
|
Live2D,
|
||||||
SplitObjects,
|
SplitObjects,
|
||||||
|
Animator,
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum AssetGroupOption
|
internal enum AssetGroupOption
|
||||||
@ -68,6 +69,13 @@ namespace AssetStudioCLI.Options
|
|||||||
NameAndContainer,
|
NameAndContainer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum AnimationExportMode
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
Skip,
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
internal static class CLIOptions
|
internal static class CLIOptions
|
||||||
{
|
{
|
||||||
public static bool isParsed;
|
public static bool isParsed;
|
||||||
@ -102,6 +110,7 @@ namespace AssetStudioCLI.Options
|
|||||||
//fbx
|
//fbx
|
||||||
public static Option<float> o_fbxScaleFactor;
|
public static Option<float> o_fbxScaleFactor;
|
||||||
public static Option<int> o_fbxBoneSize;
|
public static Option<int> o_fbxBoneSize;
|
||||||
|
public static Option<AnimationExportMode> o_fbxAnimMode;
|
||||||
public static Option<bool> f_fbxUvsAsDiffuseMaps;
|
public static Option<bool> f_fbxUvsAsDiffuseMaps;
|
||||||
//filter
|
//filter
|
||||||
public static Option<List<string>> o_filterByName;
|
public static Option<List<string>> o_filterByName;
|
||||||
@ -186,14 +195,15 @@ namespace AssetStudioCLI.Options
|
|||||||
optionDefaultValue: WorkMode.Export,
|
optionDefaultValue: WorkMode.Export,
|
||||||
optionName: "-m, --mode <value>",
|
optionName: "-m, --mode <value>",
|
||||||
optionDescription: "Specify working mode\n" +
|
optionDescription: "Specify working mode\n" +
|
||||||
"<Value: extract | export(default) | exportRaw | dump | info | live2d | splitObjects>\n" +
|
"<Value: extract | export(default) | exportRaw | dump | info | live2d |\nsplitObjects | animator>\n" +
|
||||||
"Extract - Extracts(Decompresses) asset bundles\n" +
|
"Extract - Extract(Decompress) asset bundles\n" +
|
||||||
"Export - Exports converted assets\n" +
|
"Export - Convert and export assets\n" +
|
||||||
"ExportRaw - Exports raw data\n" +
|
"ExportRaw - Export raw assets\n" +
|
||||||
"Dump - Makes asset dumps\n" +
|
"Dump - Generate json dumps of loaded asset\n" +
|
||||||
"Info - Loads file(s), shows the number of available for export assets and exits\n" +
|
"Info - Load file(s) and show the number of available for export assets\n" +
|
||||||
"Live2D - Exports Live2D Cubism models\n" +
|
"Live2D - Export Live2D Cubism models\n" +
|
||||||
"SplitObjects - Exports split objects (fbx)\n",
|
"SplitObjects - Export all model objects (split) (fbx)\n" +
|
||||||
|
"Animator - Export Animator assets (fbx)\n",
|
||||||
optionExample: "Example: \"-m info\"\n",
|
optionExample: "Example: \"-m info\"\n",
|
||||||
optionHelpGroup: HelpGroups.General
|
optionHelpGroup: HelpGroups.General
|
||||||
);
|
);
|
||||||
@ -368,6 +378,18 @@ namespace AssetStudioCLI.Options
|
|||||||
optionExample: "Example: \"--fbx-bone-size 10\"\n",
|
optionExample: "Example: \"--fbx-bone-size 10\"\n",
|
||||||
optionHelpGroup: HelpGroups.FBX
|
optionHelpGroup: HelpGroups.FBX
|
||||||
);
|
);
|
||||||
|
o_fbxAnimMode = new GroupedOption<AnimationExportMode>
|
||||||
|
(
|
||||||
|
optionDefaultValue: AnimationExportMode.Auto,
|
||||||
|
optionName: "--fbx-animation",
|
||||||
|
optionDescription: "Specify the FBX animation export mode\n" +
|
||||||
|
"<Value: auto(default) | skip | all>\n" +
|
||||||
|
"Auto - Search for model-related animations and export model with them\n" +
|
||||||
|
"Skip - Don't export animations\n" +
|
||||||
|
"All - Try to bind all loaded animations to each loaded model\n",
|
||||||
|
optionExample: "--fbx-animation skip\n",
|
||||||
|
optionHelpGroup: HelpGroups.FBX
|
||||||
|
);
|
||||||
f_fbxUvsAsDiffuseMaps = new GroupedOption<bool>
|
f_fbxUvsAsDiffuseMaps = new GroupedOption<bool>
|
||||||
(
|
(
|
||||||
optionDefaultValue: false,
|
optionDefaultValue: false,
|
||||||
@ -613,22 +635,23 @@ namespace AssetStudioCLI.Options
|
|||||||
o_workMode.Value = WorkMode.Live2D;
|
o_workMode.Value = WorkMode.Live2D;
|
||||||
o_exportAssetTypes.Value = new List<ClassIDType>
|
o_exportAssetTypes.Value = new List<ClassIDType>
|
||||||
{
|
{
|
||||||
|
ClassIDType.Animation,
|
||||||
ClassIDType.AnimationClip,
|
ClassIDType.AnimationClip,
|
||||||
ClassIDType.Animator,
|
ClassIDType.AnimatorController,
|
||||||
ClassIDType.MonoBehaviour,
|
ClassIDType.MonoBehaviour,
|
||||||
ClassIDType.Texture2D,
|
ClassIDType.Texture2D,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case "animator":
|
||||||
case "splitobjects":
|
case "splitobjects":
|
||||||
o_workMode.Value = WorkMode.SplitObjects;
|
o_workMode.Value = value.ToLower() == "animator"
|
||||||
|
? WorkMode.Animator
|
||||||
|
: WorkMode.SplitObjects;
|
||||||
o_exportAssetTypes.Value = new List<ClassIDType>
|
o_exportAssetTypes.Value = new List<ClassIDType>
|
||||||
{
|
{
|
||||||
ClassIDType.Texture2D,
|
ClassIDType.Animator,
|
||||||
ClassIDType.Material,
|
|
||||||
ClassIDType.Mesh,
|
ClassIDType.Mesh,
|
||||||
ClassIDType.MeshRenderer,
|
ClassIDType.Texture2D,
|
||||||
ClassIDType.MeshFilter,
|
|
||||||
ClassIDType.SkinnedMeshRenderer,
|
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -719,13 +742,19 @@ namespace AssetStudioCLI.Options
|
|||||||
{
|
{
|
||||||
case "-t":
|
case "-t":
|
||||||
case "--asset-type":
|
case "--asset-type":
|
||||||
if (o_workMode.Value == WorkMode.Live2D || o_workMode.Value == WorkMode.SplitObjects)
|
if (o_workMode.Value == WorkMode.Live2D || o_workMode.Value == WorkMode.SplitObjects || o_workMode.Value == WorkMode.Animator)
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var splittedTypes = ValueSplitter(value);
|
var splittedTypes = ValueSplitter(value);
|
||||||
o_exportAssetTypes.Value = new List<ClassIDType>();
|
o_exportAssetTypes.Value = new List<ClassIDType>();
|
||||||
|
if (splittedTypes.Contains("all", StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
o_exportAssetTypes.Value = exportableAssetTypes;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foreach (var type in splittedTypes)
|
foreach (var type in splittedTypes)
|
||||||
{
|
{
|
||||||
switch (type.ToLower())
|
switch (type.ToLower())
|
||||||
@ -742,8 +771,14 @@ namespace AssetStudioCLI.Options
|
|||||||
case "video":
|
case "video":
|
||||||
o_exportAssetTypes.Value.Add(ClassIDType.VideoClip);
|
o_exportAssetTypes.Value.Add(ClassIDType.VideoClip);
|
||||||
break;
|
break;
|
||||||
case "all":
|
case "animator":
|
||||||
o_exportAssetTypes.Value = exportableAssetTypes;
|
if (o_workMode.Value == WorkMode.Export)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{"Not supported in current mode".Color(brightYellow)}. To export Animator assets use \"Animator mode\".\n");
|
||||||
|
ShowOptionDescription(o_workMode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
o_exportAssetTypes.Value.Add(ClassIDType.Animator);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
var isKnownType = knownAssetTypesDict.TryGetValue(type.ToLower(), out var assetType);
|
var isKnownType = knownAssetTypesDict.TryGetValue(type.ToLower(), out var assetType);
|
||||||
@ -1007,6 +1042,24 @@ namespace AssetStudioCLI.Options
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "--fbx-animation":
|
||||||
|
switch (value.ToLower())
|
||||||
|
{
|
||||||
|
case "auto":
|
||||||
|
o_fbxAnimMode.Value = AnimationExportMode.Auto;
|
||||||
|
break;
|
||||||
|
case "skip":
|
||||||
|
o_fbxAnimMode.Value = AnimationExportMode.Skip;
|
||||||
|
break;
|
||||||
|
case "all":
|
||||||
|
o_fbxAnimMode.Value = AnimationExportMode.All;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option.Color(brightYellow)}] option. Unsupported animation export mode: [{value.Color(brightRed)}].\n");
|
||||||
|
ShowOptionDescription(o_fbxAnimMode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "--blockinfo-comp":
|
case "--blockinfo-comp":
|
||||||
switch (value.ToLower())
|
switch (value.ToLower())
|
||||||
{
|
{
|
||||||
@ -1357,15 +1410,17 @@ namespace AssetStudioCLI.Options
|
|||||||
break;
|
break;
|
||||||
case WorkMode.Live2D:
|
case WorkMode.Live2D:
|
||||||
case WorkMode.SplitObjects:
|
case WorkMode.SplitObjects:
|
||||||
|
case WorkMode.Animator:
|
||||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||||
if (o_workMode.Value == WorkMode.SplitObjects)
|
if (o_workMode.Value == WorkMode.SplitObjects || o_workMode.Value == WorkMode.Animator)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
||||||
sb.AppendLine($"# Filter by Name(s): \"{string.Join("\", \"", o_filterByName.Value)}\"");
|
sb.AppendLine($"# Filter by Name(s): \"{string.Join("\", \"", o_filterByName.Value)}\"");
|
||||||
sb.AppendLine($"# FBX Scale Factor: {o_fbxScaleFactor}");
|
sb.AppendLine($"# FBX Scale Factor: {o_fbxScaleFactor}");
|
||||||
sb.AppendLine($"# FBX Bone Size: {o_fbxBoneSize}");
|
sb.AppendLine($"# FBX Bone Size: {o_fbxBoneSize}");
|
||||||
|
sb.AppendLine($"# FBX Animation Mode: {o_fbxAnimMode}");
|
||||||
sb.AppendLine($"# FBX UVs as Diffuse Maps: {f_fbxUvsAsDiffuseMaps}");
|
sb.AppendLine($"# FBX UVs as Diffuse Maps: {f_fbxUvsAsDiffuseMaps}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@ -58,6 +58,9 @@ namespace AssetStudioCLI
|
|||||||
case WorkMode.SplitObjects:
|
case WorkMode.SplitObjects:
|
||||||
Studio.ExportSplitObjects();
|
Studio.ExportSplitObjects();
|
||||||
break;
|
break;
|
||||||
|
case WorkMode.Animator:
|
||||||
|
Studio.ExportAnimator();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Studio.ExportAssets();
|
Studio.ExportAssets();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -526,6 +526,7 @@ namespace AssetStudioCLI
|
|||||||
{
|
{
|
||||||
case WorkMode.Live2D:
|
case WorkMode.Live2D:
|
||||||
case WorkMode.SplitObjects:
|
case WorkMode.SplitObjects:
|
||||||
|
case WorkMode.Animator:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
FilterAssets();
|
FilterAssets();
|
||||||
@ -917,6 +918,62 @@ namespace AssetStudioCLI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ExportAnimator()
|
||||||
|
{
|
||||||
|
var animationList = CLIOptions.o_fbxAnimMode.Value == AnimationExportMode.Auto
|
||||||
|
? null
|
||||||
|
: new List<AssetItem>();
|
||||||
|
var exportAllAnimations = CLIOptions.o_fbxAnimMode.Value == AnimationExportMode.All;
|
||||||
|
|
||||||
|
Logger.Info("Searching for Animator assets...");
|
||||||
|
var animatorList = new List<AssetItem>();
|
||||||
|
foreach (var asset in parsedAssetsList)
|
||||||
|
{
|
||||||
|
switch (asset.Type)
|
||||||
|
{
|
||||||
|
case ClassIDType.Animator:
|
||||||
|
animatorList.Add(asset);
|
||||||
|
break;
|
||||||
|
case ClassIDType.AnimationClip when exportAllAnimations:
|
||||||
|
animationList?.Add(asset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsedAssetsList = animatorList;
|
||||||
|
Logger.Info($"Found {parsedAssetsList.Count} exportable Animator asset(s).");
|
||||||
|
if (parsedAssetsList.Count > 0 && CLIOptions.filterBy != FilterBy.None)
|
||||||
|
{
|
||||||
|
FilterAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
var savePath = CLIOptions.o_outputFolder.Value;
|
||||||
|
var toExportCount = parsedAssetsList.Count;
|
||||||
|
var exportedCount = 0;
|
||||||
|
Progress.Reset();
|
||||||
|
foreach (var asset in parsedAssetsList)
|
||||||
|
{
|
||||||
|
var isExported = false;
|
||||||
|
Logger.Info($"[{exportedCount + 1}/{toExportCount}] Exporting \"{asset.Text}\"...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Debug($"Animator Export: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||||
|
isExported = Exporter.ExportAnimator(asset, savePath, animationList);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error($"{asset.SourceFile.originalPath ?? asset.SourceFile.fullName}: [{$"{asset.Type}: {asset.Text}".Color(Ansi.BrightRed)}] : Export error\n{ex}");
|
||||||
|
}
|
||||||
|
if (isExported)
|
||||||
|
{
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var status = exportedCount > 0
|
||||||
|
? $"Finished exporting [{exportedCount}/{toExportCount}] Animator asset(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\""
|
||||||
|
: "Nothing exported";
|
||||||
|
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryGetCubismMoc(MonoBehaviour m_MonoBehaviour, out MonoBehaviour mocMono)
|
private static bool TryGetCubismMoc(MonoBehaviour m_MonoBehaviour, out MonoBehaviour mocMono)
|
||||||
{
|
{
|
||||||
mocMono = null;
|
mocMono = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user