mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-07-18 03:24:15 -04:00
Add support for Live2D Fade motions
This commit is contained in:
@ -18,7 +18,7 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
public static class Live2DExtractor
|
||||
{
|
||||
public static void ExtractLive2D(IGrouping<string, AssetStudio.Object> assets, string destPath, string modelName, AssemblyLoader assemblyLoader)
|
||||
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;
|
||||
@ -27,6 +27,7 @@ namespace CubismLive2DExtractor
|
||||
Directory.CreateDirectory(destTexturePath);
|
||||
|
||||
var expressionList = new List<MonoBehaviour>();
|
||||
var fadeMotionList = new List<MonoBehaviour>();
|
||||
var gameObjects = new List<GameObject>();
|
||||
var animationClips = new List<AnimationClip>();
|
||||
|
||||
@ -53,6 +54,9 @@ namespace CubismLive2DExtractor
|
||||
case "CubismExpressionData":
|
||||
expressionList.Add(m_MonoBehaviour);
|
||||
break;
|
||||
case "CubismFadeMotionData":
|
||||
fadeMotionList.Add(m_MonoBehaviour);
|
||||
break;
|
||||
case "CubismEyeBlinkParameter":
|
||||
if (m_MonoBehaviour.m_GameObject.TryGet(out var blinkGameObject))
|
||||
{
|
||||
@ -110,7 +114,42 @@ namespace CubismLive2DExtractor
|
||||
//motion
|
||||
var motions = new SortedDictionary<string, JArray>();
|
||||
|
||||
if (gameObjects.Count > 0)
|
||||
if (motionMode == Live2DMotionMode.MonoBehaviour && fadeMotionList.Count > 0) //motion from MonoBehaviour
|
||||
{
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
foreach (var fadeMotionMono in fadeMotionList)
|
||||
{
|
||||
var fadeMotionObj = fadeMotionMono.ToType();
|
||||
if (fadeMotionObj == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
var fadeMotion = JsonConvert.DeserializeObject<CubismFadeMotion>(JsonConvert.SerializeObject(fadeMotionObj));
|
||||
if (fadeMotion.ParameterIds.Length == 0)
|
||||
continue;
|
||||
|
||||
var motionJson = new CubismMotion3Json(fadeMotion, 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()));
|
||||
}
|
||||
}
|
||||
else if (gameObjects.Count > 0) //motion from AnimationClip
|
||||
{
|
||||
var rootTransform = gameObjects[0].m_Transform;
|
||||
while (rootTransform.m_Father.TryGet(out var m_Father))
|
||||
@ -123,111 +162,27 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
}
|
||||
foreach (ImportedKeyframedAnimation animation in converter.AnimationList)
|
||||
foreach (var animation in converter.AnimationList)
|
||||
{
|
||||
var json = new CubismMotion3Json
|
||||
{
|
||||
Version = 3,
|
||||
Meta = new CubismMotion3Json.SerializableMeta
|
||||
{
|
||||
Duration = animation.Duration,
|
||||
Fps = animation.SampleRate,
|
||||
Loop = true,
|
||||
AreBeziersRestricted = true,
|
||||
CurveCount = animation.TrackList.Count,
|
||||
UserDataCount = animation.Events.Count
|
||||
},
|
||||
Curves = new CubismMotion3Json.SerializableCurve[animation.TrackList.Count]
|
||||
};
|
||||
int totalSegmentCount = 1;
|
||||
int totalPointCount = 1;
|
||||
for (int i = 0; i < animation.TrackList.Count; i++)
|
||||
{
|
||||
var track = animation.TrackList[i];
|
||||
json.Curves[i] = new CubismMotion3Json.SerializableCurve
|
||||
{
|
||||
Target = track.Target,
|
||||
Id = track.Name,
|
||||
Segments = new List<float> { 0f, track.Curve[0].value }
|
||||
};
|
||||
for (var j = 1; j < track.Curve.Count; j++)
|
||||
{
|
||||
var curve = track.Curve[j];
|
||||
var preCurve = track.Curve[j - 1];
|
||||
if (Math.Abs(curve.time - preCurve.time - 0.01f) < 0.0001f) //InverseSteppedSegment
|
||||
{
|
||||
var nextCurve = track.Curve[j + 1];
|
||||
if (nextCurve.value == curve.value)
|
||||
{
|
||||
json.Curves[i].Segments.Add(3f);
|
||||
json.Curves[i].Segments.Add(nextCurve.time);
|
||||
json.Curves[i].Segments.Add(nextCurve.value);
|
||||
j += 1;
|
||||
totalPointCount += 1;
|
||||
totalSegmentCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (float.IsPositiveInfinity(curve.inSlope)) //SteppedSegment
|
||||
{
|
||||
json.Curves[i].Segments.Add(2f);
|
||||
json.Curves[i].Segments.Add(curve.time);
|
||||
json.Curves[i].Segments.Add(curve.value);
|
||||
totalPointCount += 1;
|
||||
}
|
||||
else if (preCurve.outSlope == 0f && Math.Abs(curve.inSlope) < 0.0001f) //LinearSegment
|
||||
{
|
||||
json.Curves[i].Segments.Add(0f);
|
||||
json.Curves[i].Segments.Add(curve.time);
|
||||
json.Curves[i].Segments.Add(curve.value);
|
||||
totalPointCount += 1;
|
||||
}
|
||||
else //BezierSegment
|
||||
{
|
||||
var tangentLength = (curve.time - preCurve.time) / 3f;
|
||||
json.Curves[i].Segments.Add(1f);
|
||||
json.Curves[i].Segments.Add(preCurve.time + tangentLength);
|
||||
json.Curves[i].Segments.Add(preCurve.outSlope * tangentLength + preCurve.value);
|
||||
json.Curves[i].Segments.Add(curve.time - tangentLength);
|
||||
json.Curves[i].Segments.Add(curve.value - curve.inSlope * tangentLength);
|
||||
json.Curves[i].Segments.Add(curve.time);
|
||||
json.Curves[i].Segments.Add(curve.value);
|
||||
totalPointCount += 3;
|
||||
}
|
||||
totalSegmentCount++;
|
||||
}
|
||||
}
|
||||
json.Meta.TotalSegmentCount = totalSegmentCount;
|
||||
json.Meta.TotalPointCount = totalPointCount;
|
||||
|
||||
json.UserData = new CubismMotion3Json.SerializableUserData[animation.Events.Count];
|
||||
var totalUserDataSize = 0;
|
||||
for (var i = 0; i < animation.Events.Count; i++)
|
||||
{
|
||||
var @event = animation.Events[i];
|
||||
json.UserData[i] = new CubismMotion3Json.SerializableUserData
|
||||
{
|
||||
Time = @event.time,
|
||||
Value = @event.value
|
||||
};
|
||||
totalUserDataSize += @event.value.Length;
|
||||
}
|
||||
json.Meta.TotalUserDataSize = totalUserDataSize;
|
||||
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(json, Formatting.Indented, new MyJsonConverter()));
|
||||
File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning($"No motions found for \"{modelName}\" model.");
|
||||
}
|
||||
|
||||
//expression
|
||||
var expressions = new JArray();
|
||||
|
Reference in New Issue
Block a user