From 70aa8bec598d2dd2f282a873723df02042c6b1e4 Mon Sep 17 00:00:00 2001 From: VaDiM Date: Sun, 3 Nov 2024 02:06:21 +0300 Subject: [PATCH] Add support for exporting l2d poses (pose3.json) --- .../CubismLive2DExtractor/CubismModel3Json.cs | 3 +- .../CubismLive2DExtractor/CubismParsers.cs | 4 ++ .../CubismLive2DExtractor/CubismPose3Json.cs | 14 ++++ .../CubismLive2DExtractor/Live2DExtractor.cs | 71 ++++++++++++++++++- 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 AssetStudioUtility/CubismLive2DExtractor/CubismPose3Json.cs diff --git a/AssetStudioUtility/CubismLive2DExtractor/CubismModel3Json.cs b/AssetStudioUtility/CubismLive2DExtractor/CubismModel3Json.cs index cf2ddd5..95a1115 100644 --- a/AssetStudioUtility/CubismLive2DExtractor/CubismModel3Json.cs +++ b/AssetStudioUtility/CubismLive2DExtractor/CubismModel3Json.cs @@ -13,8 +13,9 @@ namespace CubismLive2DExtractor { public string Moc; public string[] Textures; - public string DisplayInfo; public string Physics; + public string Pose; + public string DisplayInfo; public JObject Motions; public JArray Expressions; } diff --git a/AssetStudioUtility/CubismLive2DExtractor/CubismParsers.cs b/AssetStudioUtility/CubismLive2DExtractor/CubismParsers.cs index 69f3a42..aa38a5f 100644 --- a/AssetStudioUtility/CubismLive2DExtractor/CubismParsers.cs +++ b/AssetStudioUtility/CubismLive2DExtractor/CubismParsers.cs @@ -15,6 +15,7 @@ namespace CubismLive2DExtractor Expression, Physics, DisplayInfo, + PosePart, } public static string ParsePhysics(OrderedDictionary physicsDict) @@ -147,6 +148,9 @@ namespace CubismLive2DExtractor case CubismMonoBehaviourType.DisplayInfo: fieldName = "name"; break; + case CubismMonoBehaviourType.PosePart: + fieldName = "groupindex"; + break; } if (m_Type.m_Nodes.FindIndex(x => x.m_Name.ToLower() == fieldName) < 0) { diff --git a/AssetStudioUtility/CubismLive2DExtractor/CubismPose3Json.cs b/AssetStudioUtility/CubismLive2DExtractor/CubismPose3Json.cs new file mode 100644 index 0000000..563164f --- /dev/null +++ b/AssetStudioUtility/CubismLive2DExtractor/CubismPose3Json.cs @@ -0,0 +1,14 @@ +namespace CubismLive2DExtractor +{ + public class CubismPose3Json + { + public string Type; + public ControlNode[][] Groups; + + public class ControlNode + { + public string Id; + public string[] Link; + } + } +} diff --git a/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs b/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs index 1f9d2ba..9ce1469 100644 --- a/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs +++ b/AssetStudioUtility/CubismLive2DExtractor/Live2DExtractor.cs @@ -33,6 +33,7 @@ namespace CubismLive2DExtractor private MonoBehaviour FadeMotionLst { get; set; } private List ParametersCdi { get; set; } private List PartsCdi { get; set; } + private List PoseParts { get; set; } public Live2DExtractor(IGrouping assets, List inClipMotions = null, List inFadeMotions = null, MonoBehaviour inFadeMotionLst = null) { @@ -48,6 +49,7 @@ namespace CubismLive2DExtractor FadeMotionLst = inFadeMotionLst; ParametersCdi = new List(); PartsCdi = new List(); + PoseParts = new List(); Logger.Debug("Sorting model assets.."); foreach (var asset in assets) @@ -116,6 +118,12 @@ namespace CubismLive2DExtractor PartsCdi.Add(m_MonoBehaviour); } break; + case "CubismPosePart": + if (m_MonoBehaviour.m_GameObject.TryGet(out _)) + { + PoseParts.Add(m_MonoBehaviour); + } + break; } } break; @@ -366,6 +374,21 @@ namespace CubismLive2DExtractor } #endregion + #region pose3.json + var isPoseExported = false; + if (PoseParts.Count > 0) + { + try + { + isPoseExported = ExportPoseJson(destPath, modelName, assemblyLoader); + } + catch (Exception e) + { + Logger.Warning($"An error occurred while exporting pose3.json\n{e}"); + } + } + #endregion + #region model3.json var groups = new List(); @@ -408,8 +431,9 @@ namespace CubismLive2DExtractor { Moc = $"{modelName}.moc3", Textures = textures.ToArray(), - DisplayInfo = isCdiParsed ? $"{modelName}.cdi3.json" : null, Physics = PhysicsMono != null ? $"{modelName}.physics3.json" : null, + Pose = isPoseExported ? $"{modelName}.pose3.json" : null, + DisplayInfo = isCdiParsed ? $"{modelName}.cdi3.json" : null, Motions = JObject.FromObject(motions), Expressions = expressions, }, @@ -479,6 +503,51 @@ namespace CubismLive2DExtractor } } + private bool ExportPoseJson(string destPath, string modelName, AssemblyLoader assemblyLoader) + { + var groupDict = new SortedDictionary>(); + foreach (var posePartMono in PoseParts) + { + var posePartDict = ParseMonoBehaviour(posePartMono, CubismMonoBehaviourType.PosePart, assemblyLoader); + if (posePartDict == null) + continue; + + if (!posePartMono.m_GameObject.TryGet(out var partObj)) + continue; + + var poseNode = new CubismPose3Json.ControlNode + { + Id = partObj.m_Name, + Link = Array.ConvertAll((object[])posePartDict["Link"], x => x?.ToString()) + }; + var groupIndex = (int)posePartDict["GroupIndex"]; + if (groupDict.ContainsKey(groupIndex)) + { + groupDict[groupIndex].Add(poseNode); + } + else + { + groupDict.Add(groupIndex, new List {poseNode}); + } + } + + if (groupDict.Count == 0) + return false; + + var poseJson = new CubismPose3Json + { + Type = "Live2D Pose", + Groups = new CubismPose3Json.ControlNode[groupDict.Count][] + }; + var i = 0; + foreach (var nodeList in groupDict.Values) + { + poseJson.Groups[i++] = nodeList.ToArray(); + } + File.WriteAllText($"{destPath}{modelName}.pose3.json", JsonConvert.SerializeObject(poseJson, Formatting.Indented)); + return true; + } + private static string GetDisplayName(MonoBehaviour cdiMono, AssemblyLoader assemblyLoader) { var dict = ParseMonoBehaviour(cdiMono, CubismMonoBehaviourType.DisplayInfo, assemblyLoader);