Skip to content

Commit

Permalink
Supports conversions of new MLTD animations
Browse files Browse the repository at this point in the history
  • Loading branch information
hozuki committed May 7, 2020
1 parent 50782b8 commit 3e21599
Show file tree
Hide file tree
Showing 16 changed files with 1,006 additions and 211 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public sealed class ScriptableObjectSerializer {
public ScriptableObjectSerializer() {
_createdTypeConverters = new Dictionary<Type, ISimpleTypeConverter>(10);
_createdSetters = new Dictionary<(Type, string, ScriptableObjectPropertyAttribute), PropOrField>();
_propertyAttributeCache = new Dictionary<PropertyInfo, ScriptableObjectPropertyAttribute>();
_fieldAttributeCache = new Dictionary<FieldInfo, ScriptableObjectPropertyAttribute>();

// In old versions(?) Unity serializes booleans as bytes
WithConverter<ByteToBooleanConverter>();
Expand Down Expand Up @@ -260,16 +262,24 @@ public ScriptableObjectSerializer WithConverter<T>()
PropOrField result;

foreach (var prop in properties) {
var mbp = prop.GetCustomAttribute<ScriptableObjectPropertyAttribute>();
var propName = !string.IsNullOrEmpty(mbp?.Name) ? mbp.Name : (naming != null ? naming.GetCorrected(prop.Name) : prop.Name);
ScriptableObjectPropertyAttribute sopa;

if (_propertyAttributeCache.ContainsKey(prop)) {
sopa = _propertyAttributeCache[prop];
} else {
sopa = prop.GetCustomAttribute<ScriptableObjectPropertyAttribute>();
_propertyAttributeCache[prop] = sopa;
}

var propName = !string.IsNullOrEmpty(sopa?.Name) ? sopa.Name : (naming != null ? naming.GetCorrected(prop.Name) : prop.Name);

if (propName == name) {
var key = (objectType, propName, mbp);
var key = (objectType, propName, mbp: sopa);

if (_createdSetters.ContainsKey(key)) {
result = _createdSetters[key];
} else {
result = new PropOrField(prop, mbp);
result = new PropOrField(prop, sopa);
_createdSetters[key] = result;
}

Expand All @@ -278,16 +288,24 @@ public ScriptableObjectSerializer WithConverter<T>()
}

foreach (var field in fields) {
var mbp = field.GetCustomAttribute<ScriptableObjectPropertyAttribute>();
var fieldName = !string.IsNullOrEmpty(mbp?.Name) ? mbp.Name : (naming != null ? naming.GetCorrected(field.Name) : field.Name);
ScriptableObjectPropertyAttribute sopa;

if (_fieldAttributeCache.ContainsKey(field)) {
sopa = _fieldAttributeCache[field];
} else {
sopa = field.GetCustomAttribute<ScriptableObjectPropertyAttribute>();
_fieldAttributeCache[field] = sopa;
}

var fieldName = !string.IsNullOrEmpty(sopa?.Name) ? sopa.Name : (naming != null ? naming.GetCorrected(field.Name) : field.Name);

if (fieldName == name) {
var key = (objectType, fieldName, mbp);
var key = (objectType, fieldName, mbp: sopa);

if (_createdSetters.ContainsKey(key)) {
result = _createdSetters[key];
} else {
result = new PropOrField(field, mbp);
result = new PropOrField(field, sopa);
_createdSetters[key] = result;
}

Expand Down Expand Up @@ -425,6 +443,12 @@ public ScriptableObjectSerializer WithConverter<T>()
[NotNull]
private readonly Dictionary<(Type, string, ScriptableObjectPropertyAttribute), PropOrField> _createdSetters;

[NotNull]
private readonly Dictionary<PropertyInfo, ScriptableObjectPropertyAttribute> _propertyAttributeCache;

[NotNull]
private readonly Dictionary<FieldInfo, ScriptableObjectPropertyAttribute> _fieldAttributeCache;

[NotNull, ItemNotNull]
private static readonly string[] FilteredNames = {
"m_GameObject",
Expand Down
15 changes: 15 additions & 0 deletions src/MillionDance/Core/IBodyAnimationSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using JetBrains.Annotations;
using OpenMLTD.MillionDance.Entities.Internal;

namespace OpenMLTD.MillionDance.Core {
public interface IBodyAnimationSource {

/// <summary>
/// Converts read animation data in to internal, unified body animation data.
/// </summary>
/// <returns>Unified body animation data.</returns>
[NotNull]
BodyAnimation Convert();

}
}
24 changes: 19 additions & 5 deletions src/MillionDance/Core/VmdCreator.BoneAnimation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using AssetStudio.Extended.CompositeModels;
using JetBrains.Annotations;
using OpenMLTD.MillionDance.Entities.Internal;
using OpenMLTD.MillionDance.Entities.Mltd;
using OpenMLTD.MillionDance.Entities.Pmx;
using OpenMLTD.MillionDance.Entities.Vmd;
using OpenMLTD.MillionDance.Extensions;
Expand All @@ -16,7 +15,7 @@ namespace OpenMLTD.MillionDance.Core {
partial class VmdCreator {

[NotNull, ItemNotNull]
private static IReadOnlyList<VmdBoneFrame> CreateBoneFrames([NotNull] CharacterImasMotionAsset bodyMotion, [NotNull] PrettyAvatar avatar, [NotNull] PmxModel pmx) {
private static IReadOnlyList<VmdBoneFrame> CreateBoneFrames([NotNull] IBodyAnimationSource bodyMotionSource, [NotNull] PrettyAvatar avatar, [NotNull] PmxModel pmx) {
var mltdHierarchy = BoneUtils.BuildBoneHierarchy(avatar);
var pmxHierarchy = BoneUtils.BuildBoneHierarchy(pmx);

Expand All @@ -34,7 +33,7 @@ partial class VmdCreator {
pmxBone.Initialize();
}

var animation = BodyAnimation.CreateFrom(bodyMotion);
var animation = bodyMotionSource.Convert();
var boneCount = mltdHierarchy.Count;
var animatedBoneCount = animation.BoneCount;
var keyFrameCount = animation.KeyFrames.Count;
Expand Down Expand Up @@ -68,6 +67,15 @@ partial class VmdCreator {
var iterationTimes = keyFrameCount / animatedBoneCount;
var boneFrameList = new List<VmdBoneFrame>();

// Reduce memory pressure of allocating new delegates (see mltdHierarchy.FirstOrDefault(...))
var boneMatchPredicateCache = new Func<PmxBone, bool>[boneCount];

for (var j = 0; j < boneCount; j += 1) {
var refBone = pmx.Bones[j];
boneMatchPredicateCache[j] = bone => bone.Name == refBone.Name;
}

// OK, now perform iterations
for (var i = 0; i < iterationTimes; ++i) {
if (ConversionConfig.Current.Transform60FpsTo30Fps) {
if (i % 2 == 1) {
Expand Down Expand Up @@ -147,9 +155,15 @@ partial class VmdCreator {
var mltdBone = mltdHierarchy[j];

{
var pb = pmx.Bones.FirstOrDefault(b => b.Name == pmxBone.Name);
var predicate = boneMatchPredicateCache[j];
var pb = pmx.Bones.FirstOrDefault(predicate);

Debug.Assert(pb != null, $"PMX bone with the name \"{pmxBone.Name}\" should exist.");
#if DEBUG
if (pb == null) {
// Lazy evaluation of the assertion message
Debug.Assert(pb != null, $"PMX bone with the name \"{pmxBone.Name}\" should exist.");
}
#endif

if (!pb.IsMltdKeyBone) {
continue;
Expand Down
6 changes: 3 additions & 3 deletions src/MillionDance/Core/VmdCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ public sealed partial class VmdCreator {
public uint FixedFov { get; set; } = 20;

[NotNull]
public VmdMotion CreateFrom([CanBeNull] CharacterImasMotionAsset bodyMotion, [CanBeNull] PrettyAvatar avatar, [CanBeNull] PmxModel mltdPmxModel,
public VmdMotion CreateFrom([CanBeNull] IBodyAnimationSource bodyAnimationSource, [CanBeNull] PrettyAvatar avatar, [CanBeNull] PmxModel mltdPmxModel,
[CanBeNull] CharacterImasMotionAsset cameraMotion,
[CanBeNull] ScenarioObject scenarioObject, int songPosition) {
IReadOnlyList<VmdBoneFrame> boneFrames;
IReadOnlyList<VmdCameraFrame> cameraFrames;
IReadOnlyList<VmdFacialFrame> facialFrames;
IReadOnlyList<VmdLightFrame> lightFrames;

if (ProcessBoneFrames && (bodyMotion != null && avatar != null && mltdPmxModel != null)) {
boneFrames = CreateBoneFrames(bodyMotion, avatar, mltdPmxModel);
if (ProcessBoneFrames && (bodyAnimationSource != null && avatar != null && mltdPmxModel != null)) {
boneFrames = CreateBoneFrames(bodyAnimationSource, avatar, mltdPmxModel);
} else {
boneFrames = EmptyArray.Of<VmdBoneFrame>();
}
Expand Down
25 changes: 25 additions & 0 deletions src/MillionDance/Entities/Extensions/KeyTypeExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using JetBrains.Annotations;
using OpenMLTD.MillionDance.Entities.Mltd;

namespace OpenMLTD.MillionDance.Entities.Extensions {
internal static class KeyTypeExtension {

[NotNull]
public static string ToAttributeTextFast(this KeyType k) {
switch (k) {
case KeyType.Const:
return "key_type Const";
case KeyType.Discrete:
return "key_type Discreate"; // This typo exists in MLTD, not my fault.
case KeyType.FullFrame:
return "key_type FullFrame";
case KeyType.FCurve:
return "key_type FCurve";
default:
throw new ArgumentOutOfRangeException(nameof(k), k, null);
}
}

}
}
31 changes: 31 additions & 0 deletions src/MillionDance/Entities/Extensions/PropertyTypeExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using JetBrains.Annotations;
using OpenMLTD.MillionDance.Entities.Mltd;

namespace OpenMLTD.MillionDance.Entities.Extensions {
internal static class PropertyTypeExtension {

[NotNull]
public static string ToAttributeTextFast(this PropertyType p) {
switch (p) {
case PropertyType.General:
return "property_type General";
case PropertyType.AngleX:
return "property_type AngleX";
case PropertyType.AngleY:
return "property_type AngleY";
case PropertyType.AngleZ:
return "property_type AngleZ";
case PropertyType.PositionX:
return "property_type PositionX";
case PropertyType.PositionY:
return "property_type PositionY";
case PropertyType.PositionZ:
return "property_type PositionZ";
default:
throw new ArgumentOutOfRangeException(nameof(p), p, null);
}
}

}
}
Loading

0 comments on commit 3e21599

Please sign in to comment.