mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-07-18 03:24:15 -04:00
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:
@ -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))
|
||||||
|
@ -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 ?
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Reference in New Issue
Block a user