[GUI] Some fixes for animation export

- Disabled animation converting if animation export is disabled in the options
- Fixed a bug with ignoring animation selection order when exporting models with selected animationClips via the “Model” tab
This commit is contained in:
VaDiM
2025-08-10 02:23:22 +03:00
parent 054906a426
commit be11fdf14f
6 changed files with 82 additions and 79 deletions

View File

@@ -267,7 +267,7 @@ namespace AssetStudioCLI
}
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, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(m_Animator, CLIOptions.o_imageFormat.Value);
ExportFbx(convert, exportFullPath);
return true;
@@ -370,7 +370,7 @@ 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, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
var modelName = FixFileName(gameObject.m_Name);
var exportFullPath = Path.Combine(exportPath, "FBX_GameObjects", modelName, modelName + ".fbx");

View File

@@ -150,10 +150,7 @@ namespace AssetStudioGUI
assetsManager.Options.BundleOptions.DecompressToDisk = Properties.Settings.Default.decompressToDisk;
FMODinit();
listSearchFilterMode.SelectedIndex = 0;
if (string.IsNullOrEmpty(Properties.Settings.Default.fbxSettings))
{
FBXinitOptions();
}
FbxInitOptions(Properties.Settings.Default.fbxSettings);
logger = new GUILogger(StatusStripUpdate);
Logger.Default = logger;
@@ -1700,26 +1697,18 @@ namespace AssetStudioGUI
private void exportAnimatorWithAnimationClipMenuItem_Click(object sender, EventArgs e)
{
AssetItem animator = null;
var selectedAssets = GetSelectedAssets();
foreach (var assetPreloadData in selectedAssets)
{
if (assetPreloadData.Type == ClassIDType.Animator)
{
animator = assetPreloadData;
}
}
var animator = selectedAssets.FirstOrDefault(x => x.Type == ClassIDType.Animator);
if (animator == null)
return;
if (animator != null)
var saveFolderDialog = new OpenFolderDialog();
saveFolderDialog.InitialFolder = saveDirectoryBackup;
if (saveFolderDialog.ShowDialog(this) == DialogResult.OK)
{
var saveFolderDialog = new OpenFolderDialog();
saveFolderDialog.InitialFolder = saveDirectoryBackup;
if (saveFolderDialog.ShowDialog(this) == DialogResult.OK)
{
saveDirectoryBackup = saveFolderDialog.Folder;
var exportPath = Path.Combine(saveFolderDialog.Folder, "Animator") + Path.DirectorySeparatorChar;
ExportAnimatorWithAnimationClip(animator, selectedAnimationAssetsList, exportPath);
}
saveDirectoryBackup = saveFolderDialog.Folder;
var exportPath = Path.Combine(saveFolderDialog.Folder, "Animator") + Path.DirectorySeparatorChar;
ExportAnimatorWithAnimationClip(animator, selectedAnimationAssetsList, exportPath);
}
}
@@ -1744,13 +1733,9 @@ namespace AssetStudioGUI
saveDirectoryBackup = saveFolderDialog.Folder;
var exportPath = Path.Combine(saveFolderDialog.Folder, "GameObject") + Path.DirectorySeparatorChar;
List<AssetItem> animationList = null;
if (animation)
if(animation && selectedAnimationAssetsList.Count > 0)
{
animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList();
if (animationList.Count == 0)
{
animationList = null;
}
animationList = selectedAnimationAssetsList;
}
ExportObjectsWithAnimationClip(exportPath, sceneTreeView.Nodes, animationList);
}
@@ -1789,13 +1774,9 @@ namespace AssetStudioGUI
saveDirectoryBackup = Path.GetDirectoryName(saveFileDialog.FileName);
var exportPath = saveFileDialog.FileName;
List<AssetItem> animationList = null;
if (animation)
if (animation && selectedAnimationAssetsList.Count > 0)
{
animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList();
if (animationList.Count == 0)
{
animationList = null;
}
animationList = selectedAnimationAssetsList;
}
ExportObjectsMergeWithAnimationClip(exportPath, gameObjects, animationList);
}
@@ -2672,10 +2653,18 @@ namespace AssetStudioGUI
Properties.Settings.Default.Save();
}
private void FBXinitOptions()
private static void FbxInitOptions(string base64String)
{
Properties.Settings.Default.fbxSettings = new Fbx.Settings().ToBase64();
Properties.Settings.Default.Save();
if (string.IsNullOrEmpty(base64String))
{
Studio.FbxSettings = new Fbx.Settings();
Properties.Settings.Default.fbxSettings = Studio.FbxSettings.ToBase64();
Properties.Settings.Default.Save();
}
else
{
Studio.FbxSettings = Fbx.Settings.FromBase64(base64String);
}
}
#region FMOD

View File

@@ -1,5 +1,6 @@
using AssetStudio;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
@@ -7,7 +8,7 @@ namespace AssetStudioGUI
{
public partial class ExportOptions : Form
{
private static Fbx.Settings fbxSettings;
private static Dictionary<int, int> uvBindings;
public ExportOptions()
{
@@ -34,8 +35,7 @@ namespace AssetStudioGUI
((RadioButton)l2dMotionExportMethodPanel.Controls.Cast<Control>().First(x => x.AccessibleName == defaultMotionMode)).Checked = true;
l2dForceBezierCheckBox.Checked = Properties.Settings.Default.l2dForceBezier;
fbxSettings = Fbx.Settings.FromBase64(Properties.Settings.Default.fbxSettings);
SetFromFbxSettings();
SetFromFbxSettings(Studio.FbxSettings);
}
private void OKbutton_Click(object sender, EventArgs e)
@@ -58,26 +58,27 @@ namespace AssetStudioGUI
Properties.Settings.Default.l2dMotionMode = (CubismLive2DExtractor.Live2DMotionMode)Enum.Parse(typeof(CubismLive2DExtractor.Live2DMotionMode), checkedMotionMode.AccessibleName);
Properties.Settings.Default.l2dForceBezier = l2dForceBezierCheckBox.Checked;
fbxSettings.EulerFilter = eulerFilter.Checked;
fbxSettings.FilterPrecision = (float)filterPrecision.Value;
fbxSettings.ExportAllNodes = exportAllNodes.Checked;
fbxSettings.ExportSkins = exportSkins.Checked;
fbxSettings.ExportAnimations = exportAnimations.Checked;
fbxSettings.ExportBlendShape = exportBlendShape.Checked;
fbxSettings.CastToBone = castToBone.Checked;
fbxSettings.ExportAllUvsAsDiffuseMaps = exportAllUvsAsDiffuseMaps.Checked;
fbxSettings.BoneSize = (int)boneSize.Value;
fbxSettings.ScaleFactor = (float)scaleFactor.Value;
fbxSettings.FbxVersionIndex = fbxVersion.SelectedIndex;
fbxSettings.FbxFormat = fbxFormat.SelectedIndex;
Studio.FbxSettings.EulerFilter = eulerFilter.Checked;
Studio.FbxSettings.FilterPrecision = (float)filterPrecision.Value;
Studio.FbxSettings.ExportAllNodes = exportAllNodes.Checked;
Studio.FbxSettings.ExportSkins = exportSkins.Checked;
Studio.FbxSettings.ExportAnimations = exportAnimations.Checked;
Studio.FbxSettings.ExportBlendShape = exportBlendShape.Checked;
Studio.FbxSettings.CastToBone = castToBone.Checked;
Studio.FbxSettings.ExportAllUvsAsDiffuseMaps = exportAllUvsAsDiffuseMaps.Checked;
Studio.FbxSettings.BoneSize = (int)boneSize.Value;
Studio.FbxSettings.ScaleFactor = (float)scaleFactor.Value;
Studio.FbxSettings.FbxVersionIndex = fbxVersion.SelectedIndex;
Studio.FbxSettings.FbxFormat = fbxFormat.SelectedIndex;
for (var i = 0; i < uvIndicesCheckedListBox.Items.Count; i++)
{
var isChecked = uvIndicesCheckedListBox.GetItemChecked(i);
var type = fbxSettings.UvBindings[i];
var type = uvBindings[i];
if ((isChecked && type < 0) || (!isChecked && type > 0))
fbxSettings.UvBindings[i] *= -1;
uvBindings[i] *= -1;
}
Properties.Settings.Default.fbxSettings = fbxSettings.ToBase64();
Studio.FbxSettings.UvBindings = uvBindings;
Properties.Settings.Default.fbxSettings = Studio.FbxSettings.ToBase64();
Properties.Settings.Default.Save();
DialogResult = DialogResult.OK;
@@ -100,7 +101,7 @@ namespace AssetStudioGUI
if (exportAllUvsAsDiffuseMaps.Checked)
return;
if (fbxSettings.UvBindings.TryGetValue(uvIndicesCheckedListBox.SelectedIndex, out var uvType))
if (uvBindings.TryGetValue(uvIndicesCheckedListBox.SelectedIndex, out var uvType))
{
uvTypesListBox.SelectedIndex = (int)MathF.Abs(uvType) - 1;
}
@@ -109,7 +110,7 @@ namespace AssetStudioGUI
private void uvTypesListBox_SelectedIndexChanged(object sender, EventArgs e)
{
var selectedUv = uvIndicesCheckedListBox.SelectedIndex;
fbxSettings.UvBindings[selectedUv] = uvTypesListBox.SelectedIndex + 1;
uvBindings[selectedUv] = uvTypesListBox.SelectedIndex + 1;
}
private void exportAllUvsAsDiffuseMaps_CheckedChanged(object sender, EventArgs e)
@@ -118,7 +119,7 @@ namespace AssetStudioGUI
uvIndicesCheckedListBox.Enabled = !exportAllUvsAsDiffuseMaps.Checked;
}
private void SetFromFbxSettings()
private void SetFromFbxSettings(Fbx.Settings fbxSettings)
{
eulerFilter.Checked = fbxSettings.EulerFilter;
filterPrecision.Value = (decimal)fbxSettings.FilterPrecision;
@@ -132,9 +133,10 @@ namespace AssetStudioGUI
scaleFactor.Value = (decimal)fbxSettings.ScaleFactor;
fbxVersion.SelectedIndex = fbxSettings.FbxVersionIndex;
fbxFormat.SelectedIndex = fbxSettings.FbxFormat;
uvBindings = new Dictionary<int, int>(fbxSettings.UvBindings);
for (var i = 0; i < uvIndicesCheckedListBox.Items.Count; i++)
{
var isChecked = fbxSettings.UvBindings[i] > 0;
var isChecked = uvBindings[i] > 0;
uvIndicesCheckedListBox.SetItemChecked(i, isChecked);
}
uvTypesListBox.Enabled = !exportAllUvsAsDiffuseMaps.Checked;
@@ -143,8 +145,7 @@ namespace AssetStudioGUI
private void resetButton_Click(object sender, EventArgs e)
{
fbxSettings.Init();
SetFromFbxSettings();
SetFromFbxSettings(new Fbx.Settings());
uvIndicesCheckedListBox_SelectedIndexChanged(sender, e);
}
}

View File

@@ -222,9 +222,11 @@ namespace AssetStudioGUI
{
exportFullPath = Path.Combine(exportPath, item.Text + item.UniqueID, item.Text + ".fbx");
}
if (!Studio.FbxSettings.ExportAnimations)
animationList = new List<AssetItem>();
var m_Animator = (Animator)item.Asset;
var convert = animationList != null
? new ModelConverter(m_Animator, Properties.Settings.Default.convertType, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
? new ModelConverter(m_Animator, Properties.Settings.Default.convertType, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(m_Animator, Properties.Settings.Default.convertType);
ExportFbx(convert, exportFullPath);
return true;
@@ -232,8 +234,7 @@ namespace AssetStudioGUI
private static void ExportFbx(IImported convert, string exportPath)
{
var fbxSettings = Fbx.Settings.FromBase64(Properties.Settings.Default.fbxSettings);
ModelExporter.ExportFbx(exportPath, convert, fbxSettings);
ModelExporter.ExportFbx(exportPath, convert, Studio.FbxSettings);
}
public static bool ExportRawFile(AssetItem item, string exportPath)
@@ -322,8 +323,10 @@ namespace AssetStudioGUI
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
{
if (!Studio.FbxSettings.ExportAnimations)
animationList = new List<AssetItem>();
var convert = animationList != null
? new ModelConverter(gameObject, Properties.Settings.Default.convertType, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
? new ModelConverter(gameObject, Properties.Settings.Default.convertType, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(gameObject, Properties.Settings.Default.convertType);
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
ExportFbx(convert, exportPath);
@@ -332,8 +335,10 @@ namespace AssetStudioGUI
public static void ExportGameObjectMerge(List<GameObject> gameObject, string exportPath, List<AssetItem> animationList = null)
{
var rootName = Path.GetFileNameWithoutExtension(exportPath);
if (!Studio.FbxSettings.ExportAnimations)
animationList = new List<AssetItem>();
var convert = animationList != null
? new ModelConverter(rootName, gameObject, Properties.Settings.Default.convertType, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
? new ModelConverter(rootName, gameObject, Properties.Settings.Default.convertType, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(rootName, gameObject, Properties.Settings.Default.convertType);
ExportFbx(convert, exportPath);
}

View File

@@ -90,6 +90,7 @@ namespace AssetStudioGUI
public static Dictionary<MonoBehaviour, CubismModel> l2dModelDict = new Dictionary<MonoBehaviour, CubismModel>();
private static Dictionary<Object, string> l2dAssetContainers = new Dictionary<Object, string>();
internal static Action<string> StatusStripUpdate = x => { };
internal static Fbx.Settings FbxSettings;
public static int ExtractFolder(string path, string savePath)
{
@@ -861,7 +862,6 @@ namespace AssetStudioGUI
{
Progress.Reset();
Logger.Info($"Exporting {animator.Text}");
Logger.Debug($"Selected AnimationClip(s):\n\"{string.Join("\"\n\"", animationList.Select(x => x.Text))}\"");
try
{
ExportAnimator(animator, exportPath, animationList);

View File

@@ -23,14 +23,16 @@ namespace AssetStudio
private Dictionary<Transform, ImportedFrame> transformDictionary = new Dictionary<Transform, ImportedFrame>();
Dictionary<uint, string> morphChannelNames = new Dictionary<uint, string>();
private IEqualityComparer<AnimationClip> animationClipEqComparer = new AnimationClip.EqComparer();
private bool collectAnimationClips;
public ModelConverter(GameObject m_GameObject, ImageFormat imageFormat, AnimationClip[] animationList = null)
public ModelConverter(GameObject m_GameObject, ImageFormat imageFormat, List<AnimationClip> animationList = null)
{
collectAnimationClips = animationList == null;
this.imageFormat = imageFormat;
if (m_GameObject.m_Animator != null)
{
InitWithAnimator(m_GameObject.m_Animator);
if (animationList == null)
if (collectAnimationClips)
{
CollectAnimationClip(m_GameObject.m_Animator);
}
@@ -39,20 +41,22 @@ namespace AssetStudio
{
InitWithGameObject(m_GameObject);
}
if (animationList != null)
if (animationList != null && animationList.Count > 0)
{
Logger.Debug($"Selected AnimationClip(s):\n\"{string.Join("\"\n\"", animationList.Select(x => x.m_Name))}\"");
animationClipUniqArray = animationList.Distinct(animationClipEqComparer).ToArray();
}
ConvertAnimations();
}
public ModelConverter(string rootName, List<GameObject> m_GameObjects, ImageFormat imageFormat, AnimationClip[] animationList = null)
public ModelConverter(string rootName, List<GameObject> m_GameObjects, ImageFormat imageFormat, List<AnimationClip> animationList = null)
{
collectAnimationClips = animationList == null;
this.imageFormat = imageFormat;
RootFrame = CreateFrame(rootName, Vector3.Zero, new Quaternion(0, 0, 0, 0), Vector3.One);
foreach (var m_GameObject in m_GameObjects)
{
if (m_GameObject.m_Animator != null && animationList == null)
if (m_GameObject.m_Animator != null && collectAnimationClips)
{
CollectAnimationClip(m_GameObject.m_Animator);
}
@@ -66,23 +70,26 @@ namespace AssetStudio
var m_Transform = m_GameObject.m_Transform;
ConvertMeshRenderer(m_Transform);
}
if (animationList != null)
if (animationList != null && animationList.Count > 0)
{
Logger.Debug($"Selected AnimationClip(s):\n\"{string.Join("\"\n\"", animationList.Select(x => x.m_Name))}\"");
animationClipUniqArray = animationList.Distinct(animationClipEqComparer).ToArray();
}
ConvertAnimations();
}
public ModelConverter(Animator m_Animator, ImageFormat imageFormat, AnimationClip[] animationList = null)
public ModelConverter(Animator m_Animator, ImageFormat imageFormat, List<AnimationClip> animationList = null)
{
collectAnimationClips = animationList == null;
this.imageFormat = imageFormat;
InitWithAnimator(m_Animator);
if (animationList == null)
if (collectAnimationClips)
{
CollectAnimationClip(m_Animator);
}
else
else if (animationList?.Count > 0)
{
Logger.Debug($"Selected AnimationClip(s):\n\"{string.Join("\"\n\"", animationList.Select(x => x.m_Name))}\"");
animationClipUniqArray = animationList.Distinct(animationClipEqComparer).ToArray();
}
ConvertAnimations();
@@ -150,7 +157,7 @@ namespace AssetStudio
ConvertMeshRenderer(m_GameObject.m_SkinnedMeshRenderer);
}
if (m_GameObject.m_Animation != null)
if (m_GameObject.m_Animation != null && collectAnimationClips)
{
var animationList = new List<AnimationClip>();
foreach (var animation in m_GameObject.m_Animation.m_Animations)
@@ -775,7 +782,8 @@ namespace AssetStudio
private void ConvertAnimations()
{
var totalCount = animationClipUniqArray.Length;
Logger.Info($"Trying to convert {totalCount} animation(s)...");
if (totalCount > 0)
Logger.Info($"Trying to convert {totalCount} animation(s)...");
for (var k = 0; k < totalCount; k++)
{