diff --git a/.gitattributes b/.gitattributes index 83af38c..daa432e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -63,3 +63,4 @@ #*.RTF diff=astextplain *.vmd filter=lfs diff=lfs merge=lfs -text *.json filter=lfs diff=lfs merge=lfs -text +*.pmm filter=lfs diff=lfs merge=lfs -text diff --git a/README.md b/README.md index d1ce4d0..0a0d7ee 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,6 @@ Or download an archive from [Releases](https://github.com/paralleltree/Scallion/ ## Usage -This library supports only MMD Motion File for now. - ```csharp // using Scallion.DomainModels; @@ -35,6 +33,9 @@ var motion = new Motion().Load(@"path\to\motion.vmd"); // motion.Bones... // motion.Morphs... +var project = new Project().Load(@"path\to\project.pmm"); +// project.Camera... +// project.Models... ``` ## Contributing diff --git a/Scallion.Tests/ConverterTests/Project/CameraTest.cs b/Scallion.Tests/ConverterTests/Project/CameraTest.cs new file mode 100644 index 0000000..544b477 --- /dev/null +++ b/Scallion.Tests/ConverterTests/Project/CameraTest.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using NUnit.Framework; +using System.Numerics; +using Scallion.Tests.Internal; + +namespace Scallion.Tests.ConverterTests.Project +{ + internal class CameraTest + { + private object[] cameraViewPositionTestSource = new[] + { + new object[] { new Vector3(0, 10, 5), 45, new Vector3(90, 0, 0), new Vector3(0, 55, 5), new Vector3(0, 5, -55) }, + new object[] { new Vector3(5, 10, -5), 45, new Vector3(45, 135, -90), new Vector3(27.5f, 41.8198f, 17.5f), new Vector3(7.07f, 7.07f, -52.07f) }, + new object[] { new Vector3(5, 5, 5), 25, new Vector3(45, 90, 135), new Vector3(22.677f, 22.677f, 5), new Vector3(-3.5355f, 3.5355f, -32.071f) }, + new object[] { new Vector3(5, 5, 10), -25, new Vector3(30, 90, 135), new Vector3(-16.65f, -7.5f, 10), new Vector3(-8.365f, 5.7769f, 18.1698f) }, + new object[] { new Vector3(-10, 10, 20), 45, new Vector3(-90, 180, 270), new Vector3(-10, -35, 20), new Vector3(20, -10, -35) }, + new object[] { new Vector3(0, 10, 0), 45, new Vector3(0, 90, 0), new Vector3(45, 10, 0), new Vector3(0, 10, -45) }, + new object[] { new Vector3(0, 10, 0), 45, new Vector3(0, 0, 90), new Vector3(0, 10, -45), new Vector3(-10, 0, -45) }, + }; + [Test] + [TestCaseSource("cameraViewPositionTestSource")] + public void ModelViewPositionTest(Vector3 centerPos, float dist, Vector3 ang, Vector3 worldPos, Vector3 localPos) + { + var raw = new Raw.Components.Project.Camera() + { + CurrentStatus = new Raw.Components.Project.CurrentCameraState() + { + CenterPosition = centerPos, + Rotation = ang.ToRad(), + OffsetPosition = localPos + }, + InitialKeyFrame = new Raw.Components.Project.CameraKeyFrame() + { + Interpolation = new Raw.Components.Project.CameraInterpolationImpl(), + Value = new Raw.Components.Project.CameraState() + }, + KeyFrames = new List() + }; + var conv = new Scallion.Internal.Converters.Project.CameraConverter(true); + var dest = conv.Convert(raw); + Assert.AreEqual(dist, dest.CurrentStatus.Distance, 0.005f); + localPos.AssertEquals(conv.ConvertBack(dest).CurrentStatus.OffsetPosition, 0.005f); + } + + [Test] + public void CameraViewPositionTest() + { + var raw = new Raw.Components.Project.Camera() + { + CurrentStatus = new Raw.Components.Project.CurrentCameraState() + { + CenterPosition = new Vector3(0, 10, 0), + Rotation = new Vector3(0, 0, 0).ToRad(), + OffsetPosition = new Vector3(0, 0, -30) + }, + InitialKeyFrame = new Raw.Components.Project.CameraKeyFrame() + { + Interpolation = new Raw.Components.Project.CameraInterpolationImpl(), + Value = new Raw.Components.Project.CameraState() + }, + KeyFrames = new List() + }; + var conv = new Scallion.Internal.Converters.Project.CameraConverter(false); + var dest = conv.Convert(raw); + Assert.AreEqual(30, dest.CurrentStatus.Distance); + Assert.AreEqual(raw.CurrentStatus.OffsetPosition, conv.ConvertBack(dest).CurrentStatus.OffsetPosition); + } + } +} diff --git a/Scallion.Tests/Internal/HelperExtensions.cs b/Scallion.Tests/Internal/HelperExtensions.cs index a563bd1..e57a5ba 100644 --- a/Scallion.Tests/Internal/HelperExtensions.cs +++ b/Scallion.Tests/Internal/HelperExtensions.cs @@ -40,7 +40,9 @@ public static void AssertPropertyValuesAreEquals(this object actual, object expe object expectedValue = property.GetValue(expected, null); object actualValue = property.GetValue(actual, null); - if (actualValue is ICollection) + if (actualValue == null) + Assert.Null(expectedValue); + else if (actualValue is ICollection) // Comparison for elements contained in the list property.AssertListsAreEquals((ICollection)actualValue, (ICollection)expectedValue); else if (!actualValue.GetType().IsValueType && !actualValue.GetType().Namespace.StartsWith("System")) @@ -73,6 +75,13 @@ private static void AssertListsAreEquals(this PropertyInfo property, ICollection actual.Current.AssertPropertyValuesAreEquals(expected.Current); } } + + public static void AssertEquals(this System.Numerics.Vector3 expected, System.Numerics.Vector3 actual, float delta) + { + Assert.AreEqual(expected.X, actual.X, delta); + Assert.AreEqual(expected.Y, actual.Y, delta); + Assert.AreEqual(expected.Z, actual.Z, delta); + } } [TestFixture] diff --git a/Scallion.Tests/Internal/NumericsExtensions.cs b/Scallion.Tests/Internal/NumericsExtensions.cs new file mode 100644 index 0000000..f65fb53 --- /dev/null +++ b/Scallion.Tests/Internal/NumericsExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using System.Numerics; + +namespace Scallion.Tests.Internal +{ + internal static class NumericsExtensions + { + public static Vector3 ToRad(this Vector3 deg) + { + return new Vector3((float)Math.PI * deg.X / 180, (float)Math.PI * deg.Y / 180, (float)Math.PI * deg.Z / 180); + } + + public static Vector3 ToDeg(this Vector3 rad) + { + return new Vector3(rad.X * 180 / (float)Math.PI, rad.Y * 180 / (float)Math.PI, rad.Z * 180 / (float)Math.PI); + } + } +} diff --git a/Scallion.Tests/Properties/AssemblyInfo.cs b/Scallion.Tests/Properties/AssemblyInfo.cs index 83615a8..8036597 100644 --- a/Scallion.Tests/Properties/AssemblyInfo.cs +++ b/Scallion.Tests/Properties/AssemblyInfo.cs @@ -11,4 +11,4 @@ [assembly: ComVisible(false)] [assembly: Guid("2b69216b-f367-46a9-8d9b-56b16771019c")] -[assembly: AssemblyVersion("0.9.1.0")] +[assembly: AssemblyVersion("0.10.0.0")] diff --git a/Scallion.Tests/Resources/Binaries/Motion_Bone_1.vmd b/Scallion.Tests/Resources/Binaries/Motion_Bone_1.vmd deleted file mode 100644 index be01d00..0000000 --- a/Scallion.Tests/Resources/Binaries/Motion_Bone_1.vmd +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59ffb534cde248b20eb3b387b8834c8d3ccaa8109c91590a49d1c907d43d1ae3 -size 944 diff --git a/Scallion.Tests/Resources/Binaries/Motion_Camera_1.vmd b/Scallion.Tests/Resources/Binaries/Motion_Camera_1.vmd deleted file mode 100644 index ad78fab..0000000 --- a/Scallion.Tests/Resources/Binaries/Motion_Camera_1.vmd +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e0a9bb1a1dc185ff96d6f7c864086dd3f208b313d435a114c96ad6c48437e230 -size 288 diff --git a/Scallion.Tests/Resources/Fixtures/DomainModels/Motion_Bone_1.json b/Scallion.Tests/Resources/Fixtures/DomainModels/Motion_Bone_1.json deleted file mode 100644 index 36b65b3..0000000 --- a/Scallion.Tests/Resources/Fixtures/DomainModels/Motion_Bone_1.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b9ecf82f1e88611e7b0063c659614c8e5070368b152ada999427d1d3e1b40ddd -size 10939 diff --git a/Scallion.Tests/Resources/Fixtures/DomainModels/Motion_Camera_1.json b/Scallion.Tests/Resources/Fixtures/DomainModels/Motion_Camera_1.json deleted file mode 100644 index 33f756a..0000000 --- a/Scallion.Tests/Resources/Fixtures/DomainModels/Motion_Camera_1.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b1934e65355cd212034cc60bcc6daf9024b07abcfd3414989270d8890d00d54b -size 6401 diff --git a/Scallion.Tests/Resources/Fixtures/Raw/Motion_Bone_1.json b/Scallion.Tests/Resources/Fixtures/Raw/Motion_Bone_1.json deleted file mode 100644 index 23ccc51..0000000 --- a/Scallion.Tests/Resources/Fixtures/Raw/Motion_Bone_1.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0f32fdfdc39516a9282dda5beab5931ef2c346a43b451249d1b6530f5814d601 -size 8556 diff --git a/Scallion.Tests/Resources/Fixtures/Raw/Motion_Camera_1.json b/Scallion.Tests/Resources/Fixtures/Raw/Motion_Camera_1.json deleted file mode 100644 index b2203fa..0000000 --- a/Scallion.Tests/Resources/Fixtures/Raw/Motion_Camera_1.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c74412ce1abeb20a9c8bb385e03857abc108a21de05c25c358bec5dc89674aab -size 5525 diff --git a/Scallion.Tests/Scallion.Tests.csproj b/Scallion.Tests/Scallion.Tests.csproj index e2c2081..923f0ea 100644 --- a/Scallion.Tests/Scallion.Tests.csproj +++ b/Scallion.Tests/Scallion.Tests.csproj @@ -60,14 +60,14 @@ False + + + - - - @@ -77,24 +77,10 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - + + + + diff --git a/Scallion.Tests/UnitTests/DomainModels/MotionTest.cs b/Scallion.Tests/UnitTests/DomainModels/MotionTest.cs deleted file mode 100644 index 4353bd4..0000000 --- a/Scallion.Tests/UnitTests/DomainModels/MotionTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; - -using Scallion.DomainModels; - -namespace Scallion.Tests.UnitTests.DomainModels -{ - internal class MotionTest : IOTest - { - protected override IEnumerable TestCases() - { - return Directory.EnumerateFiles(@"Resources\Binaries", "*.vmd") - .Select(p => - { - string name = Path.GetFileNameWithoutExtension(p); - return new object[] - { - string.Format(@"Resources\Binaries\{0}.vmd",name), - string.Format(@"Resources\Fixtures\DomainModels\{0}.json",name) - }; - }); - } - } -} diff --git a/Scallion.Tests/UnitTests/IOTest.cs b/Scallion.Tests/UnitTests/IOTest.cs deleted file mode 100644 index 805189c..0000000 --- a/Scallion.Tests/UnitTests/IOTest.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; -using System.Text.RegularExpressions; - -using NUnit.Framework; -using Newtonsoft.Json; -using Scallion.Core; -using Scallion.Tests.Internal; - -namespace Scallion.Tests.UnitTests -{ - /// - /// Tests serialization and deserialization of the interface. - /// This class is abstract. - /// - /// The type to test - [TestFixture] - public abstract class IOTest where T : IMMDFile, new() - { - /// - /// Tests the deserialization process for by comparing with a expected result. - /// - /// The path to a binary file to be read - /// The path to a json formatted file that the test expects - [Test] - [TestCaseSource("TestCases")] - public void LoadingRawBinaryTest(string binaryPath, string fixturePath) - { - var expected = JsonConvert.DeserializeObject(File.ReadAllText(fixturePath)); - var actual = new T().Load(binaryPath); - actual.AssertPropertyValuesAreEquals(expected); - } - - /// - /// Tests the serialization and deserialization process for by comparing with a expected result after serialization. - /// - /// The path to a binary file to be read - /// The path to a json formatted file that the test expects - [Test] - [Category("UseTempFile")] - [TestCaseSource("TestCases")] - public void LoadingSavedBinaryTest(string binaryPath, string fixturePath) - { - var expected = JsonConvert.DeserializeObject(File.ReadAllText(fixturePath)); - var source = new T(); - source.Load(binaryPath); - string tempPath = binaryPath.GetTempPath(); - try - { - source.Save(tempPath); - var restored = new T(); - restored.Load(tempPath); - restored.AssertPropertyValuesAreEquals(expected); - } - finally - { - File.Delete(tempPath); - } - } - - /// - /// Enumerates the parameters of test cases. - /// - /// A collection containing the path to the binary file and the json formatted file of expected results - protected abstract IEnumerable TestCases(); - } -} diff --git a/Scallion.Tests/UnitTests/Raw/MotionTest.cs b/Scallion.Tests/UnitTests/Raw/MotionTest.cs deleted file mode 100644 index 2711b41..0000000 --- a/Scallion.Tests/UnitTests/Raw/MotionTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; - -using Scallion.Raw; - -namespace Scallion.Tests.UnitTests.Raw -{ - internal class MotionTest : IOTest - { - protected override IEnumerable TestCases() - { - return Directory.EnumerateFiles(@"Resources\Binaries", "*.vmd") - .Select(p => - { - string name = Path.GetFileNameWithoutExtension(p); - return new object[] - { - string.Format(@"Resources\Binaries\{0}.vmd",name), - string.Format(@"Resources\Fixtures\Raw\{0}.json",name) - }; - }); - } - } -} diff --git a/Scallion/Core/MoDeserializer.cs b/Scallion/Core/MoDeserializer.cs index 258f390..52b9e2f 100644 --- a/Scallion/Core/MoDeserializer.cs +++ b/Scallion/Core/MoDeserializer.cs @@ -8,7 +8,7 @@ namespace Scallion.Core /// /// Represents a deserializer for . /// - internal class MoDeserializer : MoIO + internal class MoDeserializer : MoIO, IDisposable { private BinaryReader _stream; @@ -52,9 +52,31 @@ public T Deserialize(T obj) where T : MMDObject /// The deserialized collection of the class public List DeserializeList() where T : MMDObject, new() { - int count = ReadInt32(); + return DeserializeList(ReadInt32()); + } + + /// + /// Deserializes a collection of the class from the stream. + /// + /// The type of a object to be deserialized + /// The number of elements to deserialize + /// The deserialized collection of the class + public List DeserializeList(int count) where T : MMDObject, new() + { + return DeserializeList(count, () => Deserialize()); + } + + /// + /// Returns a collection of the class by using the specified selector. + /// + /// The type of a object to be collected + /// The number of elements to collect + /// The function to collect each element + /// The collection of the class + public List DeserializeList(int count, Func selector) + { var list = new List(count); - for (int i = 0; i < count; i++) list.Add(Deserialize()); + for (int i = 0; i < count; i++) list.Add(selector()); return list; } @@ -106,5 +128,10 @@ public string ReadVariableString() byte length = _stream.ReadByte(); return ReadByteString(length); } + + public void Dispose() + { + _stream.Dispose(); + } } } diff --git a/Scallion/Core/MoSerializer.cs b/Scallion/Core/MoSerializer.cs index 3aab1f6..1a02e24 100644 --- a/Scallion/Core/MoSerializer.cs +++ b/Scallion/Core/MoSerializer.cs @@ -8,7 +8,7 @@ namespace Scallion.Core /// /// Represents a serializer for . /// - internal class MoSerializer : MoIO + internal class MoSerializer : MoIO, IDisposable { private BinaryWriter _stream; @@ -34,14 +34,24 @@ public void Serialize(T item) where T : MMDObject } /// - /// Serializes a collection of the class to the stream. + /// Serializes a collection of the class without the number of its elements to the stream. + /// + /// The type of objects to be serialized + /// The collection to be serialized + public void SerializeListWithoutCount(List list) where T : MMDObject + { + foreach (var item in list) item.Serialize(this); + } + + /// + /// Serializes a collection of the class with the number of its elements to the stream. /// /// The type of objects to be serialized /// The collection to be serialized public void SerializeList(List list) where T : MMDObject { WriteInt32(list.Count); - foreach (var item in list) item.Serialize(this); + SerializeListWithoutCount(list); } @@ -95,5 +105,10 @@ public void WriteVariableString(string value) _stream.Write((byte)data.Length); _stream.Write(data); } + + public void Dispose() + { + _stream.Dispose(); + } } } diff --git a/Scallion/DomainModels/Components/Accessory.cs b/Scallion/DomainModels/Components/Accessory.cs new file mode 100644 index 0000000..d4ae5f5 --- /dev/null +++ b/Scallion/DomainModels/Components/Accessory.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents a MMD Accessory object. + /// + public class Accessory + { + /// + /// Gets or sets the index of the accessory. + /// + public int Index { get; set; } + + /// + /// Gets or sets the name of the accessory. + /// + public string Name { get; set; } + + /// + /// Gets or sets the path of the accessory file. + /// + public string Path { get; set; } + + /// + /// Gets or sets the index indicating the order of rendering. + /// + public byte RenderingOrder { get; set; } + + /// + /// Gets or sets a value indicating whether the additive blending is enabled. + /// + public bool IsAdditiveBlending { get; set; } + + /// + /// Gets or sets a collection of the class. + /// + public List KeyFrames { get; set; } + + /// + /// Gets or sets a instance of indicating current accessory status. + /// + public AccessoryState CurrentStatus { get; set; } + + + /// + /// Initializes a new instance of the class. + /// + public Accessory() + { + CurrentStatus = new AccessoryState(); + KeyFrames = new List(); + } + } + + public class AccessoryKeyFrame : KeyFrame + { + } + + /// + /// Represents a state for . + /// + public class AccessoryState + { + /// + /// Gets or sets the opacity factor applied to the accessory. + /// + public float Opacity { get; set; } + + /// + /// Gets or sets a value indicating whether the accessory is visible. + /// + public bool IsVisible { get; set; } + + /// + /// Gets or sets the reference to the external parent bone. + /// + public BoneReference ExternalParent { get; set; } + + /// + /// Gets or sets the position of the accessory. + /// + public Vector3 Position { get; set; } + + /// + /// Gets or sets the angle of rotation. + /// + public Vector3 Rotation { get; set; } + + /// + /// Gets or sets the scale factor applied to the accessory. + /// + public float Scale { get; set; } + + /// + /// Gets or sets a value indicating whether the shadowing is enabled. + /// + public bool IsShadowEnabled { get; set; } + } +} diff --git a/Scallion/DomainModels/Components/Bone.cs b/Scallion/DomainModels/Components/Bone.cs index 9443d61..732635e 100644 --- a/Scallion/DomainModels/Components/Bone.cs +++ b/Scallion/DomainModels/Components/Bone.cs @@ -21,12 +21,24 @@ public class Bone /// public List KeyFrames { get; set; } + /// + /// Gets or sets a instance of indicating current bone status. + /// + public CurrentBoneState CurrentStatus { get; set; } + + /// + /// Gets or sets a collection of the class. + /// + public List ExternalParentKeyFrames { get; set; } + /// /// Initializes a new instance of the class. /// public Bone() { + CurrentStatus = new CurrentBoneState(); KeyFrames = new List(); + ExternalParentKeyFrames = new List(); } } @@ -36,23 +48,43 @@ public Bone() public class IKBone : Bone { /// - /// Gets or sets a collection of the class. + /// Gets or sets the current IK status. + /// + public IKBoneState CurrentIKStatus { get; set; } + + /// + /// Gets or sets a collection of the class. /// - public List IKStateKeyFrames { get; set; } + public List IKStateKeyFrames { get; set; } /// /// Initializes a new instance of the class. /// public IKBone() { - IKStateKeyFrames = new List(); + IKStateKeyFrames = new List(); + } + + /// + /// Initializes a new instance of the from the specified . + /// + /// The source bone + public IKBone(Bone bone) : this() + { + Name = bone.Name; + KeyFrames = bone.KeyFrames; + CurrentStatus = bone.CurrentStatus; } } + public class BoneKeyFrame : KeyFrame + { + } + /// - /// Represents a key frame for a bone. + /// Represents a state for a bone. /// - public class BoneKeyFrame : KeyFrame + public class BoneState { /// /// Gets or sets a position of the bone in this key frame. @@ -69,19 +101,47 @@ public class BoneKeyFrame : KeyFrame /// public BoneInterpolation Interpolation { get; set; } + /// + /// Gets or sets a value indicating whether the physics calculation for the bone is enabled. + /// + public bool IsPhysicsEnabled { get; set; } + + /// + /// Gets or sets the reference to the external parent bone. + /// + public BoneReference ExternalParent { get; set; } + + /// /// Initializes a new instance of the class. /// - public BoneKeyFrame() + public BoneState() { Interpolation = new BoneInterpolation(); } } + public class CurrentBoneState : BoneState + { + /// + /// Gets or sets a value indicating whether the bone state is saved. + /// + public bool IsSaved { get; set; } + + /// + /// Gets or sets a value indicating whether the bone is selected on the timeline panel. + /// + public bool IsRowSelected { get; set; } + } + + public class IKBoneKeyFrame : KeyFrame + { + } + /// - /// Represents a key frame indicating whether IK of the bone is enabled. + /// Represents the IK bone state. /// - public class IKStateKeyFrame : KeyFrame + public class IKBoneState { /// /// Gets or sets a value indicating whether IK of the bone is enabled. diff --git a/Scallion/DomainModels/Components/BoneReference.cs b/Scallion/DomainModels/Components/BoneReference.cs new file mode 100644 index 0000000..9fdcdc0 --- /dev/null +++ b/Scallion/DomainModels/Components/BoneReference.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents a reference for the bone of a external model. + /// + public class BoneReference + { + /// + /// Gets the parent model. + /// + public Model TargetModel { get; } + + /// + /// Gets the parent bone of . + /// The null value represents the root of model. + /// + public Bone TargetBone { get; } + + /// + /// Initializes a new instance of the class. + /// + public BoneReference() + { + } + + /// + /// Initializes a new instance of the class from the model and bone. + /// + /// The parent model + /// The parent bone being a part of + public BoneReference(Model model, Bone bone) + { + if (!model.Bones.Contains(bone)) + throw new ArgumentException("The collection of bones of model must contain the bone."); + TargetModel = model; + TargetBone = bone; + } + } +} diff --git a/Scallion/DomainModels/Components/Camera.cs b/Scallion/DomainModels/Components/Camera.cs index 0eba7dc..33cc7ee 100644 --- a/Scallion/DomainModels/Components/Camera.cs +++ b/Scallion/DomainModels/Components/Camera.cs @@ -16,19 +16,29 @@ public class Camera /// public List KeyFrames { get; set; } + /// + /// Gets or sets a instance of indicating current camera status. + /// + public CameraState CurrentStatus { get; set; } + /// /// Initializes a new instance of the class. /// public Camera() { + CurrentStatus = new CameraState(); KeyFrames = new List(); } } + public class CameraKeyFrame : KeyFrame + { + } + /// - /// Represents a key frame for a camera. + /// Represents a state for a camera. /// - public class CameraKeyFrame : KeyFrame + public class CameraState { /// /// Gets or sets the position of the camera in this key frame. @@ -63,10 +73,15 @@ public class CameraKeyFrame : KeyFrame /// public bool IsPerspectiveEnabled { get; set; } + /// + /// Gets or sets the reference to the external model bone. + /// + public BoneReference FollowingBone { get; set; } + /// /// Initializes a new instance of the class. /// - public CameraKeyFrame() + public CameraState() { Interpolation = new CameraInterpolation(); } diff --git a/Scallion/DomainModels/Components/Enum.cs b/Scallion/DomainModels/Components/Enum.cs new file mode 100644 index 0000000..ce7bac5 --- /dev/null +++ b/Scallion/DomainModels/Components/Enum.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Scallion.DomainModels.Components +{ + /// + /// Specifies the mode of screen capturing. + /// + public enum ScreenCaptureMode + { + /// + /// Do not capture the screen. + /// + Off, + + /// + /// Captures the screen. + /// + FullScreen, + + /// + /// Captures the screen as 4:3. + /// + FourByThree, + + /// + /// Displays the video file. + /// + BackgroundVideo + } + + /// + /// Specifies the mode of using physics engine. + /// + public enum PhysicsMode + { + /// + /// Do not use the physics engine. + /// + Off, + + /// + /// Always use the physics engine. + /// + Always, + + /// + /// Switches whether the physics calculation is enabled for each bone. + /// + OnOff, + + /// + /// Locks rigid bodies while the physics calculation for the related bone is disabled. + /// + Tracing + } + + /// + /// Specifies the method of selecting bones. + /// + public enum BoneSelectionType + { + /// + /// Selects a bone. + /// + SingleSelection, + + /// + /// Selects bones in the rectangle made by dragging. + /// + BoxSelection, + + /// + /// Do not select any bones. + /// + None, + + /// + /// Selects a bone and edits its rotation. + /// + Rotate, + + /// + /// Selects a bone and edits its location. + /// + Move + } + + /// + /// Specifies a target object that the camera follows. + /// + public enum CameraFollowingType + { + /// + /// Do not follow any targets. + /// + None, + + /// + /// Follows the selected model. + /// + Model, + + /// + /// Follows the selected bone of the selected model. + /// + Bone + } + + /// + /// Specifies a value of limited fps. + /// + public enum FpsLimit + { + /// + /// No limits. + /// + None, + + /// + /// Limits fps to 30. + /// + Thirty, + + /// + /// Limits fps to 60. + /// + Sixty + } +} diff --git a/Scallion/DomainModels/Components/Interpolation.cs b/Scallion/DomainModels/Components/Interpolation.cs index da3fea4..7c70542 100644 --- a/Scallion/DomainModels/Components/Interpolation.cs +++ b/Scallion/DomainModels/Components/Interpolation.cs @@ -27,9 +27,19 @@ public class Interpolation /// Initializes a new instance of the class that defined as linear interpolation. /// public Interpolation() + : this(new InterpolationParameter(20, 20), new InterpolationParameter(107, 107)) { - First = new InterpolationParameter(20, 20); - Second = new InterpolationParameter(107, 107); + } + + /// + /// Initializes a new instance of the class with the specified objects. + /// + /// The instance of the class for property of this interpolation. + /// The instance of the class for property of this interpolation. + public Interpolation(InterpolationParameter first, InterpolationParameter second) + { + First = first; + Second = second; } } diff --git a/Scallion/DomainModels/Components/KeyFrame.cs b/Scallion/DomainModels/Components/KeyFrame.cs index 15267e8..e441d17 100644 --- a/Scallion/DomainModels/Components/KeyFrame.cs +++ b/Scallion/DomainModels/Components/KeyFrame.cs @@ -8,22 +8,53 @@ namespace Scallion.DomainModels.Components /// Represents a key frame used for transition. /// This class is abstract. /// - public abstract class KeyFrame + public class KeyFrame where T : new() { /// /// Gets or sets the index of this key frame. /// public int KeyFrameIndex { get; set; } + + /// + /// Gets or sets a value indicating whether the key frame is selected. + /// + public bool IsSelected { get; set; } + + public T Value { get; set; } + + public KeyFrame() + { + Value = new T(); + } + } + + public class VisibilityKeyFrame : KeyFrame + { } /// /// Represents a key frame that indicates whether a object is visible. /// - public class VisibilityKeyFrame : KeyFrame + public class VisibilityState { /// /// Gets or sets a value indicating whether the object is visible. /// public bool IsVisible { get; set; } } + + public class ExternalParentKeyFrame : KeyFrame + { + } + + /// + /// Represents a key frame that indicates the external parent bone. + /// + public class ExternalParentState + { + /// + /// Gets or sets the reference to the external parent bone. + /// + public BoneReference Reference { get; set; } + } } diff --git a/Scallion/DomainModels/Components/Light.cs b/Scallion/DomainModels/Components/Light.cs index c4a816e..89da878 100644 --- a/Scallion/DomainModels/Components/Light.cs +++ b/Scallion/DomainModels/Components/Light.cs @@ -12,6 +12,11 @@ namespace Scallion.DomainModels.Components /// public class Light { + /// + /// Gets or sets a instance of indicating current lighting status. + /// + public LightState CurrentStatus { get; set; } + /// /// Gets or sets a collection of the class. /// @@ -22,22 +27,27 @@ public class Light /// public Light() { + CurrentStatus = new LightState(); KeyFrames = new List(); } } + public class LightKeyFrame : KeyFrame + { + } + /// - /// Represents a key frame for a light configuration. + /// Represents a state for light configuration. /// - public class LightKeyFrame : KeyFrame + public class LightState { /// - /// Gets or sets a position of the light in this key frame. + /// Gets or sets a position of the light. /// public Vector3 Position { get; set; } /// - /// Gets or sets a color of the light in this key frame. + /// Gets or sets a color of the light. /// public Color Color { get; set; } } diff --git a/Scallion/DomainModels/Components/Media.cs b/Scallion/DomainModels/Components/Media.cs new file mode 100644 index 0000000..967de23 --- /dev/null +++ b/Scallion/DomainModels/Components/Media.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Drawing; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents a set of media in MMD. + /// + public class Media + { + /// + /// Gets or sets a instance of the class. + /// + public Audio Audio { get; set; } + + /// + /// Gets or sets a instance of the class. + /// + public Video Video { get; set; } + + /// + /// Gets or sets a instance of the class. + /// + public BackgroundImage BackgroundImage { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public Media() + { + Audio = new Audio(); + Video = new Video(); + BackgroundImage = new BackgroundImage(); + } + } + + /// + /// Represents a Audio in MMD. + /// + public class Audio + { + /// + /// Gets or sets a value indicating whether this audio is enabled. + /// + public bool IsEnabled { get; set; } + + /// + /// Gets or sets the path of the audio file. + /// + public string Path { get; set; } + } + + /// + /// Represents a Video in MMD. + /// + public class Video + { + /// + /// Gets or sets the position of the video. + /// + public Point Position { get; set; } + + /// + /// Gets or sets the scale factor applied to the video. + /// + public float Scale { get; set; } + + /// + /// Gets or sets a value indicating whether this video is visible. + /// + public bool IsVisible { get; set; } + + /// + /// Gets or sets the path of the video file. + /// + public string Path { get; set; } + } + + /// + /// Represents a BackgroundImage in MMD. + /// + public class BackgroundImage + { + /// + /// Gets or sets the position of the image. + /// + public Point Position { get; set; } + + /// + /// Gets or sets the scale factor applied to the image. + /// + public float Scale { get; set; } + + /// + /// Gets or sets a value indicating whether this image is visible. + /// + public bool IsVisible { get; set; } + + /// + /// Gets or sets the path of the background image file. + /// + public string Path { get; set; } + } +} diff --git a/Scallion/DomainModels/Components/Model.cs b/Scallion/DomainModels/Components/Model.cs new file mode 100644 index 0000000..ce78d06 --- /dev/null +++ b/Scallion/DomainModels/Components/Model.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents MMD Model object. + /// + public class Model + { + /// + /// Gets or sets the index of the model. + /// + public int Index { get; set; } + + /// + /// Gets or sets the name of the model. + /// + public string Name { get; set; } + + /// + /// Gets or sets the name of the model in English. + /// + public string NameEn { get; set; } + + /// + /// Gets or sets the path of the model file. + /// + public string Path { get; set; } + + /// + /// Gets or sets a collection of the class. + /// + public List Bones { get; set; } + + /// + /// Gets a collection of the class in property. + /// + public IEnumerable IKBones + { + get { return Bones.Where(p => p is IKBone).Cast(); } + } + + /// + /// Gets or sets a collection of the class for this model. + /// + public List VisibilityKeyFrames { get; set; } + + /// + /// Gets or sets a collection of the class. + /// + public List Morphs { get; set; } + + /// + /// Gets or sets the index indicating the order of rendering starting from 1. + /// + public int RenderingOrder { get; set; } + + /// + /// Gets or sets the index indicating the order of calculation. + /// + public int CalculationOrder { get; set; } + + /// + /// Gets or sets a value indicating whether the model is visible. + /// + public bool IsVisible { get; set; } + + /// + /// Gets or sets the index of the selected bone. + /// + public int SelectedBoneIndex { get; set; } + + /// + /// Gets or sets the index of last frame. + /// + public int LastFrameIndex { get; set; } + + /// + /// Gets or sets a value indicating whether the additive blending is enabled. + /// + public bool IsAdditiveBlending { get; set; } + + /// + /// Gets or sets the width of edges. + /// + public float EdgeWidth { get; set; } + + /// + /// Gets or sets a value indicating whether self-shadowing is enabled. + /// + public bool IsSelfShadowEnabled { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public ModelTimelinePanel TimelinePanel { get; set; } + + /// + /// Gets or sets the collection of bone groups expansion status. + /// + public List BoneGroupsExpansion { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public Model() + { + Bones = new List(); + Morphs = new List(); + VisibilityKeyFrames = new List(); + TimelinePanel = new ModelTimelinePanel(); + } + } +} diff --git a/Scallion/DomainModels/Components/Morph.cs b/Scallion/DomainModels/Components/Morph.cs index 0078620..fb1b129 100644 --- a/Scallion/DomainModels/Components/Morph.cs +++ b/Scallion/DomainModels/Components/Morph.cs @@ -19,6 +19,8 @@ public class Morph /// public List KeyFrames { get; set; } + public MorphState CurrentStatus { get; set; } + /// /// Initializes a new instance of the class. /// @@ -28,10 +30,14 @@ public Morph() } } + public class MorphKeyFrame : KeyFrame + { + } + /// - /// Represents a key frame for a morph. + /// Represents a state for a morph. /// - public class MorphKeyFrame : KeyFrame + public class MorphState { /// /// Gets or sets a value of the morph in this key frame. diff --git a/Scallion/DomainModels/Components/Panel.cs b/Scallion/DomainModels/Components/Panel.cs new file mode 100644 index 0000000..07e53dd --- /dev/null +++ b/Scallion/DomainModels/Components/Panel.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents User Interface of MMD. + /// + public class Panel + { + /// + /// Gets or sets a value indicating whether any model is selected. + /// + public bool IsModelSelected { get; set; } + + /// + /// Gets or sets the index of the selected model. + /// + public int SelectedModelIndex { get; set; } + + /// + /// Gets or sets the index of the selected accessory. + /// + public int SelectedAccessoryIndex { get; set; } + + /// + /// Gets or sets the index of frame in the box. + /// + public int FrameJumpingBoxValue { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public PreviewPanel PreviewPanel { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public ViewPanel ViewPanel { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public TimelinePanel TimelinePanel { get; set; } + + /// + /// Gets or sets the expansion status of each control panel. + /// + public PanelExpansion PanelExpansion { get; set; } + + /// + /// Gets or sets a value indicating the way of selecting bone(s). + /// + public BoneSelectionType BoneSelectionType { get; set; } + + + /// + /// Initializes a new instance of the class. + /// + public Panel() + { + PreviewPanel = new PreviewPanel(); + ViewPanel = new ViewPanel(); + TimelinePanel = new TimelinePanel(); + PanelExpansion = new PanelExpansion(); + } + } + + /// + /// Represents a preview panel. + /// + public class PreviewPanel + { + /// + /// Gets or sets a value indicating whether repeating is enabled on previewing. + /// + public bool IsRepeating { get; set; } + + /// + /// Gets or sets a value indicating whether previewing starts from current frame. + /// + public bool IsStartingFromCurrentFrame { get; set; } + + /// + /// Gets or sets a value indicating whether the key frame position stays at end of the range after previewing finished. + /// + public bool IsStayingAtStoppedFrame { get; set; } + + /// + /// Gets or sets the index of the frame beginning of previewing range. + /// + public int StartFrameIndex { get; set; } + + /// + /// Gets or sets the index of the frame ending of previewing range. + /// + public int EndFrameIndex { get; set; } + } + + /// + /// Represents a view panel. + /// + public class ViewPanel + { + /// + /// Gets or sets a value indicating whether the camera follows any target. + /// + public bool IsFollowingViewEnabled { get; set; } + + /// + /// Gets or sets the way of following bone. + /// + public CameraFollowingType CameraFollowingType { get; set; } + } + + /// + /// Represents a timeline panel. + /// + public class TimelinePanel + { + /// + /// Gets or sets the width of the timeline panel. + /// + public int PanelWidth { get; set; } + + /// + /// Gets or sets the index of the accessory that is on the top of timeline panel. + /// + public int TopAccessoryIndex { get; set; } + + /// + /// Gets or sets the index of the current frame. + /// + public int CurrentFrameIndex { get; set; } + + /// + /// Gets or sets the index of the head frame on timeline panel. + /// + public int HorizontalHeadFrameIndex { get; set; } + + /// + /// Gets or sets the magnification of the horizontal scroll bar. + /// + public int HorizontalScale { get; set; } + } + + /// + /// Represents a timeline panel for each model. + /// + public class ModelTimelinePanel + { + /// + /// Gets or sets the number of rows on the model timeline panel. + /// + public int RowsCount { get; set; } + + /// + /// Gets or sets the index of the row being on the top of the panel. + /// + public int TopRowIndex { get; set; } + + /// + /// Gets or sets the index of the item selected range-selector combobox. + /// + public int RangeSelectorSelectedIndex { get; set; } + } + + /// + /// Represents the expansion status of each control panel. + /// + public class PanelExpansion + { + /// + /// Gets or sets a value indicating whether the camera panel is expanded. + /// + public bool Camera { get; set; } + + /// + /// Gets or sets a value indicating whether the light panel is expanded. + /// + public bool Light { get; set; } + + /// + /// Gets or sets a value indicating whether the accessory panel is expanded. + /// + public bool Accessory { get; set; } + + /// + /// Gets or sets a value indicating whether the bone panel is expanded. + /// + public bool Bone { get; set; } + + /// + /// Gets or sets a value indicating whether the morph panel is expanded. + /// + public bool Morph { get; set; } + + /// + /// Gets or sets a value indicating whether the self-shadow panel is expanded. + /// + public bool SelfShadow { get; set; } + } +} diff --git a/Scallion/DomainModels/Components/Physics.cs b/Scallion/DomainModels/Components/Physics.cs new file mode 100644 index 0000000..1c8c4a2 --- /dev/null +++ b/Scallion/DomainModels/Components/Physics.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents a set of MMD Physics Engine parameters. + /// + public class Physics + { + /// + /// Gets or sets a instance of the class. + /// + public Gravity Gravity { get; set; } + + /// + /// Gets or sets a value indicating the type of physics calculation. + /// + public PhysicsMode PhysicsMode { get; set; } + + /// + /// Gets or sets a value indicating whether the floor for physics is enabled. + /// + public bool IsGroundPhysicsEnabled { get; set; } + + + /// + /// Initializes a new instance of the class. + /// + public Physics() + { + Gravity = new Gravity(); + } + } + + /// + /// Represents the attraction of gravity. + /// + public class Gravity + { + /// + /// Gets or sets a collection of the class. + /// + public List KeyFrames { get; set; } + + /// + /// Gets or sets a instance of indicating current gravity status. + /// + public GravityState CurrentStatus { get; set; } + + + /// + /// Initializes a new instance of the class. + /// + public Gravity() + { + KeyFrames = new List(); + CurrentStatus = new GravityState(); + } + } + + public class GravityKeyFrame : KeyFrame + { + } + + /// + /// Represents a gravity state. + /// + public class GravityState + { + /// + /// Gets or sets a value indicating gravitational acceleration. + /// + public float Acceleration { get; set; } + + /// + /// Gets or sets a value indicating the amount of noise for physics calculation. + /// + public int NoiseAmount { get; set; } + + /// + /// Gets or sets a direction of gravitational acceleration. + /// + public Vector3 Direction { get; set; } + + /// + /// Gets or sets a value indicating whether the noise is applied. + /// + public bool IsNoiseEnabled { get; set; } + } +} diff --git a/Scallion/DomainModels/Components/SelfShadow.cs b/Scallion/DomainModels/Components/SelfShadow.cs index 24b4903..76cb3dd 100644 --- a/Scallion/DomainModels/Components/SelfShadow.cs +++ b/Scallion/DomainModels/Components/SelfShadow.cs @@ -9,24 +9,39 @@ namespace Scallion.DomainModels.Components /// public class SelfShadow { + /// + /// Gets or sets the value whether self-shadow is enabled. + /// + public bool IsEnabled { get; set; } + /// /// Gets or sets a collection of the class. /// public List KeyFrames { get; set; } + /// + /// Gets or sets a instance of indicating current self shadow status. + /// + public SelfShadowState CurrentStatus { get; set; } + /// /// Initializes a new instance of the class. /// public SelfShadow() { + CurrentStatus = new SelfShadowState(); KeyFrames = new List(); } } + public class SelfShadowKeyFrame : KeyFrame + { + } + /// - /// Represents a key frame for a self shadow configuration. + /// Represents a state for a self shadow configuration. /// - public class SelfShadowKeyFrame : KeyFrame + public class SelfShadowState { /// /// Gets or sets a type of self-shadowing methods in . diff --git a/Scallion/DomainModels/Components/View.cs b/Scallion/DomainModels/Components/View.cs new file mode 100644 index 0000000..94d23d8 --- /dev/null +++ b/Scallion/DomainModels/Components/View.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Drawing; + +namespace Scallion.DomainModels.Components +{ + /// + /// Represents MMD 3D View. + /// + public class View + { + /// + /// Gets or sets a value indicating whether the information is visible. + /// + public bool IsInformationVisible { get; set; } + + /// + /// Gets or sets a value indicating whether the axes are visible. + /// + public bool IsAxesVisible { get; set; } + + /// + /// Gets or sets the color of edges. + /// + public Color EdgeColor { get; set; } + + /// + /// Gets or sets a value indicating whether the background color is black. + /// + public bool IsBackgroundBlack { get; set; } + + /// + /// Gets or sets a value indicating the limited fps. + /// + public FpsLimit FpsLimit { get; set; } + + /// + /// Gets or sets a value indicating whether surface shadows are enabled. + /// + public bool IsSurfaceShadowEnabled { get; set; } + + /// + /// Gets or sets a value indicating the brightness of surface shadows. + /// + public float SurfaceShadowBrightness { get; set; } + + /// + /// Gets or sets a value indicating whether surface shadows are transparent. + /// + public bool IsSurfaceShadowTransparent { get; set; } + + /// + /// Gets or sets the first index(order) of the accessories being rendered after models. + /// + public int AccessoryRenderedAfterModelIndex { get; set; } + + /// + /// Gets or sets the mode of screen capturing. + /// + public ScreenCaptureMode ScreenCapturingMode { get; set; } + } +} diff --git a/Scallion/DomainModels/Project.cs b/Scallion/DomainModels/Project.cs new file mode 100644 index 0000000..a70bcf4 --- /dev/null +++ b/Scallion/DomainModels/Project.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Drawing; + +using Scallion.Core; +using Scallion.DomainModels.Components; +using Scallion.Internal.Converters.Project; + +namespace Scallion.DomainModels +{ + /// + /// Represents a MMD Project File(.pmm). + /// + public class Project : IMMDFile + { + /// + /// Gets or sets the size of the ouput image. + /// + public Size OutputSize { get; set; } + + /// + /// Gets or sets a collection of the class. + /// + public List Models { get; set; } + + /// + /// Gets or sets a collection of the class. + /// + public List Accessories { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public Camera Camera { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public Light Light { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public SelfShadow SelfShadow { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public Physics Physics { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public Media Media { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public Panel Panel { get; set; } + + /// + /// Gets or sets an instance of the class. + /// + public View View { get; set; } + + + /// + /// Initializes a new instance of the class. + /// + public Project() + { + OutputSize = new Size(640, 360); + Models = new List(); + Accessories = new List(); + Camera = new Camera(); + Light = new Light(); + SelfShadow = new SelfShadow(); + Physics = new Physics(); + Media = new Media(); + Panel = new Panel(); + View = new View(); + } + + /// + /// Loads a project from the specified file. + /// + /// The file path to load a project + /// The self instance assigned values from file + /// + /// Thrown when the specified file is invalid or unsupported file. + /// + public Project Load(string path) + { + return new ProjectConverter().Convert(new Raw.Project().Load(path), this); + } + + /// + /// Saves this project to the specified file. + /// + /// The file path to save this project + public void Save(string path) + { + new ProjectConverter().ConvertBack(this, new Raw.Project()).Save(path); + } + } +} diff --git a/Scallion/Internal/Converters/Motion/BoneConverter.cs b/Scallion/Internal/Converters/Motion/BoneConverter.cs index 6af8361..c61e37e 100644 --- a/Scallion/Internal/Converters/Motion/BoneConverter.cs +++ b/Scallion/Internal/Converters/Motion/BoneConverter.cs @@ -17,9 +17,12 @@ public IEnumerable Convert(IEnumerable dic[item.BoneName].KeyFrames.Add(new BoneKeyFrame() { KeyFrameIndex = item.KeyFrameIndex, - Position = item.Position, - Quaternion = item.Quaternion, - Interpolation = item.Interpolation + Value = new BoneState() + { + Position = item.Position, + Quaternion = item.Quaternion, + Interpolation = item.Interpolation + } }); } return dic.Values.ToList(); @@ -31,14 +34,14 @@ public IEnumerable Convert(IEnumerable { KeyFrameIndex = q.KeyFrameIndex, BoneName = p.Name, - Position = q.Position, - Quaternion = q.Quaternion, + Position = q.Value.Position, + Quaternion = q.Value.Quaternion, Interpolation = new Raw.Components.Motion.BoneInterpolationImpl() { - X = q.Interpolation.X, - Y = q.Interpolation.Y, - Z = q.Interpolation.Z, - R = q.Interpolation.R + X = q.Value.Interpolation.X, + Y = q.Value.Interpolation.Y, + Z = q.Value.Interpolation.Z, + R = q.Value.Interpolation.R } })); } diff --git a/Scallion/Internal/Converters/Motion/ModelKeyFrameConverter.cs b/Scallion/Internal/Converters/Motion/ModelKeyFrameConverter.cs index d432298..971aaeb 100644 --- a/Scallion/Internal/Converters/Motion/ModelKeyFrameConverter.cs +++ b/Scallion/Internal/Converters/Motion/ModelKeyFrameConverter.cs @@ -35,17 +35,17 @@ public DomainModels.Motion Convert(Raw.Motion src, DomainModels.Motion obj) { foreach (var ik in item.IKData) { - ikdic[ik.BoneName].IKStateKeyFrames.Add(new IKStateKeyFrame() + ikdic[ik.BoneName].IKStateKeyFrames.Add(new IKBoneKeyFrame() { KeyFrameIndex = item.KeyFrameIndex, - IsIKEnabled = ik.IsEnabled + Value = new IKBoneState() { IsIKEnabled = ik.IsEnabled } }); } obj.VisibilityKeyFrames.Add(new VisibilityKeyFrame() { KeyFrameIndex = item.KeyFrameIndex, - IsVisible = item.IsVisible + Value = new VisibilityState() { IsVisible = item.IsVisible } }); } @@ -56,24 +56,40 @@ public DomainModels.Motion Convert(Raw.Motion src, DomainModels.Motion obj) public Raw.Motion ConvertBack(DomainModels.Motion src, Raw.Motion obj) { var ikbones = src.IKBones.ToList(); - var visibilities = src.VisibilityKeyFrames.OrderBy(p => p.KeyFrameIndex).ToList(); + var ikKeyFrameNodes = ikbones.Select(p => new LinkedList(p.IKStateKeyFrames.OrderBy(q => q.KeyFrameIndex)).First).ToList(); + var visibilitiesNode = new LinkedList(src.VisibilityKeyFrames.OrderBy(p => p.KeyFrameIndex)).First; + var keyframeIndices = new HashSet(); + + foreach (int idx in src.IKBones.SelectMany(p => p.IKStateKeyFrames.Select(q => q.KeyFrameIndex))) + keyframeIndices.Add(idx); + foreach (int idx in src.VisibilityKeyFrames.Select(p => p.KeyFrameIndex)) + keyframeIndices.Add(idx); // Merge model visibilities and IK bone states into a key frame by indices - obj.ModelKeyFrames = src.IKBones.SelectMany(p => p.IKStateKeyFrames.Select(q => q.KeyFrameIndex)) - .Concat(visibilities.Select(p => p.KeyFrameIndex)) - .OrderBy(p => p) - .Distinct() - .Select(p => new Raw.Components.Motion.ModelKeyFrame() + var list = new List(keyframeIndices.Count); + foreach (int idx in keyframeIndices.OrderBy(p => p)) + { + for (int i = 0; i < ikKeyFrameNodes.Count; i++) + { + if (ikKeyFrameNodes[i].Next != null && idx >= ikKeyFrameNodes[i].Next.Value.KeyFrameIndex) + ikKeyFrameNodes[i] = ikKeyFrameNodes[i].Next; + } + if (visibilitiesNode.Next != null && idx >= visibilitiesNode.Next.Value.KeyFrameIndex) + visibilitiesNode = visibilitiesNode.Next; + + list.Add(new Raw.Components.Motion.ModelKeyFrame() { - KeyFrameIndex = p, - IKData = ikbones.Select(q => new Raw.Components.Motion.IKBoneData() + KeyFrameIndex = idx, + IKData = ikKeyFrameNodes.Select((p, i) => new Raw.Components.Motion.IKBoneData() { - BoneName = q.Name, - IsEnabled = (q.IKStateKeyFrames.LastOrDefault(r => r.KeyFrameIndex <= p) ?? q.IKStateKeyFrames.First()).IsIKEnabled + BoneName = ikbones[i].Name, + IsEnabled = ikKeyFrameNodes[i].Value.Value.IsIKEnabled }).ToList(), - IsVisible = (visibilities.LastOrDefault(q => q.KeyFrameIndex <= p) ?? visibilities.First()).IsVisible - }) - .ToList(); + IsVisible = visibilitiesNode.Value.Value.IsVisible + }); + } + + obj.ModelKeyFrames = list; return obj; } } diff --git a/Scallion/Internal/Converters/Motion/MorphConverter.cs b/Scallion/Internal/Converters/Motion/MorphConverter.cs index c987a86..8fda409 100644 --- a/Scallion/Internal/Converters/Motion/MorphConverter.cs +++ b/Scallion/Internal/Converters/Motion/MorphConverter.cs @@ -20,7 +20,7 @@ public IEnumerable Convert(IEnumerable Convert(IEnumerable new CameraKeyFrame() { - Distance = p.Distance, - Position = p.Position, - Rotation = p.Rotation, - Interpolation = p.Interpolation, - AngleOfView = p.AngleOfView, - IsPerspectiveEnabled = p.IsPerspectiveEnabled, - KeyFrameIndex = p.KeyFrameIndex + KeyFrameIndex = p.KeyFrameIndex, + Value = new CameraState() + { + Distance = p.Distance, + Position = p.Position, + Rotation = p.Rotation, + Interpolation = p.Interpolation, + AngleOfView = p.AngleOfView, + IsPerspectiveEnabled = p.IsPerspectiveEnabled, + }, }).ToList() }; obj.Light = new Light() @@ -31,8 +34,11 @@ public DomainModels.Motion Convert(Raw.Motion src, DomainModels.Motion obj) KeyFrames = src.LightKeyFrames.Select(p => new LightKeyFrame() { KeyFrameIndex = p.KeyFrameIndex, - Position = p.Position, - Color = p.Color + Value = new LightState() + { + Position = p.Position, + Color = p.Color + } }).ToList() }; obj.SelfShadow = new SelfShadow() @@ -40,8 +46,11 @@ public DomainModels.Motion Convert(Raw.Motion src, DomainModels.Motion obj) KeyFrames = src.SelfShadowKeyFrames.Select(p => new SelfShadowKeyFrame() { KeyFrameIndex = p.KeyFrameIndex, - Type = p.Type, - Distance = p.Distance + Value = new SelfShadowState() + { + Type = p.Type, + Distance = p.Distance + } }).ToList() }; new ModelKeyFrameConverter().Convert(src, obj); @@ -55,33 +64,33 @@ public Raw.Motion ConvertBack(DomainModels.Motion src, Raw.Motion obj) obj.MorphKeyFrames = new MorphConverter().ConvertBack(src.Morphs).ToList(); obj.CameraKeyFrames = src.Camera.KeyFrames.Select(p => new Raw.Components.Motion.CameraKeyFrame() { - Distance = p.Distance, - Position = p.Position, - Rotation = p.Rotation, - Interpolation = new Raw.Components.CameraInterpolation() + Distance = p.Value.Distance, + Position = p.Value.Position, + Rotation = p.Value.Rotation, + Interpolation = new Raw.Components.Motion.CameraInterpolationImpl() { - X = p.Interpolation.X, - Y = p.Interpolation.Y, - Z = p.Interpolation.Z, - R = p.Interpolation.R, - D = p.Interpolation.D, - V = p.Interpolation.V + X = p.Value.Interpolation.X, + Y = p.Value.Interpolation.Y, + Z = p.Value.Interpolation.Z, + R = p.Value.Interpolation.R, + D = p.Value.Interpolation.D, + V = p.Value.Interpolation.V }, - AngleOfView = p.AngleOfView, - IsPerspectiveEnabled = p.IsPerspectiveEnabled, + AngleOfView = p.Value.AngleOfView, + IsPerspectiveEnabled = p.Value.IsPerspectiveEnabled, KeyFrameIndex = p.KeyFrameIndex }).ToList(); obj.LightKeyFrames = src.Light.KeyFrames.Select(p => new Raw.Components.Motion.LightKeyFrame() { KeyFrameIndex = p.KeyFrameIndex, - Position = p.Position, - Color = p.Color + Position = p.Value.Position, + Color = p.Value.Color }).ToList(); obj.SelfShadowKeyFrames = src.SelfShadow.KeyFrames.Select(p => new Raw.Components.Motion.SelfShadowKeyFrame() { KeyFrameIndex = p.KeyFrameIndex, - Type = p.Type, - Distance = p.Distance + Type = p.Value.Type, + Distance = p.Value.Distance }).ToList(); new ModelKeyFrameConverter().ConvertBack(src, obj); return obj; diff --git a/Scallion/Internal/Converters/Project/AccessoryConverter.cs b/Scallion/Internal/Converters/Project/AccessoryConverter.cs new file mode 100644 index 0000000..a79060f --- /dev/null +++ b/Scallion/Internal/Converters/Project/AccessoryConverter.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class AccessoryConverter : IConverter + { + public DomainModels.Components.Accessory Convert(Raw.Components.Project.Accessory src) + { + var obj = new DomainModels.Components.Accessory() + { + Index = src.Index, + Name = src.Name, + Path = src.Path, + RenderingOrder = src.RenderingOrder, + IsAdditiveBlending = src.IsAdditiveBlending, + CurrentStatus = new AccessoryState() + { + Opacity = src.CurrentStatus.Opacity, + IsVisible = src.CurrentStatus.IsVisible, + IsShadowEnabled = src.CurrentStatus.IsShadowEnabled, + // ExternalParent: will be assigned in top level. + Position = src.CurrentStatus.Position, + Rotation = src.CurrentStatus.Rotation, + Scale = src.CurrentStatus.Scale, + } + }; + new AccessoryKeyFrameConverter().Convert(src, obj); + return obj; + } + + public Raw.Components.Project.Accessory ConvertBack(DomainModels.Components.Accessory src) + { + var obj = new Raw.Components.Project.Accessory() + { + Index = (byte)src.Index, + Name = src.Name, + Path = src.Path, + RenderingOrder = src.RenderingOrder, + IsAdditiveBlending = src.IsAdditiveBlending, + CurrentStatus = new Raw.Components.Project.AccessoryState() + { + Opacity = src.CurrentStatus.Opacity, + IsVisible = src.CurrentStatus.IsVisible, + IsShadowEnabled = src.CurrentStatus.IsShadowEnabled, + // ExternalParent: will be assigned in top level. + Position = src.CurrentStatus.Position, + Rotation = src.CurrentStatus.Rotation, + Scale = src.CurrentStatus.Scale, + } + }; + new AccessoryKeyFrameConverter().ConvertBack(src, obj); + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/AccessoryKeyFrameConverter.cs b/Scallion/Internal/Converters/Project/AccessoryKeyFrameConverter.cs new file mode 100644 index 0000000..26eb1c6 --- /dev/null +++ b/Scallion/Internal/Converters/Project/AccessoryKeyFrameConverter.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class AccessoryKeyFrameConverter : IInstanceConverter + { + public DomainModels.Components.Accessory Convert(Raw.Components.Project.Accessory src, DomainModels.Components.Accessory obj) + { + obj.KeyFrames = src.KeyFrames.Extract(src.InitialKeyFrame).Select(p => new AccessoryKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new AccessoryState() + { + Opacity = p.Value.Opacity, + IsVisible = p.Value.IsVisible, + IsShadowEnabled = p.Value.IsShadowEnabled, + // ExternalParent: will be assgined in top level. + Position = p.Value.Position, + Rotation = p.Value.Rotation, + Scale = p.Value.Scale + } + }).ToList(); + + return obj; + } + + public Raw.Components.Project.Accessory ConvertBack(DomainModels.Components.Accessory src, Raw.Components.Project.Accessory obj) + { + var frames = src.KeyFrames.Select(p => new Raw.Components.Project.AccessoryKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new Raw.Components.Project.AccessoryState() + { + Opacity = p.Value.Opacity, + IsVisible = p.Value.IsVisible, + IsShadowEnabled = p.Value.IsShadowEnabled, + // ExternalParent: will be assign in top level. + Position = p.Value.Position, + Rotation = p.Value.Rotation, + Scale = p.Value.Scale + }, + IsSelected = p.IsSelected + }).ToList(); + obj.InitialKeyFrame = frames.Single(p => p.KeyFrameIndex == 0); + obj.InitialKeyFrame.IsInitialKeyFrame = true; + obj.InitialKeyFrame.NextDataIndex = frames.Count > 1 ? 1 : 0; + obj.KeyFrames = frames.Where(p => p.KeyFrameIndex > 0).ToList().Pack(1); + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/BoneConverter.cs b/Scallion/Internal/Converters/Project/BoneConverter.cs new file mode 100644 index 0000000..159d94c --- /dev/null +++ b/Scallion/Internal/Converters/Project/BoneConverter.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class BoneConverter : IInstanceConverter + { + public Model Convert(Raw.Components.Project.Model src, Model obj) + { + var list = new List(src.BonesCount); + for (int i = 0; i < src.BonesCount; i++) + { + var bone = new Bone() + { + Name = src.BoneNames[i], + KeyFrames = src.BoneKeyFrames.Extract(src.InitialBoneKeyFrames[i]).Select(p => new BoneKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new BoneState() + { + Position = p.Value.Position, + Quaternion = p.Value.Quaternion, + Interpolation = p.Value.Interpolation, + IsPhysicsEnabled = p.Value.IsPhysicsEnabled, + }, + IsSelected = p.IsSelected + }).ToList(), + CurrentStatus = new CurrentBoneState() + { + Position = src.CurrentBoneStatuses[i].Position, + Quaternion = src.CurrentBoneStatuses[i].Quaternion, + IsSaved = src.CurrentBoneStatuses[i].IsSaved, + IsPhysicsEnabled = src.CurrentBoneStatuses[i].IsPhysicsEnabled, + IsRowSelected = src.CurrentBoneStatuses[i].IsRowSelected + } + }; + list.Add(src.IKBoneIndices.Contains(i) ? + new IKBone(bone) { CurrentIKStatus = new IKBoneState() { IsIKEnabled = src.IKBonesEnabled[src.IKBoneIndices.IndexOf(i)] } } : bone); + } + obj.Bones = list; + return obj; + } + + public Raw.Components.Project.Model ConvertBack(Model src, Raw.Components.Project.Model obj) + { + obj.BoneNames = src.Bones.Select(p => p.Name).ToList(); + + var currentList = new List(); + var initList = new List(); + var framesList = new List(); + int dataIndex = src.Bones.Count; + for (int i = 0; i < src.Bones.Count; i++) + { + var current = src.Bones[i].CurrentStatus; + currentList.Add(new Raw.Components.Project.CurrentBoneState() + { + Position = current.Position, + Quaternion = current.Quaternion, + IsSaved = current.IsSaved, + IsPhysicsEnabled = current.IsPhysicsEnabled, + IsRowSelected = current.IsRowSelected + }); + + var frames = src.Bones[i].KeyFrames.Select(p => new Raw.Components.Project.BoneKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new Raw.Components.Project.BoneState() + { + Position = p.Value.Position, + Quaternion = p.Value.Quaternion, + Interpolation = p.Value.Interpolation, + IsPhysicsEnabled = p.Value.IsPhysicsEnabled + }, + IsSelected = p.IsSelected + }).ToList(); + + var init = frames.Single(p => p.KeyFrameIndex == 0); + init.IsInitialKeyFrame = true; + init.NextDataIndex = frames.Count > 1 ? dataIndex : 0; + initList.Add(init); + + framesList.AddRange(frames.Where(p => p.KeyFrameIndex > 0).ToList().Pack(dataIndex, i)); + dataIndex += frames.Count - 1; + } + obj.CurrentBoneStatuses = currentList; + obj.InitialBoneKeyFrames = initList; + obj.BoneKeyFrames = framesList; + + obj.IKBoneIndices = src.Bones.Select((p, i) => new { Bone = p, Index = i }).Where(p => p.Bone is IKBone).Select(p => p.Index).ToList(); + obj.IKBonesEnabled = src.Bones.Where(p => p is IKBone).Cast().Select(p => p.CurrentIKStatus.IsIKEnabled).ToList(); + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/CameraConverter.cs b/Scallion/Internal/Converters/Project/CameraConverter.cs new file mode 100644 index 0000000..bd58540 --- /dev/null +++ b/Scallion/Internal/Converters/Project/CameraConverter.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; +using Scallion.DomainModels.Components; +using Scallion.Raw.Components.Project; + +namespace Scallion.Internal.Converters.Project +{ + internal class CameraConverter : IConverter + { + private bool IsModelSelected { get; } + + public CameraConverter(bool isModelSelected) + { + IsModelSelected = isModelSelected; + } + + public DomainModels.Components.Camera Convert(Raw.Components.Project.Camera src) + { + return new DomainModels.Components.Camera() + { + CurrentStatus = new DomainModels.Components.CameraState() + { + Position = src.CurrentStatus.CenterPosition, + Rotation = src.CurrentStatus.Rotation, + // AngleOfView = Project.AngleOfView, // will be assigned in top-level. + IsPerspectiveEnabled = src.CurrentStatus.IsPerspectiveEnabled, + Distance = IsModelSelected ? GetCameraViewDistance(src.CurrentStatus) : -src.CurrentStatus.OffsetPosition.Z + }, + KeyFrames = src.KeyFrames.Extract(src.InitialKeyFrame).Select(p => new DomainModels.Components.CameraKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new DomainModels.Components.CameraState() + { + Position = p.Value.CenterPosition, + Rotation = p.Value.Rotation, + Interpolation = p.Interpolation, + Distance = p.Distance, + IsPerspectiveEnabled = p.Value.IsPerspectiveEnabled, + AngleOfView = p.AngleOfView, + }, + IsSelected = p.IsSelected + }).ToList() + }; + } + + public Raw.Components.Project.Camera ConvertBack(DomainModels.Components.Camera src) + { + var frames = src.KeyFrames.Select(p => new Raw.Components.Project.CameraKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + AngleOfView = p.Value.AngleOfView, + Distance = p.Value.Distance, + Value = new Raw.Components.Project.CameraState() + { + CenterPosition = p.Value.Position, + Rotation = p.Value.Rotation, + // FollowingBone: will be assigned in top-level. + IsPerspectiveEnabled = p.Value.IsPerspectiveEnabled + }, + Interpolation = p.Value.Interpolation, + IsSelected = p.IsSelected + }).ToList(); + + var obj = new Raw.Components.Project.Camera() + { + CurrentStatus = new Raw.Components.Project.CurrentCameraState() + { + CenterPosition = src.CurrentStatus.Position, + Rotation = src.CurrentStatus.Rotation, + // AngleOfView: will be assigned in top-level. + IsPerspectiveEnabled = src.CurrentStatus.IsPerspectiveEnabled, + OffsetPosition = IsModelSelected ? GetLocalCameraPosition(src.CurrentStatus) : new Vector3(0, 0, -src.CurrentStatus.Distance) + }, + InitialKeyFrame = frames.Single(p => p.KeyFrameIndex == 0), + KeyFrames = frames.Where(p => p.KeyFrameIndex > 0).ToList().Pack(1) + }; + obj.InitialKeyFrame.IsInitialKeyFrame = true; + obj.InitialKeyFrame.NextDataIndex = frames.Count > 1 ? 1 : 0; + return obj; + } + + private Vector3 GetLocalCameraPosition(DomainModels.Components.CameraState state) + { + // カメラのローカル座標系において、ワールド原点を基準にした際の相対位置 + var rotVec = new Vector3(0, 0, 1); + var q = Quaternion.CreateFromYawPitchRoll(-state.Rotation.Y, state.Rotation.X, -state.Rotation.Z); + + var absPosition = state.Position - Vector3.Transform(rotVec, q) * state.Distance; + var trans = Matrix4x4.CreateFromQuaternion(q) * Matrix4x4.CreateTranslation(absPosition); + Matrix4x4.Invert(trans, out Matrix4x4 inv); + return -Vector3.Transform(Vector3.Zero, inv); + } + + private float GetCameraViewDistance(Raw.Components.Project.CurrentCameraState state) + { + var localPos = state.OffsetPosition; + var q = Quaternion.CreateFromYawPitchRoll(-state.Rotation.Y, state.Rotation.X, -state.Rotation.Z); + var trans = Matrix4x4.CreateTranslation(localPos) * Matrix4x4.CreateFromQuaternion(q); + Matrix4x4.Invert(trans, out Matrix4x4 inv); + var relativeCenter = Vector3.Transform(state.CenterPosition, inv); + return relativeCenter.Z; + } + } +} diff --git a/Scallion/Internal/Converters/Project/GravityConverter.cs b/Scallion/Internal/Converters/Project/GravityConverter.cs new file mode 100644 index 0000000..7ccbae9 --- /dev/null +++ b/Scallion/Internal/Converters/Project/GravityConverter.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class GravityConverter : IConverter + { + public DomainModels.Components.Gravity Convert(Raw.Components.Project.Gravity src) + { + return new Gravity() + { + CurrentStatus = new GravityState() + { + Acceleration = src.CurrentStatus.Acceleration, + NoiseAmount = src.CurrentStatus.NoiseAmount, + Direction = src.CurrentStatus.Direction, + IsNoiseEnabled = src.CurrentStatus.IsNoiseEnabled + }, + KeyFrames = src.KeyFrames.Extract(src.InitialKeyFrame).Select(p => new GravityKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new GravityState() + { + Acceleration = p.Value.Acceleration, + NoiseAmount = p.Value.NoiseAmount, + Direction = p.Value.Direction, + IsNoiseEnabled = p.Value.IsNoiseEnabled, + }, + IsSelected = p.IsSelected + }).ToList() + }; + } + + public Raw.Components.Project.Gravity ConvertBack(DomainModels.Components.Gravity src) + { + var frames = src.KeyFrames.Select(p => new Raw.Components.Project.GravityKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new Raw.Components.Project.GravityState() + { + Acceleration = p.Value.Acceleration, + NoiseAmount = p.Value.NoiseAmount, + Direction = p.Value.Direction, + IsNoiseEnabled = p.Value.IsNoiseEnabled + }, + IsSelected = p.IsSelected + }).ToList(); + var obj = new Raw.Components.Project.Gravity() + { + CurrentStatus = new Raw.Components.Project.GravityState() + { + Acceleration = src.CurrentStatus.Acceleration, + NoiseAmount = src.CurrentStatus.NoiseAmount, + Direction = src.CurrentStatus.Direction, + IsNoiseEnabled = src.CurrentStatus.IsNoiseEnabled + }, + InitialKeyFrame = frames.Single(p => p.KeyFrameIndex == 0), + KeyFrames = frames.Where(p => p.KeyFrameIndex > 0).ToList().Pack(1) + }; + obj.InitialKeyFrame.IsInitialKeyFrame = true; + obj.InitialKeyFrame.NextDataIndex = frames.Count > 1 ? 1 : 0; + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/LightConverter.cs b/Scallion/Internal/Converters/Project/LightConverter.cs new file mode 100644 index 0000000..0908a1b --- /dev/null +++ b/Scallion/Internal/Converters/Project/LightConverter.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; +using Scallion.Internal.Converters.Project; + +namespace Scallion.Internal.Converters.Project +{ + internal class LightConverter : IConverter + { + public DomainModels.Components.Light Convert(Raw.Components.Project.Light src) + { + return new Light() + { + CurrentStatus = new LightState() + { + Position = src.CurrentStatus.Position, + Color = src.CurrentStatus.Color + }, + KeyFrames = src.KeyFrames.Extract(src.InitialKeyFrame).Select(p => new LightKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new LightState() + { + Position = p.Value.Position, + Color = p.Value.Color, + }, + IsSelected = p.IsSelected + }).ToList() + }; + } + + public Raw.Components.Project.Light ConvertBack(DomainModels.Components.Light src) + { + var init = src.KeyFrames.Single(p => p.KeyFrameIndex == 0); + var frames = src.KeyFrames.Where(p => p.KeyFrameIndex > 0).Select(p => new Raw.Components.Project.LightKeyFrame() + { + Value = new Raw.Components.Project.LightState() + { + Position = p.Value.Position, + Color = p.Value.Color + }, + IsSelected = p.IsSelected + }).ToList().Pack(1); + + return new Raw.Components.Project.Light() + { + CurrentStatus = new Raw.Components.Project.LightState() + { + Position = src.CurrentStatus.Position, + Color = src.CurrentStatus.Color + }, + InitialKeyFrame = new Raw.Components.Project.LightKeyFrame() + { + IsInitialKeyFrame = true, + NextDataIndex = frames.Count > 0 ? 1 : 0, + Value = new Raw.Components.Project.LightState() + { + Position = init.Value.Position, + Color = init.Value.Color + }, + IsSelected = init.IsSelected + }, + KeyFrames = frames + }; + } + } +} diff --git a/Scallion/Internal/Converters/Project/LinkableKeyFrameConverter.cs b/Scallion/Internal/Converters/Project/LinkableKeyFrameConverter.cs new file mode 100644 index 0000000..d556454 --- /dev/null +++ b/Scallion/Internal/Converters/Project/LinkableKeyFrameConverter.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Scallion.Internal.Converters.Project +{ + internal static class LinkableKeyFrameConverter + { + public static LinkedList Extract(this List src, T initial) where T : Raw.Components.Project.ILinkableKeyFrame + { + var dic = new Dictionary(src.Count + 1); + foreach (var item in src) + dic.Add(item.DataIndex, item); + + var list = new LinkedList(); + list.AddLast(initial); + + var current = initial; // treat as DataIndex = 0 + while (current.NextDataIndex != 0) + { + current = dic[current.NextDataIndex]; + list.AddLast(current); + } + + return list; + } + + public static List Pack(this List src, int initIndex) where T : Raw.Components.Project.ILinkableKeyFrame + { + return src.Pack(initIndex, 0); + } + + public static List Pack(this List src, int initIndex, int prevIndex) where T : Raw.Components.Project.ILinkableKeyFrame + { + for (int i = 0; i < src.Count; i++) + { + src[i].DataIndex = initIndex + i; + src[i].NextDataIndex = i == src.Count - 1 ? 0 : initIndex + i + 1; + src[i].PreviousDataIndex = i == 0 ? prevIndex : initIndex + i - 1; + } + return src; + } + } +} diff --git a/Scallion/Internal/Converters/Project/MediaConverter.cs b/Scallion/Internal/Converters/Project/MediaConverter.cs new file mode 100644 index 0000000..acd20f7 --- /dev/null +++ b/Scallion/Internal/Converters/Project/MediaConverter.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class MediaConverter : IConverter + { + public DomainModels.Components.Media Convert(Raw.Components.Project.Media src) + { + return new Media() + { + Audio = new Audio() + { + IsEnabled = src.Audio.IsEnabled, + Path = src.Audio.Path + }, + Video = new Video() + { + IsVisible = src.Video.IsVisible, + Path = src.Video.Path, + Position = src.Video.Position, + Scale = src.Video.Scale + }, + BackgroundImage = new BackgroundImage() + { + IsVisible = src.BackgroundImage.IsVisible, + Path = src.BackgroundImage.Path, + Position = src.BackgroundImage.Position, + Scale = src.BackgroundImage.Scale + } + }; + } + + public Raw.Components.Project.Media ConvertBack(DomainModels.Components.Media src) + { + return new Raw.Components.Project.Media() + { + Audio = new Raw.Components.Project.Audio() + { + IsEnabled = src.Audio.IsEnabled, + Path = src.Audio.Path + }, + Video = new Raw.Components.Project.Video() + { + IsVisible = src.Video.IsVisible, + Path = src.Video.Path, + Position = src.Video.Position, + Scale = src.Video.Scale + }, + BackgroundImage = new Raw.Components.Project.BackgroundImage() + { + IsVisible = src.BackgroundImage.IsVisible, + Path = src.BackgroundImage.Path, + Position = src.BackgroundImage.Position, + Scale = src.BackgroundImage.Scale + } + }; + } + } +} diff --git a/Scallion/Internal/Converters/Project/ModelConverter.cs b/Scallion/Internal/Converters/Project/ModelConverter.cs new file mode 100644 index 0000000..26dc0e9 --- /dev/null +++ b/Scallion/Internal/Converters/Project/ModelConverter.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class ModelConverter : IConverter + { + public DomainModels.Components.Model Convert(Raw.Components.Project.Model src) + { + var obj = new Model() + { + Index = src.Index, + Name = src.Name, + NameEn = src.NameEn, + Path = src.Path, + IsVisible = src.IsVisible, + IsAdditiveBlending = src.IsAdditiveBlending, + IsSelfShadowEnabled = src.IsSelfShadowEnabled, + EdgeWidth = src.EdgeWidth, + RenderingOrder = src.RenderingOrder, + CalculationOrder = src.CalculationOrder, + LastFrameIndex = src.LastFrameIndex, + SelectedBoneIndex = src.SelectedBoneIndex, + TimelinePanel = new ModelTimelinePanel() + { + RowsCount = src.TimelinePanelRowsCount, + TopRowIndex = src.TimelinePanelTopRowIndex, + // RangeSelectorSelectedIndex: will be assigned in top level(ProjectConverter). + }, + BoneGroupsExpansion = src.BoneGroupsExpansion + }; + + new BoneConverter().Convert(src, obj); + new MorphConverter().Convert(src, obj); + // Model keyframes will be converted in top-level. + + return obj; + } + + public Raw.Components.Project.Model ConvertBack(DomainModels.Components.Model src) + { + var obj = new Raw.Components.Project.Model() + { + Index = (byte)src.Index, + Name = src.Name, + NameEn = src.NameEn, + Path = src.Path, + IsVisible = src.IsVisible, + IsAdditiveBlending = src.IsAdditiveBlending, + IsSelfShadowEnabled = src.IsSelfShadowEnabled, + EdgeWidth = src.EdgeWidth, + RenderingOrder = (byte)src.RenderingOrder, + CalculationOrder = (byte)src.CalculationOrder, + LastFrameIndex = src.LastFrameIndex, + SelectedBoneIndex = src.SelectedBoneIndex, + TimelinePanelRowsCount = (byte)src.TimelinePanel.RowsCount, + TimelinePanelTopRowIndex = src.TimelinePanel.TopRowIndex, + BoneGroupsExpansion = src.BoneGroupsExpansion + }; + + new BoneConverter().ConvertBack(src, obj); + new MorphConverter().ConvertBack(src, obj); + // Model keyframes will be converted in top-level. + + obj.MorphPanel = new Raw.Components.Project.MorphSelection() + { + EyebrowIndex = -1, + EyeIndex = -1, + LipIndex = -1, + OtherIndex = -1 + }; + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/ModelKeyFrameConverter.cs b/Scallion/Internal/Converters/Project/ModelKeyFrameConverter.cs new file mode 100644 index 0000000..a9b2516 --- /dev/null +++ b/Scallion/Internal/Converters/Project/ModelKeyFrameConverter.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + /// + /// Converts key frames of a model consist of external parent status, IK status, and visibility. + /// + internal class ModelKeyFrameConverter : IInstanceConverter + { + public DomainModels.Project Convert(Raw.Project src, DomainModels.Project obj) + { + var modeldic = obj.Models.ToDictionary(p => p.Index, p => p); + foreach (var model in src.Models) + { + var ikdic = model.IKBoneIndices.ToDictionary(p => p, p => (IKBone)modeldic[model.Index].Bones[p]); + foreach (var keyframe in model.ModelKeyFrames.Extract(model.InitialModelKeyFrame)) + { + for (int i = 0; i < keyframe.Value.ExternalParentBonesCount; i++) + { + if (model.ExternalParentBoneIndices[i] < 0) continue; + var reference = keyframe.Value.ExternalParentBoneStatuses[i]; + modeldic[model.Index].Bones[model.ExternalParentBoneIndices[i]].ExternalParentKeyFrames.Add(new ExternalParentKeyFrame() + { + KeyFrameIndex = keyframe.KeyFrameIndex, + Value = new ExternalParentState() + { + Reference = reference.ModelIndex == -1 ? new BoneReference() : new BoneReference( + model: modeldic[reference.ModelIndex], + bone: reference.BoneIndex == -1 ? null : modeldic[reference.ModelIndex].Bones[reference.BoneIndex]) + }, + IsSelected = keyframe.IsSelected + }); + } + + for (int i = 0; i < model.IKBonesCount; i++) + { + ikdic[model.IKBoneIndices[i]].IKStateKeyFrames.Add(new IKBoneKeyFrame() + { + KeyFrameIndex = keyframe.KeyFrameIndex, + Value = new IKBoneState() { IsIKEnabled = keyframe.Value.IKEnabled[i] }, + IsSelected = keyframe.IsSelected + }); + } + + modeldic[model.Index].VisibilityKeyFrames.Add(new VisibilityKeyFrame() + { + KeyFrameIndex = keyframe.KeyFrameIndex, + Value = new VisibilityState() { IsVisible = keyframe.Value.IsVisible }, + IsSelected = keyframe.IsSelected + }); + } + + // resolve current external parent + for (int i = 0; i < model.ExternalParentBonesCount; i++) + { + int bone = model.ExternalParentBoneIndices[i]; + if (bone < 0) continue; + var reference = model.ExternalParentStatuses[i].ExternalParent; + modeldic[model.Index].Bones[bone].CurrentStatus.ExternalParent = reference.ModelIndex == -1 ? new BoneReference() : new BoneReference( + model: modeldic[reference.ModelIndex], + bone: reference.BoneIndex == -1 ? null : modeldic[reference.ModelIndex].Bones[reference.BoneIndex]); + } + + // current ik + for (int i = 0; i < model.IKBoneIndices.Count; i++) + { + (modeldic[model.Index].Bones[model.IKBoneIndices[i]] as IKBone).CurrentIKStatus = new IKBoneState() { IsIKEnabled = model.IKBonesEnabled[i] }; + } + } + + return obj; + } + + public Raw.Project ConvertBack(DomainModels.Project src, Raw.Project obj) + { + var modeldic = obj.Models.ToDictionary(p => p.Index, p => p); + var boneIndexDic = src.Models.ToDictionary(p => p, p => p.Bones.Select((q, i) => new { Bone = q, Index = i }).ToDictionary(q => q.Bone, q => q.Index)); + foreach (var model in src.Models) + { + var ikBoneIndices = model.Bones.Select((p, i) => new { Index = i, Bone = p }).Where(p => p.Bone is IKBone).Select(p => p.Index).ToList(); + var ikKeyFrameNodes = ikBoneIndices.Select(p => new LinkedList((model.Bones[p] as IKBone).IKStateKeyFrames.OrderBy(q => q.KeyFrameIndex)).First).ToList(); + var externalParentBoneIndices = model.Bones.Select((p, i) => new { Index = i, Count = p.ExternalParentKeyFrames.Count }).Where(p => p.Count > 0).Select(p => p.Index).ToList(); + var externalParentKeyFrameNodes = externalParentBoneIndices.Select(p => new LinkedList(model.Bones[p].ExternalParentKeyFrames.OrderBy(q => q.KeyFrameIndex)).First).ToList(); + var visibilityKeyFrameNode = new LinkedList(model.VisibilityKeyFrames.OrderBy(p => p.KeyFrameIndex)).First; + + modeldic[(byte)model.Index].IKBoneIndices = ikBoneIndices; + modeldic[(byte)model.Index].ExternalParentBoneIndices = externalParentBoneIndices; + + var modelKeyFrames = new List(); + var keyFrameIndices = new HashSet(); + + foreach (int idx in ikBoneIndices.SelectMany(p => (model.Bones[p] as IKBone).IKStateKeyFrames.Select(q => q.KeyFrameIndex))) + keyFrameIndices.Add(idx); + foreach (int idx in externalParentBoneIndices.SelectMany(p => model.Bones[p].ExternalParentKeyFrames.Select(q => q.KeyFrameIndex))) + keyFrameIndices.Add(idx); + foreach (int idx in model.VisibilityKeyFrames.Select(p => p.KeyFrameIndex)) + keyFrameIndices.Add(idx); + + // first item is -1(strange). + externalParentBoneIndices.Insert(0, -1); + externalParentKeyFrameNodes.Insert(0, new LinkedList(new[] { new ExternalParentKeyFrame() { Value = new ExternalParentState() { Reference = new BoneReference() } } }).First); + + foreach (int idx in keyFrameIndices.OrderBy(p => p)) + { + for (int i = 0; i < ikKeyFrameNodes.Count; i++) + { + if (ikKeyFrameNodes[i].Next != null && idx >= ikKeyFrameNodes[i].Next.Value.KeyFrameIndex) + ikKeyFrameNodes[i] = ikKeyFrameNodes[i].Next; + } + for (int i = 0; i < externalParentKeyFrameNodes.Count; i++) + { + if (externalParentKeyFrameNodes[i].Next != null && idx >= externalParentKeyFrameNodes[i].Next.Value.KeyFrameIndex) + { + externalParentKeyFrameNodes[i] = externalParentKeyFrameNodes[i].Next; + } + } + if (visibilityKeyFrameNode.Next != null && idx >= visibilityKeyFrameNode.Next.Value.KeyFrameIndex) + visibilityKeyFrameNode = visibilityKeyFrameNode.Next; + + + var boneRefs = externalParentKeyFrameNodes.Select(p => + { + var reference = p.Value.Value.Reference; + return reference.TargetModel == null ? Raw.Components.Project.BoneReference.Empty : + new Raw.Components.Project.BoneReference(reference.TargetModel.Index, reference.TargetBone == null ? -1 : boneIndexDic[reference.TargetModel][reference.TargetBone]); + }).ToList(); + modelKeyFrames.Add(new Raw.Components.Project.ModelKeyFrame() + { + KeyFrameIndex = idx, + Value = new Raw.Components.Project.ModelState() + { + IKEnabled = ikKeyFrameNodes.Select(p => p.Value.Value.IsIKEnabled).ToList(), + ExternalParentBoneStatuses = boneRefs, + IsVisible = visibilityKeyFrameNode.Value.Value.IsVisible + }, + IsSelected = false + }); + } + + var init = modelKeyFrames.Single(p => p.KeyFrameIndex == 0); + init.IsInitialKeyFrame = true; + init.NextDataIndex = modelKeyFrames.Count > 1 ? 1 : 0; + modeldic[(byte)model.Index].InitialModelKeyFrame = init; + modeldic[(byte)model.Index].ModelKeyFrames = modelKeyFrames.Where(p => p.KeyFrameIndex > 0).ToList().Pack(1); + + // current external parent + modeldic[(byte)model.Index].ExternalParentStatuses = externalParentBoneIndices.Select(p => + { + if (p == -1) return new Raw.Components.Project.ExternalParentState() { ExternalParent = Raw.Components.Project.BoneReference.Empty }; + var reference = model.Bones[p].CurrentStatus.ExternalParent; + var indexRef = reference.TargetModel == null ? Raw.Components.Project.BoneReference.Empty : + new Raw.Components.Project.BoneReference(reference.TargetModel.Index, reference.TargetBone == null ? -1 : boneIndexDic[reference.TargetModel][reference.TargetBone]); + return new Raw.Components.Project.ExternalParentState() + { + ExternalParent = indexRef, + CurrentKeyFrameIndex = keyFrameIndices.OrderBy(q => q).FirstOrDefault(q => q <= src.Panel.TimelinePanel.CurrentFrameIndex), + NextKeyFrameIndex = keyFrameIndices.OrderBy(q => q).FirstOrDefault(q => q > src.Panel.TimelinePanel.CurrentFrameIndex) + }; + }).ToList(); + + // current ik status + modeldic[(byte)model.Index].IKBonesEnabled = ikBoneIndices.Select(p => (model.Bones[p] as IKBone).CurrentIKStatus.IsIKEnabled).ToList(); + } + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/MorphConverter.cs b/Scallion/Internal/Converters/Project/MorphConverter.cs new file mode 100644 index 0000000..4c7d06d --- /dev/null +++ b/Scallion/Internal/Converters/Project/MorphConverter.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class MorphConverter : IInstanceConverter + { + public Model Convert(Raw.Components.Project.Model src, Model obj) + { + var list = new List(src.MorphsCount); + for (int i = 0; i < src.MorphsCount; i++) + { + list.Add(new Morph() + { + Name = src.MorphNames[i], + KeyFrames = src.MorphKeyFrames.Extract(src.InitialMorphKeyFrames[i]).Select(p => new MorphKeyFrame + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new MorphState() { Weight = p.Value.Weight }, + IsSelected = p.IsSelected + }).ToList(), + CurrentStatus = new MorphState() { Weight = src.CurrentMorphStatuses[i].Weight } + }); + } + obj.Morphs = list; + return obj; + } + + public Raw.Components.Project.Model ConvertBack(Model src, Raw.Components.Project.Model obj) + { + obj.MorphNames = src.Morphs.Select(p => p.Name).ToList(); + + var currentList = new List(); + var initList = new List(); + var framesList = new List(); + int dataIndex = src.Morphs.Count; + + for (int i = 0; i < src.Morphs.Count; i++) + { + currentList.Add(new Raw.Components.Project.MorphState() { Weight = src.Morphs[i].CurrentStatus.Weight }); + + var frames = src.Morphs[i].KeyFrames.Select(p => new Raw.Components.Project.MorphKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new Raw.Components.Project.MorphState() { Weight = p.Value.Weight }, + IsSelected = p.IsSelected + }).ToList(); + + var init = frames.Single(p => p.KeyFrameIndex == 0); + init.IsInitialKeyFrame = true; + init.NextDataIndex = frames.Count > 1 ? dataIndex : 0; + initList.Add(init); + + framesList.AddRange(frames.Where(p => p.KeyFrameIndex > 0).ToList().Pack(dataIndex, i)); + dataIndex += frames.Count - 1; + } + obj.CurrentMorphStatuses = currentList; + obj.InitialMorphKeyFrames = initList; + obj.MorphKeyFrames = framesList; + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/PanelConverter.cs b/Scallion/Internal/Converters/Project/PanelConverter.cs new file mode 100644 index 0000000..93f1914 --- /dev/null +++ b/Scallion/Internal/Converters/Project/PanelConverter.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + class PanelConverter : IInstanceConverter + { + public DomainModels.Project Convert(Raw.Project src, DomainModels.Project obj) + { + obj.Panel = new Panel() + { + IsModelSelected = src.IsModelSelected, + SelectedModelIndex = src.SelectedModelIndex, + SelectedAccessoryIndex = src.SelectedAccessoryIndex, + FrameJumpingBoxValue = src.FrameJumpingBoxValue, + BoneSelectionType = src.BoneSelectionType, + PreviewPanel = new PreviewPanel() + { + StartFrameIndex = src.PreviewPanel.StartFrameIndex, + EndFrameIndex = src.PreviewPanel.EndFrameIndex, + IsRepeating = src.PreviewPanel.IsRepeating, + IsStartingFromCurrentFrame = src.PreviewPanel.IsStartingFromCurrentFrame, + IsStayingAtStoppedFrame = src.PreviewPanel.IsStayingAtStoppedFrame + }, + ViewPanel = new ViewPanel() + { + IsFollowingViewEnabled = src.IsFollowingViewEnabled, + CameraFollowingType = src.CameraFollowingType + }, + TimelinePanel = new TimelinePanel() + { + PanelWidth = src.TimelinePanelWidth, + CurrentFrameIndex = src.TimelinePanelStatus.CurrentFrameIndex, + HorizontalHeadFrameIndex = src.TimelinePanelStatus.HorizontalHeadFrameIndex, + HorizontalScale = src.TimelinePanelStatus.HorizontalScale, + TopAccessoryIndex = src.TimelinePanelTopAccessoryIndex + }, + PanelExpansion = new PanelExpansion() + { + Camera = src.PanelExpansion.Camera, + Light = src.PanelExpansion.Light, + Accessory = src.PanelExpansion.Accessory, + Bone = src.PanelExpansion.Bone, + Morph = src.PanelExpansion.Morph, + SelfShadow = src.PanelExpansion.SelfShadow + } + }; + + return obj; + } + + public Raw.Project ConvertBack(DomainModels.Project src, Raw.Project obj) + { + obj.IsModelSelected = src.Panel.IsModelSelected; + obj.SelectedModelIndex = (byte)src.Panel.SelectedModelIndex; + obj.SelectedAccessoryIndex = (byte)src.Panel.SelectedAccessoryIndex; + obj.FrameJumpingBoxValue = src.Panel.FrameJumpingBoxValue; + obj.BoneSelectionType = src.Panel.BoneSelectionType; + + obj.PreviewPanel = new Raw.Components.Project.PreviewPanel() + { + StartFrameIndex = src.Panel.PreviewPanel.StartFrameIndex, + EndFrameIndex = src.Panel.PreviewPanel.EndFrameIndex, + IsRepeating = src.Panel.PreviewPanel.IsRepeating, + IsStartingFromCurrentFrame = src.Panel.PreviewPanel.IsStartingFromCurrentFrame, + IsStayingAtStoppedFrame = src.Panel.PreviewPanel.IsStayingAtStoppedFrame + }; + + obj.IsFollowingViewEnabled = src.Panel.ViewPanel.IsFollowingViewEnabled; + obj.CameraFollowingType = src.Panel.ViewPanel.CameraFollowingType; + + obj.TimelinePanelWidth = src.Panel.TimelinePanel.PanelWidth; + obj.TimelinePanelStatus = new Raw.Components.Project.TimelinePanelState() + { + CurrentFrameIndex = src.Panel.TimelinePanel.CurrentFrameIndex, + HorizontalHeadFrameIndex = src.Panel.TimelinePanel.HorizontalHeadFrameIndex, + HorizontalScale = src.Panel.TimelinePanel.HorizontalScale + }; + obj.TimelinePanelTopAccessoryIndex = src.Panel.TimelinePanel.TopAccessoryIndex; + + obj.PanelExpansion = new Raw.Components.Project.PanelExpansion() + { + Camera = src.Panel.PanelExpansion.Camera, + Light = src.Panel.PanelExpansion.Light, + Accessory = src.Panel.PanelExpansion.Accessory, + Bone = src.Panel.PanelExpansion.Bone, + Morph = src.Panel.PanelExpansion.Morph, + SelfShadow = src.Panel.PanelExpansion.SelfShadow + }; + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/PhysicsConverter.cs b/Scallion/Internal/Converters/Project/PhysicsConverter.cs new file mode 100644 index 0000000..212f222 --- /dev/null +++ b/Scallion/Internal/Converters/Project/PhysicsConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class PhysicsConverter : IInstanceConverter + { + public DomainModels.Project Convert(Raw.Project src, DomainModels.Project obj) + { + obj.Physics = new Physics() + { + PhysicsMode = src.PhysicsMode, + IsGroundPhysicsEnabled = src.IsGroundPhysicsEnabled, + Gravity = new GravityConverter().Convert(src.Gravity) + }; + return obj; + } + + public Raw.Project ConvertBack(DomainModels.Project src, Raw.Project obj) + { + obj.PhysicsMode = src.Physics.PhysicsMode; + obj.IsGroundPhysicsEnabled = src.Physics.IsGroundPhysicsEnabled; + obj.Gravity = new GravityConverter().ConvertBack(src.Physics.Gravity); + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/ProjectConverter.cs b/Scallion/Internal/Converters/Project/ProjectConverter.cs new file mode 100644 index 0000000..2e4dbd2 --- /dev/null +++ b/Scallion/Internal/Converters/Project/ProjectConverter.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class ProjectConverter : IInstanceConverter + { + public DomainModels.Project Convert(Raw.Project src, DomainModels.Project obj) + { + obj.OutputSize = src.OutputSize; + obj.Models = src.Models.Select(p => new ModelConverter().Convert(p)).ToList(); + obj.Accessories = src.Accessories.Select(p => new AccessoryConverter().Convert(p)).ToList(); + obj.Camera = new CameraConverter(src.IsModelSelected).Convert(src.Camera); + obj.Light = new LightConverter().Convert(src.Light); + obj.Media = new MediaConverter().Convert(src.Media); + obj.SelfShadow = new SelfShadowConverter().Convert(src.SelfShadow); + + new PanelConverter().Convert(src, obj); + new ViewConverter().Convert(src, obj); + new PhysicsConverter().Convert(src, obj); + + obj.Camera.CurrentStatus.AngleOfView = (int)src.AngleOfView; // :thinking_face: + + // resolving bone references + var modeldic = obj.Models.ToDictionary(p => p.Index, p => p); + + BoneReference ResolveReference(Raw.Components.Project.BoneReference reference) + { + return reference.ModelIndex == -1 ? new BoneReference() : + new BoneReference(modeldic[reference.ModelIndex], modeldic[reference.ModelIndex].Bones[reference.BoneIndex]); + } + + // for bones + new ModelKeyFrameConverter().Convert(src, obj); + + // for camera + var camframedic = obj.Camera.KeyFrames.ToDictionary(p => p.KeyFrameIndex, p => p); + foreach (var frame in src.Camera.KeyFrames.Extract(src.Camera.InitialKeyFrame)) + { + camframedic[frame.KeyFrameIndex].Value.FollowingBone = ResolveReference(frame.FollowingBone); + } + + obj.Camera.CurrentStatus.FollowingBone = ResolveReference(src.CameraFollowingBone); + + // for accessories + var accdic = obj.Accessories.ToDictionary(p => p.Index, p => p); + foreach (var accessory in src.Accessories) + { + var framedic = accdic[accessory.Index].KeyFrames.ToDictionary(p => p.KeyFrameIndex, p => p); + foreach (var keyframe in accessory.KeyFrames.Extract(accessory.InitialKeyFrame)) + { + var reference = keyframe.Value.ExternalParent; + framedic[keyframe.KeyFrameIndex].Value.ExternalParent = ResolveReference(keyframe.Value.ExternalParent); + } + + accdic[accessory.Index].CurrentStatus.ExternalParent = ResolveReference(accessory.CurrentStatus.ExternalParent); + } + + // resolving range selections + foreach (var item in src.RangeSelections) + { + modeldic[item.ModelIndex].TimelinePanel.RangeSelectorSelectedIndex = item.SelectedIndex; + } + + return obj; + } + + public Raw.Project ConvertBack(DomainModels.Project src, Raw.Project obj) + { + obj.OutputSize = src.OutputSize; + obj.Models = src.Models.Select(p => new ModelConverter().ConvertBack(p)).ToList(); + obj.Accessories = src.Accessories.Select(p => new AccessoryConverter().ConvertBack(p)).ToList(); + obj.Camera = new CameraConverter(src.Panel.IsModelSelected).ConvertBack(src.Camera); + obj.Light = new LightConverter().ConvertBack(src.Light); + obj.Media = new MediaConverter().ConvertBack(src.Media); + obj.SelfShadow = new SelfShadowConverter().ConvertBack(src.SelfShadow); + + new PanelConverter().ConvertBack(src, obj); + new ViewConverter().ConvertBack(src, obj); + new PhysicsConverter().ConvertBack(src, obj); + + obj.AngleOfView = src.Camera.CurrentStatus.AngleOfView; + + // bone references + var boneIndexDic = src.Models.ToDictionary(p => p, p => p.Bones.Select((q, i) => new { Bone = q, Index = i }).ToDictionary(q => q.Bone, q => q.Index)); + + Raw.Components.Project.BoneReference ResolveReference(BoneReference reference) + { + return reference.TargetModel == null ? Raw.Components.Project.BoneReference.Empty : + new Raw.Components.Project.BoneReference(reference.TargetModel.Index, boneIndexDic[reference.TargetModel][reference.TargetBone]); + } + + new ModelKeyFrameConverter().ConvertBack(src, obj); + + // for camera + var camframedic = obj.Camera.KeyFrames.Extract(obj.Camera.InitialKeyFrame).ToDictionary(p => p.KeyFrameIndex, p => p); + foreach (var frame in src.Camera.KeyFrames) + { + camframedic[frame.KeyFrameIndex].FollowingBone = ResolveReference(frame.Value.FollowingBone); + } + + obj.CameraFollowingBone = ResolveReference(src.Camera.CurrentStatus.FollowingBone); + + // for accesories + var accdic = obj.Accessories.ToDictionary(p => p.Index, p => p); + foreach (var accessory in src.Accessories) + { + var framedic = accdic[(byte)accessory.Index].KeyFrames.Extract(accdic[(byte)accessory.Index].InitialKeyFrame).ToDictionary(p => p.KeyFrameIndex, p => p); + foreach (var keyframe in accessory.KeyFrames) + { + framedic[keyframe.KeyFrameIndex].Value.ExternalParent = ResolveReference(keyframe.Value.ExternalParent); + } + accdic[(byte)accessory.Index].CurrentStatus.ExternalParent = ResolveReference(accessory.CurrentStatus.ExternalParent); + } + + // range selections + obj.RangeSelections = src.Models.Select(p => new Raw.Components.Project.RangeSelection() + { + ModelIndex = (byte)p.Index, + SelectedIndex = p.TimelinePanel.RangeSelectorSelectedIndex + }).ToList(); + + return obj; + } + } +} diff --git a/Scallion/Internal/Converters/Project/SelfShadowConverter.cs b/Scallion/Internal/Converters/Project/SelfShadowConverter.cs new file mode 100644 index 0000000..7d9925a --- /dev/null +++ b/Scallion/Internal/Converters/Project/SelfShadowConverter.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class SelfShadowConverter : IConverter + { + public DomainModels.Components.SelfShadow Convert(Raw.Components.Project.SelfShadow src) + { + return new SelfShadow() + { + IsEnabled = src.IsEnabled, + CurrentStatus = new SelfShadowState() + { + Distance = src.CurrentStatus.Distance + }, + KeyFrames = src.KeyFrames.Extract(src.InitialKeyFrame).Select(p => new SelfShadowKeyFrame() + { + KeyFrameIndex = p.KeyFrameIndex, + Value = new SelfShadowState() + { + Type = p.SelfShadowType, + Distance = p.Value.Distance + } + }).ToList() + }; + } + + public Raw.Components.Project.SelfShadow ConvertBack(DomainModels.Components.SelfShadow src) + { + var init = src.KeyFrames.Single(p => p.KeyFrameIndex == 0); + var frames = src.KeyFrames.Where(p => p.KeyFrameIndex > 0).Select(p => new Raw.Components.Project.SelfShadowKeyFrame() + { + SelfShadowType = p.Value.Type, + Value = new Raw.Components.Project.SelfShadowState() + { + Distance = p.Value.Distance + }, + IsSelected = p.IsSelected + }).ToList().Pack(1); + + return new Raw.Components.Project.SelfShadow() + { + IsEnabled = src.IsEnabled, + CurrentStatus = new Raw.Components.Project.SelfShadowState() + { + Distance = src.CurrentStatus.Distance + }, + InitialKeyFrame = new Raw.Components.Project.SelfShadowKeyFrame() + { + IsInitialKeyFrame = true, + NextDataIndex = frames.Count > 0 ? 1 : 0, + SelfShadowType = init.Value.Type, + Value = new Raw.Components.Project.SelfShadowState() + { + Distance = init.Value.Distance + }, + IsSelected = init.IsSelected + }, + KeyFrames = frames + }; + } + } +} diff --git a/Scallion/Internal/Converters/Project/ViewConverter.cs b/Scallion/Internal/Converters/Project/ViewConverter.cs new file mode 100644 index 0000000..d0a4a6f --- /dev/null +++ b/Scallion/Internal/Converters/Project/ViewConverter.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.DomainModels.Components; + +namespace Scallion.Internal.Converters.Project +{ + internal class ViewConverter : IInstanceConverter + { + public DomainModels.Project Convert(Raw.Project src, DomainModels.Project obj) + { + obj.View = new View() + { + IsInformationVisible = src.IsInformationVisible, + IsAxesVisible = src.IsAxesVisible, + EdgeColor = src.EdgeColor, + IsBackgroundBlack = src.IsBackgroundBlack, + FpsLimit = src.FpsLimit == 60f ? FpsLimit.Sixty : src.FpsLimit == 30f ? FpsLimit.Thirty : FpsLimit.None, + IsSurfaceShadowEnabled = src.IsSurfaceShadowEnabled, + SurfaceShadowBrightness = src.SurfaceShadowBrightness, + IsSurfaceShadowTransparent = src.IsSurfaceShadowTransparent, + AccessoryRenderedAfterModelIndex = src.AccessoryRenderedAfterModelIndex, + ScreenCapturingMode = src.ScreenCapturingMode + }; + + return obj; + } + + public Raw.Project ConvertBack(DomainModels.Project src, Raw.Project obj) + { + obj.IsInformationVisible = src.View.IsInformationVisible; + obj.IsAxesVisible = src.View.IsAxesVisible; + obj.EdgeColor = src.View.EdgeColor; + obj.IsBackgroundBlack = src.View.IsBackgroundBlack; + obj.FpsLimit = src.View.FpsLimit == FpsLimit.Sixty ? 60f : src.View.FpsLimit == FpsLimit.Thirty ? 30f : 1000f; + obj.IsSurfaceShadowEnabled = src.View.IsSurfaceShadowEnabled; + obj.SurfaceShadowBrightness = src.View.SurfaceShadowBrightness; + obj.IsSurfaceShadowTransparent = src.View.IsSurfaceShadowTransparent; + obj.AccessoryRenderedAfterModelIndex = src.View.AccessoryRenderedAfterModelIndex; + obj.ScreenCapturingMode = src.View.ScreenCapturingMode; + + return obj; + } + } +} diff --git a/Scallion/Internal/StructWrappers.cs b/Scallion/Internal/StructWrappers.cs index 6d17a4d..03e90a7 100644 --- a/Scallion/Internal/StructWrappers.cs +++ b/Scallion/Internal/StructWrappers.cs @@ -77,4 +77,94 @@ public override void Deserialize(MoDeserializer archive) Value = Color.FromArgb((int)(archive.ReadSingle() * 256), (int)(archive.ReadSingle() * 256), (int)(archive.ReadSingle() * 256)); } } + + internal class Int32ColorWrapper : StructWrapper + { + public Int32ColorWrapper() + { + } + + public Int32ColorWrapper(Color value) : base(value) + { + } + + public override void Serialize(MoSerializer archive) + { + archive.WriteInt32((int)Value.R); + archive.WriteInt32((int)Value.G); + archive.WriteInt32((int)Value.B); + } + + public override void Deserialize(MoDeserializer archive) + { + Value = Color.FromArgb(archive.ReadInt32(), archive.ReadInt32(), archive.ReadInt32()); + } + } + + internal class PointWrapper : StructWrapper + { + public PointWrapper() + { + } + + public PointWrapper(Point value) : base(value) + { + } + + public override void Serialize(MoSerializer archive) + { + archive.WriteInt32(Value.X); + archive.WriteInt32(Value.Y); + } + + public override void Deserialize(MoDeserializer archive) + { + Value = new Point(archive.ReadInt32(), archive.ReadInt32()); + } + } + + internal class SizeWrapper : StructWrapper + { + public SizeWrapper() + { + } + + public SizeWrapper(Size value) : base(value) + { + } + + public override void Serialize(MoSerializer archive) + { + archive.WriteInt32(Value.Width); + archive.WriteInt32(Value.Height); + } + + public override void Deserialize(MoDeserializer archive) + { + Value = new Size(archive.ReadInt32(), archive.ReadInt32()); + } + } + + internal class BoneReferenceWrapper : StructWrapper + { + public BoneReferenceWrapper() + { + } + + public BoneReferenceWrapper(Raw.Components.Project.BoneReference value) + : base(value) + { + } + + public override void Serialize(MoSerializer archive) + { + archive.WriteInt32(Value.ModelIndex); + archive.WriteInt32(Value.BoneIndex); + } + + public override void Deserialize(MoDeserializer archive) + { + Value = new Raw.Components.Project.BoneReference(archive.ReadInt32(), archive.ReadInt32()); + } + } } diff --git a/Scallion/Properties/AssemblyInfo.cs b/Scallion/Properties/AssemblyInfo.cs index 27960b9..97c5117 100644 --- a/Scallion/Properties/AssemblyInfo.cs +++ b/Scallion/Properties/AssemblyInfo.cs @@ -11,8 +11,7 @@ [assembly: ComVisible(false)] [assembly: Guid("3934e015-89f5-4214-80df-71b57f62c2cd")] -[assembly: AssemblyVersion("0.9.1.0")] -[assembly: AssemblyInformationalVersion("0.9.1")] +[assembly: AssemblyVersion("0.10.0.0")] // Exposes internal components to the testing project. [assembly: InternalsVisibleTo("Scallion.Tests")] diff --git a/Scallion/Raw/Components/CameraInterpolation.cs b/Scallion/Raw/Components/CameraInterpolation.cs index dccaa93..3749cc1 100644 --- a/Scallion/Raw/Components/CameraInterpolation.cs +++ b/Scallion/Raw/Components/CameraInterpolation.cs @@ -7,7 +7,7 @@ namespace Scallion.Raw.Components { - internal class CameraInterpolation : MMDObject + internal abstract class CameraInterpolation : MMDObject { public Interpolation X { get; set; } public Interpolation Y { get; set; } @@ -27,88 +27,6 @@ public CameraInterpolation() } - public override void Serialize(MoSerializer archive) - { - archive.WriteByte(X.First.X); - archive.WriteByte(X.Second.X); - archive.WriteByte(X.First.Y); - archive.WriteByte(X.Second.Y); - - archive.WriteByte(Y.First.X); - archive.WriteByte(Y.Second.X); - archive.WriteByte(Y.First.Y); - archive.WriteByte(Y.Second.Y); - - archive.WriteByte(Z.First.X); - archive.WriteByte(Z.Second.X); - archive.WriteByte(Z.First.Y); - archive.WriteByte(Z.Second.Y); - - archive.WriteByte(R.First.X); - archive.WriteByte(R.Second.X); - archive.WriteByte(R.First.Y); - archive.WriteByte(R.Second.Y); - - archive.WriteByte(D.First.X); - archive.WriteByte(D.Second.X); - archive.WriteByte(D.First.Y); - archive.WriteByte(D.Second.Y); - - archive.WriteByte(V.First.X); - archive.WriteByte(V.Second.X); - archive.WriteByte(V.First.Y); - archive.WriteByte(V.Second.Y); - } - - public override void Deserialize(MoDeserializer archive) - { - var first = new InterpolationParameter(); - var second = new InterpolationParameter(); - - first.X = archive.ReadByte(); - second.X = archive.ReadByte(); - first.Y = archive.ReadByte(); - second.Y = archive.ReadByte(); - X.First = first; - X.Second = second; - - first.X = archive.ReadByte(); - second.X = archive.ReadByte(); - first.Y = archive.ReadByte(); - second.Y = archive.ReadByte(); - Y.First = first; - Y.Second = second; - - first.X = archive.ReadByte(); - second.X = archive.ReadByte(); - first.Y = archive.ReadByte(); - second.Y = archive.ReadByte(); - Z.First = first; - Z.Second = second; - - first.X = archive.ReadByte(); - second.X = archive.ReadByte(); - first.Y = archive.ReadByte(); - second.Y = archive.ReadByte(); - R.First = first; - R.Second = second; - - first.X = archive.ReadByte(); - second.X = archive.ReadByte(); - first.Y = archive.ReadByte(); - second.Y = archive.ReadByte(); - D.First = first; - D.Second = second; - - first.X = archive.ReadByte(); - second.X = archive.ReadByte(); - first.Y = archive.ReadByte(); - second.Y = archive.ReadByte(); - V.First = first; - V.Second = second; - } - - public static implicit operator DomainModels.Components.CameraInterpolation(CameraInterpolation c) { return new DomainModels.Components.CameraInterpolation() diff --git a/Scallion/Raw/Components/Motion/CameraInterpolationImpl.cs b/Scallion/Raw/Components/Motion/CameraInterpolationImpl.cs new file mode 100644 index 0000000..97f903f --- /dev/null +++ b/Scallion/Raw/Components/Motion/CameraInterpolationImpl.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.Core; +using Scallion.DomainModels.Components; + +namespace Scallion.Raw.Components.Motion +{ + internal class CameraInterpolationImpl : CameraInterpolation + { + public override void Serialize(MoSerializer archive) + { + archive.WriteByte(X.First.X); + archive.WriteByte(X.Second.X); + archive.WriteByte(X.First.Y); + archive.WriteByte(X.Second.Y); + + archive.WriteByte(Y.First.X); + archive.WriteByte(Y.Second.X); + archive.WriteByte(Y.First.Y); + archive.WriteByte(Y.Second.Y); + + archive.WriteByte(Z.First.X); + archive.WriteByte(Z.Second.X); + archive.WriteByte(Z.First.Y); + archive.WriteByte(Z.Second.Y); + + archive.WriteByte(R.First.X); + archive.WriteByte(R.Second.X); + archive.WriteByte(R.First.Y); + archive.WriteByte(R.Second.Y); + + archive.WriteByte(D.First.X); + archive.WriteByte(D.Second.X); + archive.WriteByte(D.First.Y); + archive.WriteByte(D.Second.Y); + + archive.WriteByte(V.First.X); + archive.WriteByte(V.Second.X); + archive.WriteByte(V.First.Y); + archive.WriteByte(V.Second.Y); + } + + public override void Deserialize(MoDeserializer archive) + { + var first = new InterpolationParameter(); + var second = new InterpolationParameter(); + + first.X = archive.ReadByte(); + second.X = archive.ReadByte(); + first.Y = archive.ReadByte(); + second.Y = archive.ReadByte(); + X.First = first; + X.Second = second; + + first.X = archive.ReadByte(); + second.X = archive.ReadByte(); + first.Y = archive.ReadByte(); + second.Y = archive.ReadByte(); + Y.First = first; + Y.Second = second; + + first.X = archive.ReadByte(); + second.X = archive.ReadByte(); + first.Y = archive.ReadByte(); + second.Y = archive.ReadByte(); + Z.First = first; + Z.Second = second; + + first.X = archive.ReadByte(); + second.X = archive.ReadByte(); + first.Y = archive.ReadByte(); + second.Y = archive.ReadByte(); + R.First = first; + R.Second = second; + + first.X = archive.ReadByte(); + second.X = archive.ReadByte(); + first.Y = archive.ReadByte(); + second.Y = archive.ReadByte(); + D.First = first; + D.Second = second; + + first.X = archive.ReadByte(); + second.X = archive.ReadByte(); + first.Y = archive.ReadByte(); + second.Y = archive.ReadByte(); + V.First = first; + V.Second = second; + } + } +} diff --git a/Scallion/Raw/Components/Motion/CameraKeyFrame.cs b/Scallion/Raw/Components/Motion/CameraKeyFrame.cs index b52813d..e3af3b3 100644 --- a/Scallion/Raw/Components/Motion/CameraKeyFrame.cs +++ b/Scallion/Raw/Components/Motion/CameraKeyFrame.cs @@ -13,7 +13,7 @@ internal class CameraKeyFrame : KeyFrame public float Distance { get; set; } public Vector3 Position { get; set; } public Vector3 Rotation { get; set; } - public CameraInterpolation Interpolation { get; set; } + public CameraInterpolationImpl Interpolation { get; set; } public int AngleOfView { get; set; } public bool IsPerspectiveEnabled { get; set; } @@ -36,7 +36,7 @@ public override void Deserialize(MoDeserializer archive) Position = new Vector3(pos.X, pos.Y, pos.Z); var r = archive.Deserialize().Value; Rotation = new Vector3(-r.X, r.Y, r.Z); - Interpolation = archive.Deserialize(); + Interpolation = archive.Deserialize(); AngleOfView = archive.ReadInt32(); IsPerspectiveEnabled = archive.ReadByte() == 0; } diff --git a/Scallion/Raw/Components/Project/Accessory.cs b/Scallion/Raw/Components/Project/Accessory.cs new file mode 100644 index 0000000..f82188e --- /dev/null +++ b/Scallion/Raw/Components/Project/Accessory.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; +using Scallion.Core; +using Scallion.Internal; + +namespace Scallion.Raw.Components.Project +{ + internal class Accessory : MMDObject + { + public byte Index { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public byte RenderingOrder { get; set; } + public bool IsAdditiveBlending { get; set; } + + public AccessoryKeyFrame InitialKeyFrame { get; set; } + public List KeyFrames { get; set; } + public AccessoryState CurrentStatus { get; set; } + + public override void Serialize(MoSerializer archive) + { + archive.WriteByte(Index); + archive.WriteByteString(Name, 100); + archive.WriteByteString(Path, 256); + archive.WriteByte(RenderingOrder); + + if (!InitialKeyFrame.IsInitialKeyFrame) throw new InvalidOperationException("IsInitial property of InitialKeyFrame must be true."); + archive.Serialize(InitialKeyFrame); + archive.SerializeList(KeyFrames); + archive.Serialize(CurrentStatus); + archive.WriteByte((byte)(IsAdditiveBlending ? 1 : 0)); + } + + public override void Deserialize(MoDeserializer archive) + { + Index = archive.ReadByte(); + Name = archive.ReadByteString(100).TrimNull(); + Path = archive.ReadByteString(256).TrimNull(); + RenderingOrder = archive.ReadByte(); + InitialKeyFrame = archive.Deserialize(new AccessoryKeyFrame() { IsInitialKeyFrame = true }); + KeyFrames = archive.DeserializeList(archive.ReadInt32()); + CurrentStatus = archive.Deserialize(); + IsAdditiveBlending = archive.ReadByte() == 1; + } + } + + internal class AccessoryKeyFrame : LinkableKeyFrame + { + } + + internal class AccessoryState : MMDObject + { + public float Opacity { get; set; } + public bool IsVisible { get; set; } + public BoneReference ExternalParent { get; set; } + public Vector3 Position { get; set; } + public Vector3 Rotation { get; set; } + public float Scale { get; set; } + public bool IsShadowEnabled { get; set; } + + + public override void Serialize(MoSerializer archive) + { + byte data = (byte)((int)(100 - 100 * Opacity) << 1); + data |= (byte)(IsVisible ? 1 : 0); + archive.WriteByte(data); + archive.WriteInt32(ExternalParent.ModelIndex); + archive.WriteInt32(ExternalParent.BoneIndex); + archive.Serialize(new Vector3Wrapper(Position)); + archive.Serialize(new Vector3Wrapper(Rotation)); + archive.WriteSingle(Scale); + archive.WriteByte((byte)(IsShadowEnabled ? 1 : 0)); + } + + public override void Deserialize(MoDeserializer archive) + { + byte data = archive.ReadByte(); + IsVisible = (data & 1) == 1; + Opacity = (float)((100 - (data >> 1)) / 100.0); + ExternalParent = new BoneReference(archive.ReadInt32(), archive.ReadInt32()); + Position = archive.Deserialize().Value; + Rotation = archive.Deserialize().Value; + Scale = archive.ReadSingle(); + IsShadowEnabled = archive.ReadByte() == 1; + } + } +} diff --git a/Scallion/Raw/Components/Project/BoneInterpolationImpl.cs b/Scallion/Raw/Components/Project/BoneInterpolationImpl.cs new file mode 100644 index 0000000..46bfc87 --- /dev/null +++ b/Scallion/Raw/Components/Project/BoneInterpolationImpl.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.Core; +using Scallion.DomainModels.Components; + +namespace Scallion.Raw.Components.Project +{ + internal class BoneInterpolationImpl : Components.BoneInterpolation + { + public override void Serialize(MoSerializer archive) + { + archive.WriteByte(X.First.X); + archive.WriteByte(X.First.Y); + archive.WriteByte(X.Second.X); + archive.WriteByte(X.Second.Y); + archive.WriteByte(Y.First.X); + archive.WriteByte(Y.First.Y); + archive.WriteByte(Y.Second.X); + archive.WriteByte(Y.Second.Y); + archive.WriteByte(Z.First.X); + archive.WriteByte(Z.First.Y); + archive.WriteByte(Z.Second.X); + archive.WriteByte(Z.Second.Y); + archive.WriteByte(R.First.X); + archive.WriteByte(R.First.Y); + archive.WriteByte(R.Second.X); + archive.WriteByte(R.Second.Y); + + } + + public override void Deserialize(MoDeserializer archive) + { + X = new Interpolation(new InterpolationParameter(archive.ReadByte(), archive.ReadByte()), new InterpolationParameter(archive.ReadByte(), archive.ReadByte())); + Y = new Interpolation(new InterpolationParameter(archive.ReadByte(), archive.ReadByte()), new InterpolationParameter(archive.ReadByte(), archive.ReadByte())); + Z = new Interpolation(new InterpolationParameter(archive.ReadByte(), archive.ReadByte()), new InterpolationParameter(archive.ReadByte(), archive.ReadByte())); + R = new Interpolation(new InterpolationParameter(archive.ReadByte(), archive.ReadByte()), new InterpolationParameter(archive.ReadByte(), archive.ReadByte())); + } + + public static implicit operator BoneInterpolationImpl(DomainModels.Components.BoneInterpolation obj) + { + return new BoneInterpolationImpl() + { + X = obj.X, + Y = obj.Y, + Z = obj.Z, + R = obj.R + }; + } + } +} diff --git a/Scallion/Raw/Components/Project/BoneKeyFrame.cs b/Scallion/Raw/Components/Project/BoneKeyFrame.cs new file mode 100644 index 0000000..3d85705 --- /dev/null +++ b/Scallion/Raw/Components/Project/BoneKeyFrame.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; +using Scallion.Core; +using Scallion.Internal; + +namespace Scallion.Raw.Components.Project +{ + internal class BoneState : MMDObject + { + public BoneInterpolationImpl Interpolation { get; set; } + public Vector3 Position { get; set; } + public Quaternion Quaternion { get; set; } + public bool IsPhysicsEnabled { get; set; } + + public override void Serialize(MoSerializer archive) + { + throw new InvalidOperationException("This instance will not be serialized directly."); + } + + public override void Deserialize(MoDeserializer archive) + { + throw new InvalidOperationException("This instance will not be deserialized directly."); + } + } + + internal class BoneKeyFrame : LinkableKeyFrame + { + internal override void SerializeKeyFrame(MoSerializer archive) + { + archive.Serialize(Value.Interpolation); + archive.Serialize(new Vector3Wrapper(Value.Position)); + archive.Serialize(new QuaternionWrapper(Value.Quaternion)); + archive.WriteByte((byte)(IsSelected ? 1 : 0)); + archive.WriteByte((byte)(Value.IsPhysicsEnabled ? 0 : 1)); + } + + internal override void DeserializeKeyFrame(MoDeserializer archive) + { + Value = new BoneState(); + Value.Interpolation = archive.Deserialize(); + Value.Position = archive.Deserialize().Value; + Value.Quaternion = archive.Deserialize().Value; + IsSelected = archive.ReadByte() == 1; + Value.IsPhysicsEnabled = archive.ReadByte() == 0; + } + } + + internal class CurrentBoneState : MMDObject + { + public Vector3 Position { get; set; } + public Quaternion Quaternion { get; set; } + public bool IsSaved { get; set; } + public bool IsPhysicsEnabled { get; set; } + public bool IsRowSelected { get; set; } + + public override void Serialize(MoSerializer archive) + { + archive.Serialize(new Vector3Wrapper(Position)); + archive.Serialize(new QuaternionWrapper(Quaternion)); + archive.WriteByte((byte)(IsSaved ? 0 : 1)); + archive.WriteByte((byte)(IsPhysicsEnabled ? 0 : 1)); + archive.WriteByte((byte)(IsRowSelected ? 1 : 0)); + } + + public override void Deserialize(MoDeserializer archive) + { + Position = archive.Deserialize().Value; + Quaternion = archive.Deserialize().Value; + IsSaved = archive.ReadByte() == 0; + IsPhysicsEnabled = archive.ReadByte() == 0; + IsRowSelected = archive.ReadByte() == 1; + } + } +} diff --git a/Scallion/Raw/Components/Project/BoneReference.cs b/Scallion/Raw/Components/Project/BoneReference.cs new file mode 100644 index 0000000..873bc0f --- /dev/null +++ b/Scallion/Raw/Components/Project/BoneReference.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.Core; + +namespace Scallion.Raw.Components.Project +{ + internal struct BoneReference + { + public readonly static BoneReference Empty = new BoneReference(-1, 0); + + public int ModelIndex { get; set; } + public int BoneIndex { get; set; } + + public BoneReference(int modelIndex, int boneIndex) + : this() + { + ModelIndex = modelIndex; + BoneIndex = boneIndex; + } + } +} diff --git a/Scallion/Raw/Components/Project/Camera.cs b/Scallion/Raw/Components/Project/Camera.cs new file mode 100644 index 0000000..36c100c --- /dev/null +++ b/Scallion/Raw/Components/Project/Camera.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; +using Scallion.Core; +using Scallion.Internal; + +namespace Scallion.Raw.Components.Project +{ + internal class Camera : MMDObject + { + public CameraKeyFrame InitialKeyFrame { get; set; } + public List KeyFrames { get; set; } + public CurrentCameraState CurrentStatus { get; set; } + + public override void Serialize(MoSerializer archive) + { + if (!InitialKeyFrame.IsInitialKeyFrame) throw new InvalidOperationException("IsInitial property of InitialKeyFrame must be true."); + archive.Serialize(InitialKeyFrame); + archive.SerializeList(KeyFrames); + archive.Serialize(CurrentStatus); + } + + public override void Deserialize(MoDeserializer archive) + { + InitialKeyFrame = archive.Deserialize(new CameraKeyFrame() { IsInitialKeyFrame = true }); + KeyFrames = archive.DeserializeList(archive.ReadInt32()); + CurrentStatus = archive.Deserialize(); + } + } + + internal class CameraKeyFrame : LinkableKeyFrame + { + public float Distance { get; set; } + public BoneReference FollowingBone { get; set; } + public CameraInterpolationImpl Interpolation { get; set; } + public int AngleOfView { get; set; } + + + internal override void SerializeKeyFrameValue(MoSerializer archive) + { + archive.WriteSingle(-Distance); + archive.Serialize(new Vector3Wrapper(Value.CenterPosition)); + archive.Serialize(new Vector3Wrapper(new Vector3(-Value.Rotation.X, Value.Rotation.Y, Value.Rotation.Z))); + archive.WriteInt32(FollowingBone.ModelIndex); + archive.WriteInt32(FollowingBone.BoneIndex); + archive.Serialize(Interpolation); + archive.WriteByte((byte)(Value.IsPerspectiveEnabled ? 0 : 1)); + archive.WriteInt32(AngleOfView); + } + + internal override void DeserializeKeyFrameValue(MoDeserializer archive) + { + Distance = -archive.ReadSingle(); + Value = new CameraState(); + Value.CenterPosition = archive.Deserialize().Value; + var r = archive.Deserialize().Value; + Value.Rotation = new Vector3(-r.X, r.Y, r.Z); + FollowingBone = new BoneReference(archive.ReadInt32(), archive.ReadInt32()); + Interpolation = archive.Deserialize(); + Value.IsPerspectiveEnabled = archive.ReadByte() == 0; + AngleOfView = archive.ReadInt32(); + } + } + + internal class CameraState : MMDObject + { + public Vector3 CenterPosition { get; set; } + public Vector3 Rotation { get; set; } + public bool IsPerspectiveEnabled { get; set; } + + + public override void Serialize(MoSerializer archive) + { + archive.Serialize(new Vector3Wrapper(CenterPosition)); + archive.Serialize(new Vector3Wrapper(Rotation)); + archive.WriteByte((byte)(IsPerspectiveEnabled ? 1 : 0)); + } + + public override void Deserialize(MoDeserializer archive) + { + CenterPosition = archive.Deserialize().Value; + Rotation = archive.Deserialize().Value; + IsPerspectiveEnabled = archive.ReadByte() == 1; + } + } + + internal class CurrentCameraState : CameraState + { + public Vector3 OffsetPosition { get; set; } + + public override void Serialize(MoSerializer archive) + { + archive.Serialize(new Vector3Wrapper(CenterPosition)); + archive.Serialize(new Vector3Wrapper(OffsetPosition)); + archive.Serialize(new Vector3Wrapper(Rotation)); + archive.WriteByte((byte)(IsPerspectiveEnabled ? 0 : 1)); + } + + public override void Deserialize(MoDeserializer archive) + { + CenterPosition = archive.Deserialize().Value; + OffsetPosition = archive.Deserialize().Value; + Rotation = archive.Deserialize().Value; + IsPerspectiveEnabled = archive.ReadByte() == 0; + } + } +} diff --git a/Scallion/Raw/Components/Project/CameraInterpolationImpl.cs b/Scallion/Raw/Components/Project/CameraInterpolationImpl.cs new file mode 100644 index 0000000..eeb2dbc --- /dev/null +++ b/Scallion/Raw/Components/Project/CameraInterpolationImpl.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.Core; +using Scallion.DomainModels.Components; + +namespace Scallion.Raw.Components.Project +{ + internal class CameraInterpolationImpl : CameraInterpolation + { + public override void Serialize(MoSerializer archive) + { + archive.WriteByte(X.First.X); + archive.WriteByte(X.First.Y); + archive.WriteByte(X.Second.X); + archive.WriteByte(X.Second.Y); + + archive.WriteByte(Y.First.X); + archive.WriteByte(Y.First.Y); + archive.WriteByte(Y.Second.X); + archive.WriteByte(Y.Second.Y); + + archive.WriteByte(Z.First.X); + archive.WriteByte(Z.First.Y); + archive.WriteByte(Z.Second.X); + archive.WriteByte(Z.Second.Y); + + archive.WriteByte(R.First.X); + archive.WriteByte(R.First.Y); + archive.WriteByte(R.Second.X); + archive.WriteByte(R.Second.Y); + + archive.WriteByte(D.First.X); + archive.WriteByte(D.First.Y); + archive.WriteByte(D.Second.X); + archive.WriteByte(D.Second.Y); + + archive.WriteByte(V.First.X); + archive.WriteByte(V.First.Y); + archive.WriteByte(V.Second.X); + archive.WriteByte(V.Second.Y); + } + + public override void Deserialize(MoDeserializer archive) + { + X.First = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + X.Second = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + + Y.First = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + Y.Second = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + + Z.First = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + Z.Second = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + + R.First = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + R.Second = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + + D.First = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + D.Second = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + + V.First = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + V.Second = new InterpolationParameter(archive.ReadByte(), archive.ReadByte()); + } + + public static implicit operator CameraInterpolationImpl(DomainModels.Components.CameraInterpolation obj) + { + return new CameraInterpolationImpl() + { + X = obj.X, + Y = obj.Y, + Z = obj.Z, + D = obj.D, + R = obj.R, + V = obj.V + }; + } + } +} diff --git a/Scallion/Raw/Components/Project/Gravity.cs b/Scallion/Raw/Components/Project/Gravity.cs new file mode 100644 index 0000000..fbb8653 --- /dev/null +++ b/Scallion/Raw/Components/Project/Gravity.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Numerics; +using Scallion.Core; +using Scallion.Internal; + +namespace Scallion.Raw.Components.Project +{ + internal class Gravity : MMDObject + { + public GravityKeyFrame InitialKeyFrame { get; set; } + public List KeyFrames { get; set; } + public GravityState CurrentStatus { get; set; } + + public override void Serialize(MoSerializer archive) + { + archive.Serialize(CurrentStatus); + if (!InitialKeyFrame.IsInitialKeyFrame) throw new InvalidOperationException("IsInitial property of InitialKeyFrame must be true."); + archive.Serialize(InitialKeyFrame); + archive.SerializeList(KeyFrames); + } + + public override void Deserialize(MoDeserializer archive) + { + CurrentStatus = archive.Deserialize(); + InitialKeyFrame = archive.Deserialize(new GravityKeyFrame() { IsInitialKeyFrame = true }); + KeyFrames = archive.DeserializeList(archive.ReadInt32()); + } + } + + internal class GravityState : MMDObject + { + public float Acceleration { get; set; } + public int NoiseAmount { get; set; } + public Vector3 Direction { get; set; } + public bool IsNoiseEnabled { get; set; } + + public override void Serialize(MoSerializer archive) + { + archive.WriteSingle(Acceleration); + archive.WriteInt32(NoiseAmount); + archive.Serialize(new Vector3Wrapper(Direction)); + archive.WriteByte((byte)(IsNoiseEnabled ? 1 : 0)); + } + + public override void Deserialize(MoDeserializer archive) + { + Acceleration = archive.ReadSingle(); + NoiseAmount = archive.ReadInt32(); + Direction = archive.Deserialize().Value; + IsNoiseEnabled = archive.ReadByte() == 1; + } + } + + internal class GravityKeyFrame : LinkableKeyFrame + { + internal override void SerializeKeyFrameValue(MoSerializer archive) + { + archive.WriteByte((byte)(Value.IsNoiseEnabled ? 1 : 0)); + archive.WriteInt32(Value.NoiseAmount); + archive.WriteSingle(Value.Acceleration); + archive.Serialize(new Vector3Wrapper(Value.Direction)); + } + + internal override void DeserializeKeyFrameValue(MoDeserializer archive) + { + Value = new GravityState() + { + IsNoiseEnabled = archive.ReadByte() == 1, + NoiseAmount = archive.ReadInt32(), + Acceleration = archive.ReadSingle(), + Direction = archive.Deserialize().Value + }; + } + } +} diff --git a/Scallion/Raw/Components/Project/Light.cs b/Scallion/Raw/Components/Project/Light.cs new file mode 100644 index 0000000..845d5b2 --- /dev/null +++ b/Scallion/Raw/Components/Project/Light.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Drawing; +using System.Numerics; +using Scallion.Core; +using Scallion.Internal; + +namespace Scallion.Raw.Components.Project +{ + internal class Light : MMDObject + { + public LightKeyFrame InitialKeyFrame { get; set; } + public List KeyFrames { get; set; } + public LightState CurrentStatus { get; set; } + + public override void Serialize(MoSerializer archive) + { + if (!InitialKeyFrame.IsInitialKeyFrame) throw new InvalidOperationException("IsInitial property of InitialKeyFrame must be true."); + archive.Serialize(InitialKeyFrame); + archive.SerializeList(KeyFrames); + archive.Serialize(CurrentStatus); + } + + public override void Deserialize(MoDeserializer archive) + { + InitialKeyFrame = archive.Deserialize(new LightKeyFrame() { IsInitialKeyFrame = true }); + KeyFrames = archive.DeserializeList(archive.ReadInt32()); + CurrentStatus = archive.Deserialize(); + } + } + + internal class LightKeyFrame : LinkableKeyFrame + { + } + + internal class LightState : MMDObject + { + public Color Color { get; set; } + public Vector3 Position { get; set; } + + public override void Serialize(MoSerializer archive) + { + archive.Serialize(new ColorWrapper(Color)); + archive.Serialize(new Vector3Wrapper(Position)); + } + + public override void Deserialize(MoDeserializer archive) + { + Color = archive.Deserialize().Value; + Position = archive.Deserialize().Value; + } + } +} diff --git a/Scallion/Raw/Components/Project/LinkableKeyFrame.cs b/Scallion/Raw/Components/Project/LinkableKeyFrame.cs new file mode 100644 index 0000000..16fac77 --- /dev/null +++ b/Scallion/Raw/Components/Project/LinkableKeyFrame.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Scallion.Core; + +namespace Scallion.Raw.Components.Project +{ + /// + /// Represents a key frame used in MMD Project File. + /// + /// + internal abstract class LinkableKeyFrame : KeyFrame, ILinkableKeyFrame where T : MMDObject, new() + { + public int DataIndex { get; set; } + public int PreviousDataIndex { get; set; } + public int NextDataIndex { get; set; } + public bool IsInitialKeyFrame { get; set; } + public bool IsSelected { get; set; } + public T Value { get; set; } + + public override void Serialize(MoSerializer archive) + { + if (!IsInitialKeyFrame) archive.WriteInt32(DataIndex); + archive.WriteInt32(KeyFrameIndex); + archive.WriteInt32(PreviousDataIndex); + archive.WriteInt32(NextDataIndex); + SerializeKeyFrame(archive); + } + + public override void Deserialize(MoDeserializer archive) + { + if (!IsInitialKeyFrame) DataIndex = archive.ReadInt32(); + KeyFrameIndex = archive.ReadInt32(); + PreviousDataIndex = archive.ReadInt32(); + NextDataIndex = archive.ReadInt32(); + DeserializeKeyFrame(archive); + } + + /// + /// When overridden in a derived class, serializes this key frame except its metadata by its own way. + /// + /// The instance of to serialize + internal virtual void SerializeKeyFrame(MoSerializer archive) + { + SerializeKeyFrameValue(archive); + archive.WriteByte((byte)(IsSelected ? 1 : 0)); + } + + /// + /// When overriden in a derived class, deserializes this key frame except its metadata by its own way. + /// + /// The instance of to deserialize + internal virtual void DeserializeKeyFrame(MoDeserializer archive) + { + DeserializeKeyFrameValue(archive); + IsSelected = archive.ReadByte() == 1; + } + + /// + /// When overridden in a derived class, serializes the value of this key frame by its own way. + /// + /// The instance of to serialize + internal virtual void SerializeKeyFrameValue(MoSerializer archive) + { + archive.Serialize(Value); + } + + /// + /// When overriden in a derived class, deserializes the value of this key frame by its own way. + /// + /// The instance of to deserialize + internal virtual void DeserializeKeyFrameValue(MoDeserializer archive) + { + Value = archive.Deserialize(); + } + } + + /// + /// Provides structures for + /// + internal interface ILinkableKeyFrame + { + int DataIndex { get; set; } + int PreviousDataIndex { get; set; } + int NextDataIndex { get; set; } + } +} diff --git a/Scallion/Raw/Components/Project/Media.cs b/Scallion/Raw/Components/Project/Media.cs new file mode 100644 index 0000000..fd018eb --- /dev/null +++ b/Scallion/Raw/Components/Project/Media.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Drawing; +using Scallion.Core; +using Scallion.Internal; + +namespace Scallion.Raw.Components.Project +{ + internal class Media : MMDObject + { + public Audio Audio { get; set; } + public Video Video { get; set; } + public BackgroundImage BackgroundImage { get; set; } + + public override void Serialize(MoSerializer archive) + { + Audio.Serialize(archive); + Video.Serialize(archive); + BackgroundImage.Serialize(archive); + } + + public override void Deserialize(MoDeserializer archive) + { + Audio = archive.Deserialize