From 65aa3598a2725892e7c625cd3161e0fa82f7826f Mon Sep 17 00:00:00 2001 From: KillzXGaming Date: Sun, 12 Jan 2020 17:39:47 -0500 Subject: [PATCH] BFRES : Add JSON/YAML converting with scene animations. --- .../BFRES/Bfres Structs/SubFiles/FSCN.cs | 30 ++- .../File_Format_Library.csproj | 1 + .../GUI/BFRES/Shape/BfresShapeEditor.cs | 2 + .../NodeWrappers/FileFilters.cs | 6 +- File_Format_Library/YAML/YamlFscn.cs | 239 ++++++++++++++++++ 5 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 File_Format_Library/YAML/YamlFscn.cs diff --git a/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FSCN.cs b/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FSCN.cs index 820deed8..ea153c2e 100644 --- a/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FSCN.cs +++ b/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FSCN.cs @@ -85,12 +85,38 @@ namespace Bfres.Structs public override void Export(string FileName) { - SceneAnim.Export(FileName, ((BFRESGroupNode)Parent).GetResFile()); + string ext = Utils.GetExtension(FileName); + + if (ext == ".bfscn") + { + SceneAnim.Export(FileName, ((BFRESGroupNode)Parent).GetResFile()); + } + else if (ext == ".yaml") + { + System.IO.File.WriteAllText(FileName, YamlFscn.ToYaml(FileName, SceneAnim)); + } + else if (ext == ".json") + { + System.IO.File.WriteAllText(FileName, YamlFscn.ToJson(FileName, SceneAnim)); + } } public override void Replace(string FileName) { - Replace(FileName, GetResFile(), GetResFileU()); + string ext = Utils.GetExtension(FileName); + + if (ext == ".bfscn") + { + Replace(FileName, GetResFile(), GetResFileU()); + } + else if (ext == ".yaml") + { + SceneAnim = YamlFscn.FromYaml(FileName); + } + else if (ext == ".json") + { + SceneAnim = YamlFscn.FromJson(FileName); + } } public void Replace(string FileName, ResFile resFileNX, ResU.ResFile resFileU) diff --git a/File_Format_Library/File_Format_Library.csproj b/File_Format_Library/File_Format_Library.csproj index ac638b36..a8e52894 100644 --- a/File_Format_Library/File_Format_Library.csproj +++ b/File_Format_Library/File_Format_Library.csproj @@ -1454,6 +1454,7 @@ + diff --git a/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs b/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs index e960c32a..62720b62 100644 --- a/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs +++ b/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs @@ -141,6 +141,8 @@ namespace FirstPlugin private void GetShapeTransform() { + if (activeShape.boundingBoxes.Count == 0) return; + Vector3 translate = new Vector3(0); Vector3 scale = new Vector3(1); Vector4 rotate = new Vector4(0); diff --git a/File_Format_Library/NodeWrappers/FileFilters.cs b/File_Format_Library/NodeWrappers/FileFilters.cs index 025b8f01..9bae082f 100644 --- a/File_Format_Library/NodeWrappers/FileFilters.cs +++ b/File_Format_Library/NodeWrappers/FileFilters.cs @@ -38,10 +38,10 @@ namespace FirstPlugin public static string FTXP = GetFilter(".bftxp", ".yaml", ".gif"); public static string FMTV = GetFilter(".bfmvi"); public static string FBNV = GetFilter(".bfbvi"); - public static string FSCN = GetFilter(".bfscn"); + public static string FSCN = GetFilter(".bfscn", ".yaml", ".json"); public static string FSHA = GetFilter(".bfspa"); - public static string CMDL = GetFilter(".dae"); + public static string CMDL = GetFilter(".dae", ".cmdl"); public static string NUTEXB = GetFilter(".dds",".png", ".bmp", ".tga", ".jpg", ".tiff", ".tif", ".gif"); public static string XTX = GetFilter(".dds", ".png", ".bmp", ".tga", ".jpg", ".tiff", ".tif", ".gif"); @@ -61,6 +61,7 @@ namespace FirstPlugin else if (type == typeof(FTXP)) return FTXP; else if (type == typeof(FSHA)) return FSHA; else if (type == typeof(FTEX)) return FTEX; + else if (type == typeof(FSCN)) return FSCN; else if (type == typeof(FSHU)) { if (IsExporting) @@ -137,6 +138,7 @@ namespace FirstPlugin case ".anim": filters.Add(ext, "Maya Animation"); break; case ".yaml": filters.Add(ext, "Yet Another Markup Language"); break; case ".gif": filters.Add(ext, "Graphics Interchange Format"); break; + case ".cmdl": filters.Add(ext, "CTR Model"); break; default: filters.Add(ext, ""); break; } diff --git a/File_Format_Library/YAML/YamlFscn.cs b/File_Format_Library/YAML/YamlFscn.cs new file mode 100644 index 00000000..30689513 --- /dev/null +++ b/File_Format_Library/YAML/YamlFscn.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpYaml; +using SharpYaml.Events; +using SharpYaml.Serialization; +using SharpYaml.Serialization.Serializers; +using Syroot.NintenTools.NSW.Bfres; +using Newtonsoft.Json; + +namespace FirstPlugin +{ + public class YamlFscn + { + public static string ToJson(string Name, SceneAnim sceneAnim) + { + var config = new AnimConfig(); + config.ToYaml(sceneAnim); + return JsonConvert.SerializeObject(config, Formatting.Indented); + } + + public static SceneAnim FromJson(string Name) + { + AnimConfig config = JsonConvert.DeserializeObject( + System.IO.File.ReadAllText(Name)); + + return config.FromYaml(); + } + + public static string ToYaml(string Name, SceneAnim sceneAnim) + { + var serializerSettings = new SerializerSettings() + { + // EmitTags = false + }; + + serializerSettings.DefaultStyle = YamlStyle.Any; + serializerSettings.ComparerForKeySorting = null; + serializerSettings.RegisterTagMapping("AnimConfig", typeof(AnimConfig)); + + var config = new AnimConfig(); + config.ToYaml(sceneAnim); + + var serializer = new Serializer(serializerSettings); + string yaml = serializer.Serialize(config, typeof(AnimConfig)); + + return yaml; + } + + public static SceneAnim FromYaml(string Name) + { + var serializerSettings = new SerializerSettings() + { + // EmitTags = false + }; + + serializerSettings.DefaultStyle = YamlStyle.Any; + serializerSettings.ComparerForKeySorting = null; + serializerSettings.RegisterTagMapping("AnimConfig", typeof(AnimConfig)); + + var serializer = new Serializer(serializerSettings); + AnimConfig config = serializer.Deserialize(System.IO.File.ReadAllText(Name)); + + return config.FromYaml(); + } + + public class AnimConfig + { + [JsonProperty(Order = 0)] + public string Name { get; set; } + [JsonProperty(Order = 1)] + public string Path { get; set; } + + [JsonProperty(Order = 2)] + public List CameraAnimations = new List(); + + public void ToYaml(SceneAnim sceneAnim) + { + Name = sceneAnim.Name; + Path = sceneAnim.Path; + + foreach (var cameraAnim in sceneAnim.CameraAnims) + CameraAnimations.Add(new CameraAnimConfig(cameraAnim)); + } + + public SceneAnim FromYaml() + { + SceneAnim anim = new SceneAnim(); + anim.Name = Name; + anim.Path = Path; + foreach (var camera in CameraAnimations) + { + CameraAnim camAnim = new CameraAnim(); + camAnim.Name = camera.Name; + camAnim.FrameCount = camera.FrameCount; + camAnim.BaseData = camera.BaseData; + if (camera.EulerZXY) + camAnim.Flags |= CameraAnimFlags.EulerZXY; + if (camera.Loop) + camAnim.Flags |= CameraAnimFlags.Looping; + if (camera.Perspective) + camAnim.Flags |= CameraAnimFlags.Perspective; + + foreach (var curve in camera.Curves) { + AnimCurve animCurve = new AnimCurve(); + animCurve.AnimDataOffset = ConvertOffset(curve.Type); + animCurve.Scale = 1; + animCurve.CurveType = AnimCurveType.Linear; + + int FrameCount = curve.KeyFrames.Count; + animCurve.Frames = new float[FrameCount]; + animCurve.Keys = new float[FrameCount, 2]; + + var values = curve.KeyFrames.Values.ToList(); + for (int i = 0; i < curve.KeyFrames.Count; i++) + { + var KeyFrame = curve.KeyFrames.ElementAt(i); + animCurve.Frames[i] = KeyFrame.Key; + animCurve.Keys[i, 0] = KeyFrame.Value; + + //Calculate delta + float Delta = 0; + if (i < values.Count - 1) + Delta = values[i + 1] - values[i]; + + animCurve.Keys[i, 1] = Delta; + } + + animCurve.EndFrame = animCurve.Frames.Max(); + if (animCurve.Keys.Length > 1) + animCurve.Delta = values[values.Count - 1] - values[0]; + + animCurve.KeyType = AnimCurveKeyType.Single; + animCurve.FrameType = AnimCurveFrameType.Single; + + + camAnim.Curves.Add(animCurve); + } + + anim.CameraAnims.Add(camAnim); + anim.CameraAnimDict.Add(camAnim.Name); + } + + return anim; + } + + private static uint ConvertOffset(string type) + { + CameraOffsetType flags; + bool isValid = Enum.TryParse(type, out flags); + if (!isValid) + throw new Exception($"Invalid camera curve type {type}!"); + + return (uint)flags; + } + } + + public class CameraAnimConfig + { + [JsonProperty(Order = 0)] + public string Name { get; set; } + + [JsonProperty(Order = 1)] + public bool Loop { get; set; } + + [JsonProperty(Order = 2)] + public int FrameCount { get; set; } + + [JsonProperty(Order = 3)] + public bool EulerZXY { get; set; } + + [JsonProperty(Order = 4)] + public bool Perspective { get; set; } + + [JsonProperty(Order = 5)] + public CameraAnimData BaseData { get; set; } + + [JsonProperty(Order = 6)] + public List Curves = new List(); + + public CameraAnimConfig() { } + + public CameraAnimConfig(CameraAnim camAnim) + { + Name = camAnim.Name; + Loop = camAnim.Flags.HasFlag(CameraAnimFlags.Looping); + EulerZXY = camAnim.Flags.HasFlag(CameraAnimFlags.EulerZXY); + Perspective = camAnim.Flags.HasFlag(CameraAnimFlags.Perspective); + + BaseData = camAnim.BaseData; + + foreach (var curve in camAnim.Curves) + Curves.Add(new CameraAnimCurve(curve)); + } + } + + public class CameraAnimCurve + { + [JsonProperty(Order = 0)] + public string Type; + + [JsonProperty(Order = 1)] + public Dictionary KeyFrames { get; set; } + + public CameraAnimCurve() { } + + public CameraAnimCurve(AnimCurve curve) { + KeyFrames = new Dictionary(); + + Type = ((CameraOffsetType)curve.AnimDataOffset).ToString(); + + for (int f = 0; f < curve.Frames.Length; f++) + { + int frame = (int)curve.Frames[f]; + float Value = curve.Offset + curve.Keys[f, 0] * curve.Scale; + KeyFrames.Add(frame, Value); + } + } + } + + //Offsets to get data from "Values" + public enum CameraOffsetType + { + ClipNear = 0, + ClipFar = 4, + AspectRatio = 8, + FieldOFView = 12, + PositionX = 16, + PositionY = 20, + PositionZ = 24, + RotationX = 28, + RotationY = 32, + RotationZ = 36, + Twist = 40, + } + } +}