Some fixes for Live2D exporter

- Added Live2DOptions class
- Added filtering of Live2D textures when the searchByFilename flag is used
This commit is contained in:
VaDiM
2026-06-19 21:10:55 +03:00
parent f7352c18dd
commit 92602fc4a9
4 changed files with 74 additions and 42 deletions
+13 -10
View File
@@ -1164,10 +1164,13 @@ namespace AssetStudioCLI
public static void ExportLive2D()
{
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
var motionMode = CLIOptions.o_l2dMotionMode.Value;
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
var modelGroupOption = CLIOptions.o_l2dGroupOption.Value;
var searchByFilename = CLIOptions.f_l2dAssetSearchByFilename.Value;
var l2dOptions = new Live2DOptions()
{
MotionMode = CLIOptions.o_l2dMotionMode.Value,
ForceBezier = CLIOptions.f_l2dForceBezier.Value,
SearchByFilename = CLIOptions.f_l2dAssetSearchByFilename.Value,
};
if (CLIOptions.filterBy == FilterBy.NameOrContainer)
FilterMocDict();
if (l2dAssetManager.ModelDict.Count == 0)
@@ -1198,30 +1201,30 @@ namespace AssetStudioCLI
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)
var cubismExtractor = new Live2DExtractor(assetGroupKvp, l2dOptions);
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;
: fileName;
Logger.Info($"Model name: \"{modelName}\"");
string modelPath;
switch (modelGroupOption)
{
case Live2DModelGroupOption.SourceFileName:
modelPath = filename;
modelPath = fileName;
break;
case Live2DModelGroupOption.ModelName:
modelPath = modelName;
break;
default: //ContainerPath
var container = searchByFilename && cubismExtractor.Model != null
var container = l2dOptions.SearchByFilename && cubismExtractor.Model != null
? cubismExtractor.Model.Container
: srcContainer;
container = container == assetGroupKvp.Key.assetsFile.fullName
? filename
? fileName
: container;
modelPath = Path.HasExtension(container)
? container.Replace(Path.GetExtension(container), "")
@@ -1230,7 +1233,7 @@ namespace AssetStudioCLI
}
var destPath = Exporter.SafePathCombine(baseDestPath, modelPath);
cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, parallelTaskCount);
cubismExtractor.ExtractCubismModel(destPath, parallelTaskCount);
modelCounter++;
}
catch (Exception ex)
+18 -12
View File
@@ -1138,14 +1138,20 @@ namespace AssetStudioGUI
public static void ExportLive2D(string exportPath, List<MonoBehaviour> selMocs = null, List<AnimationClip> selClipMotions = null, List<MonoBehaviour> selFadeMotions = null, MonoBehaviour selFadeLst = null)
{
var baseDestPath = Path.Combine(exportPath, "Live2DOutput");
var forceBezier = Properties.Settings.Default.l2dForceBezier;
var modelGroupOption = Properties.Settings.Default.l2dModelGroupOption;
var searchByFilename = Properties.Settings.Default.l2dAssetSearchByFilename;
var motionMode = Properties.Settings.Default.l2dMotionMode;
var l2dOptions = new Live2DOptions()
{
SelectedClipMotions = selClipMotions,
SelectedFadeMotions = selFadeMotions,
SelectedFadeMotionList = selFadeLst,
MotionMode = Properties.Settings.Default.l2dMotionMode,
ForceBezier = Properties.Settings.Default.l2dForceBezier,
SearchByFilename = Properties.Settings.Default.l2dAssetSearchByFilename,
};
if (selClipMotions != null)
motionMode = Live2DMotionMode.AnimationClipV2;
l2dOptions.MotionMode = Live2DMotionMode.AnimationClipV2;
else if (selFadeMotions != null || selFadeLst != null)
motionMode = Live2DMotionMode.MonoBehaviour;
l2dOptions.MotionMode = Live2DMotionMode.MonoBehaviour;
var mocDict = selMocs != null
? selMocs.ToDictionary(moc => moc, moc => l2dAssetManager.ModelDict[moc])
: l2dAssetManager.ModelDict;
@@ -1171,30 +1177,30 @@ namespace AssetStudioGUI
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)
var cubismExtractor = new Live2DExtractor(assetGroupKvp, l2dOptions);
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;
: fileName;
Logger.Info($"Model name: \"{modelName}\"");
string modelPath;
switch (modelGroupOption)
{
case Live2DModelGroupOption.SourceFileName:
modelPath = filename;
modelPath = fileName;
break;
case Live2DModelGroupOption.ModelName:
modelPath = modelName;
break;
default: //ContainerPath
var container = searchByFilename && cubismExtractor.Model != null
var container = l2dOptions.SearchByFilename && cubismExtractor.Model != null
? cubismExtractor.Model.Container
: srcContainer;
container = container == assetGroupKvp.Key.assetsFile.fullName
? filename
? fileName
: container;
modelPath = Path.HasExtension(container)
? container.Replace(Path.GetExtension(container), "")
@@ -1202,7 +1208,7 @@ namespace AssetStudioGUI
break;
}
var destPath = Exporter.SafePathCombine(baseDestPath, modelPath);
cubismExtractor.ExtractCubismModel(destPath, motionMode, forceBezier, GetParallelExportCount());
cubismExtractor.ExtractCubismModel(destPath, GetParallelExportCount());
modelCounter++;
}
catch (Exception ex)
@@ -26,6 +26,7 @@ namespace CubismLive2DExtractor
public static Dictionary<MonoBehaviour, CubismModel> MocDict { get; set; }
public static AssemblyLoader Assembly { get; set; }
public CubismModel Model { get; set; }
private Live2DOptions Options { get; }
private List<MonoBehaviour> FadeMotions { get; set; }
private List<AnimationClip> AnimationClips { get; set; }
private List<MonoBehaviour> Expressions { get; set; }
@@ -42,12 +43,13 @@ namespace CubismLive2DExtractor
private HashSet<string> EyeBlinkParameters { get; set; }
private HashSet<string> LipSyncParameters { get; set; }
public Live2DExtractor(KeyValuePair<MonoBehaviour, List<Object>> assetGroupKvp, List<AnimationClip> selClipMotions = null, List<MonoBehaviour> selFadeMotions = null, MonoBehaviour selFadeMotionLst = null)
public Live2DExtractor(KeyValuePair<MonoBehaviour, List<Object>> assetGroupKvp, Live2DOptions l2dOptions)
{
Options = l2dOptions;
Expressions = new List<MonoBehaviour>();
FadeMotions = selFadeMotions ?? new List<MonoBehaviour>();
AnimationClips = selClipMotions ?? new List<AnimationClip>();
FadeMotionLst = selFadeMotionLst;
FadeMotions = Options.SelectedFadeMotions ?? new List<MonoBehaviour>();
AnimationClips = Options.SelectedClipMotions ?? new List<AnimationClip>();
FadeMotionLst = Options.SelectedFadeMotionList;
Texture2Ds = new List<Texture2D>();
EyeBlinkParameters = new HashSet<string>();
LipSyncParameters = new HashSet<string>();
@@ -63,9 +65,9 @@ namespace CubismLive2DExtractor
var searchModelPartCdi = true;
var searchPoseParts = true;
var searchFadeMotions =
selClipMotions == null
&& selFadeMotions == null
&& selFadeMotionLst == null;
Options.SelectedClipMotions == null
&& Options.SelectedFadeMotions == null
&& Options.SelectedFadeMotionList == null;
Logger.Debug("Sorting model assets..");
@@ -109,9 +111,9 @@ namespace CubismLive2DExtractor
PoseParts = Model.PosePartList;
searchPoseParts = false;
}
if (Model.ClipMotionList.Count > 0 && selClipMotions == null)
if (Model.ClipMotionList.Count > 0 && Options.SelectedClipMotions == null)
{
AnimationClips = Model.ClipMotionList;
AnimationClips.AddRange(Model.ClipMotionList);
}
}
foreach (var asset in assetGroupKvp.Value)
@@ -197,7 +199,7 @@ namespace CubismLive2DExtractor
}
break;
case AnimationClip m_AnimationClip:
if (selClipMotions == null)
if (Options.SelectedClipMotions == null)
{
AnimationClips.Add(m_AnimationClip);
}
@@ -207,17 +209,23 @@ namespace CubismLive2DExtractor
break;
}
}
if (renderTextureSet.Count > 0)
{
Texture2Ds = renderTextureSet.ToList();
}
else if (Options.SearchByFilename)
{
Texture2Ds = Texture2Ds.Where(x => x.m_Name.ToLower().StartsWith("texture")).ToList();
}
if (AnimationClips.Count > 0)
{
AnimationClips = AnimationClips.Distinct().ToList();
}
}
public void ExtractCubismModel(string destPath, Live2DMotionMode motionMode, bool forceBezier = false, int parallelTaskCount = 1)
public void ExtractCubismModel(string destPath, int parallelTaskCount = 1)
{
var modelName = Model?.Name ?? destPath.Split('/', '\\').Last();
destPath += Path.DirectorySeparatorChar;
@@ -311,7 +319,7 @@ namespace CubismLive2DExtractor
var destMotionPath = Path.Combine(destPath, "motions") + Path.DirectorySeparatorChar;
var motionFps = 0f;
if (motionMode == Live2DMotionMode.MonoBehaviour) //Fade motions from MonoBehaviour
if (Options.MotionMode == Live2DMotionMode.MonoBehaviour) //Fade motions from MonoBehaviour
{
if (FadeMotionLst != null) //Fade motions from fadeMotionList
{
@@ -340,7 +348,7 @@ namespace CubismLive2DExtractor
if (FadeMotions.Count > 0)
{
Logger.Debug("Motion export method: MonoBehaviour (Fade motion)");
ExportFadeMotions(destMotionPath, forceBezier, motions, ref motionFps);
ExportFadeMotions(destMotionPath, motions, ref motionFps);
}
}
@@ -348,7 +356,7 @@ namespace CubismLive2DExtractor
{
CubismMotion3Converter converter;
var exportMethod = "AnimationClip";
switch (motionMode)
switch (Options.MotionMode)
{
case Live2DMotionMode.AnimationClipV1 when Model?.ModelGameObject != null:
exportMethod += "V1";
@@ -356,7 +364,7 @@ namespace CubismLive2DExtractor
break;
default: //AnimationClipV2
exportMethod += "V2";
if (motionMode == Live2DMotionMode.MonoBehaviour)
if (Options.MotionMode == Live2DMotionMode.MonoBehaviour)
{
exportMethod = FadeMotions.Count > 0
? exportMethod + " (unable to export motions using Fade motion method)"
@@ -367,7 +375,7 @@ namespace CubismLive2DExtractor
}
Logger.Debug($"Motion export method: {exportMethod}");
ExportClipMotions(destMotionPath, converter, forceBezier, motions, ref motionFps);
ExportClipMotions(destMotionPath, converter, motions, ref motionFps);
}
if (motions.Count == 0)
@@ -520,7 +528,7 @@ namespace CubismLive2DExtractor
#endregion
}
private void ExportFadeMotions(string destMotionPath, bool forceBezier, SortedDictionary<string, JArray> motions, ref float fps)
private void ExportFadeMotions(string destMotionPath, SortedDictionary<string, JArray> motions, ref float fps)
{
Directory.CreateDirectory(destMotionPath);
foreach (var fadeMotionMono in FadeMotions)
@@ -533,7 +541,7 @@ namespace CubismLive2DExtractor
if (fadeMotion.ParameterIds.Length == 0)
continue;
var motionJson = new CubismMotion3Json(fadeMotion, ParameterNames, PartNames, forceBezier);
var motionJson = new CubismMotion3Json(fadeMotion, ParameterNames, PartNames, Options.ForceBezier);
fps = motionJson.Meta.Fps;
var animName = Path.GetFileNameWithoutExtension(fadeMotion.m_Name);
@@ -549,7 +557,7 @@ namespace CubismLive2DExtractor
}
}
private static void ExportClipMotions(string destMotionPath, CubismMotion3Converter converter, bool forceBezier, SortedDictionary<string, JArray> motions, ref float fps)
private void ExportClipMotions(string destMotionPath, CubismMotion3Converter converter, SortedDictionary<string, JArray> motions, ref float fps)
{
if (converter == null)
return;
@@ -566,7 +574,7 @@ namespace CubismLive2DExtractor
Logger.Warning($"Motion \"{animName}\" is empty. Export skipped");
continue;
}
var motionJson = new CubismMotion3Json(animation, forceBezier);
var motionJson = new CubismMotion3Json(animation, Options.ForceBezier);
fps = motionJson.Meta.Fps;
if (motions.ContainsKey(animName))
@@ -0,0 +1,15 @@
using AssetStudio;
using System.Collections.Generic;
namespace CubismLive2DExtractor
{
public sealed class Live2DOptions
{
public List<AnimationClip> SelectedClipMotions { get; set; }
public List<MonoBehaviour> SelectedFadeMotions { get; set; }
public MonoBehaviour SelectedFadeMotionList { get; set; }
public Live2DMotionMode MotionMode { get; set; } = Live2DMotionMode.MonoBehaviour;
public bool ForceBezier { get; set; }
public bool SearchByFilename { get; set; }
}
}