mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-06-03 00:58:13 -04:00
Improve integration with Live2D assets
- Improved export method of AnimationClip motions - [GUI] Added display of model info on the preview tab - [GUI] Added support for partial export: - selected models - model + selected AnimationClip motions - model + selected Fade motions - model + selected Fade Motion List
This commit is contained in:
parent
2f6d9ec77f
commit
70213e3012
@ -22,26 +22,26 @@ namespace SevenZip
|
||||
}
|
||||
}
|
||||
|
||||
uint _value = 0xFFFFFFFF;
|
||||
private uint _value = 0xFFFFFFFF;
|
||||
|
||||
public void Init() { _value = 0xFFFFFFFF; }
|
||||
|
||||
public void UpdateByte(byte b)
|
||||
{
|
||||
_value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8);
|
||||
_value = Table[(byte)_value ^ b] ^ (_value >> 8);
|
||||
}
|
||||
|
||||
public void Update(byte[] data, uint offset, uint size)
|
||||
{
|
||||
for (uint i = 0; i < size; i++)
|
||||
_value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8);
|
||||
_value = Table[(byte)_value ^ data[offset + i]] ^ (_value >> 8);
|
||||
}
|
||||
|
||||
public uint GetDigest() { return _value ^ 0xFFFFFFFF; }
|
||||
|
||||
static uint CalculateDigest(byte[] data, uint offset, uint size)
|
||||
public static uint CalculateDigest(byte[] data, uint offset, uint size)
|
||||
{
|
||||
CRC crc = new CRC();
|
||||
var crc = new CRC();
|
||||
// crc.Init();
|
||||
crc.Update(data, offset, size);
|
||||
return crc.GetDigest();
|
||||
@ -49,7 +49,7 @@ namespace SevenZip
|
||||
|
||||
static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size)
|
||||
{
|
||||
return (CalculateDigest(data, offset, size) == digest);
|
||||
return CalculateDigest(data, offset, size) == digest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
135
AssetStudio/CubismModel.cs
Normal file
135
AssetStudio/CubismModel.cs
Normal file
@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using static AssetStudio.EndianSpanReader;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public enum CubismSDKVersion : byte
|
||||
{
|
||||
V30 = 1,
|
||||
V33,
|
||||
V40,
|
||||
V42,
|
||||
V50
|
||||
}
|
||||
|
||||
public sealed class CubismModel : IDisposable
|
||||
{
|
||||
public CubismSDKVersion Version { get; }
|
||||
public string VersionDescription { get; }
|
||||
public float CanvasWidth { get; }
|
||||
public float CanvasHeight { get; }
|
||||
public float CentralPosX { get; }
|
||||
public float CentralPosY { get; }
|
||||
public float PixelPerUnit { get; }
|
||||
public uint PartCount { get; }
|
||||
public uint ParamCount { get; }
|
||||
public HashSet<string> PartNames { get; }
|
||||
public HashSet<string> ParamNames { get; }
|
||||
public byte[] ModelData { get; }
|
||||
private static bool IsBigEndian { get; set; }
|
||||
|
||||
public CubismModel(MonoBehaviour moc)
|
||||
{
|
||||
var reader = moc.reader;
|
||||
reader.Reset();
|
||||
reader.Position += 28; //PPtr<GameObject> m_GameObject, m_Enabled, PPtr<MonoScript>
|
||||
reader.ReadAlignedString(); //m_Name
|
||||
var modelDataSize = (int)reader.ReadUInt32();
|
||||
ModelData = BigArrayPool<byte>.Shared.Rent(modelDataSize);
|
||||
_ = reader.Read(ModelData, 0, modelDataSize);
|
||||
|
||||
var sdkVer = ModelData[4];
|
||||
if (Enum.IsDefined(typeof(CubismSDKVersion), sdkVer))
|
||||
{
|
||||
Version = (CubismSDKVersion)sdkVer;
|
||||
VersionDescription = ParseVersion();
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = $"Unknown SDK version ({sdkVer})";
|
||||
VersionDescription = msg;
|
||||
Version = 0;
|
||||
Logger.Warning($"Live2D model \"{moc.m_Name}\": " + msg);
|
||||
return;
|
||||
}
|
||||
IsBigEndian = BitConverter.ToBoolean(ModelData, 5);
|
||||
|
||||
//offsets
|
||||
var countInfoTableOffset = (int)SpanToUint32(ModelData, 64, IsBigEndian);
|
||||
var canvasInfoOffset = (int)SpanToUint32(ModelData, 68, IsBigEndian);
|
||||
var partIdsOffset = SpanToUint32(ModelData, 76, IsBigEndian);
|
||||
var parameterIdsOffset = SpanToUint32(ModelData, 264, IsBigEndian);
|
||||
|
||||
//canvas
|
||||
PixelPerUnit = ToSingle(ModelData, canvasInfoOffset);
|
||||
CentralPosX = ToSingle(ModelData, canvasInfoOffset + 4);
|
||||
CentralPosY = ToSingle(ModelData, canvasInfoOffset + 8);
|
||||
CanvasWidth = ToSingle(ModelData, canvasInfoOffset + 12);
|
||||
CanvasHeight = ToSingle(ModelData, canvasInfoOffset + 16);
|
||||
|
||||
//model
|
||||
PartCount = SpanToUint32(ModelData, countInfoTableOffset, IsBigEndian);
|
||||
ParamCount = SpanToUint32(ModelData, countInfoTableOffset + 20, IsBigEndian);
|
||||
PartNames = ReadMocStringHashSet(ModelData, (int)partIdsOffset, (int)PartCount);
|
||||
ParamNames = ReadMocStringHashSet(ModelData, (int)parameterIdsOffset, (int)ParamCount);
|
||||
}
|
||||
|
||||
private string ParseVersion()
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case CubismSDKVersion.V30:
|
||||
return "SDK3.0/Cubism3.0(3.2)";
|
||||
case CubismSDKVersion.V33:
|
||||
return "SDK3.3/Cubism3.3";
|
||||
case CubismSDKVersion.V40:
|
||||
return "SDK4.0/Cubism4.0";
|
||||
case CubismSDKVersion.V42:
|
||||
return "SDK4.2/Cubism4.2";
|
||||
case CubismSDKVersion.V50:
|
||||
return "SDK5.0/Cubism5.0";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static float ToSingle(ReadOnlySpan<byte> data, int index) //net framework ver
|
||||
{
|
||||
var bytes = data.Slice(index, index + 4).ToArray();
|
||||
if ((IsBigEndian && BitConverter.IsLittleEndian) || (!IsBigEndian && !BitConverter.IsLittleEndian))
|
||||
(bytes[0], bytes[1], bytes[2], bytes[3]) = (bytes[3], bytes[2], bytes[1], bytes[0]);
|
||||
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
private static HashSet<string> ReadMocStringHashSet(ReadOnlySpan<byte> data, int index, int count)
|
||||
{
|
||||
const int strLen = 64;
|
||||
var strHashSet = new HashSet<string>();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (index + i * strLen <= data.Length)
|
||||
{
|
||||
var buff = data.Slice(index + i * strLen, strLen);
|
||||
strHashSet.Add(Encoding.UTF8.GetString(buff.ToArray()).TrimEnd('\0'));
|
||||
}
|
||||
}
|
||||
return strHashSet;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BigArrayPool<byte>.Shared.Return(ModelData);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,13 @@ namespace AssetStudio
|
||||
: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start));
|
||||
}
|
||||
|
||||
public static uint SpanToUint16(Span<byte> data, int start, bool isBigEndian)
|
||||
{
|
||||
return isBigEndian
|
||||
? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start))
|
||||
: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start));
|
||||
}
|
||||
|
||||
public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian)
|
||||
{
|
||||
return isBigEndian
|
||||
|
@ -748,7 +748,11 @@ namespace AssetStudioCLI.Options
|
||||
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.MonoBehaviour;
|
||||
break;
|
||||
case "animationclip":
|
||||
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.AnimationClip;
|
||||
case "animationclipv2":
|
||||
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.AnimationClipV2;
|
||||
break;
|
||||
case "animationclipv1":
|
||||
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.AnimationClipV1;
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported Live2D motion mode: [{value.Color(brightRed)}].\n");
|
||||
|
@ -1,12 +1,12 @@
|
||||
using AssetStudio;
|
||||
using AssetStudioCLI.Options;
|
||||
using CubismLive2DExtractor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using static AssetStudioCLI.Exporter;
|
||||
using static CubismLive2DExtractor.Live2DExtractor;
|
||||
using Ansi = AssetStudio.ColorConsole;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
@ -17,6 +17,7 @@ namespace AssetStudioCLI
|
||||
public static List<AssetItem> parsedAssetsList = new List<AssetItem>();
|
||||
public static List<BaseNode> gameObjectTree = new List<BaseNode>();
|
||||
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
|
||||
public static List<MonoBehaviour> cubismMocList = new List<MonoBehaviour>();
|
||||
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
|
||||
|
||||
static Studio()
|
||||
@ -125,14 +126,16 @@ namespace AssetStudioCLI
|
||||
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
|
||||
break;
|
||||
case MonoBehaviour m_MonoBehaviour:
|
||||
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
var assetName = m_MonoBehaviour.m_Name;
|
||||
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
{
|
||||
assetItem.Text = m_Script.m_ClassName;
|
||||
}
|
||||
else
|
||||
{
|
||||
assetItem.Text = m_MonoBehaviour.m_Name;
|
||||
assetName = assetName == "" ? m_Script.m_ClassName : assetName;
|
||||
if (m_Script.m_ClassName == "CubismMoc")
|
||||
{
|
||||
cubismMocList.Add(m_MonoBehaviour);
|
||||
}
|
||||
}
|
||||
assetItem.Text = assetName;
|
||||
break;
|
||||
case GameObject m_GameObject:
|
||||
assetItem.Text = m_GameObject.m_Name;
|
||||
@ -620,64 +623,57 @@ namespace AssetStudioCLI
|
||||
public static void ExportLive2D()
|
||||
{
|
||||
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
|
||||
var useFullContainerPath = false;
|
||||
var useFullContainerPath = true;
|
||||
var mocPathList = new List<string>();
|
||||
var basePathSet = new HashSet<string>();
|
||||
var motionMode = CLIOptions.o_l2dMotionMode.Value;
|
||||
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
|
||||
|
||||
Progress.Reset();
|
||||
Logger.Info($"Searching for Live2D files...");
|
||||
|
||||
var cubismMocs = parsedAssetsList.Where(x =>
|
||||
{
|
||||
if (x.Type == ClassIDType.MonoBehaviour)
|
||||
{
|
||||
((MonoBehaviour)x.Asset).m_Script.TryGet(out var m_Script);
|
||||
return m_Script?.m_ClassName == "CubismMoc";
|
||||
}
|
||||
return false;
|
||||
}).Select(x => x.Asset).ToArray();
|
||||
|
||||
if (cubismMocs.Length == 0)
|
||||
if (cubismMocList.Count == 0)
|
||||
{
|
||||
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
|
||||
return;
|
||||
}
|
||||
if (cubismMocs.Length > 1)
|
||||
|
||||
Progress.Reset();
|
||||
Logger.Info($"Searching for Live2D files...");
|
||||
|
||||
foreach (var mocMonoBehaviour in cubismMocList)
|
||||
{
|
||||
var basePathSet = cubismMocs.Select(x =>
|
||||
{
|
||||
var pathLen = containers.TryGetValue(x, out var itemContainer) ? itemContainer.LastIndexOf("/") : 0;
|
||||
pathLen = pathLen < 0 ? containers[x].Length : pathLen;
|
||||
return itemContainer?.Substring(0, pathLen);
|
||||
}).ToHashSet();
|
||||
if (!containers.TryGetValue(mocMonoBehaviour, out var fullContainerPath))
|
||||
continue;
|
||||
|
||||
if (basePathSet.All(x => x == null))
|
||||
{
|
||||
Logger.Error($"Live2D Cubism export error: Cannot find any model related files.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (basePathSet.Count != cubismMocs.Length)
|
||||
{
|
||||
useFullContainerPath = true;
|
||||
Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
|
||||
}
|
||||
var pathSepIndex = fullContainerPath.LastIndexOf('/');
|
||||
var basePath = pathSepIndex > 0
|
||||
? fullContainerPath.Substring(0, pathSepIndex)
|
||||
: fullContainerPath;
|
||||
basePathSet.Add(basePath);
|
||||
mocPathList.Add(fullContainerPath);
|
||||
}
|
||||
|
||||
var basePathList = cubismMocs.Select(x =>
|
||||
if (mocPathList.Count == 0)
|
||||
{
|
||||
containers.TryGetValue(x, out var container);
|
||||
container = useFullContainerPath
|
||||
? container
|
||||
: container?.Substring(0, container.LastIndexOf("/"));
|
||||
return container;
|
||||
}).Where(x => x != null).ToList();
|
||||
Logger.Error("Live2D Cubism export error: Cannot find any model related files.");
|
||||
return;
|
||||
}
|
||||
if (basePathSet.Count == mocPathList.Count)
|
||||
{
|
||||
mocPathList = basePathSet.ToList();
|
||||
useFullContainerPath = false;
|
||||
Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
|
||||
}
|
||||
basePathSet.Clear();
|
||||
|
||||
var lookup = containers.ToLookup(
|
||||
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
||||
x => mocPathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
||||
x => x.Key
|
||||
);
|
||||
|
||||
if (cubismMocList[0].serializedType?.m_Type == null && CLIOptions.o_assemblyPath.Value == "")
|
||||
{
|
||||
Logger.Warning("Specifying the assembly folder may be needed for proper extraction");
|
||||
}
|
||||
|
||||
var totalModelCount = lookup.LongCount(x => x.Key != null);
|
||||
Logger.Info($"Found {totalModelCount} model(s).");
|
||||
var modelCounter = 0;
|
||||
@ -691,11 +687,16 @@ namespace AssetStudioCLI
|
||||
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\"");
|
||||
try
|
||||
{
|
||||
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
|
||||
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
|
||||
var modelName = useFullContainerPath
|
||||
? Path.GetFileNameWithoutExtension(container)
|
||||
: container.Substring(container.LastIndexOf('/') + 1);
|
||||
container = Path.HasExtension(container)
|
||||
? container.Replace(Path.GetExtension(container), "")
|
||||
: container;
|
||||
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
|
||||
|
||||
ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
|
||||
var modelExtractor = new Live2DExtractor(assets);
|
||||
modelExtractor.ExtractCubismModel(destPath, modelName, motionMode, assemblyLoader, forceBezier);
|
||||
modelCounter++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
154
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
154
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
@ -68,7 +68,12 @@
|
||||
this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.allLive2DModelsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.live2DCubismModelsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.allL2DModelsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.selectedL2DModelsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.l2DModelWithFadeListToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.l2DModelWithFadeMotionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.l2DModelWithClipsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@ -140,8 +145,12 @@
|
||||
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportSelectedAssetsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.dumpSelectedAssetsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportAsLive2DModelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportL2DWithFadeLstToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportL2DWithFadeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exportL2DWithClipsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
@ -327,7 +336,7 @@
|
||||
this.exportAllObjectssplitToolStripMenuItem1.Name = "exportAllObjectssplitToolStripMenuItem1";
|
||||
this.exportAllObjectssplitToolStripMenuItem1.Size = new System.Drawing.Size(382, 22);
|
||||
this.exportAllObjectssplitToolStripMenuItem1.Text = "Export all objects (split)";
|
||||
this.exportAllObjectssplitToolStripMenuItem1.Click += new System.EventHandler(this.exportAllObjectssplitToolStripMenuItem1_Click);
|
||||
this.exportAllObjectssplitToolStripMenuItem1.Click += new System.EventHandler(this.exportAllObjectsSplitToolStripMenuItem1_Click);
|
||||
//
|
||||
// exportSelectedObjectsToolStripMenuItem
|
||||
//
|
||||
@ -341,7 +350,7 @@
|
||||
this.exportSelectedObjectsWithAnimationClipToolStripMenuItem.Name = "exportSelectedObjectsWithAnimationClipToolStripMenuItem";
|
||||
this.exportSelectedObjectsWithAnimationClipToolStripMenuItem.Size = new System.Drawing.Size(382, 22);
|
||||
this.exportSelectedObjectsWithAnimationClipToolStripMenuItem.Text = "Export selected objects (split) + selected AnimationClips";
|
||||
this.exportSelectedObjectsWithAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportObjectswithAnimationClipMenuItem_Click);
|
||||
this.exportSelectedObjectsWithAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportObjectsWithAnimationClipMenuItem_Click);
|
||||
//
|
||||
// toolStripSeparator1
|
||||
//
|
||||
@ -353,14 +362,14 @@
|
||||
this.exportSelectedObjectsmergeToolStripMenuItem.Name = "exportSelectedObjectsmergeToolStripMenuItem";
|
||||
this.exportSelectedObjectsmergeToolStripMenuItem.Size = new System.Drawing.Size(382, 22);
|
||||
this.exportSelectedObjectsmergeToolStripMenuItem.Text = "Export selected objects (merge)";
|
||||
this.exportSelectedObjectsmergeToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedObjectsmergeToolStripMenuItem_Click);
|
||||
this.exportSelectedObjectsmergeToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedObjectsMergeToolStripMenuItem_Click);
|
||||
//
|
||||
// exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem
|
||||
//
|
||||
this.exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem.Name = "exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem";
|
||||
this.exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem.Size = new System.Drawing.Size(382, 22);
|
||||
this.exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem.Text = "Export selected objects (merge) + selected AnimationClips";
|
||||
this.exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem_Click);
|
||||
this.exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedObjectsMergeWithAnimationClipToolStripMenuItem_Click);
|
||||
//
|
||||
// exportToolStripMenuItem
|
||||
//
|
||||
@ -374,7 +383,7 @@
|
||||
this.toolStripMenuItem2,
|
||||
this.toolStripMenuItem3,
|
||||
this.toolStripSeparator6,
|
||||
this.allLive2DModelsToolStripMenuItem,
|
||||
this.live2DCubismModelsToolStripMenuItem,
|
||||
this.toolStripSeparator2,
|
||||
this.toolStripMenuItem10});
|
||||
this.exportToolStripMenuItem.Name = "exportToolStripMenuItem";
|
||||
@ -412,7 +421,7 @@
|
||||
this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Name = "exportAnimatorWithSelectedAnimationClipToolStripMenuItem";
|
||||
this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Size = new System.Drawing.Size(266, 22);
|
||||
this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Text = "Animator + selected AnimationClips";
|
||||
this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportAnimatorwithAnimationClipMenuItem_Click);
|
||||
this.exportAnimatorWithSelectedAnimationClipToolStripMenuItem.Click += new System.EventHandler(this.exportAnimatorWithAnimationClipMenuItem_Click);
|
||||
//
|
||||
// toolStripSeparator4
|
||||
//
|
||||
@ -486,12 +495,52 @@
|
||||
this.toolStripSeparator6.Name = "toolStripSeparator6";
|
||||
this.toolStripSeparator6.Size = new System.Drawing.Size(263, 6);
|
||||
//
|
||||
// allLive2DModelsToolStripMenuItem
|
||||
// live2DCubismModelsToolStripMenuItem
|
||||
//
|
||||
this.allLive2DModelsToolStripMenuItem.Name = "allLive2DModelsToolStripMenuItem";
|
||||
this.allLive2DModelsToolStripMenuItem.Size = new System.Drawing.Size(266, 22);
|
||||
this.allLive2DModelsToolStripMenuItem.Text = "Live2D Cubism models";
|
||||
this.allLive2DModelsToolStripMenuItem.Click += new System.EventHandler(this.allLive2DModelsToolStripMenuItem_Click);
|
||||
this.live2DCubismModelsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.allL2DModelsToolStripMenuItem,
|
||||
this.selectedL2DModelsToolStripMenuItem,
|
||||
this.l2DModelWithFadeListToolStripMenuItem,
|
||||
this.l2DModelWithFadeMotionsToolStripMenuItem,
|
||||
this.l2DModelWithClipsToolStripMenuItem});
|
||||
this.live2DCubismModelsToolStripMenuItem.Name = "live2DCubismModelsToolStripMenuItem";
|
||||
this.live2DCubismModelsToolStripMenuItem.Size = new System.Drawing.Size(266, 22);
|
||||
this.live2DCubismModelsToolStripMenuItem.Text = "Live2D Cubism models";
|
||||
//
|
||||
// allL2DModelsToolStripMenuItem
|
||||
//
|
||||
this.allL2DModelsToolStripMenuItem.Name = "allL2DModelsToolStripMenuItem";
|
||||
this.allL2DModelsToolStripMenuItem.Size = new System.Drawing.Size(292, 22);
|
||||
this.allL2DModelsToolStripMenuItem.Text = "All models";
|
||||
this.allL2DModelsToolStripMenuItem.Click += new System.EventHandler(this.exportAllL2D_Click);
|
||||
//
|
||||
// selectedL2DModelsToolStripMenuItem
|
||||
//
|
||||
this.selectedL2DModelsToolStripMenuItem.Name = "selectedL2DModelsToolStripMenuItem";
|
||||
this.selectedL2DModelsToolStripMenuItem.Size = new System.Drawing.Size(292, 22);
|
||||
this.selectedL2DModelsToolStripMenuItem.Text = "Selected models";
|
||||
this.selectedL2DModelsToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2D_Click);
|
||||
//
|
||||
// l2DModelWithFadeListToolStripMenuItem
|
||||
//
|
||||
this.l2DModelWithFadeListToolStripMenuItem.Name = "l2DModelWithFadeListToolStripMenuItem";
|
||||
this.l2DModelWithFadeListToolStripMenuItem.Size = new System.Drawing.Size(292, 22);
|
||||
this.l2DModelWithFadeListToolStripMenuItem.Text = "Model + selected Fade Motion List";
|
||||
this.l2DModelWithFadeListToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2DWithFadeList_Click);
|
||||
//
|
||||
// l2DModelWithFadeMotionsToolStripMenuItem
|
||||
//
|
||||
this.l2DModelWithFadeMotionsToolStripMenuItem.Name = "l2DModelWithFadeMotionsToolStripMenuItem";
|
||||
this.l2DModelWithFadeMotionsToolStripMenuItem.Size = new System.Drawing.Size(292, 22);
|
||||
this.l2DModelWithFadeMotionsToolStripMenuItem.Text = "Model + selected Fade motions";
|
||||
this.l2DModelWithFadeMotionsToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2DWithFadeMotions_Click);
|
||||
//
|
||||
// l2DModelWithClipsToolStripMenuItem
|
||||
//
|
||||
this.l2DModelWithClipsToolStripMenuItem.Name = "l2DModelWithClipsToolStripMenuItem";
|
||||
this.l2DModelWithClipsToolStripMenuItem.Size = new System.Drawing.Size(292, 22);
|
||||
this.l2DModelWithClipsToolStripMenuItem.Text = "Model + selected AnimationClip motions";
|
||||
this.l2DModelWithClipsToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2DWithClips_Click);
|
||||
//
|
||||
// toolStripSeparator2
|
||||
//
|
||||
@ -1226,46 +1275,82 @@
|
||||
this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.copyToolStripMenuItem,
|
||||
this.exportSelectedAssetsToolStripMenuItem,
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem,
|
||||
this.dumpSelectedAssetsToolStripMenuItem,
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem,
|
||||
this.exportAsLive2DModelToolStripMenuItem,
|
||||
this.exportL2DWithFadeLstToolStripMenuItem,
|
||||
this.exportL2DWithFadeToolStripMenuItem,
|
||||
this.exportL2DWithClipsToolStripMenuItem,
|
||||
this.goToSceneHierarchyToolStripMenuItem,
|
||||
this.showOriginalFileToolStripMenuItem});
|
||||
this.contextMenuStrip1.Name = "contextMenuStrip1";
|
||||
this.contextMenuStrip1.Size = new System.Drawing.Size(304, 136);
|
||||
this.contextMenuStrip1.Size = new System.Drawing.Size(332, 246);
|
||||
//
|
||||
// copyToolStripMenuItem
|
||||
//
|
||||
this.copyToolStripMenuItem.Name = "copyToolStripMenuItem";
|
||||
this.copyToolStripMenuItem.Size = new System.Drawing.Size(303, 22);
|
||||
this.copyToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.copyToolStripMenuItem.Text = "Copy text";
|
||||
this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click);
|
||||
//
|
||||
// exportSelectedAssetsToolStripMenuItem
|
||||
//
|
||||
this.exportSelectedAssetsToolStripMenuItem.Name = "exportSelectedAssetsToolStripMenuItem";
|
||||
this.exportSelectedAssetsToolStripMenuItem.Size = new System.Drawing.Size(303, 22);
|
||||
this.exportSelectedAssetsToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.exportSelectedAssetsToolStripMenuItem.Text = "Export selected assets";
|
||||
this.exportSelectedAssetsToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedAssetsToolStripMenuItem_Click);
|
||||
//
|
||||
// exportAnimatorwithselectedAnimationClipMenuItem
|
||||
//
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem.Name = "exportAnimatorwithselectedAnimationClipMenuItem";
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem.Size = new System.Drawing.Size(303, 22);
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem.Text = "Export Animator + selected AnimationClips";
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem.Visible = false;
|
||||
this.exportAnimatorwithselectedAnimationClipMenuItem.Click += new System.EventHandler(this.exportAnimatorwithAnimationClipMenuItem_Click);
|
||||
//
|
||||
// dumpSelectedAssetsToolStripMenuItem
|
||||
//
|
||||
this.dumpSelectedAssetsToolStripMenuItem.Name = "dumpSelectedAssetsToolStripMenuItem";
|
||||
this.dumpSelectedAssetsToolStripMenuItem.Size = new System.Drawing.Size(303, 22);
|
||||
this.dumpSelectedAssetsToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.dumpSelectedAssetsToolStripMenuItem.Text = "Dump selected assets";
|
||||
this.dumpSelectedAssetsToolStripMenuItem.Click += new System.EventHandler(this.dumpSelectedAssetsToolStripMenuItem_Click);
|
||||
//
|
||||
// exportAnimatorWithSelectedAnimationClipMenuItem
|
||||
//
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem.Name = "exportAnimatorWithSelectedAnimationClipMenuItem";
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem.Text = "Export Animator + selected AnimationClips";
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem.Visible = false;
|
||||
this.exportAnimatorWithSelectedAnimationClipMenuItem.Click += new System.EventHandler(this.exportAnimatorWithAnimationClipMenuItem_Click);
|
||||
//
|
||||
// exportAsLive2DModelToolStripMenuItem
|
||||
//
|
||||
this.exportAsLive2DModelToolStripMenuItem.Name = "exportAsLive2DModelToolStripMenuItem";
|
||||
this.exportAsLive2DModelToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.exportAsLive2DModelToolStripMenuItem.Text = "Export as Live2D model(s)";
|
||||
this.exportAsLive2DModelToolStripMenuItem.Visible = false;
|
||||
this.exportAsLive2DModelToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2D_Click);
|
||||
//
|
||||
// exportL2DWithFadeLstToolStripMenuItem
|
||||
//
|
||||
this.exportL2DWithFadeLstToolStripMenuItem.Name = "exportL2DWithFadeLstToolStripMenuItem";
|
||||
this.exportL2DWithFadeLstToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.exportL2DWithFadeLstToolStripMenuItem.Text = "Export Live2D model + selected Fade Motion List";
|
||||
this.exportL2DWithFadeLstToolStripMenuItem.Visible = false;
|
||||
this.exportL2DWithFadeLstToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2DWithFadeList_Click);
|
||||
//
|
||||
// exportL2DWithFadeToolStripMenuItem
|
||||
//
|
||||
this.exportL2DWithFadeToolStripMenuItem.Name = "exportL2DWithFadeToolStripMenuItem";
|
||||
this.exportL2DWithFadeToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.exportL2DWithFadeToolStripMenuItem.Text = "Export Live2D model + selected Fade motions";
|
||||
this.exportL2DWithFadeToolStripMenuItem.Visible = false;
|
||||
this.exportL2DWithFadeToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2DWithFadeMotions_Click);
|
||||
//
|
||||
// exportL2DWithClipsToolStripMenuItem
|
||||
//
|
||||
this.exportL2DWithClipsToolStripMenuItem.Name = "exportL2DWithClipsToolStripMenuItem";
|
||||
this.exportL2DWithClipsToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.exportL2DWithClipsToolStripMenuItem.Text = "Export Live2D model + selected AnimationClips";
|
||||
this.exportL2DWithClipsToolStripMenuItem.Visible = false;
|
||||
this.exportL2DWithClipsToolStripMenuItem.Click += new System.EventHandler(this.exportSelectedL2DWithClips_Click);
|
||||
//
|
||||
// goToSceneHierarchyToolStripMenuItem
|
||||
//
|
||||
this.goToSceneHierarchyToolStripMenuItem.Name = "goToSceneHierarchyToolStripMenuItem";
|
||||
this.goToSceneHierarchyToolStripMenuItem.Size = new System.Drawing.Size(303, 22);
|
||||
this.goToSceneHierarchyToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.goToSceneHierarchyToolStripMenuItem.Text = "Go to scene hierarchy";
|
||||
this.goToSceneHierarchyToolStripMenuItem.Visible = false;
|
||||
this.goToSceneHierarchyToolStripMenuItem.Click += new System.EventHandler(this.goToSceneHierarchyToolStripMenuItem_Click);
|
||||
@ -1273,7 +1358,7 @@
|
||||
// showOriginalFileToolStripMenuItem
|
||||
//
|
||||
this.showOriginalFileToolStripMenuItem.Name = "showOriginalFileToolStripMenuItem";
|
||||
this.showOriginalFileToolStripMenuItem.Size = new System.Drawing.Size(303, 22);
|
||||
this.showOriginalFileToolStripMenuItem.Size = new System.Drawing.Size(331, 22);
|
||||
this.showOriginalFileToolStripMenuItem.Text = "Show original file";
|
||||
this.showOriginalFileToolStripMenuItem.Visible = false;
|
||||
this.showOriginalFileToolStripMenuItem.Click += new System.EventHandler(this.showOriginalFileToolStripMenuItem_Click);
|
||||
@ -1387,7 +1472,7 @@
|
||||
private OpenTK.GLControl glControl1;
|
||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem showOriginalFileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportAnimatorwithselectedAnimationClipMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportAnimatorWithSelectedAnimationClipMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportSelectedAssetsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem filterTypeToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem allToolStripMenuItem;
|
||||
@ -1437,13 +1522,22 @@
|
||||
private System.Windows.Forms.ComboBox listSearchHistory;
|
||||
private System.Windows.Forms.RichTextBox listSearch;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator6;
|
||||
private System.Windows.Forms.ToolStripMenuItem allLive2DModelsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem showRelatedAssetsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator7;
|
||||
private System.Windows.Forms.ListView assetListView;
|
||||
private System.Windows.Forms.ToolStripMenuItem showConsoleToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem writeLogToFileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem buildTreeStructureToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportL2DWithClipsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportAsLive2DModelToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem live2DCubismModelsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem allL2DModelsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem selectedL2DModelsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem l2DModelWithClipsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem l2DModelWithFadeMotionsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportL2DWithFadeToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem l2DModelWithFadeListToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem exportL2DWithFadeLstToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -822,7 +822,16 @@ namespace AssetStudioGUI
|
||||
PreviewTextAsset(assetItem.Asset as TextAsset);
|
||||
break;
|
||||
case ClassIDType.MonoBehaviour:
|
||||
PreviewMonoBehaviour(assetItem.Asset as MonoBehaviour);
|
||||
var m_MonoBehaviour = (MonoBehaviour)assetItem.Asset;
|
||||
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
{
|
||||
if (m_Script.m_ClassName == "CubismMoc")
|
||||
{
|
||||
PreviewMoc(assetItem, m_MonoBehaviour);
|
||||
break;
|
||||
}
|
||||
}
|
||||
PreviewMonoBehaviour(m_MonoBehaviour);
|
||||
break;
|
||||
case ClassIDType.Font:
|
||||
PreviewFont(assetItem.Asset as Font);
|
||||
@ -1083,6 +1092,27 @@ namespace AssetStudioGUI
|
||||
PreviewText(str);
|
||||
}
|
||||
|
||||
private void PreviewMoc(AssetItem assetItem, MonoBehaviour m_MonoBehaviour)
|
||||
{
|
||||
using (var cubismModel = new CubismModel(m_MonoBehaviour))
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"SDK Version: {cubismModel.VersionDescription}");
|
||||
if (cubismModel.Version > 0)
|
||||
{
|
||||
sb.AppendLine($"Canvas Width: {cubismModel.CanvasWidth}");
|
||||
sb.AppendLine($"Canvas Height: {cubismModel.CanvasHeight}");
|
||||
sb.AppendLine($"Center X: {cubismModel.CentralPosX}");
|
||||
sb.AppendLine($"Center Y: {cubismModel.CentralPosY}");
|
||||
sb.AppendLine($"Pixel Per Unit: {cubismModel.PixelPerUnit}");
|
||||
sb.AppendLine($"Parameter Count: {cubismModel.ParamCount}");
|
||||
sb.AppendLine($"Part Count: {cubismModel.PartCount}");
|
||||
}
|
||||
assetItem.InfoText = sb.ToString();
|
||||
}
|
||||
StatusStripUpdate("Can be exported as Live2D Cubism model.");
|
||||
}
|
||||
|
||||
private void PreviewFont(Font m_Font)
|
||||
{
|
||||
if (m_Font.m_FontData != null)
|
||||
@ -1374,6 +1404,7 @@ namespace AssetStudioGUI
|
||||
classesListView.Groups.Clear();
|
||||
selectedAnimationAssetsList.Clear();
|
||||
selectedIndicesPrevList.Clear();
|
||||
cubismMocList.Clear();
|
||||
previewPanel.BackgroundImage = Properties.Resources.preview;
|
||||
imageTexture?.Dispose();
|
||||
imageTexture = null;
|
||||
@ -1417,7 +1448,11 @@ namespace AssetStudioGUI
|
||||
{
|
||||
goToSceneHierarchyToolStripMenuItem.Visible = false;
|
||||
showOriginalFileToolStripMenuItem.Visible = false;
|
||||
exportAnimatorwithselectedAnimationClipMenuItem.Visible = false;
|
||||
exportAnimatorWithSelectedAnimationClipMenuItem.Visible = false;
|
||||
exportAsLive2DModelToolStripMenuItem.Visible = false;
|
||||
exportL2DWithFadeLstToolStripMenuItem.Visible = false;
|
||||
exportL2DWithFadeToolStripMenuItem.Visible = false;
|
||||
exportL2DWithClipsToolStripMenuItem.Visible = false;
|
||||
|
||||
if (assetListView.SelectedIndices.Count == 1)
|
||||
{
|
||||
@ -1427,10 +1462,42 @@ namespace AssetStudioGUI
|
||||
if (assetListView.SelectedIndices.Count >= 1)
|
||||
{
|
||||
var selectedAssets = GetSelectedAssets();
|
||||
if (selectedAssets.Any(x => x.Type == ClassIDType.Animator) && selectedAssets.Any(x => x.Type == ClassIDType.AnimationClip))
|
||||
|
||||
var selectedTypes = (SelectedAssetType)0;
|
||||
foreach (var asset in selectedAssets)
|
||||
{
|
||||
exportAnimatorwithselectedAnimationClipMenuItem.Visible = true;
|
||||
switch (asset.Asset)
|
||||
{
|
||||
case MonoBehaviour m_MonoBehaviour:
|
||||
if (Studio.cubismMocList.Count > 0 && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
{
|
||||
if (m_Script.m_ClassName == "CubismMoc")
|
||||
{
|
||||
selectedTypes |= SelectedAssetType.MonoBehaviourMoc;
|
||||
}
|
||||
else if (m_Script.m_ClassName == "CubismFadeMotionData")
|
||||
{
|
||||
selectedTypes |= SelectedAssetType.MonoBehaviourFade;
|
||||
}
|
||||
else if (m_Script.m_ClassName == "CubismFadeMotionList")
|
||||
{
|
||||
selectedTypes |= SelectedAssetType.MonoBehaviourFadeLst;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AnimationClip _:
|
||||
selectedTypes |= SelectedAssetType.AnimationClip;
|
||||
break;
|
||||
case Animator _:
|
||||
selectedTypes |= SelectedAssetType.Animator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
exportAnimatorWithSelectedAnimationClipMenuItem.Visible = (selectedTypes & SelectedAssetType.Animator) !=0 && (selectedTypes & SelectedAssetType.AnimationClip) != 0;
|
||||
exportAsLive2DModelToolStripMenuItem.Visible = (selectedTypes & SelectedAssetType.MonoBehaviourMoc) != 0;
|
||||
exportL2DWithFadeLstToolStripMenuItem.Visible = (selectedTypes & SelectedAssetType.MonoBehaviourMoc) !=0 && (selectedTypes & SelectedAssetType.MonoBehaviourFadeLst) != 0;
|
||||
exportL2DWithFadeToolStripMenuItem.Visible = (selectedTypes & SelectedAssetType.MonoBehaviourMoc) != 0 && (selectedTypes & SelectedAssetType.MonoBehaviourFade) !=0;
|
||||
exportL2DWithClipsToolStripMenuItem.Visible = (selectedTypes & SelectedAssetType.MonoBehaviourMoc) !=0 && (selectedTypes & SelectedAssetType.AnimationClip) != 0;
|
||||
}
|
||||
|
||||
var selectedElement = assetListView.HitTest(new Point(e.X, e.Y));
|
||||
@ -1458,13 +1525,13 @@ namespace AssetStudioGUI
|
||||
|
||||
private void showOriginalFileToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var selectasset = (AssetItem)assetListView.Items[assetListView.SelectedIndices[0]];
|
||||
var args = $"/select, \"{selectasset.SourceFile.originalPath ?? selectasset.SourceFile.fullName}\"";
|
||||
var selectAsset = (AssetItem)assetListView.Items[assetListView.SelectedIndices[0]];
|
||||
var args = $"/select, \"{selectAsset.SourceFile.originalPath ?? selectAsset.SourceFile.fullName}\"";
|
||||
var pfi = new ProcessStartInfo("explorer.exe", args);
|
||||
Process.Start(pfi);
|
||||
}
|
||||
|
||||
private void exportAnimatorwithAnimationClipMenuItem_Click(object sender, EventArgs e)
|
||||
private void exportAnimatorWithAnimationClipMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
AssetItem animator = null;
|
||||
var selectedAssets = GetSelectedAssets();
|
||||
@ -1494,7 +1561,7 @@ namespace AssetStudioGUI
|
||||
ExportObjects(false);
|
||||
}
|
||||
|
||||
private void exportObjectswithAnimationClipMenuItem_Click(object sender, EventArgs e)
|
||||
private void exportObjectsWithAnimationClipMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportObjects(true);
|
||||
}
|
||||
@ -1527,12 +1594,12 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
|
||||
private void exportSelectedObjectsmergeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private void exportSelectedObjectsMergeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportMergeObjects(false);
|
||||
}
|
||||
|
||||
private void exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private void exportSelectedObjectsMergeWithAnimationClipToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportMergeObjects(true);
|
||||
}
|
||||
@ -1575,10 +1642,10 @@ namespace AssetStudioGUI
|
||||
|
||||
private void goToSceneHierarchyToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var selectasset = (AssetItem)assetListView.Items[assetListView.SelectedIndices[0]];
|
||||
if (selectasset.TreeNode != null)
|
||||
var selectAsset = (AssetItem)assetListView.Items[assetListView.SelectedIndices[0]];
|
||||
if (selectAsset.TreeNode != null)
|
||||
{
|
||||
sceneTreeView.SelectedNode = selectasset.TreeNode;
|
||||
sceneTreeView.SelectedNode = selectAsset.TreeNode;
|
||||
tabControl1.SelectedTab = tabPage1;
|
||||
}
|
||||
}
|
||||
@ -1643,7 +1710,7 @@ namespace AssetStudioGUI
|
||||
ExportAssetsList(ExportFilter.Filtered);
|
||||
}
|
||||
|
||||
private void exportAllObjectssplitToolStripMenuItem1_Click(object sender, EventArgs e)
|
||||
private void exportAllObjectsSplitToolStripMenuItem1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (sceneTreeView.Nodes.Count > 0)
|
||||
{
|
||||
@ -1938,42 +2005,6 @@ namespace AssetStudioGUI
|
||||
listSearch.SelectionStart = listSearch.Text.Length;
|
||||
}
|
||||
|
||||
private void allLive2DModelsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (exportableAssets.Count > 0)
|
||||
{
|
||||
var cubismMocs = exportableAssets.Where(x =>
|
||||
{
|
||||
if (x.Type == ClassIDType.MonoBehaviour)
|
||||
{
|
||||
((MonoBehaviour)x.Asset).m_Script.TryGet(out var m_Script);
|
||||
return m_Script?.m_ClassName == "CubismMoc";
|
||||
}
|
||||
return false;
|
||||
}).Select(x => x.Asset).ToArray();
|
||||
if (cubismMocs.Length == 0)
|
||||
{
|
||||
Logger.Info("Live2D Cubism models were not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var saveFolderDialog = new OpenFolderDialog();
|
||||
saveFolderDialog.InitialFolder = saveDirectoryBackup;
|
||||
if (saveFolderDialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
timer.Stop();
|
||||
saveDirectoryBackup = saveFolderDialog.Folder;
|
||||
Progress.Reset();
|
||||
BeginInvoke(new Action(() => { progressBar1.Style = ProgressBarStyle.Marquee; }));
|
||||
Studio.ExportLive2D(cubismMocs, saveFolderDialog.Folder);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("No exportable assets loaded");
|
||||
}
|
||||
}
|
||||
|
||||
private void selectRelatedAsset(object sender, EventArgs e)
|
||||
{
|
||||
var selectedItem = (ToolStripMenuItem)sender;
|
||||
@ -2077,6 +2108,138 @@ namespace AssetStudioGUI
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void exportAllL2D_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (exportableAssets.Count > 0)
|
||||
{
|
||||
if (Studio.cubismMocList.Count == 0)
|
||||
{
|
||||
Logger.Info("Live2D Cubism models were not found.");
|
||||
return;
|
||||
}
|
||||
Live2DExporter();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("No exportable assets loaded");
|
||||
}
|
||||
}
|
||||
|
||||
private void exportSelectedL2D_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportSelectedL2DModels(ExportL2DFilter.Selected);
|
||||
}
|
||||
|
||||
private void exportSelectedL2DWithClips_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportSelectedL2DModels(ExportL2DFilter.SelectedWithClips);
|
||||
}
|
||||
|
||||
private void exportSelectedL2DWithFadeMotions_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportSelectedL2DModels(ExportL2DFilter.SelectedWithFade);
|
||||
}
|
||||
|
||||
private void exportSelectedL2DWithFadeList_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExportSelectedL2DModels(ExportL2DFilter.SelectedWithFadeList);
|
||||
}
|
||||
|
||||
private void ExportSelectedL2DModels(ExportL2DFilter l2dExportMode)
|
||||
{
|
||||
if (exportableAssets.Count == 0)
|
||||
{
|
||||
Logger.Info("No exportable assets loaded");
|
||||
return;
|
||||
}
|
||||
if (Studio.cubismMocList.Count == 0)
|
||||
{
|
||||
Logger.Info("Live2D Cubism models were not found.");
|
||||
return;
|
||||
}
|
||||
var selectedAssets = GetSelectedAssets();
|
||||
if (selectedAssets.Count == 0)
|
||||
return;
|
||||
|
||||
MonoBehaviour selectedFadeLst = null;
|
||||
var selectedMocs = new List<MonoBehaviour>();
|
||||
var selectedFadeMotions = new List<MonoBehaviour>();
|
||||
var selectedClips = new List<AnimationClip>();
|
||||
foreach (var assetItem in selectedAssets)
|
||||
{
|
||||
if (assetItem.Asset is MonoBehaviour m_MonoBehaviour && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
{
|
||||
if (m_Script.m_ClassName == "CubismMoc")
|
||||
{
|
||||
selectedMocs.Add(m_MonoBehaviour);
|
||||
}
|
||||
else if (m_Script.m_ClassName == "CubismFadeMotionData")
|
||||
{
|
||||
selectedFadeMotions.Add(m_MonoBehaviour);
|
||||
}
|
||||
else if (m_Script.m_ClassName == "CubismFadeMotionList")
|
||||
{
|
||||
selectedFadeLst = m_MonoBehaviour;
|
||||
}
|
||||
}
|
||||
else if (assetItem.Asset is AnimationClip m_AnimationClip)
|
||||
{
|
||||
selectedClips.Add(m_AnimationClip);
|
||||
}
|
||||
}
|
||||
if (selectedMocs.Count == 0)
|
||||
{
|
||||
Logger.Info("Live2D Cubism models were not selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (l2dExportMode)
|
||||
{
|
||||
case ExportL2DFilter.Selected:
|
||||
Live2DExporter(selectedMocs);
|
||||
break;
|
||||
case ExportL2DFilter.SelectedWithFadeList:
|
||||
if (selectedFadeLst == null)
|
||||
{
|
||||
Logger.Info("Fade Motion List was not selected.");
|
||||
return;
|
||||
}
|
||||
Live2DExporter(selectedMocs, selFadeLst: selectedFadeLst);
|
||||
break;
|
||||
case ExportL2DFilter.SelectedWithFade:
|
||||
if (selectedFadeMotions.Count == 0)
|
||||
{
|
||||
Logger.Info("No Fade motions were selected.");
|
||||
return;
|
||||
}
|
||||
Live2DExporter(selectedMocs, selFadeMotions: selectedFadeMotions);
|
||||
break;
|
||||
case ExportL2DFilter.SelectedWithClips:
|
||||
if (selectedClips.Count == 0)
|
||||
{
|
||||
Logger.Info("No AnimationClips were selected.");
|
||||
return;
|
||||
}
|
||||
Live2DExporter(selectedMocs, selectedClips);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Live2DExporter(List<MonoBehaviour> selMocs = null, List<AnimationClip> selClipMotions = null, List<MonoBehaviour> selFadeMotions = null, MonoBehaviour selFadeLst = null)
|
||||
{
|
||||
var saveFolderDialog = new OpenFolderDialog();
|
||||
saveFolderDialog.InitialFolder = saveDirectoryBackup;
|
||||
if (saveFolderDialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
timer.Stop();
|
||||
saveDirectoryBackup = saveFolderDialog.Folder;
|
||||
Progress.Reset();
|
||||
BeginInvoke(new Action(() => { progressBar1.Style = ProgressBarStyle.Marquee; }));
|
||||
|
||||
Studio.ExportLive2D(saveFolderDialog.Folder, selMocs, selClipMotions, selFadeMotions, selFadeLst);
|
||||
}
|
||||
}
|
||||
|
||||
#region FMOD
|
||||
private void FMODinit()
|
||||
{
|
||||
|
@ -120,6 +120,9 @@
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>312, 17</value>
|
||||
</metadata>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>432, 17</value>
|
||||
</metadata>
|
||||
<data name="fontPreviewBox.Text" xml:space="preserve">
|
||||
<value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ
|
||||
1234567890.:,;'\"(!?)+-*/=
|
||||
@ -138,9 +141,6 @@ The quick brown fox jumps over the lazy dog. 1234567890
|
||||
|
||||
The quick brown fox jumps over the lazy dog. 1234567890</value>
|
||||
</data>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>432, 17</value>
|
||||
</metadata>
|
||||
<metadata name="contextMenuStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>775, 21</value>
|
||||
</metadata>
|
||||
|
2
AssetStudioGUI/ExportOptions.Designer.cs
generated
2
AssetStudioGUI/ExportOptions.Designer.cs
generated
@ -305,7 +305,7 @@
|
||||
//
|
||||
// l2dAnimationClipRadioButton
|
||||
//
|
||||
this.l2dAnimationClipRadioButton.AccessibleName = "AnimationClip";
|
||||
this.l2dAnimationClipRadioButton.AccessibleName = "AnimationClipV2";
|
||||
this.l2dAnimationClipRadioButton.AutoSize = true;
|
||||
this.l2dAnimationClipRadioButton.Location = new System.Drawing.Point(172, 5);
|
||||
this.l2dAnimationClipRadioButton.Name = "l2dAnimationClipRadioButton";
|
||||
|
@ -1,4 +1,5 @@
|
||||
using AssetStudio;
|
||||
using CubismLive2DExtractor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -9,7 +10,6 @@ using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml.Linq;
|
||||
using static AssetStudioGUI.Exporter;
|
||||
using static CubismLive2DExtractor.Live2DExtractor;
|
||||
using Object = AssetStudio.Object;
|
||||
|
||||
namespace AssetStudioGUI
|
||||
@ -28,6 +28,15 @@ namespace AssetStudioGUI
|
||||
Filtered
|
||||
}
|
||||
|
||||
internal enum ExportL2DFilter
|
||||
{
|
||||
All,
|
||||
Selected,
|
||||
SelectedWithFadeList,
|
||||
SelectedWithFade,
|
||||
SelectedWithClips,
|
||||
}
|
||||
|
||||
internal enum ExportListType
|
||||
{
|
||||
XML
|
||||
@ -49,13 +58,24 @@ namespace AssetStudioGUI
|
||||
RegexContainer,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum SelectedAssetType
|
||||
{
|
||||
Animator = 0x01,
|
||||
AnimationClip = 0x02,
|
||||
MonoBehaviourMoc = 0x04,
|
||||
MonoBehaviourFade = 0x08,
|
||||
MonoBehaviourFadeLst = 0x10
|
||||
}
|
||||
|
||||
internal static class Studio
|
||||
{
|
||||
public static AssetsManager assetsManager = new AssetsManager();
|
||||
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
|
||||
public static List<AssetItem> exportableAssets = new List<AssetItem>();
|
||||
public static List<AssetItem> visibleAssets = new List<AssetItem>();
|
||||
private static Dictionary<Object, string> allContainers = new Dictionary<Object, string>();
|
||||
public static List<MonoBehaviour> cubismMocList = new List<MonoBehaviour>();
|
||||
private static Dictionary<Object, string> l2dResourceContainers = new Dictionary<Object, string>();
|
||||
internal static Action<string> StatusStripUpdate = x => { };
|
||||
|
||||
public static int ExtractFolder(string path, string savePath)
|
||||
@ -158,7 +178,7 @@ namespace AssetStudioGUI
|
||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||
var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount);
|
||||
var containers = new List<(PPtr<Object>, string)>();
|
||||
allContainers.Clear();
|
||||
l2dResourceContainers.Clear();
|
||||
var i = 0;
|
||||
Progress.Reset();
|
||||
foreach (var assetsFile in assetsManager.assetsFileList)
|
||||
@ -218,14 +238,16 @@ namespace AssetStudioGUI
|
||||
exportable = true;
|
||||
break;
|
||||
case MonoBehaviour m_MonoBehaviour:
|
||||
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
var assetName = m_MonoBehaviour.m_Name;
|
||||
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
{
|
||||
assetItem.Text = m_Script.m_ClassName;
|
||||
}
|
||||
else
|
||||
{
|
||||
assetItem.Text = m_MonoBehaviour.m_Name;
|
||||
assetName = assetName == "" ? m_Script.m_ClassName : assetName;
|
||||
if (m_Script.m_ClassName == "CubismMoc")
|
||||
{
|
||||
cubismMocList.Add(m_MonoBehaviour);
|
||||
}
|
||||
}
|
||||
assetItem.Text = assetName;
|
||||
exportable = true;
|
||||
break;
|
||||
case PlayerSettings m_PlayerSettings:
|
||||
@ -276,7 +298,15 @@ namespace AssetStudioGUI
|
||||
if (pptr.TryGet(out var obj))
|
||||
{
|
||||
objectAssetItemDic[obj].Container = container;
|
||||
allContainers[obj] = container;
|
||||
switch (obj)
|
||||
{
|
||||
case AnimationClip _:
|
||||
case GameObject _:
|
||||
case Texture2D _:
|
||||
case MonoBehaviour _:
|
||||
l2dResourceContainers[obj] = container;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var tmp in exportableAssets)
|
||||
@ -725,6 +755,12 @@ namespace AssetStudioGUI
|
||||
}
|
||||
|
||||
public static TypeTree MonoBehaviourToTypeTree(MonoBehaviour m_MonoBehaviour)
|
||||
{
|
||||
SelectAssemblyFolder();
|
||||
return m_MonoBehaviour.ConvertToTypeTree(assemblyLoader);
|
||||
}
|
||||
|
||||
private static void SelectAssemblyFolder()
|
||||
{
|
||||
if (!assemblyLoader.Loaded)
|
||||
{
|
||||
@ -739,7 +775,6 @@ namespace AssetStudioGUI
|
||||
assemblyLoader.Loaded = true;
|
||||
}
|
||||
}
|
||||
return m_MonoBehaviour.ConvertToTypeTree(assemblyLoader);
|
||||
}
|
||||
|
||||
public static string DumpAsset(Object obj)
|
||||
@ -760,54 +795,64 @@ namespace AssetStudioGUI
|
||||
Process.Start(info);
|
||||
}
|
||||
|
||||
public static void ExportLive2D(Object[] cubismMocs, string exportPath)
|
||||
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 motionMode = Properties.Settings.Default.l2dMotionMode;
|
||||
var forceBezier = Properties.Settings.Default.l2dForceBezier;
|
||||
var mocList = selMocs ?? cubismMocList;
|
||||
var motionMode = Properties.Settings.Default.l2dMotionMode;
|
||||
if (selClipMotions != null)
|
||||
motionMode = Live2DMotionMode.AnimationClipV2;
|
||||
else if (selFadeMotions != null || selFadeLst != null)
|
||||
motionMode = Live2DMotionMode.MonoBehaviour;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
Logger.Info($"Searching for Live2D files...");
|
||||
|
||||
var useFullContainerPath = false;
|
||||
if (cubismMocs.Length > 1)
|
||||
var mocPathDict = new Dictionary<MonoBehaviour, (string, string)>();
|
||||
var mocPathList = new List<string>();
|
||||
foreach (var mocMonoBehaviour in cubismMocList)
|
||||
{
|
||||
var basePathSet = cubismMocs.Select(x =>
|
||||
{
|
||||
var pathLen = allContainers.TryGetValue(x, out var itemContainer) ? itemContainer.LastIndexOf("/") : 0;
|
||||
pathLen = pathLen < 0 ? allContainers[x].Length : pathLen;
|
||||
return itemContainer?.Substring(0, pathLen);
|
||||
}).ToHashSet();
|
||||
if (!l2dResourceContainers.TryGetValue(mocMonoBehaviour, out var fullContainerPath))
|
||||
continue;
|
||||
|
||||
if (basePathSet.All(x => x == null))
|
||||
{
|
||||
Logger.Error($"Live2D Cubism export error\r\nCannot find any model related files");
|
||||
StatusStripUpdate("Live2D export canceled");
|
||||
Progress.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (basePathSet.Count != cubismMocs.Length)
|
||||
{
|
||||
useFullContainerPath = true;
|
||||
}
|
||||
var pathSepIndex = fullContainerPath.LastIndexOf('/');
|
||||
var basePath = pathSepIndex > 0
|
||||
? fullContainerPath.Substring(0, pathSepIndex)
|
||||
: fullContainerPath;
|
||||
mocPathDict.Add(mocMonoBehaviour, (fullContainerPath, basePath));
|
||||
}
|
||||
if (mocPathDict.Count == 0)
|
||||
{
|
||||
Logger.Error("Live2D Cubism export error\r\nCannot find any model related files");
|
||||
StatusStripUpdate("Live2D export canceled");
|
||||
Progress.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
var basePathList = cubismMocs.Select(x =>
|
||||
var basePathSet = mocPathDict.Values.Select(x => x.Item2).ToHashSet();
|
||||
var useFullContainerPath = mocPathDict.Count != basePathSet.Count;
|
||||
foreach (var moc in mocList)
|
||||
{
|
||||
allContainers.TryGetValue(x, out var container);
|
||||
container = useFullContainerPath
|
||||
? container
|
||||
: container?.Substring(0, container.LastIndexOf("/"));
|
||||
return container;
|
||||
}).Where(x => x != null).ToList();
|
||||
var mocPath = useFullContainerPath
|
||||
? mocPathDict[moc].Item1 //fullContainerPath
|
||||
: mocPathDict[moc].Item2; //basePath
|
||||
mocPathList.Add(mocPath);
|
||||
}
|
||||
mocPathDict.Clear();
|
||||
|
||||
var lookup = allContainers.ToLookup(
|
||||
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
||||
var lookup = l2dResourceContainers.ToLookup(
|
||||
x => mocPathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
||||
x => x.Key
|
||||
);
|
||||
|
||||
if (mocList[0].serializedType?.m_Type == null && !assemblyLoader.Loaded)
|
||||
{
|
||||
Logger.Warning("Specifying the assembly folder may be needed for proper extraction");
|
||||
SelectAssemblyFolder();
|
||||
}
|
||||
|
||||
var totalModelCount = lookup.LongCount(x => x.Key != null);
|
||||
var modelCounter = 0;
|
||||
foreach (var assets in lookup)
|
||||
@ -820,11 +865,16 @@ namespace AssetStudioGUI
|
||||
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer}\"...");
|
||||
try
|
||||
{
|
||||
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
|
||||
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
|
||||
var modelName = useFullContainerPath
|
||||
? Path.GetFileNameWithoutExtension(container)
|
||||
: container.Substring(container.LastIndexOf('/') + 1);
|
||||
container = Path.HasExtension(container)
|
||||
? container.Replace(Path.GetExtension(container), "")
|
||||
: container;
|
||||
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
|
||||
|
||||
ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
|
||||
var modelExtractor = new Live2DExtractor(assets, selClipMotions, selFadeMotions, selFadeLst);
|
||||
modelExtractor.ExtractCubismModel(destPath, modelName, motionMode, assemblyLoader, forceBezier);
|
||||
modelCounter++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace CubismLive2DExtractor
|
||||
{
|
||||
public class AnimationCurve
|
||||
public sealed class AnimationCurve
|
||||
{
|
||||
public CubismKeyframeData[] m_Curve { get; set; }
|
||||
public int m_PreInfinity { get; set; }
|
||||
@ -10,7 +10,7 @@ namespace CubismLive2DExtractor
|
||||
public int m_RotationOrder { get; set; }
|
||||
}
|
||||
|
||||
public class CubismFadeMotion
|
||||
public sealed class CubismFadeMotion
|
||||
{
|
||||
public string m_Name { get; set; }
|
||||
public string MotionName { get; set; }
|
||||
|
@ -10,14 +10,21 @@ namespace CubismLive2DExtractor
|
||||
private Dictionary<uint, string> bonePathHash = new Dictionary<uint, string>();
|
||||
public List<ImportedKeyframedAnimation> AnimationList { get; protected set; } = new List<ImportedKeyframedAnimation>();
|
||||
|
||||
public CubismMotion3Converter(GameObject rootGameObject, AnimationClip[] animationClips)
|
||||
public CubismMotion3Converter(GameObject rootGameObject, List<AnimationClip> animationClips)
|
||||
{
|
||||
var rootTransform = GetTransform(rootGameObject);
|
||||
CreateBonePathHash(rootTransform);
|
||||
ConvertAnimations(animationClips);
|
||||
}
|
||||
|
||||
private void ConvertAnimations(AnimationClip[] animationClips)
|
||||
public CubismMotion3Converter(List<AnimationClip> animationClips, HashSet<string> partIds, HashSet<string> parameterIds)
|
||||
{
|
||||
CreateBonePathHash(partIds, pathType: "Parts/");
|
||||
CreateBonePathHash(parameterIds, pathType: "Parameters/");
|
||||
ConvertAnimations(animationClips);
|
||||
}
|
||||
|
||||
private void ConvertAnimations(List<AnimationClip> animationClips)
|
||||
{
|
||||
foreach (var animationClip in animationClips)
|
||||
{
|
||||
@ -160,21 +167,30 @@ namespace CubismLive2DExtractor
|
||||
return null;
|
||||
}
|
||||
|
||||
private void CreateBonePathHash(HashSet<string> ids, string pathType)
|
||||
{
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var name = pathType + id;;
|
||||
bonePathHash[GetCRC(name)] = name;
|
||||
int index;
|
||||
while ((index = name.IndexOf("/", StringComparison.Ordinal)) >= 0)
|
||||
{
|
||||
name = name.Substring(index + 1);
|
||||
bonePathHash[GetCRC(name)] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateBonePathHash(Transform m_Transform)
|
||||
{
|
||||
var name = GetTransformPath(m_Transform);
|
||||
var crc = new SevenZip.CRC();
|
||||
var bytes = Encoding.UTF8.GetBytes(name);
|
||||
crc.Update(bytes, 0, (uint)bytes.Length);
|
||||
bonePathHash[crc.GetDigest()] = name;
|
||||
bonePathHash[GetCRC(name)] = name;
|
||||
int index;
|
||||
while ((index = name.IndexOf("/", StringComparison.Ordinal)) >= 0)
|
||||
{
|
||||
name = name.Substring(index + 1);
|
||||
crc = new SevenZip.CRC();
|
||||
bytes = Encoding.UTF8.GetBytes(name);
|
||||
crc.Update(bytes, 0, (uint)bytes.Length);
|
||||
bonePathHash[crc.GetDigest()] = name;
|
||||
bonePathHash[GetCRC(name)] = name;
|
||||
}
|
||||
foreach (var pptr in m_Transform.m_Children)
|
||||
{
|
||||
@ -183,7 +199,13 @@ namespace CubismLive2DExtractor
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTransformPath(Transform transform)
|
||||
private static uint GetCRC(string name)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(name);
|
||||
return SevenZip.CRC.CalculateDigest(bytes, 0, (uint)bytes.Length);
|
||||
}
|
||||
|
||||
private static string GetTransformPath(Transform transform)
|
||||
{
|
||||
transform.m_GameObject.TryGet(out var m_GameObject);
|
||||
if (transform.m_Father.TryGet(out var father))
|
||||
|
62
AssetStudioUtility/CubismLive2DExtractor/CubismObjectList.cs
Normal file
62
AssetStudioUtility/CubismLive2DExtractor/CubismObjectList.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AssetStudio;
|
||||
|
||||
namespace CubismLive2DExtractor
|
||||
{
|
||||
public sealed class CubismObjectList
|
||||
{
|
||||
public static SerializedFile AssetsFile { get; set; }
|
||||
public HashSet<ObjectData> CubismExpressionObjects { get; set; }
|
||||
public HashSet<ObjectData> CubismFadeMotionObjects { get; set; }
|
||||
|
||||
public class ObjectData
|
||||
{
|
||||
private long _pathID;
|
||||
public Object Asset { get; set; }
|
||||
public int m_FileID { get; set; }
|
||||
public long m_PathID
|
||||
{
|
||||
get => _pathID;
|
||||
set
|
||||
{
|
||||
_pathID = value;
|
||||
Asset = GetObjByPathID(_pathID);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ObjectData objectData && _pathID == objectData.m_PathID;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _pathID.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public List<MonoBehaviour> GetFadeMotionAssetList()
|
||||
{
|
||||
return CubismFadeMotionObjects?.Where(x => x.Asset != null).Select(x => (MonoBehaviour)x.Asset).ToList();
|
||||
}
|
||||
|
||||
public List<MonoBehaviour> GetExpressionList()
|
||||
{
|
||||
return CubismExpressionObjects?.Where(x => x.Asset != null).Select(x => (MonoBehaviour)x.Asset).ToList();
|
||||
}
|
||||
|
||||
private static Object GetObjByPathID(long pathID)
|
||||
{
|
||||
var assetFileList = AssetsFile.assetsManager.assetsFileList;
|
||||
foreach (var assetFile in assetFileList)
|
||||
{
|
||||
if (assetFile.ObjectsDic.TryGetValue(pathID, out var obj))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
159
AssetStudioUtility/CubismLive2DExtractor/CubismParsers.cs
Normal file
159
AssetStudioUtility/CubismLive2DExtractor/CubismParsers.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CubismLive2DExtractor
|
||||
{
|
||||
public static class CubismParsers
|
||||
{
|
||||
public enum CubismMonoBehaviourType
|
||||
{
|
||||
FadeMotionList,
|
||||
FadeMotion,
|
||||
Expression,
|
||||
Physics,
|
||||
}
|
||||
|
||||
public static string ParsePhysics(OrderedDictionary physicsDict)
|
||||
{
|
||||
var cubismPhysicsRig = JsonConvert.DeserializeObject<CubismPhysics>(JsonConvert.SerializeObject(physicsDict))._rig;
|
||||
|
||||
var physicsSettings = new CubismPhysics3Json.SerializablePhysicsSettings[cubismPhysicsRig.SubRigs.Length];
|
||||
for (int i = 0; i < physicsSettings.Length; i++)
|
||||
{
|
||||
var subRigs = cubismPhysicsRig.SubRigs[i];
|
||||
physicsSettings[i] = new CubismPhysics3Json.SerializablePhysicsSettings
|
||||
{
|
||||
Id = $"PhysicsSetting{i + 1}",
|
||||
Input = new CubismPhysics3Json.SerializableInput[subRigs.Input.Length],
|
||||
Output = new CubismPhysics3Json.SerializableOutput[subRigs.Output.Length],
|
||||
Vertices = new CubismPhysics3Json.SerializableVertex[subRigs.Particles.Length],
|
||||
Normalization = new CubismPhysics3Json.SerializableNormalization
|
||||
{
|
||||
Position = new CubismPhysics3Json.SerializableNormalizationValue
|
||||
{
|
||||
Minimum = subRigs.Normalization.Position.Minimum,
|
||||
Default = subRigs.Normalization.Position.Default,
|
||||
Maximum = subRigs.Normalization.Position.Maximum
|
||||
},
|
||||
Angle = new CubismPhysics3Json.SerializableNormalizationValue
|
||||
{
|
||||
Minimum = subRigs.Normalization.Angle.Minimum,
|
||||
Default = subRigs.Normalization.Angle.Default,
|
||||
Maximum = subRigs.Normalization.Angle.Maximum
|
||||
}
|
||||
}
|
||||
};
|
||||
for (int j = 0; j < subRigs.Input.Length; j++)
|
||||
{
|
||||
var input = subRigs.Input[j];
|
||||
physicsSettings[i].Input[j] = new CubismPhysics3Json.SerializableInput
|
||||
{
|
||||
Source = new CubismPhysics3Json.SerializableParameter
|
||||
{
|
||||
Target = "Parameter", //同名GameObject父节点的名称
|
||||
Id = input.SourceId
|
||||
},
|
||||
Weight = input.Weight,
|
||||
Type = Enum.GetName(typeof(CubismPhysicsSourceComponent), input.SourceComponent),
|
||||
Reflect = input.IsInverted
|
||||
};
|
||||
}
|
||||
for (int j = 0; j < subRigs.Output.Length; j++)
|
||||
{
|
||||
var output = subRigs.Output[j];
|
||||
physicsSettings[i].Output[j] = new CubismPhysics3Json.SerializableOutput
|
||||
{
|
||||
Destination = new CubismPhysics3Json.SerializableParameter
|
||||
{
|
||||
Target = "Parameter", //同名GameObject父节点的名称
|
||||
Id = output.DestinationId
|
||||
},
|
||||
VertexIndex = output.ParticleIndex,
|
||||
Scale = output.AngleScale,
|
||||
Weight = output.Weight,
|
||||
Type = Enum.GetName(typeof(CubismPhysicsSourceComponent), output.SourceComponent),
|
||||
Reflect = output.IsInverted
|
||||
};
|
||||
}
|
||||
for (int j = 0; j < subRigs.Particles.Length; j++)
|
||||
{
|
||||
var particles = subRigs.Particles[j];
|
||||
physicsSettings[i].Vertices[j] = new CubismPhysics3Json.SerializableVertex
|
||||
{
|
||||
Position = particles.InitialPosition,
|
||||
Mobility = particles.Mobility,
|
||||
Delay = particles.Delay,
|
||||
Acceleration = particles.Acceleration,
|
||||
Radius = particles.Radius
|
||||
};
|
||||
}
|
||||
}
|
||||
var physicsDictionary = new CubismPhysics3Json.SerializablePhysicsDictionary[physicsSettings.Length];
|
||||
for (int i = 0; i < physicsSettings.Length; i++)
|
||||
{
|
||||
physicsDictionary[i] = new CubismPhysics3Json.SerializablePhysicsDictionary
|
||||
{
|
||||
Id = $"PhysicsSetting{i + 1}",
|
||||
Name = $"Dummy{i + 1}"
|
||||
};
|
||||
}
|
||||
var physicsJson = new CubismPhysics3Json
|
||||
{
|
||||
Version = 3,
|
||||
Meta = new CubismPhysics3Json.SerializableMeta
|
||||
{
|
||||
PhysicsSettingCount = cubismPhysicsRig.SubRigs.Length,
|
||||
TotalInputCount = cubismPhysicsRig.SubRigs.Sum(x => x.Input.Length),
|
||||
TotalOutputCount = cubismPhysicsRig.SubRigs.Sum(x => x.Output.Length),
|
||||
VertexCount = cubismPhysicsRig.SubRigs.Sum(x => x.Particles.Length),
|
||||
EffectiveForces = new CubismPhysics3Json.SerializableEffectiveForces
|
||||
{
|
||||
Gravity = cubismPhysicsRig.Gravity,
|
||||
Wind = cubismPhysicsRig.Wind
|
||||
},
|
||||
PhysicsDictionary = physicsDictionary
|
||||
},
|
||||
PhysicsSettings = physicsSettings
|
||||
};
|
||||
return JsonConvert.SerializeObject(physicsJson, Formatting.Indented, new MyJsonConverter2());
|
||||
}
|
||||
|
||||
public static OrderedDictionary ParseMonoBehaviour(MonoBehaviour m_MonoBehaviour, CubismMonoBehaviourType cubismMonoBehaviourType, AssemblyLoader assemblyLoader)
|
||||
{
|
||||
var orderedDict = m_MonoBehaviour.ToType();
|
||||
if (orderedDict != null)
|
||||
return orderedDict;
|
||||
|
||||
var fieldName = "";
|
||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(assemblyLoader);
|
||||
switch (cubismMonoBehaviourType)
|
||||
{
|
||||
case CubismMonoBehaviourType.FadeMotionList:
|
||||
fieldName = "cubismfademotionobjects";
|
||||
break;
|
||||
case CubismMonoBehaviourType.FadeMotion:
|
||||
fieldName = "parameterids";
|
||||
break;
|
||||
case CubismMonoBehaviourType.Expression:
|
||||
fieldName = "parameters";
|
||||
break;
|
||||
case CubismMonoBehaviourType.Physics:
|
||||
fieldName = "_rig";
|
||||
break;
|
||||
}
|
||||
if (m_Type.m_Nodes.FindIndex(x => x.m_Name.ToLower() == fieldName) < 0)
|
||||
{
|
||||
m_MonoBehaviour.m_Script.TryGet(out var m_MonoScript);
|
||||
var assetName = m_MonoBehaviour.m_Name != "" ? m_MonoBehaviour.m_Name : m_MonoScript.m_ClassName;
|
||||
Logger.Warning($"{cubismMonoBehaviourType} asset \"{assetName}\" is not readable");
|
||||
return null;
|
||||
}
|
||||
orderedDict = m_MonoBehaviour.ToType(m_Type);
|
||||
|
||||
return orderedDict;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,34 +7,43 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static CubismLive2DExtractor.CubismParsers;
|
||||
|
||||
namespace CubismLive2DExtractor
|
||||
{
|
||||
public static class Live2DExtractor
|
||||
public sealed class Live2DExtractor
|
||||
{
|
||||
public static void ExtractLive2D(IGrouping<string, AssetStudio.Object> assets, string destPath, string modelName, AssemblyLoader assemblyLoader, Live2DMotionMode motionMode, bool forceBezier = false)
|
||||
{
|
||||
var destTexturePath = Path.Combine(destPath, "textures") + Path.DirectorySeparatorChar;
|
||||
var destMotionPath = Path.Combine(destPath, "motions") + Path.DirectorySeparatorChar;
|
||||
var destExpressionPath = Path.Combine(destPath, "expressions") + Path.DirectorySeparatorChar;
|
||||
Directory.CreateDirectory(destPath);
|
||||
Directory.CreateDirectory(destTexturePath);
|
||||
private List<MonoBehaviour> Expressions { get; set; }
|
||||
private List<MonoBehaviour> FadeMotions { get; set; }
|
||||
private List<GameObject> GameObjects { get; set; }
|
||||
private List<AnimationClip> AnimationClips { get; set; }
|
||||
private List<Texture2D> Texture2Ds { get; set; }
|
||||
private HashSet<string> EyeBlinkParameters { get; set; }
|
||||
private HashSet<string> LipSyncParameters { get; set; }
|
||||
private HashSet<string> ParameterNames { get; set; }
|
||||
private HashSet<string> PartNames { get; set; }
|
||||
private MonoBehaviour MocMono { get; set; }
|
||||
private MonoBehaviour PhysicsMono { get; set; }
|
||||
private MonoBehaviour FadeMotionLst { get; set; }
|
||||
|
||||
var expressionList = new List<MonoBehaviour>();
|
||||
var fadeMotionList = new List<MonoBehaviour>();
|
||||
var gameObjects = new List<GameObject>();
|
||||
var animationClips = new List<AnimationClip>();
|
||||
|
||||
var textures = new SortedSet<string>();
|
||||
var eyeBlinkParameters = new HashSet<string>();
|
||||
var lipSyncParameters = new HashSet<string>();
|
||||
var parameterNames = new HashSet<string>();
|
||||
var partNames = new HashSet<string>();
|
||||
MonoBehaviour physics = null;
|
||||
public Live2DExtractor(IGrouping<string, AssetStudio.Object> assets, List<AnimationClip> inClipMotions = null, List<MonoBehaviour> inFadeMotions = null, MonoBehaviour inFadeMotionLst = null)
|
||||
{
|
||||
Expressions = new List<MonoBehaviour>();
|
||||
FadeMotions = inFadeMotions ?? new List<MonoBehaviour>();
|
||||
AnimationClips = inClipMotions ?? new List<AnimationClip>();
|
||||
GameObjects = new List<GameObject>();
|
||||
Texture2Ds = new List<Texture2D>();
|
||||
EyeBlinkParameters = new HashSet<string>();
|
||||
LipSyncParameters = new HashSet<string>();
|
||||
ParameterNames = new HashSet<string>();
|
||||
PartNames = new HashSet<string>();
|
||||
FadeMotionLst = inFadeMotionLst;
|
||||
|
||||
Logger.Info("Sorting model assets..");
|
||||
foreach (var asset in assets)
|
||||
{
|
||||
switch (asset)
|
||||
@ -45,210 +54,255 @@ namespace CubismLive2DExtractor
|
||||
switch (m_Script.m_ClassName)
|
||||
{
|
||||
case "CubismMoc":
|
||||
File.WriteAllBytes($"{destPath}{modelName}.moc3", ParseMoc(m_MonoBehaviour)); //moc
|
||||
MocMono = m_MonoBehaviour;
|
||||
break;
|
||||
case "CubismPhysicsController":
|
||||
physics = physics ?? m_MonoBehaviour;
|
||||
PhysicsMono = m_MonoBehaviour;
|
||||
break;
|
||||
case "CubismExpressionData":
|
||||
expressionList.Add(m_MonoBehaviour);
|
||||
Expressions.Add(m_MonoBehaviour);
|
||||
break;
|
||||
case "CubismFadeMotionData":
|
||||
fadeMotionList.Add(m_MonoBehaviour);
|
||||
if (inFadeMotions == null && inFadeMotionLst == null)
|
||||
{
|
||||
FadeMotions.Add(m_MonoBehaviour);
|
||||
}
|
||||
break;
|
||||
case "CubismFadeMotionList":
|
||||
if (inFadeMotions == null && inFadeMotionLst == null)
|
||||
{
|
||||
FadeMotionLst = m_MonoBehaviour;
|
||||
}
|
||||
break;
|
||||
case "CubismEyeBlinkParameter":
|
||||
if (m_MonoBehaviour.m_GameObject.TryGet(out var blinkGameObject))
|
||||
{
|
||||
eyeBlinkParameters.Add(blinkGameObject.m_Name);
|
||||
EyeBlinkParameters.Add(blinkGameObject.m_Name);
|
||||
}
|
||||
break;
|
||||
case "CubismMouthParameter":
|
||||
if (m_MonoBehaviour.m_GameObject.TryGet(out var mouthGameObject))
|
||||
{
|
||||
lipSyncParameters.Add(mouthGameObject.m_Name);
|
||||
LipSyncParameters.Add(mouthGameObject.m_Name);
|
||||
}
|
||||
break;
|
||||
case "CubismParameter":
|
||||
if (m_MonoBehaviour.m_GameObject.TryGet(out var paramGameObject))
|
||||
{
|
||||
parameterNames.Add(paramGameObject.m_Name);
|
||||
ParameterNames.Add(paramGameObject.m_Name);
|
||||
}
|
||||
break;
|
||||
case "CubismPart":
|
||||
if (m_MonoBehaviour.m_GameObject.TryGet(out var partGameObject))
|
||||
{
|
||||
partNames.Add(partGameObject.m_Name);
|
||||
PartNames.Add(partGameObject.m_Name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Texture2D m_Texture2D:
|
||||
using (var image = m_Texture2D.ConvertToImage(flip: true))
|
||||
case AnimationClip m_AnimationClip:
|
||||
if (inClipMotions == null)
|
||||
{
|
||||
using (var file = File.OpenWrite($"{destTexturePath}{m_Texture2D.m_Name}.png"))
|
||||
{
|
||||
image.WriteToStream(file, ImageFormat.Png);
|
||||
}
|
||||
textures.Add($"textures/{m_Texture2D.m_Name}.png"); //texture
|
||||
AnimationClips.Add(m_AnimationClip);
|
||||
}
|
||||
break;
|
||||
case GameObject m_GameObject:
|
||||
gameObjects.Add(m_GameObject);
|
||||
GameObjects.Add(m_GameObject);
|
||||
break;
|
||||
case AnimationClip m_AnimationClip:
|
||||
animationClips.Add(m_AnimationClip);
|
||||
case Texture2D m_Texture2D:
|
||||
Texture2Ds.Add(m_Texture2D);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textures.Count == 0)
|
||||
public void ExtractCubismModel(string destPath, string modelName, Live2DMotionMode motionMode, AssemblyLoader assemblyLoader, bool forceBezier = false)
|
||||
{
|
||||
Directory.CreateDirectory(destPath);
|
||||
|
||||
#region moc3
|
||||
using (var cubismModel = new CubismModel(MocMono))
|
||||
{
|
||||
Logger.Warning($"No textures found for \"{modelName}\" model.");
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Model Stats:");
|
||||
sb.AppendLine($"SDK Version: {cubismModel.VersionDescription}");
|
||||
if (cubismModel.Version > 0)
|
||||
{
|
||||
sb.AppendLine($"Canvas Width: {cubismModel.CanvasWidth}");
|
||||
sb.AppendLine($"Canvas Height: {cubismModel.CanvasHeight}");
|
||||
sb.AppendLine($"Center X: {cubismModel.CentralPosX}");
|
||||
sb.AppendLine($"Center Y: {cubismModel.CentralPosY}");
|
||||
sb.AppendLine($"Pixel Per Unit: {cubismModel.PixelPerUnit}");
|
||||
sb.AppendLine($"Part Count: {cubismModel.PartCount}");
|
||||
sb.AppendLine($"Parameter Count: {cubismModel.ParamCount}");
|
||||
Logger.Debug(sb.ToString());
|
||||
|
||||
ParameterNames = cubismModel.ParamNames;
|
||||
PartNames = cubismModel.PartNames;
|
||||
}
|
||||
File.WriteAllBytes($"{destPath}{modelName}.moc3", cubismModel.ModelData);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region textures
|
||||
var textures = new SortedSet<string>();
|
||||
var destTexturePath = Path.Combine(destPath, "textures") + Path.DirectorySeparatorChar;
|
||||
|
||||
if (Texture2Ds.Count == 0)
|
||||
{
|
||||
Logger.Warning($"No textures found for \"{modelName}\" model");
|
||||
}
|
||||
else
|
||||
{
|
||||
Directory.CreateDirectory(destTexturePath);
|
||||
}
|
||||
|
||||
//physics
|
||||
if (physics != null)
|
||||
foreach (var texture2D in Texture2Ds)
|
||||
{
|
||||
try
|
||||
using (var image = texture2D.ConvertToImage(flip: true))
|
||||
{
|
||||
var buff = ParsePhysics(physics, assemblyLoader);
|
||||
File.WriteAllText($"{destPath}{modelName}.physics3.json", buff);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warning($"Error in parsing physics data: {e.Message}");
|
||||
physics = null;
|
||||
using (var file = File.OpenWrite($"{destTexturePath}{texture2D.m_Name}.png"))
|
||||
{
|
||||
image.WriteToStream(file, ImageFormat.Png);
|
||||
}
|
||||
textures.Add($"textures/{texture2D.m_Name}.png");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//motion
|
||||
#region physics3.json
|
||||
if (PhysicsMono != null)
|
||||
{
|
||||
var physicsDict = ParseMonoBehaviour(PhysicsMono, CubismMonoBehaviourType.Physics, assemblyLoader);
|
||||
if (physicsDict != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var buff = ParsePhysics(physicsDict);
|
||||
File.WriteAllText($"{destPath}{modelName}.physics3.json", buff);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warning($"Error in parsing physics data: {e.Message}");
|
||||
PhysicsMono = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsMono = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region motion3.json
|
||||
var motions = new SortedDictionary<string, JArray>();
|
||||
var destMotionPath = Path.Combine(destPath, "motions") + Path.DirectorySeparatorChar;
|
||||
|
||||
if (motionMode == Live2DMotionMode.MonoBehaviour && fadeMotionList.Count > 0) //motion from MonoBehaviour
|
||||
if (motionMode == Live2DMotionMode.MonoBehaviour && FadeMotionLst != null) //Fade motions from Fade Motion List
|
||||
{
|
||||
Logger.Debug("Motion export method: MonoBehaviour (Fade motion)");
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
foreach (var fadeMotionMono in fadeMotionList)
|
||||
var fadeMotionLstDict = ParseMonoBehaviour(FadeMotionLst, CubismMonoBehaviourType.FadeMotionList, assemblyLoader);
|
||||
if (fadeMotionLstDict != null)
|
||||
{
|
||||
var fadeMotionObj = fadeMotionMono.ToType();
|
||||
if (fadeMotionObj == null)
|
||||
CubismObjectList.AssetsFile = FadeMotionLst.assetsFile;
|
||||
var fadeMotionAssetList = JsonConvert.DeserializeObject<CubismObjectList>(JsonConvert.SerializeObject(fadeMotionLstDict)).GetFadeMotionAssetList();
|
||||
if (fadeMotionAssetList?.Count > 0)
|
||||
{
|
||||
var m_Type = fadeMotionMono.ConvertToTypeTree(assemblyLoader);
|
||||
fadeMotionObj = fadeMotionMono.ToType(m_Type);
|
||||
if (fadeMotionObj == null)
|
||||
{
|
||||
Logger.Warning($"Fade motion \"{fadeMotionMono.m_Name}\" is not readable.");
|
||||
continue;
|
||||
}
|
||||
FadeMotions = fadeMotionAssetList;
|
||||
Logger.Debug($"\"{FadeMotionLst.m_Name}\": found {fadeMotionAssetList.Count} motion(s)");
|
||||
}
|
||||
var fadeMotion = JsonConvert.DeserializeObject<CubismFadeMotion>(JsonConvert.SerializeObject(fadeMotionObj));
|
||||
if (fadeMotion.ParameterIds.Length == 0)
|
||||
continue;
|
||||
|
||||
var motionJson = new CubismMotion3Json(fadeMotion, parameterNames, partNames, forceBezier);
|
||||
|
||||
var animName = Path.GetFileNameWithoutExtension(fadeMotion.m_Name);
|
||||
if (motions.ContainsKey(animName))
|
||||
{
|
||||
animName = $"{animName}_{fadeMotion.GetHashCode()}";
|
||||
|
||||
if (motions.ContainsKey(animName))
|
||||
continue;
|
||||
}
|
||||
var motionPath = new JObject(new JProperty("File", $"motions/{animName}.motion3.json"));
|
||||
motions.Add(animName, new JArray(motionPath));
|
||||
File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
|
||||
}
|
||||
}
|
||||
if (motions.Count == 0 && gameObjects.Count > 0) //motion from AnimationClip
|
||||
|
||||
if (motionMode == Live2DMotionMode.MonoBehaviour && FadeMotions.Count > 0) //motion from MonoBehaviour
|
||||
{
|
||||
ExportFadeMotions(destMotionPath, assemblyLoader, forceBezier, motions);
|
||||
}
|
||||
|
||||
if (motions.Count == 0) //motion from AnimationClip
|
||||
{
|
||||
CubismMotion3Converter converter = null;
|
||||
var exportMethod = "AnimationClip";
|
||||
if (motionMode != Live2DMotionMode.AnimationClipV1) //AnimationClipV2
|
||||
{
|
||||
exportMethod += "V2";
|
||||
converter = new CubismMotion3Converter(AnimationClips, PartNames, ParameterNames);
|
||||
}
|
||||
else if (GameObjects.Count > 0) //AnimationClipV1
|
||||
{
|
||||
exportMethod += "V1";
|
||||
var rootTransform = GameObjects[0].m_Transform;
|
||||
while (rootTransform.m_Father.TryGet(out var m_Father))
|
||||
{
|
||||
rootTransform = m_Father;
|
||||
}
|
||||
rootTransform.m_GameObject.TryGet(out var rootGameObject);
|
||||
converter = new CubismMotion3Converter(rootGameObject, AnimationClips);
|
||||
}
|
||||
|
||||
if (motionMode == Live2DMotionMode.MonoBehaviour)
|
||||
{
|
||||
exportMethod = fadeMotionList.Count > 0
|
||||
? exportMethod + " (unable to export motion using Fade motion method)"
|
||||
exportMethod = FadeMotions.Count > 0
|
||||
? exportMethod + " (unable to export motions using Fade motion method)"
|
||||
: exportMethod + " (no Fade motions found)";
|
||||
}
|
||||
Logger.Debug($"Motion export method: {exportMethod}");
|
||||
var rootTransform = gameObjects[0].m_Transform;
|
||||
while (rootTransform.m_Father.TryGet(out var m_Father))
|
||||
{
|
||||
rootTransform = m_Father;
|
||||
}
|
||||
rootTransform.m_GameObject.TryGet(out var rootGameObject);
|
||||
var converter = new CubismMotion3Converter(rootGameObject, animationClips.ToArray());
|
||||
if (converter.AnimationList.Count > 0)
|
||||
{
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
}
|
||||
foreach (var animation in converter.AnimationList)
|
||||
{
|
||||
var motionJson = new CubismMotion3Json(animation, forceBezier);
|
||||
|
||||
var animName = animation.Name;
|
||||
if (motions.ContainsKey(animName))
|
||||
{
|
||||
animName = $"{animName}_{animation.GetHashCode()}";
|
||||
|
||||
if (motions.ContainsKey(animName))
|
||||
continue;
|
||||
}
|
||||
var motionPath = new JObject(new JProperty("File", $"motions/{animName}.motion3.json"));
|
||||
motions.Add(animName, new JArray(motionPath));
|
||||
File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
|
||||
}
|
||||
ExportClipMotions(destMotionPath, converter, forceBezier, motions);
|
||||
}
|
||||
|
||||
if (motions.Count == 0)
|
||||
{
|
||||
Logger.Warning($"No motions found for \"{modelName}\" model.");
|
||||
Logger.Warning($"No exportable motions found for \"{modelName}\" model");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug($"Exported {motions.Count} motion(s)");
|
||||
}
|
||||
#endregion
|
||||
|
||||
//expression
|
||||
#region exp3.json
|
||||
var expressions = new JArray();
|
||||
if (expressionList.Count > 0)
|
||||
var destExpressionPath = Path.Combine(destPath, "expressions") + Path.DirectorySeparatorChar;
|
||||
|
||||
if (Expressions.Count > 0)
|
||||
{
|
||||
Directory.CreateDirectory(destExpressionPath);
|
||||
}
|
||||
foreach (var monoBehaviour in expressionList)
|
||||
foreach (var monoBehaviour in Expressions)
|
||||
{
|
||||
var expressionName = monoBehaviour.m_Name.Replace(".exp3", "");
|
||||
var expressionObj = monoBehaviour.ToType();
|
||||
if (expressionObj == null)
|
||||
{
|
||||
var m_Type = monoBehaviour.ConvertToTypeTree(assemblyLoader);
|
||||
expressionObj = monoBehaviour.ToType(m_Type);
|
||||
if (expressionObj == null)
|
||||
{
|
||||
Logger.Warning($"Expression \"{expressionName}\" is not readable.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var expression = JsonConvert.DeserializeObject<CubismExpression3Json>(JsonConvert.SerializeObject(expressionObj));
|
||||
var expressionDict = ParseMonoBehaviour(monoBehaviour, CubismMonoBehaviourType.Expression, assemblyLoader);
|
||||
if (expressionDict == null)
|
||||
continue;
|
||||
|
||||
var expression = JsonConvert.DeserializeObject<CubismExpression3Json>(JsonConvert.SerializeObject(expressionDict));
|
||||
|
||||
expressions.Add(new JObject
|
||||
{
|
||||
{ "Name", expressionName },
|
||||
{ "File", $"expressions/{expressionName}.exp3.json" }
|
||||
});
|
||||
{
|
||||
{ "Name", expressionName },
|
||||
{ "File", $"expressions/{expressionName}.exp3.json" }
|
||||
});
|
||||
File.WriteAllText($"{destExpressionPath}{expressionName}.exp3.json", JsonConvert.SerializeObject(expression, Formatting.Indented));
|
||||
}
|
||||
#endregion
|
||||
|
||||
//group
|
||||
#region model3.json
|
||||
var groups = new List<CubismModel3Json.SerializableGroup>();
|
||||
|
||||
//Try looking for group IDs among the parameter names manually
|
||||
if (eyeBlinkParameters.Count == 0)
|
||||
if (EyeBlinkParameters.Count == 0)
|
||||
{
|
||||
eyeBlinkParameters = parameterNames.Where(x =>
|
||||
EyeBlinkParameters = ParameterNames.Where(x =>
|
||||
x.ToLower().Contains("eye")
|
||||
&& x.ToLower().Contains("open")
|
||||
&& (x.ToLower().Contains('l') || x.ToLower().Contains('r'))
|
||||
).ToHashSet();
|
||||
}
|
||||
if (lipSyncParameters.Count == 0)
|
||||
if (LipSyncParameters.Count == 0)
|
||||
{
|
||||
lipSyncParameters = parameterNames.Where(x =>
|
||||
LipSyncParameters = ParameterNames.Where(x =>
|
||||
x.ToLower().Contains("mouth")
|
||||
&& x.ToLower().Contains("open")
|
||||
&& x.ToLower().Contains('y')
|
||||
@ -259,16 +313,15 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
Target = "Parameter",
|
||||
Name = "EyeBlink",
|
||||
Ids = eyeBlinkParameters.ToArray()
|
||||
Ids = EyeBlinkParameters.ToArray()
|
||||
});
|
||||
groups.Add(new CubismModel3Json.SerializableGroup
|
||||
{
|
||||
Target = "Parameter",
|
||||
Name = "LipSync",
|
||||
Ids = lipSyncParameters.ToArray()
|
||||
Ids = LipSyncParameters.ToArray()
|
||||
});
|
||||
|
||||
//model
|
||||
|
||||
var model3 = new CubismModel3Json
|
||||
{
|
||||
Version = 3,
|
||||
@ -277,140 +330,74 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
Moc = $"{modelName}.moc3",
|
||||
Textures = textures.ToArray(),
|
||||
Physics = PhysicsMono == null ? null : $"{modelName}.physics3.json",
|
||||
Motions = JObject.FromObject(motions),
|
||||
Expressions = expressions,
|
||||
},
|
||||
Groups = groups.ToArray()
|
||||
};
|
||||
if (physics != null)
|
||||
{
|
||||
model3.FileReferences.Physics = $"{modelName}.physics3.json";
|
||||
}
|
||||
File.WriteAllText($"{destPath}{modelName}.model3.json", JsonConvert.SerializeObject(model3, Formatting.Indented));
|
||||
#endregion
|
||||
}
|
||||
|
||||
private static string ParsePhysics(MonoBehaviour physics, AssemblyLoader assemblyLoader)
|
||||
private void ExportFadeMotions(string destMotionPath, AssemblyLoader assemblyLoader, bool forceBezier, SortedDictionary<string, JArray> motions)
|
||||
{
|
||||
var physicsObj = physics.ToType();
|
||||
if (physicsObj == null)
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
foreach (var fadeMotionMono in FadeMotions)
|
||||
{
|
||||
var m_Type = physics.ConvertToTypeTree(assemblyLoader);
|
||||
physicsObj = physics.ToType(m_Type);
|
||||
if (physicsObj == null)
|
||||
{
|
||||
throw new Exception("MonoBehaviour is not readable.");
|
||||
}
|
||||
}
|
||||
var cubismPhysicsRig = JsonConvert.DeserializeObject<CubismPhysics>(JsonConvert.SerializeObject(physicsObj))._rig;
|
||||
var fadeMotionDict = ParseMonoBehaviour(fadeMotionMono, CubismMonoBehaviourType.FadeMotion, assemblyLoader);
|
||||
if (fadeMotionDict == null)
|
||||
continue;
|
||||
|
||||
var fadeMotion = JsonConvert.DeserializeObject<CubismFadeMotion>(JsonConvert.SerializeObject(fadeMotionDict));
|
||||
if (fadeMotion.ParameterIds.Length == 0)
|
||||
continue;
|
||||
|
||||
var physicsSettings = new CubismPhysics3Json.SerializablePhysicsSettings[cubismPhysicsRig.SubRigs.Length];
|
||||
for (int i = 0; i < physicsSettings.Length; i++)
|
||||
{
|
||||
var subRigs = cubismPhysicsRig.SubRigs[i];
|
||||
physicsSettings[i] = new CubismPhysics3Json.SerializablePhysicsSettings
|
||||
var motionJson = new CubismMotion3Json(fadeMotion, ParameterNames, PartNames, forceBezier);
|
||||
|
||||
var animName = Path.GetFileNameWithoutExtension(fadeMotion.m_Name);
|
||||
if (motions.ContainsKey(animName))
|
||||
{
|
||||
Id = $"PhysicsSetting{i + 1}",
|
||||
Input = new CubismPhysics3Json.SerializableInput[subRigs.Input.Length],
|
||||
Output = new CubismPhysics3Json.SerializableOutput[subRigs.Output.Length],
|
||||
Vertices = new CubismPhysics3Json.SerializableVertex[subRigs.Particles.Length],
|
||||
Normalization = new CubismPhysics3Json.SerializableNormalization
|
||||
{
|
||||
Position = new CubismPhysics3Json.SerializableNormalizationValue
|
||||
{
|
||||
Minimum = subRigs.Normalization.Position.Minimum,
|
||||
Default = subRigs.Normalization.Position.Default,
|
||||
Maximum = subRigs.Normalization.Position.Maximum
|
||||
},
|
||||
Angle = new CubismPhysics3Json.SerializableNormalizationValue
|
||||
{
|
||||
Minimum = subRigs.Normalization.Angle.Minimum,
|
||||
Default = subRigs.Normalization.Angle.Default,
|
||||
Maximum = subRigs.Normalization.Angle.Maximum
|
||||
}
|
||||
}
|
||||
};
|
||||
for (int j = 0; j < subRigs.Input.Length; j++)
|
||||
{
|
||||
var input = subRigs.Input[j];
|
||||
physicsSettings[i].Input[j] = new CubismPhysics3Json.SerializableInput
|
||||
{
|
||||
Source = new CubismPhysics3Json.SerializableParameter
|
||||
{
|
||||
Target = "Parameter", //同名GameObject父节点的名称
|
||||
Id = input.SourceId
|
||||
},
|
||||
Weight = input.Weight,
|
||||
Type = Enum.GetName(typeof(CubismPhysicsSourceComponent), input.SourceComponent),
|
||||
Reflect = input.IsInverted
|
||||
};
|
||||
}
|
||||
for (int j = 0; j < subRigs.Output.Length; j++)
|
||||
{
|
||||
var output = subRigs.Output[j];
|
||||
physicsSettings[i].Output[j] = new CubismPhysics3Json.SerializableOutput
|
||||
{
|
||||
Destination = new CubismPhysics3Json.SerializableParameter
|
||||
{
|
||||
Target = "Parameter", //同名GameObject父节点的名称
|
||||
Id = output.DestinationId
|
||||
},
|
||||
VertexIndex = output.ParticleIndex,
|
||||
Scale = output.AngleScale,
|
||||
Weight = output.Weight,
|
||||
Type = Enum.GetName(typeof(CubismPhysicsSourceComponent), output.SourceComponent),
|
||||
Reflect = output.IsInverted
|
||||
};
|
||||
}
|
||||
for (int j = 0; j < subRigs.Particles.Length; j++)
|
||||
{
|
||||
var particles = subRigs.Particles[j];
|
||||
physicsSettings[i].Vertices[j] = new CubismPhysics3Json.SerializableVertex
|
||||
{
|
||||
Position = particles.InitialPosition,
|
||||
Mobility = particles.Mobility,
|
||||
Delay = particles.Delay,
|
||||
Acceleration = particles.Acceleration,
|
||||
Radius = particles.Radius
|
||||
};
|
||||
animName = $"{animName}_{fadeMotion.GetHashCode()}";
|
||||
if (motions.ContainsKey(animName))
|
||||
continue;
|
||||
}
|
||||
var motionPath = new JObject(new JProperty("File", $"motions/{animName}.motion3.json"));
|
||||
motions.Add(animName, new JArray(motionPath));
|
||||
File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
|
||||
}
|
||||
var physicsDictionary = new CubismPhysics3Json.SerializablePhysicsDictionary[physicsSettings.Length];
|
||||
for (int i = 0; i < physicsSettings.Length; i++)
|
||||
{
|
||||
physicsDictionary[i] = new CubismPhysics3Json.SerializablePhysicsDictionary
|
||||
{
|
||||
Id = $"PhysicsSetting{i + 1}",
|
||||
Name = $"Dummy{i + 1}"
|
||||
};
|
||||
}
|
||||
var physicsJson = new CubismPhysics3Json
|
||||
{
|
||||
Version = 3,
|
||||
Meta = new CubismPhysics3Json.SerializableMeta
|
||||
{
|
||||
PhysicsSettingCount = cubismPhysicsRig.SubRigs.Length,
|
||||
TotalInputCount = cubismPhysicsRig.SubRigs.Sum(x => x.Input.Length),
|
||||
TotalOutputCount = cubismPhysicsRig.SubRigs.Sum(x => x.Output.Length),
|
||||
VertexCount = cubismPhysicsRig.SubRigs.Sum(x => x.Particles.Length),
|
||||
EffectiveForces = new CubismPhysics3Json.SerializableEffectiveForces
|
||||
{
|
||||
Gravity = cubismPhysicsRig.Gravity,
|
||||
Wind = cubismPhysicsRig.Wind
|
||||
},
|
||||
PhysicsDictionary = physicsDictionary
|
||||
},
|
||||
PhysicsSettings = physicsSettings
|
||||
};
|
||||
return JsonConvert.SerializeObject(physicsJson, Formatting.Indented, new MyJsonConverter2());
|
||||
}
|
||||
|
||||
private static byte[] ParseMoc(MonoBehaviour moc)
|
||||
private static void ExportClipMotions(string destMotionPath, CubismMotion3Converter converter, bool forceBezier, SortedDictionary<string, JArray> motions)
|
||||
{
|
||||
var reader = moc.reader;
|
||||
reader.Reset();
|
||||
reader.Position += 28; //PPtr<GameObject> m_GameObject, m_Enabled, PPtr<MonoScript>
|
||||
reader.ReadAlignedString(); //m_Name
|
||||
return reader.ReadBytes(reader.ReadInt32());
|
||||
if (converter == null)
|
||||
return;
|
||||
|
||||
if (converter.AnimationList.Count > 0)
|
||||
{
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
}
|
||||
foreach (var animation in converter.AnimationList)
|
||||
{
|
||||
var animName = animation.Name;
|
||||
if (animation.TrackList.Count == 0)
|
||||
{
|
||||
Logger.Warning($"Motion \"{animName}\" is empty. Export skipped");
|
||||
continue;
|
||||
}
|
||||
var motionJson = new CubismMotion3Json(animation, forceBezier);
|
||||
|
||||
if (motions.ContainsKey(animName))
|
||||
{
|
||||
animName = $"{animName}_{animation.GetHashCode()}";
|
||||
|
||||
if (motions.ContainsKey(animName))
|
||||
continue;
|
||||
}
|
||||
var motionPath = new JObject(new JProperty("File", $"motions/{animName}.motion3.json"));
|
||||
motions.Add(animName, new JArray(motionPath));
|
||||
File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
public enum Live2DMotionMode
|
||||
{
|
||||
MonoBehaviour,
|
||||
AnimationClip
|
||||
AnimationClipV1,
|
||||
AnimationClipV2,
|
||||
}
|
||||
}
|
||||
|
@ -1012,18 +1012,16 @@ namespace AssetStudio
|
||||
private void CreateBonePathHash(Transform m_Transform)
|
||||
{
|
||||
var name = GetTransformPathByFather(m_Transform);
|
||||
var crc = new SevenZip.CRC();
|
||||
var bytes = Encoding.UTF8.GetBytes(name);
|
||||
crc.Update(bytes, 0, (uint)bytes.Length);
|
||||
bonePathHash[crc.GetDigest()] = name;
|
||||
var crc = SevenZip.CRC.CalculateDigest(bytes, 0, (uint)bytes.Length);
|
||||
bonePathHash[crc] = name;
|
||||
int index;
|
||||
while ((index = name.IndexOf("/", StringComparison.Ordinal)) >= 0)
|
||||
{
|
||||
name = name.Substring(index + 1);
|
||||
crc = new SevenZip.CRC();
|
||||
bytes = Encoding.UTF8.GetBytes(name);
|
||||
crc.Update(bytes, 0, (uint)bytes.Length);
|
||||
bonePathHash[crc.GetDigest()] = name;
|
||||
crc = SevenZip.CRC.CalculateDigest(bytes, 0, (uint)bytes.Length);
|
||||
bonePathHash[crc] = name;
|
||||
}
|
||||
foreach (var pptr in m_Transform.m_Children)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user