Fixes for Live2D export

- Fixed export of live2d models from assets without containers.
- Improved method of binding model-related assets.
This commit is contained in:
VaDiM
2025-06-12 01:53:59 +03:00
parent 3a25ed9ccd
commit 7b7eac62d8
5 changed files with 181 additions and 160 deletions

View File

@ -64,6 +64,7 @@ namespace AssetStudio
ClassIDType.ResourceManager, ClassIDType.ResourceManager,
ClassIDType.GameObject, ClassIDType.GameObject,
ClassIDType.Transform, ClassIDType.Transform,
ClassIDType.RectTransform,
}); });
if (classIDTypes.Contains(ClassIDType.MonoBehaviour)) if (classIDTypes.Contains(ClassIDType.MonoBehaviour))

View File

@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
using static AssetStudioCLI.Exporter; using static AssetStudioCLI.Exporter;
using static CubismLive2DExtractor.CubismParsers;
using Ansi = AssetStudio.ColorConsole; using Ansi = AssetStudio.ColorConsole;
namespace AssetStudioCLI namespace AssetStudioCLI
@ -279,16 +280,16 @@ namespace AssetStudioCLI
} }
break; break;
case "CubismRenderer": case "CubismRenderer":
BindCubismRenderer(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.RenderTexture);
break; break;
case "CubismDisplayInfoParameterName": case "CubismDisplayInfoParameterName":
BindParamDisplayInfo(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo, isParamInfo: true);
break; break;
case "CubismDisplayInfoPartName": case "CubismDisplayInfoPartName":
BindPartDisplayInfo(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo);
break; break;
case "CubismPosePart": case "CubismPosePart":
BindCubismPosePart(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.PosePart);
break; break;
} }
} }
@ -986,7 +987,7 @@ namespace AssetStudioCLI
private static bool TryGetCubismMoc(MonoBehaviour m_MonoBehaviour, out MonoBehaviour mocMono) private static bool TryGetCubismMoc(MonoBehaviour m_MonoBehaviour, out MonoBehaviour mocMono)
{ {
mocMono = null; 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) if (pptrDict == null)
return false; return false;
@ -999,51 +1000,28 @@ namespace AssetStudioCLI
return mocPPtr.TryGet(out mocMono); 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)) if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject))
return; return;
var rootTransform = GetRootTransform(m_GameObject.m_Transform); if (!TryGetModelGameObject(m_GameObject.m_Transform, out var modelGameObject))
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))
return; return;
var rootTransform = GetRootTransform(m_GameObject.m_Transform); switch (type)
if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null)
{ {
rootGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour); case CubismMonoBehaviourType.PosePart:
} modelGameObject.CubismModel.PosePartList.Add(m_MonoBehaviour);
} break;
case CubismMonoBehaviourType.DisplayInfo when isParamInfo:
private static void BindPartDisplayInfo(MonoBehaviour m_MonoBehaviour) modelGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour);
{ break;
if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) case CubismMonoBehaviourType.DisplayInfo:
return; modelGameObject.CubismModel.PartDisplayInfoList.Add(m_MonoBehaviour);
break;
var rootTransform = GetRootTransform(m_GameObject.m_Transform); case CubismMonoBehaviourType.RenderTexture:
if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) modelGameObject.CubismModel.RenderTextureList.Add(m_MonoBehaviour);
{ break;
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);
} }
} }
@ -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) if (m_Transform == null)
return null; return false;
while (m_Transform.m_Father.TryGet(out var m_Father)) while (m_Transform.m_Father.TryGet(out var m_Father))
{ {
m_Transform = 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<MonoBehaviour, string> GenerateMocPathDict(Dictionary<MonoBehaviour, CubismModel> mocDict, Dictionary<AssetStudio.Object, string> assetContainers, bool searchByFilename) private static Dictionary<MonoBehaviour, string> GenerateMocPathDict(Dictionary<MonoBehaviour, CubismModel> mocDict, bool searchByFilename)
{ {
var tempMocPathDict = new Dictionary<MonoBehaviour, (string, string)>(); var tempMocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
var mocPathDict = new Dictionary<MonoBehaviour, string>(); var mocPathDict = new Dictionary<MonoBehaviour, string>();
foreach (var mocMono in l2dModelDict.Keys) foreach (var mocMono in l2dModelDict.Keys)
{ {
if (!containers.TryGetValue(mocMono, out var fullContainerPath)) if (containers.TryGetValue(mocMono, out var fullContainerPath))
continue; {
var pathSepIndex = fullContainerPath.LastIndexOf('/'); var pathSepIndex = fullContainerPath.LastIndexOf('/');
var basePath = pathSepIndex > 0 var basePath = pathSepIndex > 0
? fullContainerPath.Substring(0, pathSepIndex) ? fullContainerPath.Substring(0, pathSepIndex)
: fullContainerPath; : fullContainerPath;
tempMocPathDict.Add(mocMono, (fullContainerPath, basePath)); tempMocPathDict.Add(mocMono, (fullContainerPath, basePath));
}
else if (searchByFilename)
{
tempMocPathDict.Add(mocMono, (mocMono.assetsFile.fullName, mocMono.assetsFile.fullName));
}
} }
if (tempMocPathDict.Count > 0) if (tempMocPathDict.Count > 0)
@ -1113,7 +1101,7 @@ namespace AssetStudioCLI
: tempMocPathDict[moc].Item2; //basePath : tempMocPathDict[moc].Item2; //basePath
if (searchByFilename) if (searchByFilename)
{ {
mocPathDict.Add(moc, assetContainers[moc]); mocPathDict.Add(moc, moc.assetsFile.fullName);
if (mocDict.TryGetValue(moc, out var model) && model != null) if (mocDict.TryGetValue(moc, out var model) && model != null)
model.Container = mocPath; model.Container = mocPath;
} }
@ -1180,22 +1168,33 @@ namespace AssetStudioCLI
Progress.Reset(); Progress.Reset();
Logger.Info("Searching for Live2D files..."); 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) if (searchByFilename)
{ {
foreach (var assetKvp in containers) foreach (var asset in parsedAssetsList)
{ {
var container = string.IsNullOrEmpty(assetKvp.Key.assetsFile.originalPath) switch (asset.Type)
? assetKvp.Key.assetsFile.fullName {
: assetKvp.Key.assetsFile.originalPath; case ClassIDType.AnimationClip:
l2dContainers[assetKvp.Key] = container; case ClassIDType.Texture2D:
case ClassIDType.MonoBehaviour:
l2dContainers[asset.Asset] = asset.Asset.assetsFile.fullName;
break;
}
} }
} }
var mocPathDict = GenerateMocPathDict(mocDict, l2dContainers, searchByFilename);
var assetDict = new Dictionary<MonoBehaviour, List<AssetStudio.Object>>(); var assetDict = new Dictionary<MonoBehaviour, List<AssetStudio.Object>>();
foreach (var mocKvp in mocPathDict) foreach (var mocKvp in mocPathDict)
{ {
var mocPath = mocKvp.Value; var mocPath = searchByFilename
? mocKvp.Key.assetsFile.fullName
: mocKvp.Value;
var result = l2dContainers.Select(assetKvp => var result = l2dContainers.Select(assetKvp =>
{ {
if (!assetKvp.Value.Contains(mocPath)) if (!assetKvp.Value.Contains(mocPath))
@ -1223,13 +1222,14 @@ namespace AssetStudioCLI
assetDict[mocKvp.Key] = result; assetDict[mocKvp.Key] = result;
} }
} }
if (searchByFilename) if (searchByFilename)
l2dContainers.Clear(); l2dContainers.Clear();
if (mocDict.Keys.First().serializedType?.m_Type == null && CLIOptions.o_assemblyPath.Value == "") 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 <path>".Color(Ansi.BrightCyan)}\" to specify it");
} }
var totalModelCount = assetDict.Count; var totalModelCount = assetDict.Count;
Logger.Info($"Found {totalModelCount} model(s)."); Logger.Info($"Found {totalModelCount} model(s).");
var parallelTaskCount = CLIOptions.o_maxParallelExportTasks.Value; var parallelTaskCount = CLIOptions.o_maxParallelExportTasks.Value;
@ -1238,37 +1238,45 @@ namespace AssetStudioCLI
Live2DExtractor.Assembly = assemblyLoader; Live2DExtractor.Assembly = assemblyLoader;
foreach (var assetGroupKvp in assetDict) 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 try
{ {
var cubismExtractor = new Live2DExtractor(assetGroupKvp); var cubismExtractor = new Live2DExtractor(assetGroupKvp);
var filename = string.IsNullOrEmpty(cubismExtractor.MocMono.assetsFile.originalPath) var filename = string.IsNullOrEmpty(cubismExtractor.MocMono.assetsFile.originalPath)
? Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.fileName) ? Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.fileName)
: Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath); : Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
var modelName = !string.IsNullOrEmpty(cubismExtractor.Model?.Name)
? cubismExtractor.Model.Name
: filename;
Logger.Info($"Model name: \"{modelName}\"");
string modelPath; string modelPath;
switch (modelGroupOption) switch (modelGroupOption)
{ {
case Live2DModelGroupOption.SourceFileName: case Live2DModelGroupOption.SourceFileName:
modelPath = filename; modelPath = filename;
break; break;
case Live2DModelGroupOption.ModelName: case Live2DModelGroupOption.ModelName:
modelPath = !string.IsNullOrEmpty(cubismExtractor.Model?.Name) modelPath = modelName;
? cubismExtractor.Model.Name break;
: filename; default: //ContainerPath
break; var container = searchByFilename && cubismExtractor.Model != null
default: //ContainerPath ? cubismExtractor.Model.Container
var container = searchByFilename && cubismExtractor.Model != null : srcContainer;
? cubismExtractor.Model.Container container = container == assetGroupKvp.Key.assetsFile.fullName
: srcContainer; ? filename
modelPath = Path.HasExtension(container) : container;
? container.Replace(Path.GetExtension(container), "") modelPath = Path.HasExtension(container)
: container; ? container.Replace(Path.GetExtension(container), "")
break; : container;
} break;
}
var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar; var destPath = Path.Combine(baseDestPath, modelPath);
cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelTaskCount); cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelTaskCount);
modelCounter++; modelCounter++;
} }
@ -1276,7 +1284,7 @@ namespace AssetStudioCLI
{ {
Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex); Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
} }
Progress.Report(modelCounter, (int)totalModelCount); Progress.Report(modelCounter, totalModelCount);
} }
var status = modelCounter > 0 ? var status = modelCounter > 0 ?

View File

@ -1265,6 +1265,7 @@ namespace AssetStudioGUI
sb.AppendLine($"Pixel Per Unit: {cubismMoc.PixelPerUnit}"); sb.AppendLine($"Pixel Per Unit: {cubismMoc.PixelPerUnit}");
sb.AppendLine($"Parameter Count: {cubismMoc.ParamCount}"); sb.AppendLine($"Parameter Count: {cubismMoc.ParamCount}");
sb.AppendLine($"Part Count: {cubismMoc.PartCount}"); sb.AppendLine($"Part Count: {cubismMoc.PartCount}");
sb.AppendLine($"Pre-linked AnimationClips: {model?.ClipMotionList.Count}");
} }
assetItem.InfoText = sb.ToString(); assetItem.InfoText = sb.ToString();
} }

View File

@ -14,6 +14,7 @@ using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using System.Xml.Linq; using System.Xml.Linq;
using static AssetStudioGUI.Exporter; using static AssetStudioGUI.Exporter;
using static CubismLive2DExtractor.CubismParsers;
using Object = AssetStudio.Object; using Object = AssetStudio.Object;
namespace AssetStudioGUI namespace AssetStudioGUI
@ -300,16 +301,16 @@ namespace AssetStudioGUI
} }
break; break;
case "CubismRenderer": case "CubismRenderer":
BindCubismRenderer(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.RenderTexture);
break; break;
case "CubismDisplayInfoParameterName": case "CubismDisplayInfoParameterName":
BindParamDisplayInfo(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo, isParamInfo: true);
break; break;
case "CubismDisplayInfoPartName": case "CubismDisplayInfoPartName":
BindPartDisplayInfo(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.DisplayInfo);
break; break;
case "CubismPosePart": case "CubismPosePart":
BindCubismPosePart(m_MonoBehaviour); BindCubismAsset(m_MonoBehaviour, CubismMonoBehaviourType.PosePart);
break; break;
} }
} }
@ -1030,51 +1031,28 @@ namespace AssetStudioGUI
return mocPPtr.TryGet(out mocMono); 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)) if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject))
return; return;
var rootTransform = GetRootTransform(m_GameObject.m_Transform); if (!TryGetModelGameObject(m_GameObject.m_Transform, out var modelGameObject))
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))
return; return;
var rootTransform = GetRootTransform(m_GameObject.m_Transform); switch (type)
if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null)
{ {
rootGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour); case CubismMonoBehaviourType.PosePart:
} modelGameObject.CubismModel.PosePartList.Add(m_MonoBehaviour);
} break;
case CubismMonoBehaviourType.DisplayInfo when isParamInfo:
private static void BindPartDisplayInfo(MonoBehaviour m_MonoBehaviour) modelGameObject.CubismModel.ParamDisplayInfoList.Add(m_MonoBehaviour);
{ break;
if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject)) case CubismMonoBehaviourType.DisplayInfo:
return; modelGameObject.CubismModel.PartDisplayInfoList.Add(m_MonoBehaviour);
break;
var rootTransform = GetRootTransform(m_GameObject.m_Transform); case CubismMonoBehaviourType.RenderTexture:
if (rootTransform.m_GameObject.TryGet(out var rootGameObject) && rootGameObject.CubismModel != null) modelGameObject.CubismModel.RenderTextureList.Add(m_MonoBehaviour);
{ break;
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);
} }
} }
@ -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) if (m_Transform == null)
return null; return false;
while (m_Transform.m_Father.TryGet(out var m_Father)) while (m_Transform.m_Father.TryGet(out var m_Father))
{ {
m_Transform = 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<MonoBehaviour, string> GenerateMocPathDict(Dictionary<MonoBehaviour, CubismModel> mocDict, Dictionary<Object, string> assetContainers, bool searchByFilename) private static Dictionary<MonoBehaviour, string> GenerateMocPathDict(Dictionary<MonoBehaviour, CubismModel> mocDict, bool searchByFilename)
{ {
var tempMocPathDict = new Dictionary<MonoBehaviour, (string, string)>(); var tempMocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
var mocPathDict = new Dictionary<MonoBehaviour, string>(); var mocPathDict = new Dictionary<MonoBehaviour, string>();
foreach (var mocMono in l2dModelDict.Keys) foreach (var mocMono in l2dModelDict.Keys)
{ {
if (!l2dAssetContainers.TryGetValue(mocMono, out var fullContainerPath)) if (l2dAssetContainers.TryGetValue(mocMono, out var fullContainerPath))
continue; {
var pathSepIndex = fullContainerPath.LastIndexOf('/'); var pathSepIndex = fullContainerPath.LastIndexOf('/');
var basePath = pathSepIndex > 0 var basePath = pathSepIndex > 0
? fullContainerPath.Substring(0, pathSepIndex) ? fullContainerPath.Substring(0, pathSepIndex)
: fullContainerPath; : fullContainerPath;
tempMocPathDict.Add(mocMono, (fullContainerPath, basePath)); tempMocPathDict.Add(mocMono, (fullContainerPath, basePath));
}
else if (searchByFilename)
{
tempMocPathDict.Add(mocMono, (mocMono.assetsFile.fullName, mocMono.assetsFile.fullName));
}
} }
if (tempMocPathDict.Count > 0) if (tempMocPathDict.Count > 0)
@ -1144,7 +1132,7 @@ namespace AssetStudioGUI
: tempMocPathDict[moc].Item2; //basePath : tempMocPathDict[moc].Item2; //basePath
if (searchByFilename) if (searchByFilename)
{ {
mocPathDict.Add(moc, assetContainers[moc]); mocPathDict.Add(moc, moc.assetsFile.fullName);
if (mocDict.TryGetValue(moc, out var model) && model != null) if (mocDict.TryGetValue(moc, out var model) && model != null)
model.Container = mocPath; model.Container = mocPath;
} }
@ -1178,24 +1166,36 @@ namespace AssetStudioGUI
ThreadPool.QueueUserWorkItem(state => 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) if (searchByFilename)
{ {
foreach (var assetKvp in l2dAssetContainers) foreach (var asset in exportableAssets)
{ {
var container = string.IsNullOrEmpty(assetKvp.Key.assetsFile.originalPath) switch (asset.Type)
? assetKvp.Key.assetsFile.fullName {
: assetKvp.Key.assetsFile.originalPath; case ClassIDType.AnimationClip:
l2dContainers[assetKvp.Key] = container; case ClassIDType.Texture2D:
case ClassIDType.MonoBehaviour:
l2dContainers[asset.Asset] = asset.Asset.assetsFile.fullName;
break;
}
} }
} }
var mocPathDict = GenerateMocPathDict(mocDict, l2dContainers, searchByFilename);
var assetDict = new Dictionary<MonoBehaviour, List<Object>>(); var assetDict = new Dictionary<MonoBehaviour, List<Object>>();
foreach (var mocKvp in mocPathDict) foreach (var mocKvp in mocPathDict)
{ {
var mocPath = mocKvp.Value; var mocPath = searchByFilename
? mocKvp.Key.assetsFile.fullName
: mocKvp.Value;
var result = l2dContainers.Select(assetKvp => var result = l2dContainers.Select(assetKvp =>
{ {
if (!assetKvp.Value.Contains(mocPath)) if (!assetKvp.Value.Contains(mocPath))
@ -1231,7 +1231,6 @@ namespace AssetStudioGUI
Logger.Warning("Specifying the assembly folder may be needed for proper extraction"); Logger.Warning("Specifying the assembly folder may be needed for proper extraction");
SelectAssemblyFolder(); SelectAssemblyFolder();
} }
var totalModelCount = assetDict.Count; var totalModelCount = assetDict.Count;
var modelCounter = 0; var modelCounter = 0;
var parallelExportCount = Properties.Settings.Default.parallelExportCount <= 0 var parallelExportCount = Properties.Settings.Default.parallelExportCount <= 0
@ -1242,15 +1241,22 @@ namespace AssetStudioGUI
Live2DExtractor.Assembly = assemblyLoader; Live2DExtractor.Assembly = assemblyLoader;
foreach (var assetGroupKvp in assetDict) 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 try
{ {
var cubismExtractor = new Live2DExtractor(assetGroupKvp, selClipMotions, selFadeMotions, selFadeLst); var cubismExtractor = new Live2DExtractor(assetGroupKvp, selClipMotions, selFadeMotions, selFadeLst);
var filename = string.IsNullOrEmpty(cubismExtractor.MocMono.assetsFile.originalPath) var filename = string.IsNullOrEmpty(cubismExtractor.MocMono.assetsFile.originalPath)
? Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.fileName) ? Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.fileName)
: Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath); : Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
var modelName = !string.IsNullOrEmpty(cubismExtractor.Model?.Name)
? cubismExtractor.Model.Name
: filename;
Logger.Info($"Model name: \"{modelName}\"");
string modelPath; string modelPath;
switch (modelGroupOption) switch (modelGroupOption)
{ {
@ -1258,21 +1264,21 @@ namespace AssetStudioGUI
modelPath = filename; modelPath = filename;
break; break;
case Live2DModelGroupOption.ModelName: case Live2DModelGroupOption.ModelName:
modelPath = !string.IsNullOrEmpty(cubismExtractor.Model?.Name) modelPath = modelName;
? cubismExtractor.Model.Name
: filename;
break; break;
default: //ContainerPath default: //ContainerPath
var container = searchByFilename && cubismExtractor.Model != null var container = searchByFilename && cubismExtractor.Model != null
? cubismExtractor.Model.Container ? cubismExtractor.Model.Container
: srcContainer; : srcContainer;
container = container == assetGroupKvp.Key.assetsFile.fullName
? filename
: container;
modelPath = Path.HasExtension(container) modelPath = Path.HasExtension(container)
? container.Replace(Path.GetExtension(container), "") ? container.Replace(Path.GetExtension(container), "")
: container; : container;
break; break;
} }
var destPath = Path.Combine(baseDestPath, modelPath);
var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar;
cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelExportCount); cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelExportCount);
modelCounter++; modelCounter++;
} }

View File

@ -217,8 +217,9 @@ namespace CubismLive2DExtractor
public void ExtractCubismModel(string destPath, Live2DMotionMode motionMode, bool forceBezier = false, int parallelTaskCount = 1) 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); Directory.CreateDirectory(destPath);
var modelName = Model?.Name ?? "model";
#region moc3 #region moc3
using (var cubismMoc = new CubismMoc(MocMono)) using (var cubismMoc = new CubismMoc(MocMono))
@ -234,7 +235,11 @@ namespace CubismLive2DExtractor
sb.AppendLine($"Center Y: {cubismMoc.CentralPosY}"); sb.AppendLine($"Center Y: {cubismMoc.CentralPosY}");
sb.AppendLine($"Pixel Per Unit: {cubismMoc.PixelPerUnit}"); sb.AppendLine($"Pixel Per Unit: {cubismMoc.PixelPerUnit}");
sb.AppendLine($"Part Count: {cubismMoc.PartCount}"); 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()); Logger.Debug(sb.ToString());
ParameterNames = cubismMoc.ParamNames; ParameterNames = cubismMoc.ParamNames;