using System; using System.Collections.Generic; using System.Linq; using Syroot.NintenTools.NSW.Bfres; using Syroot.NintenTools.NSW.Bfres.Helpers; using OpenTK; using System.Windows.Forms; using Switch_Toolbox.Library; using Switch_Toolbox.Library.Rendering; using WeifenLuo.WinFormsUI.Docking; using Switch_Toolbox.Library.IO; using Switch_Toolbox.Library.Forms; using ResU = Syroot.NintenTools.Bfres; using ResUGX2 = Syroot.NintenTools.Bfres.GX2; using ResGFX = Syroot.NintenTools.NSW.Bfres.GFX; using FirstPlugin; namespace Bfres.Structs { public class Misc { public static List HackyTextureList = new List(new string[] { "Alb", "alb", "Base", "base", "bonbon.167300917","Eye.00","EyeIce.00", "FaceDummy", "Eye01.17", "Dee.00", "rainbow.758540574", "Mucus._1700670200", "Eye.11", "CapTail00","eye.0","pallet_texture","Mark.930799313","InEye.1767598300","Face.00", "ThunderHair_Thunder_BaseColor.1751853236","FireHair_Thunder_BaseColor._162539711","IceHair_Thunder_BaseColor.674061150","BodyEnemy.1866226988", "Common_Scroll01._13827715" }); } public class ResourceFile : TreeNodeFile { public BFRESRender BFRESRender; public TreeNode TextureFolder = new TreeNode("Textures"); public ResourceFile(IFileFormat handler) { ImageKey = "bfres"; SelectedImageKey = "bfres"; FileHandler = handler; ContextMenu = new ContextMenu(); MenuItem save = new MenuItem("Save"); ContextMenu.MenuItems.Add(save); save.Click += Save; MenuItem newMenu = new MenuItem("New"); MenuItem import = new MenuItem("Import"); // ContextMenu.MenuItems.Add(newMenu); // ContextMenu.MenuItems.Add(import); MenuItem rename = new MenuItem("Rename"); ContextMenu.MenuItems.Add(rename); rename.Click += Rename; MenuItem remove = new MenuItem("Remove"); ContextMenu.MenuItems.Add(remove); remove.Click += Remove; if (Parent == null) remove.Enabled = false; if (BFRES.IsWiiU) { } else { MenuItem model = new MenuItem("Model"); MenuItem fska = new MenuItem("Skeletal Animation"); MenuItem fmaa = new MenuItem("Material Animation"); MenuItem bonevis = new MenuItem("Bone Visual Animation"); MenuItem shape = new MenuItem("Shape Animation"); MenuItem scene = new MenuItem("Scene Animation"); MenuItem embedded = new MenuItem("Embedded File"); MenuItem texture = new MenuItem("Texture File"); texture.Click += NewTextureFile; newMenu.MenuItems.Add(model); newMenu .MenuItems.Add(fska); newMenu.MenuItems.Add(fmaa); newMenu.MenuItems.Add(bonevis); newMenu.MenuItems.Add(shape); newMenu.MenuItems.Add(scene); newMenu.MenuItems.Add(embedded); newMenu.MenuItems.Add(texture); MenuItem importmodel = new MenuItem("Model"); MenuItem importfska = new MenuItem("Skeletal Animation"); MenuItem importfmaa = new MenuItem("Material Animation"); MenuItem importbonevis = new MenuItem("Bone Visual Animation"); MenuItem importshape = new MenuItem("Shape Animation"); MenuItem importscene = new MenuItem("Scene Animation"); MenuItem importembedded = new MenuItem("Embedded File"); MenuItem importtexture = new MenuItem("Texture File"); import.MenuItems.Add(importmodel); import.MenuItems.Add(importfska); import.MenuItems.Add(importfmaa); import.MenuItems.Add(importbonevis); import.MenuItems.Add(importshape); import.MenuItems.Add(importscene); import.MenuItems.Add(importembedded); import.MenuItems.Add(importtexture); } } public override void OnClick(TreeView treeView) { //If has models if (Nodes.ContainsKey("FMDLFolder")) { if (Nodes["FMDLFolder"].Nodes.ContainsKey("FshpFolder")) { } BFRESRender.LoadViewport(); BFRESRender.UpdateVertexData(); } } public void Load(ResU.ResFile resFile) { Text = resFile.Name; if (resFile.Models.Count > 0) Nodes.Add(new FmdlFolder()); if (resFile.Textures.Count > 0) AddFTEXTextures(resFile); if (resFile.SkeletalAnims.Count > 0) AddSkeletonAnims(resFile); if (resFile.ShaderParamAnims.Count > 0) Nodes.Add(new FshaFolder()); if (resFile.ColorAnims.Count > 0) Nodes.Add(new FshaColorFolder()); if (resFile.TexSrtAnims.Count > 0) Nodes.Add(new TexSrtFolder()); if (resFile.TexPatternAnims.Count > 0) Nodes.Add(new TexPatFolder()); if (resFile.ShapeAnims.Count > 0) Nodes.Add(new FshpaFolder()); if (resFile.BoneVisibilityAnims.Count > 0) Nodes.Add(new FbnvFolder()); if (resFile.SceneAnims.Count > 0) Nodes.Add(new FscnFolder()); if (resFile.ExternalFiles.Count > 0) Nodes.Add(new EmbeddedFilesFolder()); foreach (var anim in resFile.ShaderParamAnims) Nodes["FSHA"].Nodes.Add(anim.Key); foreach (var anim in resFile.ColorAnims) Nodes["FSHAColor"].Nodes.Add(anim.Key); foreach (var anim in resFile.TexSrtAnims) Nodes["TEXSRT"].Nodes.Add(anim.Key); foreach (var anim in resFile.TexPatternAnims) Nodes["TEXPAT"].Nodes.Add(anim.Key); int ext = 0; foreach (var extfile in resFile.ExternalFiles) { string Name = extfile.Key; FileReader f = new FileReader(extfile.Value.Data); string Magic = f.ReadMagic(0, 4); if (Magic == "FSHA") { Nodes["EXT"].Nodes.Add(new BfshaFileData(extfile.Value.Data, Name)); } else Nodes["EXT"].Nodes.Add(new ExternalFileData(extfile.Value.Data, Name)); f.Dispose(); f.Close(); ext++; } } public void Load(ResFile resFile) { Text = resFile.Name; UpdateTree(resFile); foreach (MaterialAnim anim in resFile.MaterialAnims) Nodes["FMAA"].Nodes.Add(anim.Name); foreach (ShapeAnim anim in resFile.ShapeAnims) Nodes["FSHPA"].Nodes.Add(anim.Name); foreach (VisibilityAnim anim in resFile.BoneVisibilityAnims) Nodes["FBNV"].Nodes.Add(anim.Name); foreach (SceneAnim anim in resFile.SceneAnims) Nodes["FSCN"].Nodes.Add(anim.Name); int ext = 0; foreach (ExternalFile extfile in resFile.ExternalFiles) { string Name = resFile.ExternalFileDict.GetKey(ext); FileReader f = new FileReader(extfile.Data); string Magic = f.ReadMagic(0, 4); if (Magic == "BNTX") { BinaryTextureContainer bntxTreeNode = new BinaryTextureContainer(extfile.Data, Name, resFile.Name); Nodes["EXT"].Nodes.Add(bntxTreeNode); } else if (Magic == "FSHA") { Nodes["EXT"].Nodes.Add(new BfshaFileData(extfile.Data, Name)); } else Nodes["EXT"].Nodes.Add(new ExternalFileData(extfile.Data, Name)); f.Dispose(); f.Close(); ext++; } } private void NewTextureFile(object sender, EventArgs args) { string Name = "textures"; for (int i = 0; i < BFRESRender.resFile.ExternalFiles.Count; i++) { if (BFRESRender.resFile.ExternalFileDict.GetKey(i) == Name) Name = Name + i; } if (!Nodes.ContainsKey("EXT")) { Nodes.Add(new EmbeddedFilesFolder()); } BNTX bntx = new BNTX(); bntx.Data = new byte[0]; BinaryTextureContainer bntxTreeNode = new BinaryTextureContainer(new byte[0], "textures", BFRESRender.resFile.Name); Nodes["EXT"].Nodes.Add(bntxTreeNode); } private void NewEmbeddedFile(object sender, EventArgs args) { } private void Save(object sender, EventArgs args) { BFRESRender.SaveFile(); } private void Rename(object sender, EventArgs args) { RenameDialog dialog = new RenameDialog(); dialog.SetString(Text); if (dialog.ShowDialog() == DialogResult.OK) { Text = dialog.textBox1.Text; } } private void Remove(object sender, EventArgs args) { BFRESRender.DisposeFile(); } private void UpdateTree(ResFile resFile) { if (resFile.Models.Count > 0) Nodes.Add(new FmdlFolder()); if (resFile.SkeletalAnims.Count > 0) AddSkeletonAnims(resFile); if (resFile.MaterialAnims.Count > 0) Nodes.Add(new FmmaFolder()); if (resFile.ShapeAnims.Count > 0) Nodes.Add(new FshpaFolder()); if (resFile.BoneVisibilityAnims.Count > 0) Nodes.Add(new FbnvFolder()); if (resFile.SceneAnims.Count > 0) Nodes.Add(new FscnFolder()); if (resFile.ExternalFiles.Count > 0) Nodes.Add(new EmbeddedFilesFolder()); } private void AddFTEXTextures(ResU.ResFile resFile) { FTEXContainer ftexContainer = new FTEXContainer(); foreach (ResU.Texture tex in resFile.Textures.Values) { string TextureName = tex.Name; FTEX texture = new FTEX(); texture.Read(tex); ftexContainer.Nodes.Add(texture); ftexContainer.Textures.Add(texture.Text, texture); } PluginRuntime.ftexContainers.Add(ftexContainer); Nodes.Add(ftexContainer); } private void AddSkeletonAnims(ResU.ResFile resFile) { FskaFolder FSKA = new FskaFolder(); FSKA.LoadAnimations(resFile, BFRESRender); Nodes.Add(FSKA); } private void AddSkeletonAnims(ResFile resFile) { FskaFolder FSKA = new FskaFolder(); FSKA.LoadAnimations(resFile, BFRESRender); Nodes.Add(FSKA); } } public class FskaFolder : TreeNodeCustom { public FskaFolder() { Text = "Skeleton Animations"; Name = "FSKA"; } public void LoadAnimations(ResU.ResFile resFile, BFRESRender BFRESRender) { foreach (var ska in resFile.SkeletalAnims.Values) { BfresSkeletonAnim skeletonAnim = new BfresSkeletonAnim(ska.Name); skeletonAnim.BFRESRender = BFRESRender; skeletonAnim.Read(ska, resFile); Nodes.Add(skeletonAnim); } } public void LoadAnimations(ResFile resFile, BFRESRender BFRESRender) { foreach (SkeletalAnim ska in resFile.SkeletalAnims) { BfresSkeletonAnim skeletonAnim = new BfresSkeletonAnim(ska.Name); skeletonAnim.BFRESRender = BFRESRender; skeletonAnim.Read(ska, resFile); Nodes.Add(skeletonAnim); } } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FmdlFolder : TreeNodeCustom { public FmdlFolder() { Text = "Models"; Name = "FMDLFolder"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FmmaFolder : TreeNodeCustom { public FmmaFolder() { Text = "Material Animations"; Name = "FMAA"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FshpaFolder : TreeNodeCustom { public FshpaFolder() { Text = "Shape Animations"; Name = "FSHPA"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FbnvFolder : TreeNodeCustom { public FbnvFolder() { Text = "Bone Visabilty Animations"; Name = "FBNV"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FscnFolder : TreeNodeCustom { public FscnFolder() { Text = "Scene Animations"; Name = "FSCN"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class EmbeddedFilesFolder : TreeNodeCustom { public EmbeddedFilesFolder() { Text = "Embedded Files"; Name = "EXT"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class TexPatFolder : TreeNodeCustom { public TexPatFolder() { Text = "Texture Pattern Animations"; Name = "TEXPAT"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class TexSrtFolder : TreeNodeCustom { public TexSrtFolder() { Text = "Texture SRT Animations"; Name = "TEXSRT"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FshaFolder : TreeNodeCustom { public FshaFolder() { Text = "Shader Parameter Animations"; Name = "FSHA"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class FshaColorFolder : TreeNodeCustom { public FshaColorFolder() { Text = "Color Animations"; Name = "FSHAColor"; } public override void OnClick(TreeView treeView) { FormLoader.LoadEditor(this, Text); } } public class BfshaFileData : TreeNode { public byte[] Data; public BfshaFileData(byte[] data, string Name) { Text = Name; ImageKey = "bfsha"; SelectedImageKey = "bfsha"; Data = data; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Import; } private void Import(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { Data = System.IO.File.ReadAllBytes(ofd.FileName); } } private void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "All files(*.*)|*.*"; sfd.DefaultExt = System.IO.Path.GetExtension(Text); sfd.FileName = Text; if (sfd.ShowDialog() == DialogResult.OK) { System.IO.File.WriteAllBytes(sfd.FileName, Data); } } } public class ExternalFileData : TreeNode { public byte[] Data; public ExternalFileData(byte[] data, string Name) { Text = Name; ImageKey = "folder"; Data = data; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Import; } private void Import(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { Data = System.IO.File.ReadAllBytes(ofd.FileName); } } private void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "All files(*.*)|*.*"; sfd.DefaultExt = System.IO.Path.GetExtension(Text); sfd.FileName = Text; if (sfd.ShowDialog() == DialogResult.OK) { System.IO.File.WriteAllBytes(sfd.FileName, Data); } } } public class BfresSkeletonAnim : Animation { public enum TrackType { XSCA = 0x4, YSCA = 0x8, ZSCA = 0xC, XPOS = 0x10, YPOS = 0x14, ZPOS = 0x18, XROT = 0x20, YROT = 0x24, ZROT = 0x28, } public SkeletalAnim SkeletalAnim; public BFRESRender BFRESRender; public BfresSkeletonAnim() { ImageKey = "skeletonAnimation"; SelectedImageKey = "skeletonAnimation"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; } public BfresSkeletonAnim(string name) { Text = name; ImageKey = "skeletonAnimation"; SelectedImageKey = "skeletonAnimation"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; } public void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Supported Formats|*.bfska;|" + "Bfres Object (shape/vertices) |*.bfska|" + "All files(*.*)|*.*"; sfd.FileName = Text; sfd.DefaultExt = ".bfska"; if (sfd.ShowDialog() == DialogResult.OK) { SkeletalAnim.Export(sfd.FileName, BFRESRender.resFile); } } public void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfska;|" + "Bfres Object (shape/vertices) |*.bfska|" + "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { SkeletalAnim.Import(ofd.FileName); } SkeletalAnim.Name = Text; } public static List SkeletonAnimations = new List(); public void Read(ResU.SkeletalAnim ska, ResU.ResFile b) { } public void Read(SkeletalAnim ska, ResFile b) { FrameCount = ska.FrameCount; SkeletalAnim = ska; foreach (BoneAnim bn in ska.BoneAnims) { FSKANode bonean = new FSKANode(bn); Animation.KeyNode bone = new Animation.KeyNode(""); Bones.Add(bone); if (ska.FlagsRotate == SkeletalAnimFlagsRotate.EulerXYZ) bone.RotType = Animation.RotationType.EULER; else bone.RotType = Animation.RotationType.QUATERNION; bone.Text = bonean.Text; for (int Frame = 0; Frame < ska.FrameCount; Frame++) { //Set base/start values for bones. //Note. BOTW doesn't use base values as it uses havok engine. Need to add option to disable these if (Frame == 0) { if (bn.FlagsBase.HasFlag(BoneAnimFlagsBase.Scale)) { bone.XSCA.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.sca.X }); bone.YSCA.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.sca.Y }); bone.ZSCA.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.sca.Z }); } if (bn.FlagsBase.HasFlag(BoneAnimFlagsBase.Rotate)) { bone.XROT.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.rot.X }); bone.YROT.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.rot.Y }); bone.ZROT.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.rot.Z }); bone.WROT.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.rot.W }); } if (bn.FlagsBase.HasFlag(BoneAnimFlagsBase.Translate)) { bone.XPOS.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.pos.X }); bone.YPOS.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.pos.Y }); bone.ZPOS.Keys.Add(new Animation.KeyFrame() { Frame = 0, Value = bonean.pos.Z }); } } foreach (FSKATrack track in bonean.tracks) { Animation.KeyFrame frame = new Animation.KeyFrame(); frame.InterType = Animation.InterpolationType.HERMITE; frame.Frame = Frame; FSKAKey left = track.GetLeft(Frame); FSKAKey right = track.GetRight(Frame); float value; value = Animation.Hermite(Frame, left.frame, right.frame, 0, 0, left.unk1, right.unk1); // interpolate the value and apply switch (track.flag) { case (int)TrackType.XPOS: frame.Value = value; bone.XPOS.Keys.Add(frame); break; case (int)TrackType.YPOS: frame.Value = value; bone.YPOS.Keys.Add(frame); break; case (int)TrackType.ZPOS: frame.Value = value; bone.ZPOS.Keys.Add(frame); break; case (int)TrackType.XROT: frame.Value = value; bone.XROT.Keys.Add(frame); break; case (int)TrackType.YROT: frame.Value = value; bone.YROT.Keys.Add(frame); break; case (int)TrackType.ZROT: frame.Value = value; bone.ZROT.Keys.Add(frame); break; case (int)TrackType.XSCA: frame.Value = value; bone.XSCA.Keys.Add(frame); break; case (int)TrackType.YSCA: frame.Value = value; bone.YSCA.Keys.Add(frame); break; case (int)TrackType.ZSCA: frame.Value = value; bone.ZSCA.Keys.Add(frame); break; } } } } } public class FSKANode { public int flags; public int flags2; public int stride; public int BeginRotate; public int BeginTranslate; public long offBase; public int trackCount; public int trackFlag; public long offTrack; public string Text; public Vector3 sca, pos; public Vector4 rot; public List tracks = new List(); public FSKANode(BoneAnim b) { Text = b.Name; if (b.BaseData.Scale != Syroot.Maths.Vector3F.Zero) sca = new Vector3(b.BaseData.Scale.X, b.BaseData.Scale.Y, b.BaseData.Scale.Z); if (b.BaseData.Rotate != Syroot.Maths.Vector4F.Zero) rot = new Vector4(b.BaseData.Rotate.X, b.BaseData.Rotate.Y, b.BaseData.Rotate.Z, b.BaseData.Rotate.W); if (b.BaseData.Translate != Syroot.Maths.Vector3F.Zero) pos = new Vector3(b.BaseData.Translate.X, b.BaseData.Translate.Y, b.BaseData.Translate.Z); foreach (AnimCurve tr in b.Curves) { FSKATrack t = new FSKATrack(); t.flag = (int)tr.AnimDataOffset; tracks.Add(t); float tanscale = tr.Delta; if (tanscale == 0) tanscale = 1; for (int i = 0; i < (ushort)tr.Frames.Length; i++) { if (tr.CurveType == AnimCurveType.Cubic) { int framedata = (int)tr.Frames[i]; float keydata = tr.Offset + ((tr.Keys[i, 0] * tr.Scale)); float keydata2 = tr.Offset + ((tr.Keys[i, 1] * tr.Scale)); float keydata3 = tr.Offset + ((tr.Keys[i, 2] * tr.Scale)); float keydata4 = tr.Offset + ((tr.Keys[i, 3] * tr.Scale)); } if (tr.KeyType == AnimCurveKeyType.Int16) { } else if (tr.KeyType == AnimCurveKeyType.Single) { } else if (tr.KeyType == AnimCurveKeyType.SByte) { } t.keys.Add(new FSKAKey() { frame = (int)tr.Frames[i], unk1 = tr.Offset + ((tr.Keys[i, 0] * tr.Scale)), unk2 = tr.Offset + ((tr.Keys[i, 1] * tr.Scale)), unk3 = tr.Offset + ((tr.Keys[i, 2] * tr.Scale)), unk4 = tr.Offset + ((tr.Keys[i, 3] * tr.Scale)), }); } } } } public class FSKATrack { public short type; public short keyCount; public int flag; public int unk2; public int padding1; public int padding2; public int padding3; public float frameCount; public float scale, init, unkf3; public long offtolastKeys, offtolastData; public List keys = new List(); public int offset; public FSKAKey GetLeft(int frame) { FSKAKey prev = keys[0]; for (int i = 0; i < keys.Count - 1; i++) { FSKAKey key = keys[i]; if (key.frame > frame && prev.frame <= frame) break; prev = key; } return prev; } public FSKAKey GetRight(int frame) { FSKAKey cur = keys[0]; FSKAKey prev = keys[0]; for (int i = 1; i < keys.Count; i++) { FSKAKey key = keys[i]; cur = key; if (key.frame > frame && prev.frame <= frame) break; prev = key; } return cur; } } public class FSKAKey { public int frame; public float unk1, unk2, unk3, unk4; public int offset; } } public struct DisplayVertex { // Used for rendering. public Vector3 pos; public Vector3 nrm; public Vector3 tan; public Vector3 bit; public Vector2 uv; public Vector4 col; public Vector4 node; public Vector4 weight; public Vector2 uv2; public Vector2 uv3; public Vector3 pos1; public Vector3 pos2; public static int Size = 4 * (3 + 3 + 3 + 3 + 2 + 4 + 4 + 4 + 2 + 2 + 3 + 3); } public class FSHPFolder : TreeNodeCustom { public FSHPFolder() { Text = "Objects"; Name = "FshpFolder"; ContextMenu = new ContextMenu(); MenuItem import = new MenuItem("Add Object"); ContextMenu.MenuItems.Add(import); import.Click += Import; MenuItem exportAll = new MenuItem("Export All Objects"); ContextMenu.MenuItems.Add(exportAll); exportAll.Click += ExportAll; MenuItem clear = new MenuItem("Clear All Objects"); ContextMenu.MenuItems.Add(clear); clear.Click += Clear; } public void Clear(object sender, EventArgs args) { DialogResult dialogResult = MessageBox.Show("Are you sure you want to remove all objects? This cannot be undone!", "", MessageBoxButtons.YesNo); if (dialogResult == DialogResult.Yes) { Nodes.Clear(); ((FMDL)Parent).shapes.Clear(); ((FMDL)Parent).BFRESRender.UpdateVertexData(); } } public void ExportAll(object sender, EventArgs args) { ((FMDL)Parent).ExportAll(); } public void Import(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfobj;*.fbx;*.dae; *.obj;*.csv;|" + "Bfres Object (shape/vertices) |*.bfobj|" + "FBX |*.fbx|" + "DAE |*.dae|" + "OBJ |*.obj|" + "CSV |*.csv|" + "All files(*.*)|*.*"; ofd.Multiselect = true; if (ofd.ShowDialog() == DialogResult.OK) { foreach (string file in ofd.FileNames) ((FMDL)Parent).AddOjects(file, false); } } public override void OnClick(TreeView treeView) { } } public class FMATFolder : TreeNodeCustom { public FMATFolder() { Text = "Materials"; Name = "FmatFolder"; ContextMenu = new ContextMenu(); MenuItem import = new MenuItem("Add Material"); ContextMenu.MenuItems.Add(import); import.Click += Import; } public void Import(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Bfres Material |*.bfmat;"; ofd.Multiselect = true; if (ofd.ShowDialog() == DialogResult.OK) { foreach (string file in ofd.FileNames) ((FMDL)Parent).AddMaterials(file, false); } } public override void OnClick(TreeView treeView) { } } public class FSKL : STSkeleton { public int[] Node_Array; public fsklNode node; public class fsklNode : TreeNodeCustom { public Skeleton Skeleton; public ResU.Skeleton SkeletonU; public BFRESRender BFRESRender; public fsklNode() { Text = "Skeleton"; ImageKey = "skeleton"; SelectedImageKey = "skeleton"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; } public void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Bfres Skeleton|*.bfskl;"; sfd.FileName = Text; sfd.DefaultExt = ".bfskl"; if (sfd.ShowDialog() == DialogResult.OK) { Skeleton.Export(sfd.FileName, BFRESRender.resFile); } } public void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfska;|" + "Bfres Object (shape/vertices) |*.bfska|" + "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { Skeleton.Import(ofd.FileName); } } public override void OnClick(TreeView treeView) { } } public FSKL() { } public FSKL(Skeleton skl) { node = new fsklNode(); node.Skeleton = skl; BfresSwitch.SetSkeleton(node, skl, this); } public FSKL(ResU.Skeleton skl) { node = new fsklNode(); node.SkeletonU = skl; BfresWiiU.SetSkeleton(node, skl, this); } } public class BfresBone : STBone { public bool IsVisable = true; public BoneFlagsBillboard billboard; public BoneFlagsRotation rotationFlags; public BoneFlagsTransform transformFlags; public Bone Bone; public ResU.Bone BoneU; public BFRESRender BFRESRender; public BfresBone() { ImageKey = "bone"; SelectedImageKey = "bone"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; } public void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Bfres Bone|*.bfbn;"; sfd.FileName = Text; sfd.DefaultExt = ".bfbn"; if (sfd.ShowDialog() == DialogResult.OK) { Bone.Export(sfd.FileName, BFRESRender.resFile); } } public void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfska;|" + "Bfres Object (shape/vertices) |*.bfska|" + "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { Bone.Import(ofd.FileName); } Bone.Name = Text; } public BfresBone(STSkeleton skeleton) { skeletonParent = skeleton; } public override void OnClick(TreeView treeView) { FormLoader.LoadBoneEditor(this); } } public class FMDL : STGenericModel { public List shapes = new List(); public Dictionary materials = new Dictionary(); public BFRESRender BFRESRender; public Model Model; public ResU.Model ModelU; public FMDL() { ImageKey = "model"; SelectedImageKey = "model"; Nodes.Add(new FSHPFolder()); Nodes.Add(new FMATFolder()); ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export Model"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace Model"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; MenuItem calcTansBitans = new MenuItem("Calculate Tangents/Bitangents"); ContextMenu.MenuItems.Add(calcTansBitans); calcTansBitans.Click += CalcTansBitansAllShapes; MenuItem normals = new MenuItem("Normals"); ContextMenu.MenuItems.Add(normals); MenuItem smoothNormals = new MenuItem("Smooth"); normals.MenuItems.Add(smoothNormals); smoothNormals.Click += SmoothNormals; MenuItem recalculateNormals = new MenuItem("Recalculate"); normals.MenuItems.Add(recalculateNormals); recalculateNormals.Click += RecalculateNormals; MenuItem rename = new MenuItem("Rename"); ContextMenu.MenuItems.Add(rename); rename.Click += Rename; } private void SmoothNormals(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; foreach (FSHP shp in shapes) { bool HasNormals = shp.vertexAttributes.Any(x => x.Name == "_n0"); if (HasNormals) shp.SmoothNormals(); shp.SaveVertexBuffer(); } BFRESRender.UpdateVertexData(); Cursor.Current = Cursors.Default; } private void RecalculateNormals(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; foreach (FSHP shp in shapes) { bool HasNormals = shp.vertexAttributes.Any(x => x.Name == "_n0"); if (HasNormals) shp.CalculateNormals(); shp.SaveVertexBuffer(); } BFRESRender.UpdateVertexData(); Cursor.Current = Cursors.Default; } private void Rename(object sender, EventArgs args) { RenameDialog dialog = new RenameDialog(); dialog.SetString(Text); if (dialog.ShowDialog() == DialogResult.OK) { Text = dialog.textBox1.Text; } } private void CalcTansBitansAllShapes(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; foreach (FSHP shp in shapes) { bool HasTans = shp.vertexAttributes.Any(x => x.Name == "_t0"); bool HasBiTans = shp.vertexAttributes.Any(x => x.Name == "_b0"); if (!shp.HasUV0()) { MessageBox.Show($"Error! {Text} does not have UVs!", "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!HasBiTans) { DialogResult dialogResult2 = MessageBox.Show("Mesh does not have bitangents. Do you want to create them? (will make file size bigger)", "", MessageBoxButtons.YesNo); FSHP.VertexAttribute att2 = new FSHP.VertexAttribute(); att2.Name = "_b0"; att2.Format = ResGFX.AttribFormat.Format_10_10_10_2_SNorm; if (dialogResult2 == DialogResult.Yes) { if (!HasBiTans) shp.vertexAttributes.Add(att2); } } if (!HasTans) { DialogResult dialogResult = MessageBox.Show("Mesh does not have tangets. Do you want to create them? (will make file size bigger)", "", MessageBoxButtons.YesNo); FSHP.VertexAttribute att = new FSHP.VertexAttribute(); att.Name = "_t0"; att.Format = ResGFX.AttribFormat.Format_10_10_10_2_SNorm; if (dialogResult == DialogResult.Yes) { if (!HasTans) shp.vertexAttributes.Add(att); } } shp.CalculateTangentBitangent(); shp.SaveVertexBuffer(); } BFRESRender.UpdateVertexData(); Cursor.Current = Cursors.Default; } public void CopyMaterial(FMAT selectedMaterial) { CopyMaterialMenu menu = new CopyMaterialMenu(); menu.LoadMaterials(selectedMaterial.Text, BFRESRender.models); if (menu.ShowDialog() == DialogResult.OK) { foreach (TreeNode mdl in menu.materialTreeView.Nodes) { foreach (TreeNode n in mdl.Nodes) { if (n.Checked) { if (materials.ContainsKey(n.Text)) SetCopiedMaterialData(menu, selectedMaterial, materials[n.Text]); } } } Viewport.Instance.UpdateViewport(); } } private void SetCopiedMaterialData(CopyMaterialMenu menu, FMAT selectedMaterial, FMAT targetMaterial) { targetMaterial.Material.Flags = selectedMaterial.Material.Flags; targetMaterial.Material.UserDatas = selectedMaterial.Material.UserDatas; targetMaterial.Material.UserDataDict = selectedMaterial.Material.UserDataDict; if (menu.chkBoxRenderInfo.Checked) { targetMaterial.Material.RenderInfoDict = selectedMaterial.Material.RenderInfoDict; targetMaterial.Material.RenderInfos = selectedMaterial.Material.RenderInfos; } if (menu.chkBoxShaderOptions.Checked) { targetMaterial.Material.ShaderAssign = selectedMaterial.Material.ShaderAssign; } if (menu.chkBoxShaderParams.Checked) { targetMaterial.Material.ShaderParamData = selectedMaterial.Material.ShaderParamData; targetMaterial.Material.ShaderParamDict = selectedMaterial.Material.ShaderParamDict; targetMaterial.Material.ShaderParams = selectedMaterial.Material.ShaderParams; targetMaterial.Material.VolatileFlags = selectedMaterial.Material.VolatileFlags; } if (menu.chkBoxTextures.Checked) { targetMaterial.Material.SamplerDict = selectedMaterial.Material.SamplerDict; targetMaterial.Material.Samplers = selectedMaterial.Material.Samplers; targetMaterial.Material.SamplerSlotArray = selectedMaterial.Material.SamplerSlotArray; targetMaterial.Material.TextureSlotArray = selectedMaterial.Material.TextureSlotArray; targetMaterial.Material.TextureRefs = selectedMaterial.Material.TextureRefs; } targetMaterial.ReadMaterial(targetMaterial.Material); } public void ExportAll() { FolderSelectDialog sfd = new FolderSelectDialog(); List Formats = new List(); Formats.Add("Bfres object (.bfobj)"); Formats.Add("CSV (.csv)"); if (sfd.ShowDialog() == DialogResult.OK) { string folderPath = sfd.SelectedPath; TextureFormatExport form = new TextureFormatExport(Formats); if (form.ShowDialog() == DialogResult.OK) { foreach (FSHP shp in shapes) { if (form.Index == 0) shp.ExportBinaryObject(folderPath + '\\' + shp.Text + ".bfobj"); } } } } public void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Supported Formats|*.bfmdl;*.fbx;*.dae; *.obj;*.csv;|" + "Bfres Model|*.bfmdl|" + "FBX |*.fbx|" + "DAE |*.dae|" + "OBJ |*.obj|" + "CSV |*.csv|" + "All files(*.*)|*.*"; sfd.DefaultExt = ".bfobj"; sfd.FileName = Text; if (sfd.ShowDialog() == DialogResult.OK) { string ext = System.IO.Path.GetExtension(sfd.FileName); ext = ext.ToLower(); switch (ext) { case ".bfmdl": Model.Export(sfd.FileName, BFRESRender.resFile); break; case ".csv": CsvModel csv = new CsvModel(); foreach (FSHP shape in shapes) { STGenericObject obj = new STGenericObject(); obj.ObjectName = shape.Text; obj.vertices = shape.vertices; obj.faces = shape.lodMeshes[shape.DisplayLODIndex].faces; csv.objects.Add(obj); int CurVtx = 0; foreach (Vertex v in shape.vertices) { if (v.boneIds[0] != 0) obj.vertices[CurVtx].boneNames.Add(shape.GetBoneNameFromIndex(this, v.boneIds[0])); if (v.boneIds[1] != 0) obj.vertices[CurVtx].boneNames.Add(shape.GetBoneNameFromIndex(this, v.boneIds[1])); if (v.boneIds[2] != 0) obj.vertices[CurVtx].boneNames.Add(shape.GetBoneNameFromIndex(this, v.boneIds[2])); if (v.boneIds[3] != 0) obj.vertices[CurVtx].boneNames.Add(shape.GetBoneNameFromIndex(this, v.boneIds[3])); CurVtx++; } } System.IO.File.WriteAllBytes(sfd.FileName, csv.Save()); break; default: AssimpData assimp = new AssimpData(); assimp.SaveFromModel(this, sfd.FileName); break; } } } public void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfobj;*.fbx;*.dae;*.obj;*.csv;|" + "Bfres Object (shape/vertices) |*.bfobj|" + "FBX |*.fbx|" + "DAE |*.dae|" + "OBJ |*.obj|" + "CSV |*.csv|" + "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { AddOjects(ofd.FileName); } } //Function addes shapes, vertices and meshes public void AddOjects(string FileName, bool Replace = true) { if (Replace) { shapes.Clear(); Nodes["FshpFolder"].Nodes.Clear(); } int MatStartIndex = materials.Count; string ext = System.IO.Path.GetExtension(FileName); ext = ext.ToLower(); switch (ext) { case ".bfobj": Cursor.Current = Cursors.WaitCursor; Shape shpS = new Shape(); VertexBuffer vertexBuffer = new VertexBuffer(); shpS.Import(FileName, vertexBuffer); FSHP shapeS = new FSHP(); shapeS.Shape = shpS; shapeS.BFRESRender = BFRESRender; BfresSwitch.ReadShapesVertices(shapeS, shpS, vertexBuffer, this); shapes.Add(shapeS); Nodes["FshpFolder"].Nodes.Add(shapeS); Cursor.Current = Cursors.Default; break; case ".bfmdl": Cursor.Current = Cursors.WaitCursor; shapes.Clear(); Model mdl = new Model(); mdl.Import(FileName, BFRESRender.resFile); mdl.Name = Text; shapes.Clear(); Nodes["FshpFolder"].Nodes.Clear(); foreach (Shape shp in mdl.Shapes) { FSHP shape = new FSHP(); shape.Shape = shp; BfresSwitch.ReadShapesVertices(shape, shp, mdl.VertexBuffers[shp.VertexBufferIndex], this); shapes.Add(shape); Nodes["FshpFolder"].Nodes.Add(shape); } Cursor.Current = Cursors.Default; break; case ".csv": CsvModel csvModel = new CsvModel(); csvModel.LoadFile(FileName, true); if (csvModel.objects.Count == 0) { MessageBox.Show("No models found!"); return; } BfresModelImportSettings csvsettings = new BfresModelImportSettings(); csvsettings.DisableMaterialEdits(); csvsettings.SetModelAttributes(csvModel.objects[0]); if (csvsettings.ShowDialog() == DialogResult.OK) { Cursor.Current = Cursors.WaitCursor; foreach (STGenericObject obj in csvModel.objects) { FSHP shape = new FSHP(); shape.VertexBufferIndex = shapes.Count; shape.vertices = obj.vertices; shape.MaterialIndex = 0; shape.VertexSkinCount = obj.GetMaxSkinInfluenceCount(); shape.vertexAttributes = csvsettings.CreateNewAttributes(); shape.boneIndx = 0; shape.Text = obj.ObjectName; shape.lodMeshes = obj.lodMeshes; shape.CreateNewBoundingBoxes(); shape.CreateBoneList(obj, this); shape.CreateIndexList(obj, this); shape.ApplyImportSettings(csvsettings, GetMaterial(shape.MaterialIndex)); shape.SaveShape(); shape.SaveVertexBuffer(); shape.BFRESRender = BFRESRender; shape.BoneIndices = new List(); Nodes["FshpFolder"].Nodes.Add(shape); shapes.Add(shape); } Cursor.Current = Cursors.Default; } break; default: AssimpData assimp = new AssimpData(); assimp.LoadFile(FileName); if (assimp.objects.Count == 0) { MessageBox.Show("No models found!"); return; } BfresModelImportSettings settings = new BfresModelImportSettings(); if (BFRES.IsWiiU) settings.DisableMaterialEdits(); settings.SetModelAttributes(assimp.objects[0]); if (settings.ShowDialog() == DialogResult.OK) { Cursor.Current = Cursors.WaitCursor; if (!BFRES.IsWiiU && Replace) { materials.Clear(); Nodes["FmatFolder"].Nodes.Clear(); MatStartIndex = 0; } if (!BFRES.IsWiiU) { foreach (STGenericMaterial mat in assimp.materials) { FMAT fmat = new FMAT(); fmat.Material = new Material(); if (settings.ExternalMaterialPath != string.Empty) { fmat.Material.Import(settings.ExternalMaterialPath); fmat.ReadMaterial(fmat.Material); } fmat.Text = mat.Text; //Setup placeholder textures //Note we can't add/remove samplers so we must fill these slots foreach (var t in fmat.textures) { t.wrapModeS = 0; t.wrapModeT = 0; switch (t.Type) { case STGenericMatTexture.TextureType.Diffuse: t.Name = "Basic_Alb"; break; case STGenericMatTexture.TextureType.Emission: t.Name = "Basic_Emm"; break; case STGenericMatTexture.TextureType.Normal: t.Name = "Basic_Nrm"; break; case STGenericMatTexture.TextureType.Specular: t.Name = "Basic_Spm"; break; case STGenericMatTexture.TextureType.SphereMap: t.Name = "Basic_Sphere"; break; case STGenericMatTexture.TextureType.Metalness: t.Name = "Basic_Mtl"; break; case STGenericMatTexture.TextureType.Roughness: t.Name = "Basic_Rgh"; break; case STGenericMatTexture.TextureType.MRA: t.Name = "Basic_MRA"; break; case STGenericMatTexture.TextureType.Shadow: t.Name = "Basic_Bake_st0"; break; case STGenericMatTexture.TextureType.Light: t.Name = "Basic_Bake_st1"; break; } } if (PluginRuntime.bntxContainers.Count > 0) { foreach (var node in Parent.Parent.Nodes["EXT"].Nodes) { if (node is BinaryTextureContainer) { var bntx = (BinaryTextureContainer)node; bntx.ImportBasicTextures("Basic_Alb"); bntx.ImportBasicTextures("Basic_Nrm"); bntx.ImportBasicTextures("Basic_Spm"); bntx.ImportBasicTextures("Basic_Sphere"); bntx.ImportBasicTextures("Basic_Mtl"); bntx.ImportBasicTextures("Basic_Rgh"); bntx.ImportBasicTextures("Basic_MRA"); bntx.ImportBasicTextures("Basic_Bake_st0"); bntx.ImportBasicTextures("Basic_Bake_st1"); bntx.ImportBasicTextures("Basic_Emm"); } } } foreach (var tex in mat.TextureMaps) { foreach (var t in fmat.textures) { if (t.Type == tex.Type) { t.Name = tex.Name; t.wrapModeS = tex.wrapModeS; t.wrapModeT = tex.wrapModeT; t.wrapModeW = tex.wrapModeW; t.Type = tex.Type; } } } fmat.Material.Name = Text; fmat.SetMaterial(fmat.Material); List keyList = new List(materials.Keys); fmat.Text = Utils.RenameDuplicateString(keyList, fmat.Text); materials.Add(fmat.Text, fmat); Nodes["FmatFolder"].Nodes.Add(fmat); } } foreach (STGenericObject obj in assimp.objects) { FSHP shape = new FSHP(); shape.VertexBufferIndex = shapes.Count; shape.vertices = obj.vertices; shape.VertexSkinCount = obj.MaxSkinInfluenceCount; shape.vertexAttributes = settings.CreateNewAttributes(); shape.boneIndx = obj.BoneIndex; shape.MaterialIndex = obj.MaterialIndex + MatStartIndex; if (BFRES.IsWiiU) shape.MaterialIndex = 0; shape.Text = obj.ObjectName; shape.lodMeshes = obj.lodMeshes; shape.CreateNewBoundingBoxes(); shape.CreateBoneList(obj, this); shape.CreateIndexList(obj, this); shape.ApplyImportSettings(settings, GetMaterial(shape.MaterialIndex)); shape.SaveShape(); shape.SaveVertexBuffer(); shape.BFRESRender = BFRESRender; shape.BoneIndices = new List(); List keyList = shapes.Select(o => o.Text).ToList(); shape.Text = Utils.RenameDuplicateString(keyList, shape.Text); Nodes["FshpFolder"].Nodes.Add(shape); shapes.Add(shape); } Cursor.Current = Cursors.Default; } break; } BFRESRender.UpdateVertexData(); } public FMAT GetMaterial(int index) { return materials.Values.ElementAt(index); } public void AddMaterials(string FileName, bool Replace = true) { string ext = System.IO.Path.GetExtension(FileName); ext = ext.ToLower(); switch (ext) { case ".bfmat": Cursor.Current = Cursors.WaitCursor; if (Replace) { materials.Clear(); Nodes["FmatFolder"].Nodes.Clear(); } FMAT mat = new FMAT(); mat.Material = new Material(); mat.Material.Import(FileName); mat.ReadMaterial(mat.Material); mat.BFRESRender = BFRESRender; mat.Text = mat.Material.Name; materials.Add(mat.Text, mat); Nodes["FmatFolder"].Nodes.Add(mat); break; } } public override void OnClick(TreeView treeView) { } private void CreateSkeleton() { } private void CreateBones(STBone bone) { Bone bn = new Bone(); bn.BillboardIndex = (ushort)bone.BillboardIndex; bn.Flags = BoneFlags.Visible; bn.FlagsRotation = BoneFlagsRotation.EulerXYZ; bn.FlagsTransform = BoneFlagsTransform.None; bn.FlagsTransformCumulative = BoneFlagsTransformCumulative.None; bn.Name = bone.Text; bn.RigidMatrixIndex = 0; bn.Rotation = new Syroot.Maths.Vector4F(bone.rotation[0], bone.rotation[1], bone.rotation[2], bone.rotation[3]); bn.Position = new Syroot.Maths.Vector3F(bone.position[0], bone.position[1], bone.position[2]); bn.Scale = new Syroot.Maths.Vector3F(bone.scale[0], bone.scale[1], bone.scale[2]); bn.UserData = new List(); bn.UserDataDict = new ResDict(); } public FSKL Skeleton { get { return skeleton; } set { skeleton = value; } } private FSKL skeleton = new FSKL(); } public class FMAT : STGenericMaterial { public FMAT() { Checked = true; ImageKey = "material"; SelectedImageKey = "material"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; MenuItem copy = new MenuItem("Copy"); ContextMenu.MenuItems.Add(copy); copy.Click += Copy; MenuItem rename = new MenuItem("Rename"); ContextMenu.MenuItems.Add(rename); rename.Click += Rename; } public BFRESRender BFRESRender; public bool Enabled = true; public override void OnClick(TreeView treeView) { FormLoader.LoadMatEditor(this); } public bool EditorIsActive(DockContent dock) { foreach (Control ctrl in dock.Controls) { if (ctrl is FMATEditor) { ((FMATEditor)ctrl).LoadMaterial(this, BFRESRender); return true; } } return false; } public void SetActiveGame() { string ShaderName = shaderassign.ShaderArchive; string ShaderModel = shaderassign.ShaderModel; if (ShaderName == "alRenderMaterial" || ShaderName == "alRenderCloudLayer" || ShaderName == "alRenderSky") Runtime.activeGame = Runtime.ActiveGame.SMO; else if (ShaderName == "Turbo_UBER") Runtime.activeGame = Runtime.ActiveGame.MK8D; else if (ShaderName.Contains("uking_mat")) Runtime.activeGame = Runtime.ActiveGame.BOTW; else if (ShaderName.Contains("Blitz_UBER")) Runtime.activeGame = Runtime.ActiveGame.Splatoon2; else Runtime.activeGame = Runtime.ActiveGame.KSA; } private void Rename(object sender, EventArgs args) { RenameDialog dialog = new RenameDialog(); dialog.SetString(Text); if (dialog.ShowDialog() == DialogResult.OK) { ((FMDL)Parent.Parent).materials.Remove(Text); Text = dialog.textBox1.Text; ((FMDL)Parent.Parent).materials.Add(Text, this); } } private void Copy(object sender, EventArgs args) { ((FMDL)Parent.Parent).CopyMaterial(this); } private void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Supported Formats|*.bfmat;"; sfd.DefaultExt = ".bfmat"; sfd.FileName = Text; if (sfd.ShowDialog() == DialogResult.OK) { Material.Export(sfd.FileName, BFRESRender.resFile); } } private void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfmat;"; if (ofd.ShowDialog() == DialogResult.OK) { Material.Import(ofd.FileName); Material.Name = Text; BfresSwitch.ReadMaterial(this, Material); } } public Dictionary anims = new Dictionary(); public Dictionary Samplers = new Dictionary(); public List textures = new List(); public List renderinfo = new List(); public List samplerinfo = new List(); public Dictionary matparam = new Dictionary(); public Material Material; public ResU.Material MaterialU; public ShaderAssign shaderassign = new ShaderAssign(); public class ShaderAssign { public string ShaderModel = ""; public string ShaderArchive = ""; public Dictionary options = new Dictionary(); public Dictionary samplers = new Dictionary(); public Dictionary attributes = new Dictionary(); } public class SamplerInfo { public int WrapModeU; public int WrapModeV; public int WrapModeW; } public bool HasDiffuseMap = false; public bool HasNormalMap = false; public bool HasSpecularMap = false; public bool HasEmissionMap = false; public bool HasDiffuseLayer = false; public bool HasTeamColorMap = false; //Splatoon uses this (TLC) public bool HasTransparencyMap = false; public bool HasShadowMap = false; public bool HasAmbientOcclusionMap = false; public bool HasLightMap = false; public bool HasSphereMap = false; public bool HasSubSurfaceScatteringMap = false; //PBR (Switch) data public bool HasMetalnessMap = false; public bool HasRoughnessMap = false; public bool HasMRA = false; } public class BfresShaderParam { public ShaderParamType Type; public string Name; public float[] ValueFloat; public bool[] ValueBool; public uint[] ValueUint; public int[] ValueInt; public byte[] ValueReserved; public Srt2D ValueSrt2D; public Srt3D ValueSrt3D; public TexSrt ValueTexSrt; public TexSrtEx ValueTexSrtEx; //If a data set is not defined then defaults in this to save back properly //Note this may be rarely needed or not at all public byte[] Value_Unk; private void ReadSRT2D(FileReader reader) { ValueSrt2D = new Srt2D(); ValueSrt2D.Scaling = reader.ReadVec2SY(); ValueSrt2D.Rotation = reader.ReadSingle(); ValueSrt2D.Translation = reader.ReadVec2SY(); } private void ReadSRT3D(FileReader reader) { ValueSrt3D = new Srt3D(); ValueSrt3D.Scaling = reader.ReadVec3SY(); ValueSrt3D.Rotation = reader.ReadVec3SY(); ValueSrt3D.Translation = reader.ReadVec3SY(); } private void ReadTexSrt(FileReader reader) { ValueTexSrt = new TexSrt(); ValueTexSrt.Mode = reader.ReadEnum(false); ValueTexSrt.Scaling = reader.ReadVec2SY(); ValueTexSrt.Rotation = reader.ReadSingle(); ValueTexSrt.Translation = reader.ReadVec2SY(); } private void ReadTexSrtEx(FileReader reader) { ValueTexSrtEx = new TexSrtEx(); ValueTexSrtEx.Mode = reader.ReadEnum(true); ValueTexSrtEx.Scaling = reader.ReadVec2SY(); ValueTexSrtEx.Rotation = reader.ReadSingle(); ValueTexSrtEx.Translation = reader.ReadVec2SY(); ValueTexSrtEx.MatrixPointer = reader.ReadUInt32(); } public ShaderParamType GetTypeWiiU(ResU.ShaderParamType type) { return (ShaderParamType)System.Enum.Parse(typeof(ShaderParamType), type.ToString()); } public ResU.ShaderParamType SetTypeWiiU(ShaderParamType type) { return (ResU.ShaderParamType)System.Enum.Parse(typeof(ResU.ShaderParamType), type.ToString()); } public void ReadValue(FileReader reader, int Size) { switch (Type) { case ShaderParamType.Bool: case ShaderParamType.Bool2: case ShaderParamType.Bool3: case ShaderParamType.Bool4: ValueBool = reader.ReadBooleans(Size / sizeof(bool)); break; case ShaderParamType.Float: case ShaderParamType.Float2: case ShaderParamType.Float3: case ShaderParamType.Float4: case ShaderParamType.Float2x2: case ShaderParamType.Float2x3: case ShaderParamType.Float2x4: case ShaderParamType.Float4x2: case ShaderParamType.Float4x3: case ShaderParamType.Float4x4: ValueFloat = reader.ReadSingles(Size / sizeof(float)); break; case ShaderParamType.Int: case ShaderParamType.Int2: case ShaderParamType.Int3: case ShaderParamType.Int4: ValueInt = reader.ReadInt32s(Size / sizeof(int)); break; case ShaderParamType.Reserved2: case ShaderParamType.Reserved3: case ShaderParamType.Reserved4: ValueReserved = reader.ReadBytes(Size / sizeof(byte)); break; case ShaderParamType.Srt2D: ReadSRT2D(reader); break; case ShaderParamType.Srt3D: ReadSRT3D(reader); break; case ShaderParamType.TexSrt: ReadTexSrt(reader); break; case ShaderParamType.TexSrtEx: ReadTexSrtEx(reader); break; case ShaderParamType.UInt: case ShaderParamType.UInt2: case ShaderParamType.UInt3: case ShaderParamType.UInt4: ValueUint = reader.ReadUInt32s(Size / sizeof(uint)); break; // Invalid default: throw new ArgumentException($"Invalid {nameof(ShaderParamType)} {Type}.", nameof(Type)); } } public void WriteValue(FileWriter writer) { switch (Type) { case ShaderParamType.Bool: case ShaderParamType.Bool2: case ShaderParamType.Bool3: case ShaderParamType.Bool4: writer.Write(ValueBool); break; case ShaderParamType.Float: case ShaderParamType.Float2: case ShaderParamType.Float3: case ShaderParamType.Float4: case ShaderParamType.Float2x2: case ShaderParamType.Float2x3: case ShaderParamType.Float2x4: case ShaderParamType.Float4x2: case ShaderParamType.Float4x3: case ShaderParamType.Float4x4: writer.Write(ValueFloat); break; case ShaderParamType.Int: case ShaderParamType.Int2: case ShaderParamType.Int3: case ShaderParamType.Int4: writer.Write(ValueInt); break; case ShaderParamType.Reserved2: case ShaderParamType.Reserved3: case ShaderParamType.Reserved4: writer.Write(ValueInt); break; case ShaderParamType.Srt2D: WriteSRT2D(writer); break; case ShaderParamType.Srt3D: WriteSRT3D(writer); break; case ShaderParamType.TexSrt: WriteTexSrt(writer); break; case ShaderParamType.TexSrtEx: WriteTexSrtEx(writer); break; case ShaderParamType.UInt: case ShaderParamType.UInt2: case ShaderParamType.UInt3: case ShaderParamType.UInt4: writer.Write(ValueUint); break; // Invalid default: throw new ArgumentException($"Invalid {nameof(ShaderParamType)} {Type}.", nameof(Type)); } } private void WriteSRT2D(FileWriter writer) { writer.Write(ValueSrt2D.Scaling); writer.Write(ValueSrt2D.Rotation); writer.Write(ValueSrt2D.Translation); } private void WriteSRT3D(FileWriter writer) { writer.Write(ValueSrt3D.Scaling); writer.Write(ValueSrt3D.Rotation); writer.Write(ValueSrt3D.Translation); } private void WriteTexSrt(FileWriter writer) { writer.Write((uint)ValueTexSrt.Mode); writer.Write(ValueTexSrt.Scaling); writer.Write(ValueTexSrt.Rotation); writer.Write(ValueTexSrt.Translation); } private void WriteTexSrtEx(FileWriter writer) { writer.Write((uint)ValueTexSrtEx.Mode); writer.Write(ValueTexSrtEx.Scaling); writer.Write(ValueTexSrtEx.Rotation); writer.Write(ValueTexSrtEx.Translation); writer.Write(ValueTexSrtEx.MatrixPointer); } } public class BfresRenderInfo { public string Name; public long DataOffset; public RenderInfoType Type; public int ArrayLength; //Data Section by "Type" public int[] ValueInt; public string[] ValueString; public float[] ValueFloat; public RenderInfoType GetTypeWiiU(ResU.RenderInfoType type) { return (RenderInfoType)System.Enum.Parse(typeof(RenderInfoType), type.ToString()); } public ResU.RenderInfoType SetTypeWiiU(RenderInfoType type) { return (ResU.RenderInfoType)System.Enum.Parse(typeof(ResU.RenderInfoType), type.ToString()); } } public class MatTexture : STGenericMatTexture { public int hash; public string SamplerName; //Note samplers will get converted to another sampler type sometimes in the shader assign section //Use this string if not empty for our bfres fragment shader to produce the accurate affects //An example of a conversion maybe be like a1 - t0 so texture gets used as a transparent map/alpha texture public string FragShaderSampler = ""; public MatTexture() { } } public class FSHP : STGenericObject { public FSHP() { Checked = true; ImageKey = "mesh"; SelectedImageKey = "mesh"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export Mesh"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace Mesh"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; MenuItem remove = new MenuItem("Delete Mesh"); ContextMenu.MenuItems.Add(remove); remove.Click += Remove; MenuItem calcTansBitans = new MenuItem("Recalulate Tangents/Bitangents"); ContextMenu.MenuItems.Add(calcTansBitans); calcTansBitans.Click += CalcTansBitans; MenuItem flipUVsY = new MenuItem("Flip UVs (Vertical)"); ContextMenu.MenuItems.Add(flipUVsY); flipUVsY.Click += FlipUvsVertical; MenuItem flipUVsX = new MenuItem("Flip UVs (Horizontal)"); ContextMenu.MenuItems.Add(flipUVsX); flipUVsX.Click += FlipUvsHorizontal; MenuItem normals = new MenuItem("Normals"); ContextMenu.MenuItems.Add(normals); MenuItem smoothNormals = new MenuItem("Smooth"); normals.MenuItems.Add(smoothNormals); smoothNormals.Click += SmoothNormals; MenuItem recalculateNormals = new MenuItem("Recalculate"); normals.MenuItems.Add(recalculateNormals); recalculateNormals.Click += RecalculateNormals; MenuItem matEditor = new MenuItem("Open Material Editor"); ContextMenu.MenuItems.Add(matEditor); matEditor.Click += OpenMaterialEditor; MenuItem rename = new MenuItem("Rename"); ContextMenu.MenuItems.Add(rename); rename.Click += Rename; } public BFRESRender BFRESRender; public FMATEditor editor; public int ModelIndex; //For getting the model the shape is in public VertexBuffer VertexBuffer; public Shape Shape; public ResU.VertexBuffer VertexBufferU; public ResU.Shape ShapeU; public FMAT GetMaterial() { return ((FMDL)Parent.Parent).materials.Values.ElementAt(MaterialIndex); } public void SetMaterial(FMAT material) { ((FMDL)Parent.Parent).materials[material.Text] = material; } public override void OnClick(TreeView treeView) { FormLoader.LoadShapeEditor(this); } private void SmoothNormals(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; SmoothNormals(); SaveVertexBuffer(); BFRESRender.UpdateVertexData(); Cursor.Current = Cursors.Default; } private void RecalculateNormals(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; CalculateNormals(); SaveVertexBuffer(); BFRESRender.UpdateVertexData(); Cursor.Current = Cursors.Default; } private void Rename(object sender, EventArgs args) { RenameDialog dialog = new RenameDialog(); dialog.SetString(Text); if (dialog.ShowDialog() == DialogResult.OK) { Text = dialog.textBox1.Text; } } private void Remove(object sender, EventArgs args) { DialogResult dialogResult = MessageBox.Show("Are you sure you want to remove this object? This cannot be undone!", "", MessageBoxButtons.YesNo); if (dialogResult == DialogResult.Yes) { ((FMDL)Parent.Parent).shapes.Remove(this); ((FMDL)Parent.Parent).BFRESRender.UpdateVertexData(); Parent.Nodes.Remove(this); } } public void ApplyImportSettings(BfresModelImportSettings settings, FMAT mat) { if (settings.FlipUVsVertical) { foreach (Vertex v in vertices) { v.uv0 = new Vector2(v.uv0.X, 1 - v.uv0.Y); } } if (settings.RecalculateNormals) { CalculateNormals(); } if (settings.Rotate90DegreesY) { TransformPosition(Vector3.Zero, new Vector3(90, 0, 0), new Vector3(1)); } if (settings.Rotate90DegreesNegativeY) { TransformPosition(Vector3.Zero, new Vector3(-90, 0, 0), new Vector3(1)); } if (settings.EnableTangents) { try { CalculateTangentBitangent(); } catch { MessageBox.Show($"Failed to generate tangents for mesh {Text}"); } } if (settings.SetDefaultParamData) { foreach (var param in mat.matparam.Values) { switch (param.Name) { case "const_color0": case "const_color1": case "const_color2": case "const_color3": case "base_color_mul_color": case "uniform0_mul_color": case "uniform1_mul_color": case "uniform2_mul_color": case "uniform3_mul_color": case "uniform4_mul_color": case "proc_texture_2d_mul_color": case "proc_texture_3d_mul_color": case "displacement1_color": case "ripple_emission_color": case "hack_color": case "stain_color": case "displacement_color": param.ValueFloat = new float[] {1,1,1,1 }; break; case "gsys_bake_st0": param.ValueFloat = new float[] { 1, 1, 0, 0 }; break; case "gsys_bake_st1": param.ValueFloat = new float[] { 1, 1, 0, 0 }; break; } } } } public void TransformPosition(Vector3 Position, Vector3 Rotation, Vector3 Scale) { Matrix4 BonePosExtra = Utils.TransformValues(Position, Rotation, Scale); foreach (Vertex v in vertices) { v.pos = Vector3.TransformPosition(v.pos, BonePosExtra); v.nrm = Vector3.TransformNormal(v.pos, BonePosExtra); } } private void OpenMaterialEditor(object sender, EventArgs args) { FormLoader.LoadMatEditor(GetMaterial()); } private void CalcTansBitans(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; bool HasTans = vertexAttributes.Any(x => x.Name == "_t0"); bool HasBiTans = vertexAttributes.Any(x => x.Name == "_b0"); if (!HasUV0()) { MessageBox.Show($"Error! {Text} does not have UVs!", "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!HasBiTans) { DialogResult dialogResult2 = MessageBox.Show("Mesh does not have bitangents. Do you want to create them? (will make file size bigger)", "", MessageBoxButtons.YesNo); VertexAttribute att2 = new VertexAttribute(); att2.Name = "_b0"; att2.Format = ResGFX.AttribFormat.Format_10_10_10_2_SNorm; if (dialogResult2 == DialogResult.Yes) { if (!HasBiTans) vertexAttributes.Add(att2); } } if (!HasTans) { DialogResult dialogResult = MessageBox.Show("Mesh does not have tangets. Do you want to create them? (will make file size bigger)", "", MessageBoxButtons.YesNo); VertexAttribute att = new VertexAttribute(); att.Name = "_t0"; att.Format = ResGFX.AttribFormat.Format_10_10_10_2_SNorm; if (dialogResult == DialogResult.Yes) { if (!HasTans) vertexAttributes.Add(att); } } CalculateTangentBitangent(); SaveVertexBuffer(); BFRESRender.UpdateVertexData(); Cursor.Current = Cursors.Default; } public bool HasUV0() { return vertexAttributes.Any(x => x.Name == "_u0"); } public bool HasUV1() { return vertexAttributes.Any(x => x.Name == "_u1"); } public bool HasUV2() { return vertexAttributes.Any(x => x.Name == "_u2"); } public void FlipUvsVertical(object sender, EventArgs args) { if (!HasUV0()) { MessageBox.Show($"Error! {Text} does not have UVs!", "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } FlipUvsVertical(); SaveVertexBuffer(); BFRESRender.UpdateVertexData(); } public void FlipUvsHorizontal(object sender, EventArgs args) { if (!HasUV0()) { MessageBox.Show($"Error! {Text} does not have UVs!", "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } FlipUvsHorizontal(); SaveVertexBuffer(); BFRESRender.UpdateVertexData(); } public void ExportMaterials(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Materials|*.bfmat;"; sfd.DefaultExt = ".bfmat"; sfd.FileName = GetMaterial().Text; if (sfd.ShowDialog() == DialogResult.OK) { GetMaterial().Material.Export(sfd.FileName, BFRESRender.resFile); } } public void ReplaceMaterials(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Materials|*.bfmat;"; ofd.DefaultExt = ".bfmat"; ofd.FileName = GetMaterial().Text; if (ofd.ShowDialog() == DialogResult.OK) { GetMaterial().Material.Import(ofd.FileName); } } public void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Supported Formats|*.bfobj;*.fbx;*.dae; *.obj;|" + "Bfres Object (shape/vertices) |*.bfobj|" + "FBX |*.fbx|" + "DAE |*.dae|" + "OBJ |*.obj|" + "All files(*.*)|*.*"; sfd.DefaultExt = ".bfobj"; sfd.FileName = Text; if (sfd.ShowDialog() == DialogResult.OK) { string ext = System.IO.Path.GetExtension(sfd.FileName); ext = ext.ToLower(); switch (ext) { case ".bfobj": ExportBinaryObject(sfd.FileName); break; default: AssimpData assimp = new AssimpData(); assimp.SaveFromObject(vertices, lodMeshes[DisplayLODIndex].faces, Text, sfd.FileName); break; } } } public void ExportBinaryObject(string FileName) { Shape.Export(FileName, BFRESRender.resFile); } public void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bfobj;*.fbx;*.dae; *.obj;|" + "Bfres Object (shape/vertices) |*.bfobj|" + "FBX |*.fbx|" + "DAE |*.dae|" + "OBJ |*.obj|" + "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { string ext = System.IO.Path.GetExtension(ofd.FileName); ext = ext.ToLower(); switch (ext) { case ".bfobj": Shape shp = new Shape(); shp.Import(ofd.FileName, VertexBuffer); shp.Name = Text; shp.MaterialIndex = (ushort)MaterialIndex; BfresSwitch.ReadShapesVertices(this, shp, VertexBuffer, BFRESRender.models[ModelIndex]); break; default: AssimpData assimp = new AssimpData(); assimp.LoadFile(ofd.FileName); AssimpMeshSelector selector = new AssimpMeshSelector(); selector.LoadMeshes(assimp, Index); if (selector.ShowDialog() == DialogResult.OK) { if (assimp.objects.Count == 0) { MessageBox.Show("No models found!"); return; } BfresModelImportSettings settings = new BfresModelImportSettings(); settings.SetModelAttributes(assimp.objects[0]); if (settings.ShowDialog() == DialogResult.OK) { GenericObject obj = selector.GetSelectedMesh(); Cursor.Current = Cursors.WaitCursor; VertexBufferIndex = obj.VertexBufferIndex; vertices = obj.vertices; CreateBoneList(obj, (FMDL)Parent.Parent); VertexSkinCount = obj.MaxSkinInfluenceCount; vertexAttributes = settings.CreateNewAttributes(); lodMeshes = obj.lodMeshes; CreateNewBoundingBoxes(); SaveShape(); SaveVertexBuffer(); Cursor.Current = Cursors.Default; } } break; } BFRESRender.UpdateVertexData(); } } public void CreateIndexList(GenericObject ob, FMDL mdl = null) { BoneIndices = new List(); List boneNames = new List(); foreach (Vertex v in ob.vertices) { foreach (string bn in v.boneNames) { if (!boneNames.Contains(bn)) boneNames.Add(bn); } } int index = 0; foreach (STBone bone in mdl.Skeleton.bones) { foreach (string bnam in boneNames) { if (bone.Text == bnam) { BoneIndices.Add((ushort)index); } } index++; } } public void CreateBoneList(GenericObject ob, FMDL mdl) { string[] nodeArrStrings = new string[mdl.Skeleton.Node_Array.Length]; int CurNode = 0; foreach (int thing in mdl.Skeleton.Node_Array) nodeArrStrings[CurNode++] = mdl.Skeleton.bones[thing].Text; foreach (Vertex v in ob.vertices) { foreach (string bn in v.boneNames) { foreach (var defBn in nodeArrStrings.Select((Value, Index) => new { Value, Index })) { if (bn == defBn.Value) { v.boneIds.Add(defBn.Index); } } } } } public void CreateNewBoundingBoxes() { boundingBoxes.Clear(); boundingRadius.Clear(); foreach (LOD_Mesh mesh in lodMeshes) { BoundingBox box = CalculateBoundingBox(); boundingBoxes.Add(box); boundingRadius.Add((float)(box.Center.Length + box.Extend.Length)); foreach (LOD_Mesh.SubMesh sub in mesh.subMeshes) boundingBoxes.Add(box); } } private BoundingBox CalculateBoundingBox() { Vector3 Max = new Vector3(); Vector3 Min = new Vector3(); Min = Max = vertices[0].pos; Min = CalculateBBMin(vertices); Max = CalculateBBMax(vertices); Vector3 center = (Max + Min); Vector3 extend = Max - Min; return new BoundingBox() { Center = center, Extend = extend }; } private Vector3 CalculateBBMin(List positionVectors) { Vector3 minimum = new Vector3(); foreach (Vertex vtx in positionVectors) { if (vtx.pos.X < minimum.X) minimum.X = vtx.pos.X; if (vtx.pos.Y < minimum.Y) minimum.Y = vtx.pos.Y; if (vtx.pos.Z < minimum.Z) minimum.Z = vtx.pos.Z; } return minimum; } private Vector3 CalculateBBMax(List positionVectors) { Vector3 maximum = new Vector3(); foreach (Vertex vtx in positionVectors) { if (vtx.pos.X > maximum.X) maximum.X = vtx.pos.X; if (vtx.pos.Y > maximum.Y) maximum.Y = vtx.pos.Y; if (vtx.pos.Z > maximum.Z) maximum.Z = vtx.pos.Z; } return maximum; } private void UpdateShaderAssignAttributes(FMAT material) { material.shaderassign.samplers.Clear(); foreach (VertexAttribute att in vertexAttributes) { material.shaderassign.attributes.Add(att.Name, att.Name); } } public int[] Faces; public List BoneIndices = new List(); // for drawing public int[] display; public int VertexSkinCount; public int DisplayId; public int boneIndx; public int VertexBufferIndex; public int TargetAttribCount; public List boundingRadius = new List(); public List boundingBoxes = new List(); public class BoundingBox { public Vector3 Center; public Vector3 Extend; } public int DisplayLODIndex = 0; public List vertexAttributes = new List(); public class VertexAttribute { public string Name; public ResGFX.AttribFormat Format; public override string ToString() { return Name; } public ResGFX.AttribFormat GetTypeWiiU(ResUGX2.GX2AttribFormat type) { return (ResGFX.AttribFormat)System.Enum.Parse(typeof(ResGFX.AttribFormat), $"{type.ToString()}"); } public ResUGX2.GX2AttribFormat SetTypeWiiU(ResGFX.AttribFormat type) { return (ResUGX2.GX2AttribFormat)System.Enum.Parse(typeof(ResUGX2.GX2AttribFormat), type.ToString()); } } public void SaveShape() { if (!BFRES.IsWiiU) Shape = BfresSwitch.SaveShape(this); else ShapeU = BfresWiiU.SaveShape(this); } public IList GetIndices() { IList indices = new List(); List BoneNodes = new List(); foreach (Vertex vtx in vertices) { } return indices; } public Vector3 TransformLocal(Vector3 position, bool IsPos = true) { Matrix4 trans = Matrix4.CreateTranslation(0, 0, 0); if (IsPos) return Vector3.TransformPosition(position, trans); else return Vector3.TransformNormal(position, trans); } public void SaveVertexBuffer() { if (BFRES.IsWiiU) { BfresWiiU.SaveVertexBuffer(this); return; } VertexBufferHelper helpernx = new VertexBufferHelper(new VertexBuffer(), Syroot.BinaryData.ByteOrder.LittleEndian); List atrib = new List(); UpdateVertices(); foreach (VertexAttribute att in vertexAttributes) { if (att.Name == "_p0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = verts.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_n0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = norms.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_u0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = uv0.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_u1") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = uv1.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_u2") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = uv2.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_w0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = weights.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_i0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = boneInd.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_b0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = bitans.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_t0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = tans.ToArray(); vert.Format = att.Format; atrib.Add(vert); } if (att.Name == "_c0") { VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib(); vert.Name = att.Name; vert.Data = colors.ToArray(); vert.Format = att.Format; atrib.Add(vert); } } if (atrib.Count == 0) { MessageBox.Show("Attributes are empty?"); return; } helpernx.Attributes = atrib; VertexBuffer = helpernx.ToVertexBuffer(); } internal List verts = new List(); internal List norms = new List(); internal List uv0 = new List(); internal List uv1 = new List(); internal List uv2 = new List(); internal List tans = new List(); internal List bitans = new List(); internal List weights = new List(); internal List boneInd = new List(); internal List colors = new List(); public string GetBoneNameFromIndex(FMDL mdl, int index) { if (index == 0) return ""; return mdl.Skeleton.bones[mdl.Skeleton.Node_Array[index]].Text; } public void UpdateVertices() { // CalculateTangentBitangent(); foreach (Vertex vtx in vertices) { if (VertexSkinCount == 0 || VertexSkinCount == 1) { // Console.WriteLine("Old " + vtx.pos); // vtx.pos = TransformLocal(vtx.pos); // vtx.nrm = TransformLocal(vtx.nrm, false); // Console.WriteLine("New " + vtx.pos); } // Console.WriteLine($"Weight count {vtx.boneWeights.Count}"); // Console.WriteLine($"Index count {vtx.boneIds.Count}"); verts.Add(new Syroot.Maths.Vector4F(vtx.pos.X, vtx.pos.Y, vtx.pos.Z, 1.0f)); norms.Add(new Syroot.Maths.Vector4F(vtx.nrm.X, vtx.nrm.Y, vtx.nrm.Z, 0)); uv0.Add(new Syroot.Maths.Vector4F(vtx.uv0.X, vtx.uv0.Y, 0, 0)); uv1.Add(new Syroot.Maths.Vector4F(vtx.uv1.X, vtx.uv1.Y, 0, 0)); uv2.Add(new Syroot.Maths.Vector4F(vtx.uv2.X, vtx.uv2.Y, 0, 0)); tans.Add(new Syroot.Maths.Vector4F(vtx.tan.X, vtx.tan.Y, vtx.tan.Z, vtx.tan.W)); bitans.Add(new Syroot.Maths.Vector4F(vtx.bitan.X, vtx.bitan.Y, vtx.bitan.Z, vtx.bitan.W)); if (vtx.boneWeights.Count == 0) { vtx.boneWeights.Add(0); vtx.boneWeights.Add(0); vtx.boneWeights.Add(0); vtx.boneWeights.Add(0); } if (vtx.boneWeights.Count == 1) { vtx.boneWeights.Add(0); vtx.boneWeights.Add(0); vtx.boneWeights.Add(0); } if (vtx.boneWeights.Count == 2) { vtx.boneWeights.Add(0); vtx.boneWeights.Add(0); } if (vtx.boneWeights.Count == 3) { vtx.boneWeights.Add(0); } if (vtx.boneIds.Count == 0) { vtx.boneIds.Add(0); vtx.boneIds.Add(0); vtx.boneIds.Add(0); vtx.boneIds.Add(0); } if (vtx.boneIds.Count == 1) { vtx.boneIds.Add(0); vtx.boneIds.Add(0); vtx.boneIds.Add(0); } if (vtx.boneIds.Count == 2) { vtx.boneIds.Add(0); vtx.boneIds.Add(0); } if (vtx.boneIds.Count == 3) { vtx.boneIds.Add(0); } weights.Add(new Syroot.Maths.Vector4F(vtx.boneWeights[0], vtx.boneWeights[1], vtx.boneWeights[2], vtx.boneWeights[3])); boneInd.Add(new Syroot.Maths.Vector4F(vtx.boneIds[0], vtx.boneIds[1], vtx.boneIds[2], vtx.boneIds[3])); colors.Add(new Syroot.Maths.Vector4F(vtx.col.X, vtx.col.Y, vtx.col.Z, vtx.col.W)); } } public List CreateDisplayVertices() { // rearrange faces display = lodMeshes[DisplayLODIndex].getDisplayFace().ToArray(); List displayVertList = new List(); if (lodMeshes[DisplayLODIndex].faces.Count <= 3) return displayVertList; foreach (Vertex v in vertices) { DisplayVertex displayVert = new DisplayVertex() { pos = v.pos, nrm = v.nrm, tan = v.tan.Xyz, bit = v.bitan.Xyz, col = v.col, uv = v.uv0, uv2 = v.uv1, uv3 = v.uv2, node = new Vector4( v.boneIds.Count > 0 ? v.boneIds[0] : -1, v.boneIds.Count > 1 ? v.boneIds[1] : -1, v.boneIds.Count > 2 ? v.boneIds[2] : -1, v.boneIds.Count > 3 ? v.boneIds[3] : -1), weight = new Vector4( v.boneWeights.Count > 0 ? v.boneWeights[0] : 0, v.boneWeights.Count > 1 ? v.boneWeights[1] : 0, v.boneWeights.Count > 2 ? v.boneWeights[2] : 0, v.boneWeights.Count > 3 ? v.boneWeights[3] : 0), }; displayVertList.Add(displayVert); /* Console.WriteLine($"---------------------------------------------------------------------------------------"); Console.WriteLine($"Position {displayVert.pos.X} {displayVert.pos.Y} {displayVert.pos.Z}"); Console.WriteLine($"Normal {displayVert.nrm.X} {displayVert.nrm.Y} {displayVert.nrm.Z}"); Console.WriteLine($"Binormal {displayVert.bit.X} {displayVert.bit.Y} {displayVert.bit.Z}"); Console.WriteLine($"Tanget {displayVert.tan.X} {displayVert.tan.Y} {displayVert.tan.Z}"); Console.WriteLine($"Color {displayVert.col.X} {displayVert.col.Y} {displayVert.col.Z} {displayVert.col.W}"); Console.WriteLine($"UV Layer 1 {displayVert.uv.X} {displayVert.uv.Y}"); Console.WriteLine($"UV Layer 2 {displayVert.uv2.X} {displayVert.uv2.Y}"); Console.WriteLine($"UV Layer 3 {displayVert.uv3.X} {displayVert.uv3.Y}"); Console.WriteLine($"Bone Index {displayVert.node.X} {displayVert.node.Y} {displayVert.node.Z} {displayVert.node.W}"); Console.WriteLine($"Weights {displayVert.weight.X} {displayVert.weight.Y} {displayVert.weight.Z} {displayVert.weight.W}"); Console.WriteLine($"---------------------------------------------------------------------------------------");*/ } return displayVertList; } } }