From 7b7eac62d80c8ef9a596cac7df727a2c81044a9f Mon Sep 17 00:00:00 2001 From: VaDiM Date: Thu, 12 Jun 2025 01:53:59 +0300 Subject: [PATCH] Fixes for Live2D export - Fixed export of live2d models from assets without containers. - Improved method of binding model-related assets. --- AssetStudio/AssetsManager.cs | 1 + AssetStudioCLI/Studio.cs | 182 +++++++++--------- AssetStudioGUI/AssetStudioGUIForm.cs | 1 + AssetStudioGUI/Studio.cs | 148 +++++++------- .../CubismLive2DExtractor/Live2DExtractor.cs | 9 +- 5 files changed, 181 insertions(+), 160 deletions(-) diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index a141ce4..b0caa4f 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -64,6 +64,7 @@ namespace AssetStudio ClassIDType.ResourceManager, ClassIDType.GameObject, ClassIDType.Transform, + ClassIDType.RectTransform, }); if (classIDTypes.Contains(ClassIDType.MonoBehaviour)) diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index 76595f8..cd41c9c 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -12,6 +12,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using static AssetStudioCLI.Exporter; +using static CubismLive2DExtractor.CubismParsers; using Ansi = AssetStudio.ColorConsole; namespace AssetStudioCLI @@ -279,16 +280,16 @@ namespace AssetStudioCLI } break; case "CubismRenderer": - BindCubismRenderer(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.RenderTexture); break; case "CubismDisplayInfoParameterName": - BindParamDisplayInfo(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo, isParamInfo: true); break; case "CubismDisplayInfoPartName": - BindPartDisplayInfo(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo); break; case "CubismPosePart": - BindCubismPosePart(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.PosePart); break; } } @@ -986,7 +987,7 @@ namespace AssetStudioCLI private static bool TryGetCubismMoc(MonoBehaviour m_MonoBehaviour, out MonoBehaviour mocMono) { mocMono = null; - var pptrDict = (OrderedDictionary)CubismParsers.ParseMonoBehaviour(m_MonoBehaviour, CubismParsers.CubismMonoBehaviourType.Model, assemblyLoader)?["_moc"]; + var pptrDict = (OrderedDictionary)CubismParsers.ParseMonoBehaviour(m_MonoBehaviour, CubismMonoBehaviourType.Model, assemblyLoader)?["_moc"]; if (pptrDict == null) return false; @@ -999,51 +1000,28 @@ namespace AssetStudioCLI return mocPPtr.TryGet(out mocMono); } - private static void BindCubismRenderer(MonoBehaviour m_MonoBehaviour) + private static void BindCubismAsset(MonoBehaviour m_MonoBehaviour, CubismMonoBehaviourType type, bool isParamInfo = false) { if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) return; - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) - { - rootGameObject.CubismModel.RenderTextureList.Add(m_MonoBehaviour); - } - } - - private static void BindParamDisplayInfo(MonoBehaviour m_MonoBehaviour) - { - if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) + if (!TryGetModelGameObject(m_GameObject.m_Transform, out var modelGameObject)) return; - - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) + + switch (type) { - rootGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour); - } - } - - private static void BindPartDisplayInfo(MonoBehaviour m_MonoBehaviour) - { - if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) - return; - - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) - { - rootGameObject.CubismModel.PartDisplayInfoList.Add(m_MonoBehaviour); - } - } - - private static void BindCubismPosePart(MonoBehaviour m_MonoBehaviour) - { - if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) - return; - - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) - { - rootGameObject.CubismModel.PosePartList.Add(m_MonoBehaviour); + case CubismMonoBehaviourType.PosePart: + modelGameObject.CubismModel.PosePartList.Add(m_MonoBehaviour); + break; + case CubismMonoBehaviourType.DisplayInfo when isParamInfo: + modelGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour); + break; + case CubismMonoBehaviourType.DisplayInfo: + modelGameObject.CubismModel.PartDisplayInfoList.Add(m_MonoBehaviour); + break; + case CubismMonoBehaviourType.RenderTexture: + modelGameObject.CubismModel.RenderTextureList.Add(m_MonoBehaviour); + break; } } @@ -1075,31 +1053,41 @@ namespace AssetStudioCLI } } - private static Transform GetRootTransform(Transform m_Transform) + private static bool TryGetModelGameObject(Transform m_Transform, out GameObject m_GameObject) { + m_GameObject = null; if (m_Transform == null) - return null; + return false; while (m_Transform.m_Father.TryGet(out var m_Father)) { m_Transform = m_Father; + if (m_Transform.m_GameObject.TryGet(out m_GameObject) && m_GameObject.CubismModel != null) + { + return true; + } } - return m_Transform; + return false; } - private static Dictionary GenerateMocPathDict(Dictionary mocDict, Dictionary assetContainers, bool searchByFilename) + private static Dictionary GenerateMocPathDict(Dictionary mocDict, bool searchByFilename) { var tempMocPathDict = new Dictionary(); var mocPathDict = new Dictionary(); foreach (var mocMono in l2dModelDict.Keys) { - if (!containers.TryGetValue(mocMono, out var fullContainerPath)) - continue; - var pathSepIndex = fullContainerPath.LastIndexOf('/'); - var basePath = pathSepIndex > 0 - ? fullContainerPath.Substring(0, pathSepIndex) - : fullContainerPath; - tempMocPathDict.Add(mocMono, (fullContainerPath, basePath)); + if (containers.TryGetValue(mocMono, out var fullContainerPath)) + { + var pathSepIndex = fullContainerPath.LastIndexOf('/'); + var basePath = pathSepIndex > 0 + ? fullContainerPath.Substring(0, pathSepIndex) + : fullContainerPath; + tempMocPathDict.Add(mocMono, (fullContainerPath, basePath)); + } + else if (searchByFilename) + { + tempMocPathDict.Add(mocMono, (mocMono.assetsFile.fullName, mocMono.assetsFile.fullName)); + } } if (tempMocPathDict.Count > 0) @@ -1113,7 +1101,7 @@ namespace AssetStudioCLI : tempMocPathDict[moc].Item2; //basePath if (searchByFilename) { - mocPathDict.Add(moc, assetContainers[moc]); + mocPathDict.Add(moc, moc.assetsFile.fullName); if (mocDict.TryGetValue(moc, out var model) && model != null) model.Container = mocPath; } @@ -1180,22 +1168,33 @@ namespace AssetStudioCLI Progress.Reset(); Logger.Info("Searching for Live2D files..."); + var mocPathDict = GenerateMocPathDict(mocDict, searchByFilename); + if (!searchByFilename && mocPathDict.Count != l2dModelDict.Count) + { + Logger.Warning($"Some Live2D models cannot be exported using containers\nTry to specify \"{"--l2d-search-by-filename".Color(Ansi.BrightCyan)}\" flag"); + } + if (searchByFilename) { - foreach (var assetKvp in containers) + foreach (var asset in parsedAssetsList) { - var container = string.IsNullOrEmpty(assetKvp.Key.assetsFile.originalPath) - ? assetKvp.Key.assetsFile.fullName - : assetKvp.Key.assetsFile.originalPath; - l2dContainers[assetKvp.Key] = container; + switch (asset.Type) + { + case ClassIDType.AnimationClip: + case ClassIDType.Texture2D: + case ClassIDType.MonoBehaviour: + l2dContainers[asset.Asset] = asset.Asset.assetsFile.fullName; + break; + } } } - var mocPathDict = GenerateMocPathDict(mocDict, l2dContainers, searchByFilename); var assetDict = new Dictionary>(); foreach (var mocKvp in mocPathDict) { - var mocPath = mocKvp.Value; + var mocPath = searchByFilename + ? mocKvp.Key.assetsFile.fullName + : mocKvp.Value; var result = l2dContainers.Select(assetKvp => { if (!assetKvp.Value.Contains(mocPath)) @@ -1223,13 +1222,14 @@ namespace AssetStudioCLI assetDict[mocKvp.Key] = result; } } + if (searchByFilename) l2dContainers.Clear(); if (mocDict.Keys.First().serializedType?.m_Type == null && CLIOptions.o_assemblyPath.Value == "") { - Logger.Warning("Specifying the assembly folder may be needed for proper extraction"); + Logger.Warning("Specifying the assembly folder may be needed for proper extraction\n" + + $"Use \"{"--assembly-folder ".Color(Ansi.BrightCyan)}\" to specify it"); } - var totalModelCount = assetDict.Count; Logger.Info($"Found {totalModelCount} model(s)."); var parallelTaskCount = CLIOptions.o_maxParallelExportTasks.Value; @@ -1238,37 +1238,45 @@ namespace AssetStudioCLI Live2DExtractor.Assembly = assemblyLoader; foreach (var assetGroupKvp in assetDict) { - var srcContainer = containers[assetGroupKvp.Key]; + var srcContainer = containers.TryGetValue(assetGroupKvp.Key, out var result) + ? result + : assetGroupKvp.Key.assetsFile.fullName; - Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\""); + Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D from: \"{srcContainer.Color(Ansi.BrightCyan)}\"..."); try { var cubismExtractor = new Live2DExtractor(assetGroupKvp); var filename = string.IsNullOrEmpty(cubismExtractor.MocMono.assetsFile.originalPath) ? Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.fileName) : Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath); + var modelName = !string.IsNullOrEmpty(cubismExtractor.Model?.Name) + ? cubismExtractor.Model.Name + : filename; + Logger.Info($"Model name: \"{modelName}\""); + string modelPath; switch (modelGroupOption) - { - case Live2DModelGroupOption.SourceFileName: - modelPath = filename; - break; - case Live2DModelGroupOption.ModelName: - modelPath = !string.IsNullOrEmpty(cubismExtractor.Model?.Name) - ? cubismExtractor.Model.Name - : filename; - break; - default: //ContainerPath - var container = searchByFilename && cubismExtractor.Model != null - ? cubismExtractor.Model.Container - : srcContainer; - modelPath = Path.HasExtension(container) - ? container.Replace(Path.GetExtension(container), "") - : container; - break; - } + { + case Live2DModelGroupOption.SourceFileName: + modelPath = filename; + break; + case Live2DModelGroupOption.ModelName: + modelPath = modelName; + break; + default: //ContainerPath + var container = searchByFilename && cubismExtractor.Model != null + ? cubismExtractor.Model.Container + : srcContainer; + container = container == assetGroupKvp.Key.assetsFile.fullName + ? filename + : container; + modelPath = Path.HasExtension(container) + ? container.Replace(Path.GetExtension(container), "") + : container; + break; + } - var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar; + var destPath = Path.Combine(baseDestPath, modelPath); cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelTaskCount); modelCounter++; } @@ -1276,7 +1284,7 @@ namespace AssetStudioCLI { Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex); } - Progress.Report(modelCounter, (int)totalModelCount); + Progress.Report(modelCounter, totalModelCount); } var status = modelCounter > 0 ? diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index aa16c13..c26d27d 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -1265,6 +1265,7 @@ namespace AssetStudioGUI sb.AppendLine($"Pixel Per Unit: {cubismMoc.PixelPerUnit}"); sb.AppendLine($"Parameter Count: {cubismMoc.ParamCount}"); sb.AppendLine($"Part Count: {cubismMoc.PartCount}"); + sb.AppendLine($"Pre-linked AnimationClips: {model?.ClipMotionList.Count}"); } assetItem.InfoText = sb.ToString(); } diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index 5e83845..d41c864 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; using static AssetStudioGUI.Exporter; +using static CubismLive2DExtractor.CubismParsers; using Object = AssetStudio.Object; namespace AssetStudioGUI @@ -300,16 +301,16 @@ namespace AssetStudioGUI } break; case "CubismRenderer": - BindCubismRenderer(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.RenderTexture); break; case "CubismDisplayInfoParameterName": - BindParamDisplayInfo(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo, isParamInfo: true); break; case "CubismDisplayInfoPartName": - BindPartDisplayInfo(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo); break; case "CubismPosePart": - BindCubismPosePart(m_MonoBehaviour); + BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.PosePart); break; } } @@ -1030,51 +1031,28 @@ namespace AssetStudioGUI return mocPPtr.TryGet(out mocMono); } - private static void BindCubismRenderer(MonoBehaviour m_MonoBehaviour) - { - if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) - return; - - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) - { - rootGameObject.CubismModel.RenderTextureList.Add(m_MonoBehaviour); - } - } - - private static void BindParamDisplayInfo(MonoBehaviour m_MonoBehaviour) + private static void BindCubismAsset(MonoBehaviour m_MonoBehaviour, CubismMonoBehaviourType type, bool isParamInfo = false) { if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) return; - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) - { - rootGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour); - } - } - - private static void BindPartDisplayInfo(MonoBehaviour m_MonoBehaviour) - { - if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) + if (!TryGetModelGameObject(m_GameObject.m_Transform, out var modelGameObject)) return; - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) + switch (type) { - rootGameObject.CubismModel.PartDisplayInfoList.Add(m_MonoBehaviour); - } - } - - private static void BindCubismPosePart(MonoBehaviour m_MonoBehaviour) - { - if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) - return; - - var rootTransform = GetRootTransform(m_GameObject.m_Transform); - if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) - { - rootGameObject.CubismModel.PosePartList.Add(m_MonoBehaviour); + case CubismMonoBehaviourType.PosePart: + modelGameObject.CubismModel.PosePartList.Add(m_MonoBehaviour); + break; + case CubismMonoBehaviourType.DisplayInfo when isParamInfo: + modelGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour); + break; + case CubismMonoBehaviourType.DisplayInfo: + modelGameObject.CubismModel.PartDisplayInfoList.Add(m_MonoBehaviour); + break; + case CubismMonoBehaviourType.RenderTexture: + modelGameObject.CubismModel.RenderTextureList.Add(m_MonoBehaviour); + break; } } @@ -1106,31 +1084,41 @@ namespace AssetStudioGUI } } - private static Transform GetRootTransform(Transform m_Transform) + private static bool TryGetModelGameObject(Transform m_Transform, out GameObject m_GameObject) { + m_GameObject = null; if (m_Transform == null) - return null; + return false; while (m_Transform.m_Father.TryGet(out var m_Father)) { m_Transform = m_Father; + if (m_Transform.m_GameObject.TryGet(out m_GameObject) && m_GameObject.CubismModel != null) + { + return true; + } } - return m_Transform; + return false; } - private static Dictionary GenerateMocPathDict(Dictionary mocDict, Dictionary assetContainers, bool searchByFilename) + private static Dictionary GenerateMocPathDict(Dictionary mocDict, bool searchByFilename) { var tempMocPathDict = new Dictionary(); var mocPathDict = new Dictionary(); foreach (var mocMono in l2dModelDict.Keys) { - if (!l2dAssetContainers.TryGetValue(mocMono, out var fullContainerPath)) - continue; - var pathSepIndex = fullContainerPath.LastIndexOf('/'); - var basePath = pathSepIndex > 0 - ? fullContainerPath.Substring(0, pathSepIndex) - : fullContainerPath; - tempMocPathDict.Add(mocMono, (fullContainerPath, basePath)); + if (l2dAssetContainers.TryGetValue(mocMono, out var fullContainerPath)) + { + var pathSepIndex = fullContainerPath.LastIndexOf('/'); + var basePath = pathSepIndex > 0 + ? fullContainerPath.Substring(0, pathSepIndex) + : fullContainerPath; + tempMocPathDict.Add(mocMono, (fullContainerPath, basePath)); + } + else if (searchByFilename) + { + tempMocPathDict.Add(mocMono, (mocMono.assetsFile.fullName, mocMono.assetsFile.fullName)); + } } if (tempMocPathDict.Count > 0) @@ -1144,7 +1132,7 @@ namespace AssetStudioGUI : tempMocPathDict[moc].Item2; //basePath if (searchByFilename) { - mocPathDict.Add(moc, assetContainers[moc]); + mocPathDict.Add(moc, moc.assetsFile.fullName); if (mocDict.TryGetValue(moc, out var model) && model != null) model.Container = mocPath; } @@ -1178,24 +1166,36 @@ namespace AssetStudioGUI ThreadPool.QueueUserWorkItem(state => { - Logger.Info("Searching for Live2D assets..."); + var mode = searchByFilename ? "file names" : "containers"; + Logger.Info($"Searching for Live2D assets using {mode}..."); + + var mocPathDict = GenerateMocPathDict(mocDict, searchByFilename); + if (!searchByFilename && mocPathDict.Count != l2dModelDict.Count) + { + Logger.Warning("Some Live2D models cannot be exported using containers\nTry to enable search by file name in the options"); + } if (searchByFilename) { - foreach (var assetKvp in l2dAssetContainers) + foreach (var asset in exportableAssets) { - var container = string.IsNullOrEmpty(assetKvp.Key.assetsFile.originalPath) - ? assetKvp.Key.assetsFile.fullName - : assetKvp.Key.assetsFile.originalPath; - l2dContainers[assetKvp.Key] = container; + switch (asset.Type) + { + case ClassIDType.AnimationClip: + case ClassIDType.Texture2D: + case ClassIDType.MonoBehaviour: + l2dContainers[asset.Asset] = asset.Asset.assetsFile.fullName; + break; + } } } - var mocPathDict = GenerateMocPathDict(mocDict, l2dContainers, searchByFilename); var assetDict = new Dictionary>(); foreach (var mocKvp in mocPathDict) { - var mocPath = mocKvp.Value; + var mocPath = searchByFilename + ? mocKvp.Key.assetsFile.fullName + : mocKvp.Value; var result = l2dContainers.Select(assetKvp => { if (!assetKvp.Value.Contains(mocPath)) @@ -1223,7 +1223,7 @@ namespace AssetStudioGUI assetDict[mocKvp.Key] = result; } } - + if (searchByFilename) l2dContainers.Clear(); if (mocDict.Keys.First().serializedType?.m_Type == null && !assemblyLoader.Loaded) @@ -1231,7 +1231,6 @@ namespace AssetStudioGUI Logger.Warning("Specifying the assembly folder may be needed for proper extraction"); SelectAssemblyFolder(); } - var totalModelCount = assetDict.Count; var modelCounter = 0; var parallelExportCount = Properties.Settings.Default.parallelExportCount <= 0 @@ -1242,15 +1241,22 @@ namespace AssetStudioGUI Live2DExtractor.Assembly = assemblyLoader; foreach (var assetGroupKvp in assetDict) { - var srcContainer = l2dAssetContainers[assetGroupKvp.Key]; + var srcContainer = l2dAssetContainers.TryGetValue(assetGroupKvp.Key, out var result) + ? result + : assetGroupKvp.Key.assetsFile.fullName; - Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer}\"..."); + Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D from: \"{srcContainer}\"..."); try { var cubismExtractor = new Live2DExtractor(assetGroupKvp, selClipMotions, selFadeMotions, selFadeLst); var filename = string.IsNullOrEmpty(cubismExtractor.MocMono.assetsFile.originalPath) ? Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.fileName) : Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath); + var modelName = !string.IsNullOrEmpty(cubismExtractor.Model?.Name) + ? cubismExtractor.Model.Name + : filename; + Logger.Info($"Model name: \"{modelName}\""); + string modelPath; switch (modelGroupOption) { @@ -1258,21 +1264,21 @@ namespace AssetStudioGUI modelPath = filename; break; case Live2DModelGroupOption.ModelName: - modelPath = !string.IsNullOrEmpty(cubismExtractor.Model?.Name) - ? cubismExtractor.Model.Name - : filename; + modelPath = modelName; break; default: //ContainerPath var container = searchByFilename && cubismExtractor.Model != null ? cubismExtractor.Model.Container : srcContainer; + container = container == assetGroupKvp.Key.assetsFile.fullName + ? filename + : container; modelPath = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container; break; } - - var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar; + var destPath = Path.Combine(baseDestPath, modelPath); cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelExportCount); modelCounter++; } diff --git a/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs b/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs index f125d18..2124945 100644 --- a/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs +++ b/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs @@ -217,8 +217,9 @@ namespace CubismLive2DExtractor public void ExtractCubismModel(string destPath, Live2DMotionMode motionMode, bool forceBezier = false, int parallelTaskCount = 1) { + var modelName = Model?.Name ?? destPath.Split('/', '\\').Last(); + destPath += Path.DirectorySeparatorChar; Directory.CreateDirectory(destPath); - var modelName = Model?.Name ?? "model"; #region moc3 using (var cubismMoc = new CubismMoc(MocMono)) @@ -234,7 +235,11 @@ namespace CubismLive2DExtractor sb.AppendLine($"Center Y: {cubismMoc.CentralPosY}"); sb.AppendLine($"Pixel Per Unit: {cubismMoc.PixelPerUnit}"); sb.AppendLine($"Part Count: {cubismMoc.PartCount}"); - sb.AppendLine($"Parameter Count: {cubismMoc.ParamCount}"); + sb.AppendLine($"Parameter Count: {cubismMoc.ParamCount}\n"); + sb.AppendLine($"Bound AnimationClips: {Model?.ClipMotionList.Count}"); + sb.AppendLine($"Bound ParamDisplayInfoList: {Model?.ParamDisplayInfoList.Count}"); + sb.AppendLine($"Bound PartDisplayInfoList: {Model?.PartDisplayInfoList.Count}"); + sb.AppendLine($"Bound PosePartList: {Model?.PosePartList.Count}"); Logger.Debug(sb.ToString()); ParameterNames = cubismMoc.ParamNames;