diff --git a/OpenKh.AssimpUtils/Kh2MdlxAssimp.cs b/OpenKh.AssimpUtils/Kh2MdlxAssimp.cs index 218c4c87f..d7a7882ab 100644 --- a/OpenKh.AssimpUtils/Kh2MdlxAssimp.cs +++ b/OpenKh.AssimpUtils/Kh2MdlxAssimp.cs @@ -3,6 +3,7 @@ using OpenKh.Kh2.Models; using OpenKh.Kh2.Models.VIF; using System.Numerics; +using OpenKh.Engine.Monogame.Helpers; namespace OpenKh.AssimpUtils { @@ -16,15 +17,7 @@ public static Assimp.Scene getAssimpScene(MdlxParser mParser) Assimp.Scene scene = AssimpGeneric.GetBaseScene(); // Divide the model in a mesh per texture and each mesh in its meshDescriptors - Dictionary> dic_meshDescriptorsByMeshId = new Dictionary>(); - foreach (MeshDescriptor meshDesc in mParser.MeshDescriptors) - { - if (!dic_meshDescriptorsByMeshId.ContainsKey(meshDesc.TextureIndex)) - { - dic_meshDescriptorsByMeshId.Add(meshDesc.TextureIndex, new List()); - } - dic_meshDescriptorsByMeshId[meshDesc.TextureIndex].Add(meshDesc); - } + Dictionary> dic_meshDescriptorsByMeshId = Kh2MdlxAssimp.getMeshDescriptos(mParser); // MESHES AND THEIR DATA for (int i = 0; i < dic_meshDescriptorsByMeshId.Keys.Count; i++) @@ -59,6 +52,7 @@ public static Assimp.Scene getAssimpScene(MdlxParser mParser) scene.RootNode.Children.Add(iMeshNode); } + mParser.skinnedBones = new bool[mParser.Bones.Count]; // VERTICES for (int i = 0; i < dic_meshDescriptorsByMeshId.Keys.Count; i++) { @@ -76,6 +70,7 @@ public static Assimp.Scene getAssimpScene(MdlxParser mParser) foreach (var boneAssign in boneAssigns) { Assimp.Bone bone = AssimpGeneric.FindBone(iMesh.Bones, "Bone" + boneAssign.MatrixIndex); + mParser.skinnedBones[boneAssign.MatrixIndex] = true; bone.VertexWeights.Add(new Assimp.VertexWeight(currentVertex, boneAssign.Weight)); } @@ -125,7 +120,7 @@ public static Assimp.Scene getAssimpScene(MdlxParser mParser) parentNode.Children.Add(boneNode); } - ComputeMatrices(ref matricesToReverse, mParser); + MatrixRecursivity.ComputeMatrices(ref matricesToReverse, mParser); foreach (Assimp.Mesh mesh in scene.Meshes) { @@ -138,50 +133,18 @@ public static Assimp.Scene getAssimpScene(MdlxParser mParser) return scene; } - public static void ReverseMatrices(ref List matrices, MdlxParser mParser) - { - bool[] treatedMatrices = new bool[mParser.Bones.Count]; - - int dirtyCount; - do - { - dirtyCount = mParser.Bones.Count; - for (int b = 0; b < mParser.Bones.Count; b++) - { - if (treatedMatrices[b] == false) - { - int remainingToTreat = 0; - for (int j = 0; j < mParser.Bones.Count; j++) - { - if (mParser.Bones[j].Parent == b && treatedMatrices[j] == false) - remainingToTreat++; - } - if (remainingToTreat == 0) /* Children's children are all calculated. */ - { - if (mParser.Bones[b].Parent > -1) - { - matrices[b] *= Microsoft.Xna.Framework.Matrix.Invert(matrices[mParser.Bones[b].Parent]); - } - treatedMatrices[b] = true; - dirtyCount--; - } - } - else - dirtyCount--; - } - } - while (dirtyCount > 0); - } - - public static void ComputeMatrices(ref List matrices, MdlxParser mParser) + public static Dictionary> getMeshDescriptos(MdlxParser mParser) { - for (int b = 0; b < mParser.Bones.Count; b++) + var output = new Dictionary>(); + foreach (MeshDescriptor meshDesc in mParser.MeshDescriptors) { - if (mParser.Bones[b].Parent > -1) + if (!output.ContainsKey(meshDesc.TextureIndex)) { - matrices[b] *= matrices[mParser.Bones[b].Parent]; + output.Add(meshDesc.TextureIndex, new List()); } + output[meshDesc.TextureIndex].Add(meshDesc); } + return output; } public static Assimp.Scene getAssimpScene(ModelSkeletal model) diff --git a/OpenKh.Engine.MonoGame/Helpers/MatrixRecursivity.cs b/OpenKh.Engine.MonoGame/Helpers/MatrixRecursivity.cs new file mode 100644 index 000000000..4577c8553 --- /dev/null +++ b/OpenKh.Engine.MonoGame/Helpers/MatrixRecursivity.cs @@ -0,0 +1,92 @@ +using OpenKh.Engine.Parsers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenKh; +using System.Numerics; + +namespace OpenKh.Engine.Monogame.Helpers +{ + public class MatrixRecursivity + { + public static void ReverseMatrices(ref List matrices, MdlxParser mParser) + { + bool[] treatedMatrices = new bool[mParser.Bones.Count]; + + int awaitingReverseCount; + do + { + awaitingReverseCount = mParser.Bones.Count; + for (int b = 0; b < mParser.Bones.Count; b++) + { + if (treatedMatrices[b] == false) + { + int awaitingReverseChildrenCount = 0; + for (int j = 0; j < mParser.Bones.Count; j++) + if (mParser.Bones[j].Parent == b && treatedMatrices[j] == false) + awaitingReverseChildrenCount++; + if (awaitingReverseChildrenCount == 0) + { + if (mParser.Bones[b].Parent > -1) + matrices[b] *= Microsoft.Xna.Framework.Matrix.Invert(matrices[mParser.Bones[b].Parent]); + treatedMatrices[b] = true; + awaitingReverseCount--; + } + } + else + awaitingReverseCount--; + } + } + while (awaitingReverseCount > 0); + } + + public static void ComputeMatrices(ref List matrices, MdlxParser mParser) + { + for (int b = 0; b < mParser.Bones.Count; b++) + if (mParser.Bones[b].Parent > -1) + matrices[b] *= matrices[mParser.Bones[b].Parent]; + } + + public static int LocateMostSimilarPose(List matrices, Matrix4x4[] Fk, MdlxParser mParser, float similarity, bool skinnedMatricesOnly) + { + if (Fk.Length != mParser.Bones.Count) + throw new Exception("Incorrect input pose matrices."); + Microsoft.Xna.Framework.Vector3[] scales = new Microsoft.Xna.Framework.Vector3[mParser.Bones.Count]; + Microsoft.Xna.Framework.Quaternion[] rotates = new Microsoft.Xna.Framework.Quaternion[mParser.Bones.Count]; + Microsoft.Xna.Framework.Vector3[] translates = new Microsoft.Xna.Framework.Vector3[mParser.Bones.Count]; + + for (int j = 0; j < Fk.Length; j++) + { + Fk[j].ToXnaMatrix().Decompose(out scales[j], out rotates[j], out translates[j]); + rotates[j].Normalize(); + } + float smallestDist = Single.MaxValue; + int position = (matrices.Count/ mParser.Bones.Count)-1; + for (int i=0; i< matrices.Count; i+= mParser.Bones.Count) + { + float currentDist = 0; + for (int j = 0; j < mParser.Bones.Count; j++) + { + if (skinnedMatricesOnly && mParser.skinnedBones[j] == false) continue; + Microsoft.Xna.Framework.Vector3 scale; + Microsoft.Xna.Framework.Quaternion rotate; + Microsoft.Xna.Framework.Vector3 translate; + matrices[i + j].Decompose(out scale, out rotate, out translate); + rotate.Normalize(); + currentDist += Microsoft.Xna.Framework.Vector3.Distance(scale, scales[j]); + currentDist += (float)(Math.Abs(rotate.X - rotates[j].X) + Math.Abs(rotate.Y - rotates[j].Y) + Math.Abs(rotate.Z - rotates[j].Z) + Math.Abs(rotate.W - rotates[j].W)); + currentDist += Microsoft.Xna.Framework.Vector3.Distance(translate, translates[j]); + } + if (currentDist < similarity && currentDist < smallestDist) + { + smallestDist = currentDist; + position = i / mParser.Bones.Count; + } + } + Console.WriteLine(smallestDist); + return position; + } + } +} diff --git a/OpenKh.Tools.Kh2MsetMotionEditor/Helpers/NumericsToXnaExtensions.cs b/OpenKh.Engine.MonoGame/Helpers/NumericsToXnaExtensions.cs similarity index 82% rename from OpenKh.Tools.Kh2MsetMotionEditor/Helpers/NumericsToXnaExtensions.cs rename to OpenKh.Engine.MonoGame/Helpers/NumericsToXnaExtensions.cs index a6404115b..1ebadb41b 100644 --- a/OpenKh.Tools.Kh2MsetMotionEditor/Helpers/NumericsToXnaExtensions.cs +++ b/OpenKh.Engine.MonoGame/Helpers/NumericsToXnaExtensions.cs @@ -6,9 +6,9 @@ using System.Threading.Tasks; using System.Numerics; -namespace OpenKh.Tools.Kh2MsetMotionEditor.Helpers +namespace OpenKh.Engine.Monogame.Helpers { - internal static class NumericsToXnaExtensions + public static class NumericsToXnaExtensions { public static xna.Matrix ToXnaMatrix(this Matrix4x4 it) => new xna.Matrix( it.M11, it.M12, it.M13, it.M14, diff --git a/OpenKh.Engine/Parsers/MdlxParser.cs b/OpenKh.Engine/Parsers/MdlxParser.cs index fe4db4657..1879c389c 100644 --- a/OpenKh.Engine/Parsers/MdlxParser.cs +++ b/OpenKh.Engine/Parsers/MdlxParser.cs @@ -74,6 +74,7 @@ public class MeshDescriptor public class MdlxParser : IModelMotion { private readonly Kkdf2MdlxParser _parsedModel; + public bool[] skinnedBones; public MdlxParser(Mdlx mdlx) { diff --git a/OpenKh.Tools.Kh2MsetMotionEditor/App.cs b/OpenKh.Tools.Kh2MsetMotionEditor/App.cs index fe09b47dd..e8516c04a 100644 --- a/OpenKh.Tools.Kh2MsetMotionEditor/App.cs +++ b/OpenKh.Tools.Kh2MsetMotionEditor/App.cs @@ -10,6 +10,7 @@ using OpenKh.Tools.Kh2MsetMotionEditor.Interfaces; using OpenKh.Tools.Kh2MsetMotionEditor.Usecases; using OpenKh.Tools.Kh2MsetMotionEditor.Windows; +using OpenKh.Engine.Monogame.Helpers; using System; using System.Collections.Generic; using System.IO; @@ -354,6 +355,8 @@ private void ExportFBXCOLLADA(AssimpGeneric.FileFormat fileFormat) var motionData = _loadedModel.MotionData; if (motionData != null) { + List allMatrices = new List(0); + int offset = (int)((_loadedModel.FrameEnd * 2) - motionData.InterpolatedMotionHeader.FrameCount); Animation animation = new Animation(); animation.Name = selectedMotionName; animation.TicksPerSecond = fileFormat == AssimpGeneric.FileFormat.fbx ? 24f : 30f; @@ -366,11 +369,12 @@ private void ExportFBXCOLLADA(AssimpGeneric.FileFormat fileFormat) animation.NodeAnimationChannels.Add(nodeAnimationChannel); } + for (int f = 0; f < motionData.InterpolatedMotionHeader.FrameCount; f++) { float timeKey = (float)((1 / animation.TicksPerSecond) * f); - var fkIk = _loadedModel.PoseProvider?.Invoke((float)f); + var fkIk = _loadedModel.PoseProvider?.Invoke((float)(offset+f)); List matrices = new List(0); List dirtyMatrices = new List(0); @@ -386,7 +390,8 @@ private void ExportFBXCOLLADA(AssimpGeneric.FileFormat fileFormat) matrices.Add(fkIk.Fk[mIndex].ToXnaMatrix()); } } - Kh2MdlxAssimp.ReverseMatrices(ref matrices, mParser); + allMatrices.AddRange(matrices); + MatrixRecursivity.ReverseMatrices(ref matrices, mParser); for (int b = 0; b < mParser.Bones.Count; b++) { @@ -403,11 +408,18 @@ private void ExportFBXCOLLADA(AssimpGeneric.FileFormat fileFormat) } scene.Animations.Add(animation); - } + int loopFrame = MatrixRecursivity.LocateMostSimilarPose(allMatrices, _loadedModel.PoseProvider?.Invoke((float)(offset + motionData.InterpolatedMotionHeader.FrameCount)).Fk, mParser, 2f, true); - AssimpGeneric.ExportScene(scene, fileFormat, filename); - OpenKh.Tools.Kh2MdlxEditor.Views.Main_Window.exportTextures(_loadedModel.MdlxRenderableList[0].Textures, dirPath); - System.Windows.Forms.MessageBox.Show("Export complete.", "Complete", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information); + string loopFrame_ = loopFrame.ToString(); + if (loopFrame == 0) + loopFrame_ = "BEGIN"; + else if(loopFrame == motionData.InterpolatedMotionHeader.FrameCount-1) + loopFrame_ = "END"; + + AssimpGeneric.ExportScene(scene, fileFormat, Path.GetDirectoryName(filename)+@"\" + Path.GetFileNameWithoutExtension(filename) +"_LoopAt("+ loopFrame_ + ")"+Path.GetExtension(filename)); + OpenKh.Tools.Kh2MdlxEditor.Views.Main_Window.exportTextures(_loadedModel.MdlxRenderableList[0].Textures, dirPath); + System.Windows.Forms.MessageBox.Show("Export complete.", "Complete", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information); + } } } } @@ -418,7 +430,7 @@ private void ExportFBXCOLLADA(AssimpGeneric.FileFormat fileFormat) }, FileDialogFilterComposer.Compose() .AddExtensions("Autodesk FBX ", "fbx"), - _loadedModel.MdlxFile + "-" + selectedMotionName.Replace(" ", "_") + "." + AssimpGeneric.GetFormatFileExtension(fileFormat) + Path.GetFileNameWithoutExtension(_loadedModel.MdlxFile) + "-" + selectedMotionName.Replace(" ", "_") + "." + AssimpGeneric.GetFormatFileExtension(fileFormat) ); } } diff --git a/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/InsideTools/MotionPlayerToolUsecase.cs b/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/InsideTools/MotionPlayerToolUsecase.cs index 5051620c6..c1787cc97 100644 --- a/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/InsideTools/MotionPlayerToolUsecase.cs +++ b/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/InsideTools/MotionPlayerToolUsecase.cs @@ -43,7 +43,7 @@ public Action CreateToolRunnable() var lastKeyTime = 60f / _loadedModel.FramePerSecond * _loadedModel.FrameEnd; - if (lastKeyTime < frame) + if (frame> lastKeyTime) { frame -= lastKeyTime - (60f / _loadedModel.FramePerSecond * _loadedModel.FrameLoop); } diff --git a/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/RenderModelUsecase.cs b/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/RenderModelUsecase.cs index 353ea28b4..982f36ba9 100644 --- a/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/RenderModelUsecase.cs +++ b/OpenKh.Tools.Kh2MsetMotionEditor/Usecases/RenderModelUsecase.cs @@ -6,6 +6,7 @@ using OpenKh.Engine.Parsers; using OpenKh.Kh2; using OpenKh.Tools.Kh2MsetMotionEditor.Helpers; +using OpenKh.Engine.Monogame.Helpers; using System; using System.Collections.Generic; using System.Drawing;