From f0e5ebc72358bd3d18ad26993eade5b2888ed7d8 Mon Sep 17 00:00:00 2001 From: thtrandomlurker <59970354+thtrandomlurker@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:50:31 -0330 Subject: [PATCH] Add support for Internal Skin Param (#52) * Add support for Internal Skin Param * Simplify casting of block type * Write osage block external name to skp instead of internal name * Set internal skp name to ExternalName of osgBlock on creation * Fix mistake regarding Tail of OsageInternalCollisionParameter --- .../Objects/Extra/Blocks/ClothBlock.cs | 14 +- .../Objects/Extra/Blocks/OsageBlock.cs | 48 ++- .../OsageInternalCollisionParameter.cs | 42 +++ .../Parameters/OsageInternalSkinParameter.cs | 88 ++++++ MikuMikuModel/Nodes/Objects/SkinNode.cs | 275 ++++++++++++++++++ 5 files changed, 453 insertions(+), 14 deletions(-) create mode 100644 MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalCollisionParameter.cs create mode 100644 MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalSkinParameter.cs diff --git a/MikuMikuLibrary/Objects/Extra/Blocks/ClothBlock.cs b/MikuMikuLibrary/Objects/Extra/Blocks/ClothBlock.cs index 1619a742..50ff32b4 100644 --- a/MikuMikuLibrary/Objects/Extra/Blocks/ClothBlock.cs +++ b/MikuMikuLibrary/Objects/Extra/Blocks/ClothBlock.cs @@ -1,5 +1,6 @@ using MikuMikuLibrary.IO; using MikuMikuLibrary.IO.Common; +using MikuMikuLibrary.Objects.Extra.Parameters; namespace MikuMikuLibrary.Objects.Extra.Blocks; @@ -17,7 +18,7 @@ public class ClothBlock : IBlock public List Field20 { get; set; } public ushort[] Field24 { get; set; } public ushort[] Field28 { get; set; } - public uint Field2C { get; set; } + public OsageInternalSkinParameter InternalSkinParameter { get; set; } public uint Field30 { get; set; } public void Read(EndianBinaryReader reader, StringSet stringSet) @@ -66,7 +67,11 @@ public void Read(EndianBinaryReader reader, StringSet stringSet) reader.ReadOffset(() => { Field28 = reader.ReadUInt16s(reader.ReadUInt16()); }); - Field2C = reader.ReadUInt32(); + reader.ReadOffset(() => + { + InternalSkinParameter = new OsageInternalSkinParameter(); + InternalSkinParameter.Read(reader); + }); Field30 = reader.ReadUInt32(); } @@ -112,7 +117,10 @@ public void Write(EndianBinaryWriter writer, StringSet stringSet, BinaryFormat f writer.Write(Field28); }); - writer.Write(Field2C); + writer.WriteOffsetIf(InternalSkinParameter != null, 16, AlignmentMode.Left, () => + { + InternalSkinParameter.Write(writer); + }); writer.Write(Field30); } diff --git a/MikuMikuLibrary/Objects/Extra/Blocks/OsageBlock.cs b/MikuMikuLibrary/Objects/Extra/Blocks/OsageBlock.cs index a4f3d62d..dcbd3bbe 100644 --- a/MikuMikuLibrary/Objects/Extra/Blocks/OsageBlock.cs +++ b/MikuMikuLibrary/Objects/Extra/Blocks/OsageBlock.cs @@ -1,5 +1,6 @@ using MikuMikuLibrary.IO; using MikuMikuLibrary.IO.Common; +using MikuMikuLibrary.Objects.Extra.Parameters; namespace MikuMikuLibrary.Objects.Extra.Blocks; @@ -12,6 +13,7 @@ public class OsageBlock : NodeBlock public List Nodes { get; } public string ExternalName { get; set; } + public OsageInternalSkinParameter InternalSkinParameter { get; set; } internal override void ReadBody(EndianBinaryReader reader, StringSet stringSet) { @@ -29,15 +31,21 @@ internal override void ReadBody(EndianBinaryReader reader, StringSet stringSet) reader.ReadOffset(() => { long current = reader.Position; + if (reader.ReadUInt32() == 0) { - if (reader.ReadUInt32() == 0) // Integrated SKP, yet to support. - return; + // read the internal skin param + reader.SeekBegin(current); + InternalSkinParameter = new OsageInternalSkinParameter(); + InternalSkinParameter.Read(reader); } + else + { + // read rotation + reader.SeekBegin(current); - reader.SeekBegin(current); - - foreach (var bone in Nodes) - bone.ReadOsgBlockInfo(reader, stringSet); + foreach (var bone in Nodes) + bone.ReadOsgBlockInfo(reader, stringSet); + } }); if (reader.AddressSpace == AddressSpace.Int64) @@ -53,16 +61,34 @@ internal override void WriteBody(EndianBinaryWriter writer, StringSet stringSet, stringSet.WriteString(writer, ExternalName); stringSet.WriteString(writer, Name); - bool shouldWriteRotation = format == BinaryFormat.FT && Nodes.Any(x => + bool shouldWriteRotation = Nodes.Any(x => Math.Abs(x.Rotation.X) > 0 || Math.Abs(x.Rotation.Y) > 0 || Math.Abs(x.Rotation.Z) > 0); - writer.WriteOffsetIf(shouldWriteRotation, 4, AlignmentMode.Left, () => + bool shouldWriteInternalSkinParam = InternalSkinParameter != null; + + if (format == BinaryFormat.FT) { - foreach (var bone in Nodes) - bone.WriteOsgBlockInfo(writer, stringSet); - }); + writer.WriteOffsetIf(shouldWriteRotation, 4, AlignmentMode.Left, () => + { + foreach (var bone in Nodes) + bone.WriteOsgBlockInfo(writer, stringSet); + }); + } + + else if (format == BinaryFormat.DT) + { + writer.WriteOffsetIf(shouldWriteInternalSkinParam, 16, AlignmentMode.Left, () => + { + InternalSkinParameter.Write(writer); + }); + } + + else + { + writer.WriteOffsetIf(false, () => { }); + } if (writer.AddressSpace == AddressSpace.Int64) writer.WriteNulls(4 * sizeof(ulong)); diff --git a/MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalCollisionParameter.cs b/MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalCollisionParameter.cs new file mode 100644 index 00000000..2eb73420 --- /dev/null +++ b/MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalCollisionParameter.cs @@ -0,0 +1,42 @@ +using MikuMikuLibrary.IO.Common; + +namespace MikuMikuLibrary.Objects.Extra.Parameters; + +public enum OsageInternalCollisionType : int +{ + End = 0, + Sphere = 1, + Cylinder = 2, + Plane = 3, + Ellipse = 4 +} + +public class OsageInternalCollisionParameter +{ + public OsageInternalCollisionType CollisionType { get; set; } + public uint Head { get; set; } + public uint Tail { get; set; } + public float CollisionRadius { get; set; } + public Vector3 HeadPosition { get; set; } + public Vector3 TailPosition { get; set; } + + internal void Read(EndianBinaryReader reader) + { + CollisionType = (OsageInternalCollisionType)reader.ReadInt32(); + Head = reader.ReadUInt32(); + Tail = reader.ReadUInt32(); + CollisionRadius = reader.ReadSingle(); + HeadPosition = reader.ReadVector3(); + TailPosition = reader.ReadVector3(); + } + + internal void Write(EndianBinaryWriter writer) + { + writer.Write((int)CollisionType); + writer.Write(Head); + writer.Write(Tail); + writer.Write(CollisionRadius); + writer.Write(HeadPosition); + writer.Write(TailPosition); + } +} diff --git a/MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalSkinParameter.cs b/MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalSkinParameter.cs new file mode 100644 index 00000000..a48d864b --- /dev/null +++ b/MikuMikuLibrary/Objects/Extra/Parameters/OsageInternalSkinParameter.cs @@ -0,0 +1,88 @@ +using MikuMikuLibrary.IO; +using MikuMikuLibrary.IO.Common; +using MikuMikuLibrary.Parameters; + +namespace MikuMikuLibrary.Objects.Extra.Parameters; + + +[TypeConverter(typeof(ExpandableObjectConverter))] +public class OsageInternalSkinParameter +{ + public string Name { get; set; } + public float Force { get; set; } + public float ForceGain { get; set; } + public float AirResistance { get; set; } + public float RotationY { get; set; } + public float RotationZ { get; set; } + public float HingeY { get; set; } + public float HingeZ { get; set; } + public float CollisionRadius { get; set; } + public float Friction { get; set; } + public float WindAffection { get; set; } + public List Collisions { get; } + + internal void Read(EndianBinaryReader reader) + { + long start = reader.Position; + reader.SkipNulls(4); // Truthfully, i don't know if this is inertial_cancel or just a reserved field. it seems to always be 0 though so i won't read it. + // Besides, if i did read and write it, and someone wrote it as anything but 0, then it would from that point on be falsely read as FT, which wouldn't be good. + + Force = reader.ReadSingle(); + ForceGain = reader.ReadSingle(); + AirResistance = reader.ReadSingle(); + RotationY = reader.ReadSingle(); + RotationZ = reader.ReadSingle(); + HingeY = reader.ReadSingle(); + HingeZ = reader.ReadSingle(); + Name = reader.ReadStringOffset(StringBinaryFormat.NullTerminated); + + reader.ReadOffset(() => + { + while (true) + { + OsageInternalCollisionParameter collisionParameter = new OsageInternalCollisionParameter(); + collisionParameter.Read(reader); + if (collisionParameter.CollisionType == OsageInternalCollisionType.End) + { + break; + } + Collisions.Add(collisionParameter); + + }; + }); + + CollisionRadius = reader.ReadSingle(); + Friction = reader.ReadSingle(); + WindAffection = reader.ReadSingle(); + } + + internal void Write(EndianBinaryWriter writer) + { + writer.WriteNulls(4); + writer.Write(Force); + writer.Write(ForceGain); + writer.Write(AirResistance); + writer.Write(RotationY); + writer.Write(RotationZ); + writer.Write(HingeY); + writer.Write(HingeZ); + writer.WriteStringOffset(Name); + writer.WriteOffset(16, AlignmentMode.Left, () => + { + foreach (var coll in Collisions) + { + coll.Write(writer); + } + + new OsageInternalCollisionParameter() { CollisionType = OsageInternalCollisionType.End }.Write(writer); // please suggest a better way to handle this + }); + writer.Write(CollisionRadius); + writer.Write(Friction); + writer.Write(WindAffection); + } + + public OsageInternalSkinParameter() + { + Collisions = new List(); + } +} diff --git a/MikuMikuModel/Nodes/Objects/SkinNode.cs b/MikuMikuModel/Nodes/Objects/SkinNode.cs index 7478aa74..54e3d769 100644 --- a/MikuMikuModel/Nodes/Objects/SkinNode.cs +++ b/MikuMikuModel/Nodes/Objects/SkinNode.cs @@ -1,9 +1,14 @@ using MikuMikuLibrary.Archives; using MikuMikuLibrary.Extensions; using MikuMikuLibrary.IO; +using MikuMikuLibrary.IO.Common; using MikuMikuLibrary.Objects; using MikuMikuLibrary.Objects.Extra; using MikuMikuLibrary.Objects.Extra.Blocks; +using MikuMikuLibrary.Objects.Extra.Parameters; +using MikuMikuLibrary.Parameters; +using MikuMikuLibrary.Parameters.Extensions; +using MikuMikuModel.Configurations; using MikuMikuModel.GUI.Forms; using MikuMikuModel.Modules; using MikuMikuModel.Nodes.Collections; @@ -114,6 +119,276 @@ protected override void Initialize() AddCustomHandlerSeparator(); + AddCustomHandler("Export Internal Skin Parameter", () => + { + var configuration = ConfigurationList.Instance.CurrentConfiguration; + using (SaveFileDialog dlg = new SaveFileDialog() { Filter = "Skin Parameter (*.txt)|*.txt" }) + { + if (dlg.ShowDialog() == DialogResult.OK) + { + ParameterTreeWriter externalSkinParam = new ParameterTreeWriter(); + foreach (var block in Data.Blocks) + { + if (block is OsageBlock osgBlock) + { + if (osgBlock.InternalSkinParameter != null) + { + externalSkinParam.PushScope(osgBlock.ExternalName); + { + externalSkinParam.Write("node", osgBlock.Nodes, (OsageNode x) => + { + externalSkinParam.Write("coli_r", osgBlock.InternalSkinParameter.CollisionRadius); + externalSkinParam.Write("hinge_ymin", -osgBlock.InternalSkinParameter.HingeY); + externalSkinParam.Write("hinge_ymax", osgBlock.InternalSkinParameter.HingeY); + externalSkinParam.Write("hinge_zmin", -osgBlock.InternalSkinParameter.HingeZ); + externalSkinParam.Write("hinge_zmax", osgBlock.InternalSkinParameter.HingeZ); + externalSkinParam.Write("weight", 1.0f); + externalSkinParam.Write("inertial_cancel", 0.0f); + }); + externalSkinParam.PushScope("root"); + { + externalSkinParam.Write("air_res", osgBlock.InternalSkinParameter.AirResistance); + externalSkinParam.Write("coli", osgBlock.InternalSkinParameter.Collisions, (OsageInternalCollisionParameter x) => + { + externalSkinParam.Write("type", (int)x.CollisionType); + externalSkinParam.Write("radius", x.CollisionRadius); + externalSkinParam.PushScope("bone"); + { + externalSkinParam.PushScope(0); + { + // try to get the name + string boneName = configuration?.BoneData.Skeletons[0].ObjectBoneNames[(int)x.Head]; + externalSkinParam.Write("name", boneName); + externalSkinParam.Write("posx", x.HeadPosition.X); + externalSkinParam.Write("posy", x.HeadPosition.Y); + externalSkinParam.Write("posz", x.HeadPosition.Z); + } + externalSkinParam.PopScope(); + + externalSkinParam.PushScope(1); + { + // try to get the name + string boneName = configuration?.BoneData.Skeletons[0].ObjectBoneNames[(int)(x.Tail == 0 ? x.Head : x.Tail)]; + externalSkinParam.Write("name", boneName); + externalSkinParam.Write("posx", x.TailPosition.X); + externalSkinParam.Write("posy", x.TailPosition.Y); + externalSkinParam.Write("posz", x.TailPosition.Z); + } + externalSkinParam.PopScope(); + } + externalSkinParam.PopScope(); + }); + externalSkinParam.Write("coli_type", 0); + externalSkinParam.Write("force", osgBlock.InternalSkinParameter.Force); + externalSkinParam.Write("force_gain", osgBlock.InternalSkinParameter.ForceGain); + externalSkinParam.Write("friction", osgBlock.InternalSkinParameter.Friction); + externalSkinParam.Write("init_rot_y", 0f); + externalSkinParam.Write("init_rot_z", 0f); + externalSkinParam.Write("rot_y", osgBlock.InternalSkinParameter.RotationY); + externalSkinParam.Write("rot_z", osgBlock.InternalSkinParameter.RotationZ); + externalSkinParam.Write("stiffness", 0f); + externalSkinParam.Write("wind_afc", osgBlock.InternalSkinParameter.WindAffection); + } + externalSkinParam.PopScope(); + } + externalSkinParam.PopScope(); + } + } + else if (block is ClothBlock clsBlock) + { + if (clsBlock.InternalSkinParameter != null) + { + externalSkinParam.PushScope(clsBlock.Name); + { + externalSkinParam.PushScope("root"); + { + externalSkinParam.Write("air_res", clsBlock.InternalSkinParameter.AirResistance); + externalSkinParam.Write("coli", clsBlock.InternalSkinParameter.Collisions, (OsageInternalCollisionParameter x) => + { + externalSkinParam.Write("type", (int)x.CollisionType); + externalSkinParam.Write("radius", x.CollisionRadius); + externalSkinParam.PushScope("bone"); + { + externalSkinParam.PushScope(0); + { + // try to get the name + string boneName = configuration?.BoneData.Skeletons[0].ObjectBoneNames[(int)x.Head]; + externalSkinParam.Write("name", boneName); + externalSkinParam.Write("posx", x.HeadPosition.X); + externalSkinParam.Write("posy", x.HeadPosition.Y); + externalSkinParam.Write("posz", x.HeadPosition.Z); + } + externalSkinParam.PopScope(); + + externalSkinParam.PushScope(1); + { + // try to get the name + string boneName = configuration?.BoneData.Skeletons[0].ObjectBoneNames[(int)x.Tail]; + externalSkinParam.Write("name", boneName); + externalSkinParam.Write("posx", x.TailPosition.X); + externalSkinParam.Write("posy", x.TailPosition.Y); + externalSkinParam.Write("posz", x.TailPosition.Z); + } + externalSkinParam.PopScope(); + } + externalSkinParam.PopScope(); + }); + externalSkinParam.Write("coli_type", 0); + externalSkinParam.Write("force", clsBlock.InternalSkinParameter.Force); + externalSkinParam.Write("force_gain", clsBlock.InternalSkinParameter.ForceGain); + externalSkinParam.Write("friction", clsBlock.InternalSkinParameter.Friction); + externalSkinParam.Write("init_rot_y", 0f); + externalSkinParam.Write("init_rot_z", 0f); + externalSkinParam.Write("rot_y", clsBlock.InternalSkinParameter.RotationY); + externalSkinParam.Write("rot_z", clsBlock.InternalSkinParameter.RotationZ); + externalSkinParam.Write("stiffness", 0f); + externalSkinParam.Write("wind_afc", clsBlock.InternalSkinParameter.WindAffection); + } + externalSkinParam.PopScope(); + } + externalSkinParam.PopScope(); + } + } + } + externalSkinParam.Save(dlg.FileName); + } + } + }, Keys.None, CustomHandlerFlags.None); + + AddCustomHandler("Create Internal Skin Parameter", () => + { + var configuration = ConfigurationList.Instance.CurrentConfiguration; + using (OpenFileDialog dlg = new OpenFileDialog() { Filter = "Skin Parameter (*.txt)|*.txt" }) + { + if (dlg.ShowDialog() == DialogResult.OK) + { + ParameterTree externalSkinParam = new ParameterTree(new EndianBinaryReader(File.OpenRead(dlg.FileName), Endianness.Little)); + foreach (var block in Data.Blocks) + { + if (block is OsageBlock osgBlock) + { + if (externalSkinParam.OpenScope(osgBlock.ExternalName)) + { + OsageInternalSkinParameter skp = new OsageInternalSkinParameter(); + + if (externalSkinParam.OpenScope("node")) + { + if (externalSkinParam.OpenScope(0)) + { + skp.CollisionRadius = externalSkinParam.Get("coli_r"); + skp.HingeY = externalSkinParam.Get("hinge_ymax"); + skp.HingeZ = externalSkinParam.Get("hinge_zmax"); + externalSkinParam.CloseScope(); + } + externalSkinParam.CloseScope(); + } + + if (externalSkinParam.OpenScope("root")) + { + skp.AirResistance = externalSkinParam.Get("air_res"); + externalSkinParam.Enumerate("coli", i => + { + OsageInternalCollisionParameter coll = new OsageInternalCollisionParameter(); + coll.CollisionType = (OsageInternalCollisionType)externalSkinParam.Get("type"); + coll.CollisionRadius = externalSkinParam.Get("radius"); + if (externalSkinParam.OpenScope("bone")) + { + if (externalSkinParam.OpenScope(0)) + { + coll.Head = (uint)configuration?.BoneData.Skeletons[0].ObjectBoneNames.FindIndex(x => x == externalSkinParam.Get("name")); + coll.HeadPosition = new Vector3( + externalSkinParam.Get("posx"), + externalSkinParam.Get("posy"), + externalSkinParam.Get("posz")); + externalSkinParam.CloseScope(); + } + if (externalSkinParam.OpenScope(1)) + { + coll.Tail = (uint)configuration?.BoneData.Skeletons[0].ObjectBoneNames.FindIndex(x => x == externalSkinParam.Get("name")); + coll.TailPosition = new Vector3( + externalSkinParam.Get("posx"), + externalSkinParam.Get("posy"), + externalSkinParam.Get("posz")); + externalSkinParam.CloseScope(); + } + externalSkinParam.CloseScope(); + } + skp.Collisions.Add(coll); + }); + skp.Force = externalSkinParam.Get("force"); + skp.ForceGain = externalSkinParam.Get("force_gain"); + skp.Friction = externalSkinParam.Get("friction"); + skp.Name = osgBlock.ExternalName; + skp.RotationY = externalSkinParam.Get("rot_y"); + skp.RotationZ = externalSkinParam.Get("rot_z"); + skp.WindAffection = externalSkinParam.Get("wind_afc"); + osgBlock.InternalSkinParameter = skp; + externalSkinParam.CloseScope(); + } + externalSkinParam.CloseScope(); + } + } + else if (block is ClothBlock clsBlock) + { + if (externalSkinParam.OpenScope(clsBlock.Name)) + { + OsageInternalSkinParameter skp = new OsageInternalSkinParameter(); + + if (externalSkinParam.OpenScope("root")) + { + skp.AirResistance = externalSkinParam.Get("air_res"); + externalSkinParam.Enumerate("coli", i => + { + OsageInternalCollisionParameter coll = new OsageInternalCollisionParameter(); + coll.CollisionType = (OsageInternalCollisionType)externalSkinParam.Get("type"); + coll.CollisionRadius = externalSkinParam.Get("radius"); + if (externalSkinParam.OpenScope("bone")) + { + if (externalSkinParam.OpenScope(0)) + { + coll.Head = (uint)configuration?.BoneData.Skeletons[0].ObjectBoneNames.FindIndex(x => x == externalSkinParam.Get("name")); + coll.HeadPosition = new Vector3( + externalSkinParam.Get("posx"), + externalSkinParam.Get("posy"), + externalSkinParam.Get("posz")); + externalSkinParam.CloseScope(); + } + if (externalSkinParam.OpenScope(1)) + { + coll.Tail = (uint)configuration?.BoneData.Skeletons[0].ObjectBoneNames.FindIndex(x => x == externalSkinParam.Get("name")); + coll.TailPosition = new Vector3( + externalSkinParam.Get("posx"), + externalSkinParam.Get("posy"), + externalSkinParam.Get("posz")); + externalSkinParam.CloseScope(); + } + externalSkinParam.CloseScope(); + } + skp.Collisions.Add(coll); + }); + skp.CollisionRadius = externalSkinParam.Get("coli_r"); + skp.Force = externalSkinParam.Get("force"); + skp.ForceGain = externalSkinParam.Get("force_gain"); + skp.Friction = externalSkinParam.Get("friction"); + skp.HingeY = externalSkinParam.Get("hinge_y"); + skp.HingeY = externalSkinParam.Get("hinge_z"); + skp.Name = clsBlock.Name; + skp.RotationY = externalSkinParam.Get("rot_y"); + skp.RotationZ = externalSkinParam.Get("rot_z"); + skp.WindAffection = externalSkinParam.Get("wind_afc"); + clsBlock.InternalSkinParameter = skp; + externalSkinParam.CloseScope(); + } + externalSkinParam.CloseScope(); + } + } + } + } + } + }, Keys.None, CustomHandlerFlags.ClearMementos | CustomHandlerFlags.Repopulate); + + AddCustomHandlerSeparator(); + AddCustomHandler("Import ex data from JSON", () => { //var skin = PrompImportExData();