From be11fdf14fe49a0c1c65841813537217783781fd Mon Sep 17 00:00:00 2001 From: VaDiM Date: Sun, 10 Aug 2025 02:23:22 +0300 Subject: [PATCH] [GUI] Some fixes for animation export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- AssetStudioCLI/Exporter.cs | 4 +- AssetStudioGUI/AssetStudioGUIForm.cs | 61 ++++++++++++---------------- AssetStudioGUI/ExportOptions.cs | 49 +++++++++++----------- AssetStudioGUI/Exporter.cs | 15 ++++--- AssetStudioGUI/Studio.cs | 2 +- AssetStudioUtility/ModelConverter.cs | 30 +++++++++----- 6 files changed, 82 insertions(+), 79 deletions(-) diff --git a/AssetStudioCLI/Exporter.cs b/AssetStudioCLI/Exporter.cs index d4cfe7d..72e2985 100644 --- a/AssetStudioCLI/Exporter.cs +++ b/AssetStudioCLI/Exporter.cs @@ -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 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"); diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 6708c19..a53d45e 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -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 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 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 diff --git a/AssetStudioGUI/ExportOptions.cs b/AssetStudioGUI/ExportOptions.cs index 4f089da..11a519f 100644 --- a/AssetStudioGUI/ExportOptions.cs +++ b/AssetStudioGUI/ExportOptions.cs @@ -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 uvBindings; public ExportOptions() { @@ -34,8 +35,7 @@ namespace AssetStudioGUI ((RadioButton)l2dMotionExportMethodPanel.Controls.Cast().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(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); } } diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index fb240bc..2c6446d 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -222,9 +222,11 @@ namespace AssetStudioGUI { exportFullPath = Path.Combine(exportPath, item.Text + item.UniqueID, item.Text + ".fbx"); } + if (!Studio.FbxSettings.ExportAnimations) + animationList = new List(); 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 animationList = null) { + if (!Studio.FbxSettings.ExportAnimations) + animationList = new List(); 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, string exportPath, List animationList = null) { var rootName = Path.GetFileNameWithoutExtension(exportPath); + if (!Studio.FbxSettings.ExportAnimations) + animationList = new List(); 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); } diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index 5d850ab..e7c2ec6 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -90,6 +90,7 @@ namespace AssetStudioGUI public static Dictionary l2dModelDict = new Dictionary(); private static Dictionary l2dAssetContainers = new Dictionary(); internal static Action 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); diff --git a/AssetStudioUtility/ModelConverter.cs b/AssetStudioUtility/ModelConverter.cs index 9c31353..8e84d0d 100644 --- a/AssetStudioUtility/ModelConverter.cs +++ b/AssetStudioUtility/ModelConverter.cs @@ -23,14 +23,16 @@ namespace AssetStudio private Dictionary transformDictionary = new Dictionary(); Dictionary morphChannelNames = new Dictionary(); private IEqualityComparer 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 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 m_GameObjects, ImageFormat imageFormat, AnimationClip[] animationList = null) + public ModelConverter(string rootName, List m_GameObjects, ImageFormat imageFormat, List 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 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(); 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++) {