mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-05-25 05:40:21 -04:00
Some fixes and improvements for Live2D export
- Fixed l2d model export for bundles with multiple models inside - Added support of grouping exported models by model name
This commit is contained in:
parent
02f64f3c97
commit
1cdb0b762a
@ -215,13 +215,13 @@ namespace AssetStudioCLI.Options
|
|||||||
optionDefaultValue: AssetGroupOption.ContainerPath,
|
optionDefaultValue: AssetGroupOption.ContainerPath,
|
||||||
optionName: "-g, --group-option <value>",
|
optionName: "-g, --group-option <value>",
|
||||||
optionDescription: "Specify the way in which exported assets should be grouped\n" +
|
optionDescription: "Specify the way in which exported assets should be grouped\n" +
|
||||||
"<Value: none | type | container(default) | containerFull | filename | sceneHierarchy>\n" +
|
"<Value: none | type | container(default) | containerFull | fileName | sceneHierarchy>\n" +
|
||||||
"None - Do not group exported assets\n" +
|
"None - Do not group exported assets\n" +
|
||||||
"Type - Group exported assets by type name\n" +
|
"Type - Group exported assets by type name\n" +
|
||||||
"Container - Group exported assets by container path\n" +
|
"Container - Group exported assets by container path\n" +
|
||||||
"ContainerFull - Group exported assets by full container path (e.g. with prefab name)\n" +
|
"ContainerFull - Group exported assets by full container path (e.g. with prefab name)\n" +
|
||||||
"SceneHierarchy - Group exported assets by their node path in scene hierarchy\n" +
|
"SceneHierarchy - Group exported assets by their node path in scene hierarchy\n" +
|
||||||
"Filename - Group exported assets by source file name\n",
|
"FileName - Group exported assets by source file name\n",
|
||||||
optionExample: "Example: \"-g containerFull\"\n",
|
optionExample: "Example: \"-g containerFull\"\n",
|
||||||
optionHelpGroup: HelpGroups.General
|
optionHelpGroup: HelpGroups.General
|
||||||
);
|
);
|
||||||
@ -307,10 +307,11 @@ namespace AssetStudioCLI.Options
|
|||||||
optionDefaultValue: CubismLive2DExtractor.Live2DModelGroupOption.ContainerPath,
|
optionDefaultValue: CubismLive2DExtractor.Live2DModelGroupOption.ContainerPath,
|
||||||
optionName: "--l2d-group-option <value>",
|
optionName: "--l2d-group-option <value>",
|
||||||
optionDescription: "Specify the way in which exported models should be grouped\n" +
|
optionDescription: "Specify the way in which exported models should be grouped\n" +
|
||||||
"<Value: container(default) | filename >\n" +
|
"<Value: container(default) | fileName | modelName>\n" +
|
||||||
"Container - Group exported models by container path\n" +
|
"Container - Group exported models by container path\n" +
|
||||||
"Filename - Group exported models by source file name\n",
|
"FileName - Group exported models by source file name\n" +
|
||||||
optionExample: "Example: \"--l2d-group-option filename\"\n",
|
"ModelName - Group exported models by model name\n",
|
||||||
|
optionExample: "Example: \"--l2d-group-option modelName\"\n",
|
||||||
optionHelpGroup: HelpGroups.Live2D
|
optionHelpGroup: HelpGroups.Live2D
|
||||||
);
|
);
|
||||||
o_l2dMotionMode = new GroupedOption<CubismLive2DExtractor.Live2DMotionMode>
|
o_l2dMotionMode = new GroupedOption<CubismLive2DExtractor.Live2DMotionMode>
|
||||||
@ -331,7 +332,8 @@ namespace AssetStudioCLI.Options
|
|||||||
optionName: "--l2d-search-by-filename",
|
optionName: "--l2d-search-by-filename",
|
||||||
optionDescription: "(Flag) If specified, Studio will search for model-related Live2D assets by file name\n" +
|
optionDescription: "(Flag) If specified, Studio will search for model-related Live2D assets by file name\n" +
|
||||||
"rather than by container\n" +
|
"rather than by container\n" +
|
||||||
"(Preferred option when all model-related assets are stored in a single file)\n",
|
"(Preferred option if all l2d assets of a single model are stored in a single file\n" +
|
||||||
|
"or containers are obfuscated)\n",
|
||||||
optionExample: "",
|
optionExample: "",
|
||||||
optionHelpGroup: HelpGroups.Live2D,
|
optionHelpGroup: HelpGroups.Live2D,
|
||||||
isFlag: true
|
isFlag: true
|
||||||
@ -880,6 +882,9 @@ namespace AssetStudioCLI.Options
|
|||||||
case "filename":
|
case "filename":
|
||||||
o_l2dGroupOption.Value = CubismLive2DExtractor.Live2DModelGroupOption.SourceFileName;
|
o_l2dGroupOption.Value = CubismLive2DExtractor.Live2DModelGroupOption.SourceFileName;
|
||||||
break;
|
break;
|
||||||
|
case "modelname":
|
||||||
|
o_l2dGroupOption.Value = CubismLive2DExtractor.Live2DModelGroupOption.ModelName;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option.Color(brightYellow)}] option. Unsupported model grouping option: [{value.Color(brightRed)}].\n");
|
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option.Color(brightYellow)}] option. Unsupported model grouping option: [{value.Color(brightRed)}].\n");
|
||||||
ShowOptionDescription(o_l2dGroupOption);
|
ShowOptionDescription(o_l2dGroupOption);
|
||||||
@ -1255,7 +1260,7 @@ namespace AssetStudioCLI.Options
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
sb.AppendLine($"# Model Group Option: {o_l2dGroupOption}");
|
sb.AppendLine($"# Model Group Option: {o_l2dGroupOption}");
|
||||||
sb.AppendFormat("# Search model-related assets by: {0}", f_l2dAssetSearchByFilename.Value ? "Filename" : "Container");
|
sb.AppendFormat("# Search model-related assets by: {0}\n", f_l2dAssetSearchByFilename.Value ? "FileName" : "Container");
|
||||||
sb.AppendLine($"# Motion Export Method: {o_l2dMotionMode}");
|
sb.AppendLine($"# Motion Export Method: {o_l2dMotionMode}");
|
||||||
sb.AppendLine($"# Force Bezier: {f_l2dForceBezier }");
|
sb.AppendLine($"# Force Bezier: {f_l2dForceBezier }");
|
||||||
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
|
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
|
||||||
|
@ -67,7 +67,6 @@ namespace AssetStudioCLI
|
|||||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||||
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
|
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
|
||||||
var isL2dMode = CLIOptions.o_workMode.Value == WorkMode.Live2D;
|
var isL2dMode = CLIOptions.o_workMode.Value == WorkMode.Live2D;
|
||||||
var l2dSearchByFilename = CLIOptions.f_l2dAssetSearchByFilename.Value;
|
|
||||||
|
|
||||||
Progress.Reset();
|
Progress.Reset();
|
||||||
var i = 0;
|
var i = 0;
|
||||||
@ -175,10 +174,6 @@ namespace AssetStudioCLI
|
|||||||
if (m_GameObject.CubismModel != null && TryGetCubismMoc(m_GameObject.CubismModel.CubismModelMono, out var mocMono))
|
if (m_GameObject.CubismModel != null && TryGetCubismMoc(m_GameObject.CubismModel.CubismModelMono, out var mocMono))
|
||||||
{
|
{
|
||||||
l2dModelDict[mocMono] = m_GameObject.CubismModel;
|
l2dModelDict[mocMono] = m_GameObject.CubismModel;
|
||||||
if (!m_GameObject.CubismModel.IsRoot)
|
|
||||||
{
|
|
||||||
FixCubismModelName(m_GameObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Animator m_Animator:
|
case Animator m_Animator:
|
||||||
@ -210,9 +205,7 @@ namespace AssetStudioCLI
|
|||||||
{
|
{
|
||||||
if (containers.TryGetValue(asset.Asset, out var container))
|
if (containers.TryGetValue(asset.Asset, out var container))
|
||||||
{
|
{
|
||||||
asset.Container = isL2dMode && l2dSearchByFilename
|
asset.Container = container;
|
||||||
? Path.GetFileName(asset.Asset.assetsFile.originalPath)
|
|
||||||
: container;
|
|
||||||
|
|
||||||
if (asset.Asset is GameObject m_GameObject && m_GameObject.CubismModel != null)
|
if (asset.Asset is GameObject m_GameObject && m_GameObject.CubismModel != null)
|
||||||
{
|
{
|
||||||
@ -778,15 +771,6 @@ namespace AssetStudioCLI
|
|||||||
return mocPPtr.TryGet(out mocMono);
|
return mocPPtr.TryGet(out mocMono);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FixCubismModelName(GameObject m_GameObject)
|
|
||||||
{
|
|
||||||
var rootTransform = GetRootTransform(m_GameObject.m_Transform);
|
|
||||||
if (rootTransform.m_GameObject.TryGet(out var rootGameObject))
|
|
||||||
{
|
|
||||||
m_GameObject.CubismModel.Name = rootGameObject.m_Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void BindCubismRenderer(MonoBehaviour m_MonoBehaviour)
|
private static void BindCubismRenderer(MonoBehaviour m_MonoBehaviour)
|
||||||
{
|
{
|
||||||
if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject))
|
if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject))
|
||||||
@ -847,58 +831,57 @@ namespace AssetStudioCLI
|
|||||||
return m_Transform;
|
return m_Transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> GenerateMocPathList(Dictionary<MonoBehaviour, CubismModel> mocDict, bool searchByFilename, ref bool useFullContainerPath)
|
private static Dictionary<MonoBehaviour, string> GenerateMocPathDict(Dictionary<MonoBehaviour, CubismModel> mocDict, Dictionary<AssetStudio.Object, string> assetContainers, bool searchByFilename)
|
||||||
{
|
{
|
||||||
var mocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
|
var tempMocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
|
||||||
var mocPathList = new List<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 containerPath))
|
if (!containers.TryGetValue(mocMono, out var fullContainerPath))
|
||||||
continue;
|
continue;
|
||||||
var fullContainerPath = searchByFilename
|
|
||||||
? l2dModelDict[mocMono]?.Container ?? containerPath
|
|
||||||
: containerPath;
|
|
||||||
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;
|
||||||
mocPathDict.Add(mocMono, (fullContainerPath, basePath));
|
tempMocPathDict.Add(mocMono, (fullContainerPath, basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mocPathDict.Count > 0)
|
if (tempMocPathDict.Count > 0)
|
||||||
{
|
{
|
||||||
var basePathSet = mocPathDict.Values.Select(x => x.Item2).ToHashSet();
|
var basePathSet = tempMocPathDict.Values.Select(x => x.Item2).ToHashSet();
|
||||||
useFullContainerPath = mocPathDict.Count != basePathSet.Count;
|
var useFullContainerPath = tempMocPathDict.Count != basePathSet.Count;
|
||||||
foreach (var moc in mocDict.Keys)
|
foreach (var moc in mocDict.Keys)
|
||||||
{
|
{
|
||||||
var mocPath = useFullContainerPath
|
var mocPath = useFullContainerPath
|
||||||
? mocPathDict[moc].Item1 //fullContainerPath
|
? tempMocPathDict[moc].Item1 //fullContainerPath
|
||||||
: mocPathDict[moc].Item2; //basePath
|
: tempMocPathDict[moc].Item2; //basePath
|
||||||
if (searchByFilename)
|
if (searchByFilename)
|
||||||
{
|
{
|
||||||
mocPathList.Add(containers[moc]);
|
mocPathDict.Add(moc, assetContainers[moc]);
|
||||||
if (mocDict.TryGetValue(moc, out var model) && model != null)
|
if (mocDict.TryGetValue(moc, out var model) && model != null)
|
||||||
model.Container = mocPath;
|
model.Container = mocPath;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mocPathList.Add(mocPath);
|
mocPathDict.Add(moc, mocPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mocPathDict.Clear();
|
tempMocPathDict.Clear();
|
||||||
}
|
}
|
||||||
return mocPathList;
|
return mocPathDict;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExportLive2D()
|
public static void ExportLive2D()
|
||||||
{
|
{
|
||||||
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
|
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
|
||||||
var useFullContainerPath = true;
|
|
||||||
var motionMode = CLIOptions.o_l2dMotionMode.Value;
|
var motionMode = CLIOptions.o_l2dMotionMode.Value;
|
||||||
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
|
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
|
||||||
var modelGroupOption = CLIOptions.o_l2dGroupOption.Value;
|
var modelGroupOption = CLIOptions.o_l2dGroupOption.Value;
|
||||||
var searchByFilename = CLIOptions.f_l2dAssetSearchByFilename.Value;
|
var searchByFilename = CLIOptions.f_l2dAssetSearchByFilename.Value;
|
||||||
var mocDict = l2dModelDict; //TODO: filter by name
|
var mocDict = l2dModelDict; //TODO: filter by name
|
||||||
|
var l2dContainers = searchByFilename
|
||||||
|
? new Dictionary<AssetStudio.Object, string>()
|
||||||
|
: containers;
|
||||||
|
|
||||||
if (l2dModelDict.Count == 0)
|
if (l2dModelDict.Count == 0)
|
||||||
{
|
{
|
||||||
@ -907,41 +890,50 @@ namespace AssetStudioCLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Progress.Reset();
|
Progress.Reset();
|
||||||
Logger.Info($"Searching for Live2D files...");
|
Logger.Info("Searching for Live2D files...");
|
||||||
|
|
||||||
var mocPathList = GenerateMocPathList(mocDict, searchByFilename, ref useFullContainerPath);
|
if (searchByFilename)
|
||||||
|
{
|
||||||
|
foreach (var assetKvp in containers)
|
||||||
|
{
|
||||||
|
l2dContainers[assetKvp.Key] = Path.GetFileName(assetKvp.Key.assetsFile.originalPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var mocPathDict = GenerateMocPathDict(mocDict, l2dContainers, searchByFilename);
|
||||||
|
|
||||||
#if NET9_0_OR_GREATER
|
var assetDict = new Dictionary<MonoBehaviour, List<AssetStudio.Object>>();
|
||||||
var assetDict = new Dictionary<string, List<AssetStudio.Object>>();
|
foreach (var mocKvp in mocPathDict)
|
||||||
foreach (var (asset, container) in containers)
|
|
||||||
{
|
{
|
||||||
var result = mocPathList.Find(mocPath =>
|
var mocPath = mocKvp.Value;
|
||||||
|
var result = l2dContainers.Select(assetKvp =>
|
||||||
{
|
{
|
||||||
if (!container.Contains(mocPath))
|
if (!assetKvp.Value.Contains(mocPath))
|
||||||
return false;
|
return null;
|
||||||
var mocPathSpan = mocPath.AsSpan();
|
var mocPathSpan = mocPath.AsSpan();
|
||||||
var mocPathLastSlice = mocPathSpan[(mocPathSpan.LastIndexOf('/') + 1)..];
|
var modelNameFromPath = mocPathSpan.Slice(mocPathSpan.LastIndexOf('/') + 1);
|
||||||
foreach (var range in container.AsSpan().Split('/'))
|
#if NET9_0_OR_GREATER
|
||||||
|
foreach (var range in assetKvp.Value.AsSpan().Split('/'))
|
||||||
{
|
{
|
||||||
if (mocPathLastSlice.SequenceEqual(container.AsSpan()[range]))
|
if (modelNameFromPath.SequenceEqual(assetKvp.Value.AsSpan()[range]))
|
||||||
return true;
|
return assetKvp.Key;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (result != null)
|
|
||||||
{
|
|
||||||
if (assetDict.TryGetValue(result, out var assets))
|
|
||||||
assets.Add(asset);
|
|
||||||
else
|
|
||||||
assetDict[result] = [asset];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
var assetDict = containers.AsParallel().ToLookup(
|
foreach (var str in assetKvp.Value.Split('/'))
|
||||||
x => mocPathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
{
|
||||||
x => x.Key
|
if (modelNameFromPath.SequenceEqual(str.AsSpan()))
|
||||||
).Where(x => x.Key != null).ToDictionary(x => x.Key, x => x.ToList());
|
return assetKvp.Key;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return null;
|
||||||
|
}).Where(x => x != null).ToList();
|
||||||
|
|
||||||
|
if (result.Count > 0)
|
||||||
|
{
|
||||||
|
assetDict[mocKvp.Key] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchByFilename)
|
||||||
|
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");
|
||||||
@ -953,27 +945,33 @@ namespace AssetStudioCLI
|
|||||||
var modelCounter = 0;
|
var modelCounter = 0;
|
||||||
Live2DExtractor.MocDict = mocDict;
|
Live2DExtractor.MocDict = mocDict;
|
||||||
Live2DExtractor.Assembly = assemblyLoader;
|
Live2DExtractor.Assembly = assemblyLoader;
|
||||||
foreach (var assetKvp in assetDict)
|
foreach (var assetGroupKvp in assetDict)
|
||||||
{
|
{
|
||||||
var srcContainer = assetKvp.Key;
|
var srcContainer = containers[assetGroupKvp.Key];
|
||||||
|
|
||||||
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\"");
|
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\"");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cubismExtractor = new Live2DExtractor(assetKvp.Value);
|
var cubismExtractor = new Live2DExtractor(assetGroupKvp);
|
||||||
string modelPath;
|
string modelPath;
|
||||||
if (modelGroupOption == Live2DModelGroupOption.SourceFileName)
|
switch (modelGroupOption)
|
||||||
{
|
{
|
||||||
|
case Live2DModelGroupOption.SourceFileName:
|
||||||
modelPath = Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
|
modelPath = Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
|
||||||
}
|
break;
|
||||||
else
|
case Live2DModelGroupOption.ModelName:
|
||||||
{
|
modelPath = !string.IsNullOrEmpty(cubismExtractor.Model?.Name)
|
||||||
|
? cubismExtractor.Model.Name
|
||||||
|
: Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
|
||||||
|
break;
|
||||||
|
default: //ContainerPath
|
||||||
var container = searchByFilename && cubismExtractor.Model != null
|
var container = searchByFilename && cubismExtractor.Model != null
|
||||||
? cubismExtractor.Model.Container
|
? cubismExtractor.Model.Container
|
||||||
: srcContainer;
|
: srcContainer;
|
||||||
modelPath = Path.HasExtension(container)
|
modelPath = Path.HasExtension(container)
|
||||||
? container.Replace(Path.GetExtension(container), "")
|
? container.Replace(Path.GetExtension(container), "")
|
||||||
: container;
|
: container;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar;
|
var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar;
|
||||||
|
@ -1216,6 +1216,10 @@ namespace AssetStudioGUI
|
|||||||
using (var cubismMoc = new CubismMoc(m_MonoBehaviour))
|
using (var cubismMoc = new CubismMoc(m_MonoBehaviour))
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
if (Studio.l2dModelDict.TryGetValue(m_MonoBehaviour, out var model) && model != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Model Name: {model.Name}");
|
||||||
|
}
|
||||||
sb.AppendLine($"SDK Version: {cubismMoc.VersionDescription}");
|
sb.AppendLine($"SDK Version: {cubismMoc.VersionDescription}");
|
||||||
if (cubismMoc.Version > 0)
|
if (cubismMoc.Version > 0)
|
||||||
{
|
{
|
||||||
@ -1512,11 +1516,11 @@ namespace AssetStudioGUI
|
|||||||
private void ResetForm()
|
private void ResetForm()
|
||||||
{
|
{
|
||||||
Text = guiTitle;
|
Text = guiTitle;
|
||||||
assetsManager.Clear();
|
Studio.assetsManager.Clear();
|
||||||
assemblyLoader.Clear();
|
Studio.assemblyLoader.Clear();
|
||||||
exportableAssets.Clear();
|
Studio.exportableAssets.Clear();
|
||||||
visibleAssets.Clear();
|
Studio.visibleAssets.Clear();
|
||||||
l2dModelDict.Clear();
|
Studio.l2dModelDict.Clear();
|
||||||
sceneTreeView.Nodes.Clear();
|
sceneTreeView.Nodes.Clear();
|
||||||
assetListView.VirtualListSize = 0;
|
assetListView.VirtualListSize = 0;
|
||||||
assetListView.Items.Clear();
|
assetListView.Items.Clear();
|
||||||
|
6
AssetStudioGUI/ExportOptions.Designer.cs
generated
6
AssetStudioGUI/ExportOptions.Designer.cs
generated
@ -379,7 +379,8 @@
|
|||||||
this.l2dAssetSearchByFilenameCheckBox.Size = new System.Drawing.Size(270, 17);
|
this.l2dAssetSearchByFilenameCheckBox.Size = new System.Drawing.Size(270, 17);
|
||||||
this.l2dAssetSearchByFilenameCheckBox.TabIndex = 3;
|
this.l2dAssetSearchByFilenameCheckBox.TabIndex = 3;
|
||||||
this.l2dAssetSearchByFilenameCheckBox.Text = "Search for model-related Live2D assets by file name";
|
this.l2dAssetSearchByFilenameCheckBox.Text = "Search for model-related Live2D assets by file name";
|
||||||
this.optionTooltip.SetToolTip(this.l2dAssetSearchByFilenameCheckBox, "Preferred option when all model-related assets are stored in a single file");
|
this.optionTooltip.SetToolTip(this.l2dAssetSearchByFilenameCheckBox, "Preferred option if all l2d assets of a single model are stored in a single file " +
|
||||||
|
"or containers are obfuscated");
|
||||||
this.l2dAssetSearchByFilenameCheckBox.UseVisualStyleBackColor = true;
|
this.l2dAssetSearchByFilenameCheckBox.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// l2dModelGroupComboBox
|
// l2dModelGroupComboBox
|
||||||
@ -388,7 +389,8 @@
|
|||||||
this.l2dModelGroupComboBox.FormattingEnabled = true;
|
this.l2dModelGroupComboBox.FormattingEnabled = true;
|
||||||
this.l2dModelGroupComboBox.Items.AddRange(new object[] {
|
this.l2dModelGroupComboBox.Items.AddRange(new object[] {
|
||||||
"container path",
|
"container path",
|
||||||
"source file name"});
|
"source file name",
|
||||||
|
"model name"});
|
||||||
this.l2dModelGroupComboBox.Location = new System.Drawing.Point(142, 18);
|
this.l2dModelGroupComboBox.Location = new System.Drawing.Point(142, 18);
|
||||||
this.l2dModelGroupComboBox.Name = "l2dModelGroupComboBox";
|
this.l2dModelGroupComboBox.Name = "l2dModelGroupComboBox";
|
||||||
this.l2dModelGroupComboBox.Size = new System.Drawing.Size(154, 21);
|
this.l2dModelGroupComboBox.Size = new System.Drawing.Size(154, 21);
|
||||||
|
@ -191,7 +191,6 @@ namespace AssetStudioGUI
|
|||||||
var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount);
|
var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount);
|
||||||
var containers = new List<(PPtr<Object>, string)>();
|
var containers = new List<(PPtr<Object>, string)>();
|
||||||
var tex2dArrayAssetList = new List<AssetItem>();
|
var tex2dArrayAssetList = new List<AssetItem>();
|
||||||
var l2dSearchByFilename = Properties.Settings.Default.l2dAssetSearchByFilename;
|
|
||||||
l2dAssetContainers.Clear();
|
l2dAssetContainers.Clear();
|
||||||
var i = 0;
|
var i = 0;
|
||||||
Progress.Reset();
|
Progress.Reset();
|
||||||
@ -215,10 +214,6 @@ namespace AssetStudioGUI
|
|||||||
if (m_GameObject.CubismModel != null && TryGetCubismMoc(m_GameObject.CubismModel.CubismModelMono, out var mocMono))
|
if (m_GameObject.CubismModel != null && TryGetCubismMoc(m_GameObject.CubismModel.CubismModelMono, out var mocMono))
|
||||||
{
|
{
|
||||||
l2dModelDict[mocMono] = m_GameObject.CubismModel;
|
l2dModelDict[mocMono] = m_GameObject.CubismModel;
|
||||||
if (!m_GameObject.CubismModel.IsRoot)
|
|
||||||
{
|
|
||||||
FixCubismModelName(m_GameObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Texture2D m_Texture2D:
|
case Texture2D m_Texture2D:
|
||||||
@ -359,9 +354,7 @@ namespace AssetStudioGUI
|
|||||||
case AnimationClip _:
|
case AnimationClip _:
|
||||||
case Texture2D _:
|
case Texture2D _:
|
||||||
case MonoBehaviour _:
|
case MonoBehaviour _:
|
||||||
l2dAssetContainers[obj] = l2dSearchByFilename
|
l2dAssetContainers[obj] = container;
|
||||||
? Path.GetFileName(obj.assetsFile.originalPath)
|
|
||||||
: container;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1012,15 +1005,6 @@ namespace AssetStudioGUI
|
|||||||
return mocPPtr.TryGet(out mocMono);
|
return mocPPtr.TryGet(out mocMono);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FixCubismModelName(GameObject m_GameObject)
|
|
||||||
{
|
|
||||||
var rootTransform = GetRootTransform(m_GameObject.m_Transform);
|
|
||||||
if (rootTransform.m_GameObject.TryGet(out var rootGameObject))
|
|
||||||
{
|
|
||||||
m_GameObject.CubismModel.Name = rootGameObject.m_Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void BindCubismRenderer(MonoBehaviour m_MonoBehaviour)
|
private static void BindCubismRenderer(MonoBehaviour m_MonoBehaviour)
|
||||||
{
|
{
|
||||||
if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject))
|
if (!m_MonoBehaviour.m_GameObject.TryGet(out var m_GameObject))
|
||||||
@ -1081,47 +1065,44 @@ namespace AssetStudioGUI
|
|||||||
return m_Transform;
|
return m_Transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> GenerateMocPathList(Dictionary<MonoBehaviour, CubismModel> mocDict, bool searchByFilename, ref bool useFullContainerPath)
|
private static Dictionary<MonoBehaviour, string> GenerateMocPathDict(Dictionary<MonoBehaviour, CubismModel> mocDict, Dictionary<Object, string> assetContainers, bool searchByFilename)
|
||||||
{
|
{
|
||||||
var mocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
|
var tempMocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
|
||||||
var mocPathList = new List<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 containerPath))
|
if (!l2dAssetContainers.TryGetValue(mocMono, out var fullContainerPath))
|
||||||
continue;
|
continue;
|
||||||
var fullContainerPath = searchByFilename
|
|
||||||
? l2dModelDict[mocMono]?.Container ?? containerPath
|
|
||||||
: containerPath;
|
|
||||||
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;
|
||||||
mocPathDict.Add(mocMono, (fullContainerPath, basePath));
|
tempMocPathDict.Add(mocMono, (fullContainerPath, basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mocPathDict.Count > 0)
|
if (tempMocPathDict.Count > 0)
|
||||||
{
|
{
|
||||||
var basePathSet = mocPathDict.Values.Select(x => x.Item2).ToHashSet();
|
var basePathSet = tempMocPathDict.Values.Select(x => x.Item2).ToHashSet();
|
||||||
useFullContainerPath = mocPathDict.Count != basePathSet.Count;
|
var useFullContainerPath = tempMocPathDict.Count != basePathSet.Count;
|
||||||
foreach (var moc in mocDict.Keys)
|
foreach (var moc in mocDict.Keys)
|
||||||
{
|
{
|
||||||
var mocPath = useFullContainerPath
|
var mocPath = useFullContainerPath
|
||||||
? mocPathDict[moc].Item1 //fullContainerPath
|
? tempMocPathDict[moc].Item1 //fullContainerPath
|
||||||
: mocPathDict[moc].Item2; //basePath
|
: tempMocPathDict[moc].Item2; //basePath
|
||||||
if (searchByFilename)
|
if (searchByFilename)
|
||||||
{
|
{
|
||||||
mocPathList.Add(l2dAssetContainers[moc]);
|
mocPathDict.Add(moc, assetContainers[moc]);
|
||||||
if (mocDict.TryGetValue(moc, out var model) && model != null)
|
if (mocDict.TryGetValue(moc, out var model) && model != null)
|
||||||
model.Container = mocPath;
|
model.Container = mocPath;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mocPathList.Add(mocPath);
|
mocPathDict.Add(moc, mocPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mocPathDict.Clear();
|
tempMocPathDict.Clear();
|
||||||
}
|
}
|
||||||
return mocPathList;
|
return mocPathDict;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExportLive2D(string exportPath, List<MonoBehaviour> selMocs = null, List<AnimationClip> selClipMotions = null, List<MonoBehaviour> selFadeMotions = null, MonoBehaviour selFadeLst = null)
|
public static void ExportLive2D(string exportPath, List<MonoBehaviour> selMocs = null, List<AnimationClip> selClipMotions = null, List<MonoBehaviour> selFadeMotions = null, MonoBehaviour selFadeLst = null)
|
||||||
@ -1138,46 +1119,57 @@ namespace AssetStudioGUI
|
|||||||
var mocDict = selMocs != null
|
var mocDict = selMocs != null
|
||||||
? selMocs.ToDictionary(moc => moc, moc => l2dModelDict[moc])
|
? selMocs.ToDictionary(moc => moc, moc => l2dModelDict[moc])
|
||||||
: l2dModelDict;
|
: l2dModelDict;
|
||||||
|
var l2dContainers = searchByFilename
|
||||||
|
? new Dictionary<Object, string>()
|
||||||
|
: l2dAssetContainers;
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(state =>
|
ThreadPool.QueueUserWorkItem(state =>
|
||||||
{
|
{
|
||||||
Logger.Info("Searching for Live2D assets...");
|
Logger.Info("Searching for Live2D assets...");
|
||||||
|
|
||||||
var useFullContainerPath = true;
|
if (searchByFilename)
|
||||||
var mocPathList = GenerateMocPathList(mocDict, searchByFilename, ref useFullContainerPath);
|
{
|
||||||
|
foreach (var assetKvp in l2dAssetContainers)
|
||||||
|
{
|
||||||
|
l2dContainers[assetKvp.Key] = Path.GetFileName(assetKvp.Key.assetsFile.originalPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var mocPathDict = GenerateMocPathDict(mocDict, l2dContainers, searchByFilename);
|
||||||
|
|
||||||
#if NET9_0_OR_GREATER
|
var assetDict = new Dictionary<MonoBehaviour, List<Object>>();
|
||||||
var assetDict = new Dictionary<string, List<Object>>();
|
foreach (var mocKvp in mocPathDict)
|
||||||
foreach (var (asset, container) in l2dAssetContainers)
|
|
||||||
{
|
{
|
||||||
var result = mocPathList.Find(mocPath =>
|
var mocPath = mocKvp.Value;
|
||||||
|
var result = l2dContainers.Select(assetKvp =>
|
||||||
{
|
{
|
||||||
if (!container.Contains(mocPath))
|
if (!assetKvp.Value.Contains(mocPath))
|
||||||
return false;
|
return null;
|
||||||
var mocPathSpan = mocPath.AsSpan();
|
var mocPathSpan = mocPath.AsSpan();
|
||||||
var mocPathLastSlice = mocPathSpan[(mocPathSpan.LastIndexOf('/') + 1)..];
|
var modelNameFromPath = mocPathSpan.Slice(mocPathSpan.LastIndexOf('/') + 1);
|
||||||
foreach (var range in container.AsSpan().Split('/'))
|
#if NET9_0_OR_GREATER
|
||||||
|
foreach (var range in assetKvp.Value.AsSpan().Split('/'))
|
||||||
{
|
{
|
||||||
if (mocPathLastSlice.SequenceEqual(container.AsSpan()[range]))
|
if (modelNameFromPath.SequenceEqual(assetKvp.Value.AsSpan()[range]))
|
||||||
return true;
|
return assetKvp.Key;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (result != null)
|
|
||||||
{
|
|
||||||
if (assetDict.TryGetValue(result, out var assets))
|
|
||||||
assets.Add(asset);
|
|
||||||
else
|
|
||||||
assetDict[result] = [asset];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
var assetDict = l2dAssetContainers.AsParallel().ToLookup(
|
foreach (var str in assetKvp.Value.Split('/'))
|
||||||
x => mocPathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
{
|
||||||
x => x.Key
|
if (modelNameFromPath.SequenceEqual(str.AsSpan()))
|
||||||
).Where(x => x.Key != null).ToDictionary(x=> x.Key, x => x.ToList());
|
return assetKvp.Key;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return null;
|
||||||
|
}).Where(x => x != null).ToList();
|
||||||
|
|
||||||
|
if (result.Count > 0)
|
||||||
|
{
|
||||||
|
assetDict[mocKvp.Key] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchByFilename)
|
||||||
|
l2dContainers.Clear();
|
||||||
if (mocDict.Keys.First().serializedType?.m_Type == null && !assemblyLoader.Loaded)
|
if (mocDict.Keys.First().serializedType?.m_Type == null && !assemblyLoader.Loaded)
|
||||||
{
|
{
|
||||||
Logger.Warning("Specifying the assembly folder may be needed for proper extraction");
|
Logger.Warning("Specifying the assembly folder may be needed for proper extraction");
|
||||||
@ -1192,27 +1184,33 @@ namespace AssetStudioGUI
|
|||||||
parallelExportCount = Properties.Settings.Default.parallelExport ? parallelExportCount : 1;
|
parallelExportCount = Properties.Settings.Default.parallelExport ? parallelExportCount : 1;
|
||||||
Live2DExtractor.MocDict = mocDict;
|
Live2DExtractor.MocDict = mocDict;
|
||||||
Live2DExtractor.Assembly = assemblyLoader;
|
Live2DExtractor.Assembly = assemblyLoader;
|
||||||
foreach (var assetKvp in assetDict)
|
foreach (var assetGroupKvp in assetDict)
|
||||||
{
|
{
|
||||||
var srcContainer = assetKvp.Key;
|
var srcContainer = l2dAssetContainers[assetGroupKvp.Key];
|
||||||
|
|
||||||
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer}\"...");
|
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer}\"...");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cubismExtractor = new Live2DExtractor(assetKvp.Value, selClipMotions, selFadeMotions, selFadeLst);
|
var cubismExtractor = new Live2DExtractor(assetGroupKvp, selClipMotions, selFadeMotions, selFadeLst);
|
||||||
string modelPath;
|
string modelPath;
|
||||||
if (modelGroupOption == Live2DModelGroupOption.SourceFileName)
|
switch (modelGroupOption)
|
||||||
{
|
{
|
||||||
|
case Live2DModelGroupOption.SourceFileName:
|
||||||
modelPath = Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
|
modelPath = Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
|
||||||
}
|
break;
|
||||||
else
|
case Live2DModelGroupOption.ModelName:
|
||||||
{
|
modelPath = !string.IsNullOrEmpty(cubismExtractor.Model?.Name)
|
||||||
|
? cubismExtractor.Model.Name
|
||||||
|
: Path.GetFileNameWithoutExtension(cubismExtractor.MocMono.assetsFile.originalPath);
|
||||||
|
break;
|
||||||
|
default: //ContainerPath
|
||||||
var container = searchByFilename && cubismExtractor.Model != null
|
var container = searchByFilename && cubismExtractor.Model != null
|
||||||
? cubismExtractor.Model.Container
|
? cubismExtractor.Model.Container
|
||||||
: srcContainer;
|
: srcContainer;
|
||||||
modelPath = Path.HasExtension(container)
|
modelPath = Path.HasExtension(container)
|
||||||
? container.Replace(Path.GetExtension(container), "")
|
? container.Replace(Path.GetExtension(container), "")
|
||||||
: container;
|
: container;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar;
|
var destPath = Path.Combine(baseDestPath, modelPath) + Path.DirectorySeparatorChar;
|
||||||
|
@ -41,7 +41,7 @@ namespace CubismLive2DExtractor
|
|||||||
private HashSet<string> EyeBlinkParameters { get; set; }
|
private HashSet<string> EyeBlinkParameters { get; set; }
|
||||||
private HashSet<string> LipSyncParameters { get; set; }
|
private HashSet<string> LipSyncParameters { get; set; }
|
||||||
|
|
||||||
public Live2DExtractor(List<Object> assets, List<AnimationClip> inClipMotions = null, List<MonoBehaviour> inFadeMotions = null, MonoBehaviour inFadeMotionLst = null)
|
public Live2DExtractor(KeyValuePair<MonoBehaviour, List<Object>> assetGroupKvp, List<AnimationClip> inClipMotions = null, List<MonoBehaviour> inFadeMotions = null, MonoBehaviour inFadeMotionLst = null)
|
||||||
{
|
{
|
||||||
Expressions = new List<MonoBehaviour>();
|
Expressions = new List<MonoBehaviour>();
|
||||||
FadeMotions = inFadeMotions ?? new List<MonoBehaviour>();
|
FadeMotions = inFadeMotions ?? new List<MonoBehaviour>();
|
||||||
@ -63,20 +63,11 @@ namespace CubismLive2DExtractor
|
|||||||
var searchPoseParts = true;
|
var searchPoseParts = true;
|
||||||
|
|
||||||
Logger.Debug("Sorting model assets..");
|
Logger.Debug("Sorting model assets..");
|
||||||
foreach (var asset in assets)
|
|
||||||
{
|
MocMono = assetGroupKvp.Key;
|
||||||
switch (asset)
|
if (MocDict.TryGetValue(MocMono, out var model) && model != null)
|
||||||
{
|
|
||||||
case MonoBehaviour m_MonoBehaviour:
|
|
||||||
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
|
||||||
{
|
|
||||||
switch (m_Script.m_ClassName)
|
|
||||||
{
|
|
||||||
case "CubismMoc":
|
|
||||||
MocMono = m_MonoBehaviour;
|
|
||||||
Model = MocDict[MocMono];
|
|
||||||
if (Model != null)
|
|
||||||
{
|
{
|
||||||
|
Model = model;
|
||||||
PhysicsMono = Model.PhysicsController;
|
PhysicsMono = Model.PhysicsController;
|
||||||
if (inFadeMotionLst == null && TryGetFadeList(Model.FadeController, out var fadeMono))
|
if (inFadeMotionLst == null && TryGetFadeList(Model.FadeController, out var fadeMono))
|
||||||
{
|
{
|
||||||
@ -113,7 +104,15 @@ namespace CubismLive2DExtractor
|
|||||||
searchPoseParts = false;
|
searchPoseParts = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
foreach (var asset in assetGroupKvp.Value)
|
||||||
|
{
|
||||||
|
switch (asset)
|
||||||
|
{
|
||||||
|
case MonoBehaviour m_MonoBehaviour:
|
||||||
|
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||||
|
{
|
||||||
|
switch (m_Script.m_ClassName)
|
||||||
|
{
|
||||||
case "CubismPhysicsController":
|
case "CubismPhysicsController":
|
||||||
if (PhysicsMono == null)
|
if (PhysicsMono == null)
|
||||||
PhysicsMono = m_MonoBehaviour;
|
PhysicsMono = m_MonoBehaviour;
|
||||||
@ -251,8 +250,12 @@ namespace CubismLive2DExtractor
|
|||||||
Parallel.ForEach(Texture2Ds, new ParallelOptions { MaxDegreeOfParallelism = parallelTaskCount }, texture2D =>
|
Parallel.ForEach(Texture2Ds, new ParallelOptions { MaxDegreeOfParallelism = parallelTaskCount }, texture2D =>
|
||||||
{
|
{
|
||||||
var savePath = $"{destTexturePath}{texture2D.m_Name}.png";
|
var savePath = $"{destTexturePath}{texture2D.m_Name}.png";
|
||||||
|
if (!savePathHash.TryAdd(savePath, true))
|
||||||
|
{
|
||||||
|
savePath = $"{destTexturePath}{texture2D.m_Name}_#{texture2D.GetHashCode()}.png";
|
||||||
if (!savePathHash.TryAdd(savePath, true))
|
if (!savePathHash.TryAdd(savePath, true))
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using (var image = texture2D.ConvertToImage(flip: true))
|
using (var image = texture2D.ConvertToImage(flip: true))
|
||||||
{
|
{
|
||||||
@ -293,19 +296,19 @@ namespace CubismLive2DExtractor
|
|||||||
if (fadeMotionLstDict != null)
|
if (fadeMotionLstDict != null)
|
||||||
{
|
{
|
||||||
var cubismFadeList = JsonConvert.DeserializeObject<CubismFadeMotionList>(JsonConvert.SerializeObject(fadeMotionLstDict));
|
var cubismFadeList = JsonConvert.DeserializeObject<CubismFadeMotionList>(JsonConvert.SerializeObject(fadeMotionLstDict));
|
||||||
var fadeMotionAssetList = new List<MonoBehaviour>();
|
var fadeMotionAssetSet = new HashSet<MonoBehaviour>();
|
||||||
foreach (var motionPPtr in cubismFadeList.CubismFadeMotionObjects)
|
foreach (var motionPPtr in cubismFadeList.CubismFadeMotionObjects)
|
||||||
{
|
{
|
||||||
if (motionPPtr.TryGet<MonoBehaviour>(out var fadeMono, FadeMotionLst.assetsFile))
|
if (motionPPtr.TryGet<MonoBehaviour>(out var fadeMono, FadeMotionLst.assetsFile))
|
||||||
{
|
{
|
||||||
fadeMotionAssetList.Add(fadeMono);
|
fadeMotionAssetSet.Add(fadeMono);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fadeMotionAssetList.Count > 0)
|
if (fadeMotionAssetSet.Count > 0)
|
||||||
{
|
{
|
||||||
FadeMotions = fadeMotionAssetList;
|
FadeMotions = fadeMotionAssetSet.ToList();
|
||||||
Logger.Debug($"\"{FadeMotionLst.m_Name}\": found {fadeMotionAssetList.Count} motion(s)");
|
Logger.Debug($"\"{FadeMotionLst.m_Name}\": found {fadeMotionAssetSet.Count} motion(s)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,19 +364,19 @@ namespace CubismLive2DExtractor
|
|||||||
if (expLstDict != null)
|
if (expLstDict != null)
|
||||||
{
|
{
|
||||||
var cubismExpList = JsonConvert.DeserializeObject<CubismExpressionList>(JsonConvert.SerializeObject(expLstDict));
|
var cubismExpList = JsonConvert.DeserializeObject<CubismExpressionList>(JsonConvert.SerializeObject(expLstDict));
|
||||||
var expAssetList = new List<MonoBehaviour>();
|
var expAssetSet = new HashSet<MonoBehaviour>();
|
||||||
foreach (var expPPtr in cubismExpList.CubismExpressionObjects)
|
foreach (var expPPtr in cubismExpList.CubismExpressionObjects)
|
||||||
{
|
{
|
||||||
if (expPPtr.TryGet<MonoBehaviour>(out var expMono, ExpressionLst.assetsFile))
|
if (expPPtr.TryGet<MonoBehaviour>(out var expMono, ExpressionLst.assetsFile))
|
||||||
{
|
{
|
||||||
expAssetList.Add(expMono);
|
expAssetSet.Add(expMono);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expAssetList.Count > 0)
|
if (expAssetSet.Count > 0)
|
||||||
{
|
{
|
||||||
Expressions = expAssetList;
|
Expressions = expAssetSet.ToList();
|
||||||
Logger.Debug($"\"{ExpressionLst.m_Name}\": found {expAssetList.Count} expression(s)");
|
Logger.Debug($"\"{ExpressionLst.m_Name}\": found {expAssetSet.Count} expression(s)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,6 @@
|
|||||||
{
|
{
|
||||||
ContainerPath,
|
ContainerPath,
|
||||||
SourceFileName,
|
SourceFileName,
|
||||||
|
ModelName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user