using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Threading.Tasks; using Switch_Toolbox; using System.Windows.Forms; using Switch_Toolbox.Library; using Switch_Toolbox.Library.IO; using Switch_Toolbox.Library.Forms; using Switch_Toolbox.Library.Rendering; using OpenTK; using OpenTK.Graphics.OpenGL; using GL_EditorFramework.Interfaces; using GL_EditorFramework.GL_Core; namespace FirstPlugin { public class MKAGPDX_Model : TreeNodeFile, IFileFormat { public FileType FileType { get; set; } = FileType.Model; public bool CanSave { get; set; } public string[] Description { get; set; } = new string[] { "Mario Kart Arcade GP DX" }; public string[] Extension { get; set; } = new string[] { "*.bin", "*.mot" }; public string FileName { get; set; } public string FilePath { get; set; } public IFileInfo IFileInfo { get; set; } public bool Identify(System.IO.Stream stream) { using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true)) { return reader.CheckSignature(4, "BIKE"); } } public Type[] Types { get { List types = new List(); types.Add(typeof(MenuExt)); return types.ToArray(); } } class MenuExt : IFileMenuExtension { public STToolStripItem[] NewFileMenuExtensions => null; public STToolStripItem[] NewFromFileMenuExtensions => null; public STToolStripItem[] ToolsMenuExtensions => toolExt; public STToolStripItem[] TitleBarExtensions => null; public STToolStripItem[] CompressionMenuExtensions => null; public STToolStripItem[] ExperimentalMenuExtensions => null; public STToolStripItem[] EditMenuExtensions => null; public ToolStripButton[] IconButtonMenuExtensions => null; STToolStripItem[] toolExt = new STToolStripItem[1]; public MenuExt() { toolExt[0] = new STToolStripItem("Models"); toolExt[0].DropDownItems.Add(new STToolStripItem("Batch Export (MKAGPDX .bin)", BatchExport)); toolExt[0].DropDownItems.Add(new STToolStripItem("Batch Export as Combined (MKAGPDX .bin)", BatchExportCombined)); } public void BatchExportCombined(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect = true; ofd.Filter = "Supported Formats|*.bin"; if (ofd.ShowDialog() != DialogResult.OK) return; SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Supported Formats|*.dae"; if (sfd.ShowDialog() == DialogResult.OK) { List Objects = new List(); STSkeleton Skeleton = new STSkeleton(); List Materials = new List(); int MatIndex = 0; foreach (var file in ofd.FileNames) { MKAGPDX_Model model = new MKAGPDX_Model(); var stream = File.Open(file, FileMode.Open); model.Load(stream); stream.Dispose(); foreach (STGenericMaterial mat in model.Nodes[0].Nodes) { mat.Text = $"Material {MatIndex++}"; Materials.Add(mat); } Skeleton.bones.AddRange(((STSkeleton)model.DrawableContainer.Drawables[0]).bones); Objects.AddRange(((Renderer)model.DrawableContainer.Drawables[1]).Meshes); model.Unload(); } AssimpSaver assimp = new AssimpSaver(); ExportModelSettings settings = new ExportModelSettings(); assimp.SaveFromModel(Objects, Materials, sfd.FileName, new List(), Skeleton); } } public void BatchExport(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.bin"; if (ofd.ShowDialog() != DialogResult.OK) return; FolderSelectDialog folderDlg = new FolderSelectDialog(); if (folderDlg.ShowDialog() == DialogResult.OK) { foreach (var file in ofd.FileNames) { MKAGPDX_Model model = new MKAGPDX_Model(); var stream = File.Open(file, FileMode.Open); model.Load(stream); stream.Dispose(); string Path = System.IO.Path.Combine(folderDlg.SelectedPath, System.IO.Path.GetFileNameWithoutExtension(file) + ".dae"); model.ExportModel(Path); model.Unload(); } } } } Header header; public DrawableContainer DrawableContainer = new DrawableContainer(); public void Load(System.IO.Stream stream) { DrawableContainer.Name = FileName; Text = FileName; header = new Header(); header.Read(new FileReader(stream), this); ContextMenuStrip = new STContextMenuStrip(); ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export Model", null, ExportModelAction, Keys.Control | Keys.E)); } private void ExportModelAction(object sender, EventArgs args) { ExportModel(); } private void ExportModel() { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Supported Formats|*.dae;"; if (sfd.ShowDialog() == DialogResult.OK) { ExportModel(sfd.FileName); } } private void ExportModel(string FileName) { AssimpSaver assimp = new AssimpSaver(); ExportModelSettings settings = new ExportModelSettings(); List Materials = new List(); foreach (STGenericMaterial mat in Nodes[0].Nodes) Materials.Add(mat); var model = new STGenericModel(); model.Materials = Materials; model.Objects = ((Renderer)DrawableContainer.Drawables[1]).Meshes; assimp.SaveFromModel(model, FileName, new List(), ((STSkeleton)DrawableContainer.Drawables[0])); } public void Unload() { foreach (var mesh in ((Renderer)DrawableContainer.Drawables[1]).Meshes) { mesh.vertices.Clear(); mesh.faces.Clear(); mesh.display = new int[0]; } ((Renderer)DrawableContainer.Drawables[1]).Meshes.Clear(); DrawableContainer.Drawables.Clear(); DrawableContainer = null; header.Materials.Clear(); header.TextureMaps.Clear(); header.LowerNodes.Clear(); header.UpperNodes.Clear(); Nodes.Clear(); header = null; } public byte[] Save() { return null; } public struct DisplayVertex { // Used for rendering. public Vector3 pos; public Vector3 nrm; public Vector3 tan; public Vector2 uv; public Vector4 col; public Vector4 node; public Vector4 weight; public Vector2 uv2; public Vector2 uv3; public static int Size = 4 * (3 + 3 + 3 + 2 + 4 + 4 + 4 + 2 + 2); } public class MeshWrapper : STGenericObject { public int[] display; public int DisplayId; public List CreateDisplayVertices() { 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, 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($"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; } } public class Renderer : AbstractGlDrawable { public Vector3 Max = new Vector3(0); public Vector3 Min = new Vector3(0); public List SelectedTypes = new List(); public Vector3 position = new Vector3(0, 0, 0); protected bool Selected = false; protected bool Hovered = false; // public override bool IsSelected() => Selected; // public override bool IsSelected(int partIndex) => Selected; public bool IsHovered() => Selected; // gl buffer objects int vbo_position; int ibo_elements; public List Meshes = new List(); private void GenerateBuffers() { GL.GenBuffers(1, out vbo_position); GL.GenBuffers(1, out ibo_elements); } public void Destroy() { GL.DeleteBuffer(vbo_position); GL.DeleteBuffer(ibo_elements); } public void UpdateVertexData() { if (!Runtime.OpenTKInitialized) return; DisplayVertex[] Vertices; int[] Faces; int poffset = 0; int voffset = 0; List Vs = new List(); List Ds = new List(); foreach (var m in Meshes) { m.Offset = poffset * 4; List pv = m.CreateDisplayVertices(); Vs.AddRange(pv); for (int i = 0; i < m.lodMeshes[m.DisplayLODIndex].displayFaceSize; i++) { Ds.Add(m.display[i] + voffset); } poffset += m.lodMeshes[m.DisplayLODIndex].displayFaceSize; voffset += pv.Count; } // Binds Vertices = Vs.ToArray(); Faces = Ds.ToArray(); // Bind only once! GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_position); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(Vertices.Length * DisplayVertex.Size), Vertices, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(Faces.Length * sizeof(int)), Faces, BufferUsageHint.StaticDraw); LibraryGUI.Instance.UpdateViewport(); } public ShaderProgram defaultShaderProgram; public override void Prepare(GL_ControlModern control) { string pathFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "MKAGPDX") + "\\MKAGPDX_Model.frag"; string pathVert = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "MKAGPDX") + "\\MKAGPDX_Model.vert"; var defaultFrag = new FragmentShader(File.ReadAllText(pathFrag)); var defaultVert = new VertexShader(File.ReadAllText(pathVert)); defaultShaderProgram = new ShaderProgram(defaultFrag, defaultVert, control); } public override void Prepare(GL_ControlLegacy control) { } private void CheckBuffers() { if (!Runtime.OpenTKInitialized) return; bool buffersWereInitialized = ibo_elements != 0 && vbo_position != 0; if (!buffersWereInitialized) { GenerateBuffers(); UpdateVertexData(); } } public override void Draw(GL_ControlLegacy control, Pass pass) { CheckBuffers(); if (!Runtime.OpenTKInitialized) return; } public override void Draw(GL_ControlModern control, Pass pass) { CheckBuffers(); if (!Runtime.OpenTKInitialized) return; control.CurrentShader = defaultShaderProgram; SetRenderSettings(defaultShaderProgram); Matrix4 camMat = control.ModelMatrix * control.CameraMatrix * control.ProjectionMatrix; GL.Disable(EnableCap.CullFace); GL.Uniform3(defaultShaderProgram["difLightDirection"], Vector3.TransformNormal(new Vector3(0f, 0f, -1f), camMat.Inverted()).Normalized()); GL.Uniform3(defaultShaderProgram["difLightColor"], new Vector3(1)); GL.Uniform3(defaultShaderProgram["ambLightColor"], new Vector3(1)); defaultShaderProgram.EnableVertexAttributes(); foreach (var mdl in Meshes) { DrawModel(mdl, defaultShaderProgram); } defaultShaderProgram.DisableVertexAttributes(); GL.UseProgram(0); GL.Disable(EnableCap.DepthTest); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.CullFace); } private void SetRenderSettings(ShaderProgram shader) { shader.SetBoolToInt("renderVertColor", Runtime.renderVertColor); GL.Uniform1(defaultShaderProgram["renderType"], (int)Runtime.viewportShading); } private void DrawModel(MeshWrapper m, ShaderProgram shader, bool drawSelection = false) { if (m.lodMeshes[m.DisplayLODIndex].faces.Count <= 3) return; SetVertexAttributes(m, shader); if (m.Checked) { if ((m.IsSelected)) { DrawModelSelection(m, shader); } else { if (Runtime.RenderModelWireframe) { DrawModelWireframe(m, shader); } if (Runtime.RenderModels) { GL.DrawElements(PrimitiveType.Triangles, m.lodMeshes[m.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, m.Offset); } } } } private static void DrawModelSelection(MeshWrapper p, ShaderProgram shader) { //This part needs to be reworked for proper outline. Currently would make model disappear GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset); GL.Enable(EnableCap.StencilTest); // use vertex color for wireframe color GL.Uniform1(shader["colorOverride"], 1); GL.PolygonMode(MaterialFace.Front, PolygonMode.Line); GL.Enable(EnableCap.LineSmooth); GL.LineWidth(1.5f); GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); GL.Uniform1(shader["colorOverride"], 0); GL.Enable(EnableCap.DepthTest); } private void SetVertexAttributes(MeshWrapper m, ShaderProgram shader) { GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_position); GL.VertexAttribPointer(shader.GetAttribute("vPosition"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 0); //+12 GL.VertexAttribPointer(shader.GetAttribute("vNormal"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 12); //+12 GL.VertexAttribPointer(shader.GetAttribute("vTangent"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 24); //+12 GL.VertexAttribPointer(shader.GetAttribute("vUV0"), 2, VertexAttribPointerType.Float, false, DisplayVertex.Size, 36); //+8 GL.VertexAttribPointer(shader.GetAttribute("vColor"), 4, VertexAttribPointerType.Float, false, DisplayVertex.Size, 44); //+16 GL.VertexAttribIPointer(shader.GetAttribute("vBone"), 4, VertexAttribIntegerType.Int, DisplayVertex.Size, new IntPtr(60)); //+16 GL.VertexAttribPointer(shader.GetAttribute("vWeight"), 4, VertexAttribPointerType.Float, false, DisplayVertex.Size, 76);//+16 GL.VertexAttribPointer(shader.GetAttribute("vUV1"), 2, VertexAttribPointerType.Float, false, DisplayVertex.Size, 92);//+8 GL.VertexAttribPointer(shader.GetAttribute("vUV2"), 2, VertexAttribPointerType.Float, false, DisplayVertex.Size, 100);//+8 GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements); } private static void DrawModelWireframe(MeshWrapper p, ShaderProgram shader) { // use vertex color for wireframe color GL.Uniform1(shader["colorOverride"], 1); GL.PolygonMode(MaterialFace.Front, PolygonMode.Line); GL.Enable(EnableCap.LineSmooth); GL.LineWidth(1.5f); GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); GL.Uniform1(shader["colorOverride"], 0); } } Viewport viewport { get { var editor = LibraryGUI.Instance.GetObjectEditor(); return editor.GetViewport(); } set { var editor = LibraryGUI.Instance.GetObjectEditor(); editor.LoadViewport(value); } } bool DrawablesLoaded = false; public override void OnClick(TreeView treeView) { if (Runtime.UseOpenGL) { if (viewport == null) { viewport = new Viewport(ObjectEditor.GetDrawableContainers()); viewport.Dock = DockStyle.Fill; } if (!DrawablesLoaded) { ObjectEditor.AddContainer(DrawableContainer); DrawablesLoaded = true; } viewport.ReloadDrawables(DrawableContainer); LibraryGUI.Instance.LoadEditor(viewport); viewport.Text = Text; } } public class Header { public uint Version { get; set; } public uint Alignment { get; set; } public uint HeaderSize { get; set; } public List Materials = new List(); public List TextureMaps = new List(); public List UpperNodes = new List(); public Tuple LinkNodes; //Links two nodes for some reason public List LowerNodes = new List(); public STSkeleton Skeleton { get; set; } Renderer DrawableRenderer { get; set; } public void Read(FileReader reader, MKAGPDX_Model root) { Skeleton = new STSkeleton(); DrawableRenderer = new Renderer(); root.DrawableContainer.Drawables.Add(Skeleton); root.DrawableContainer.Drawables.Add(DrawableRenderer); reader.ReadSignature(4, "BIKE"); ushort Type = reader.ReadUInt16(); ushort Unknown = reader.ReadUInt16(); if (Type == 0) { } else if ((Type == 1)) { throw new Exception("Animation files not supported yet!"); } else { throw new Exception("Unknown type found! " + Type); } Alignment = reader.ReadUInt32(); uint Padding = reader.ReadUInt32(); uint MaterialCount = reader.ReadUInt32(); HeaderSize = reader.ReadUInt32(); uint TextureMapsCount = reader.ReadUInt32(); uint TextureMapsOffset = reader.ReadUInt32(); //Seems to be a node based structure. Not %100 sure what decides which gets put into which uint UpperLevelNodeCount = reader.ReadUInt32(); uint UpperLevelNodeOffset = reader.ReadUInt32(); uint FirstNodeOffset = reader.ReadUInt32(); //Either an offset or the total size of section up to the node uint LinkNodeCount = reader.ReadUInt32(); uint LinkNodeOffset = reader.ReadUInt32(); uint LowerLevelNodeCount = reader.ReadUInt32(); uint LowerLevelNodeOffset = reader.ReadUInt32(); uint Padding2 = reader.ReadUInt32(); uint[] Unknowns = reader.ReadUInt32s(10); root.Nodes.Add("Materials"); long pos = reader.Position; if (TextureMapsOffset != 0) { reader.SeekBegin(TextureMapsOffset); for (int i = 0; i < TextureMapsCount; i++) { TextureMaps.Add(reader.ReadNameOffset(false, typeof(uint))); } } reader.SeekBegin(pos); for (int i = 0; i < MaterialCount; i++) { Material mat = new Material(); mat.Read(reader); Materials.Add(mat); var genericMat = new STGenericMaterial(); genericMat.Text = $"Material {i}"; for (int t = 0; t < mat.TextureIndices.Length; t++) { if(mat.TextureIndices[t] != -1) { Console.WriteLine("TextureIndices " + mat.TextureIndices[t]); string Texture = TextureMaps[mat.TextureIndices[t]]; var textureMap = new STGenericMatTexture(); textureMap.Name = Texture; genericMat.TextureMaps.Add(textureMap); if (Texture.EndsWith("col.dds")) textureMap.Type = STGenericMatTexture.TextureType.Diffuse; if (Texture.EndsWith("col.mot")) textureMap.Type = STGenericMatTexture.TextureType.Diffuse; } } root.Nodes[0].Nodes.Add(genericMat); } if (LowerLevelNodeCount != 0) { for (int i = 0; i < LowerLevelNodeCount; i++) { reader.SeekBegin(LowerLevelNodeOffset + (i * 8)); string NodeName = reader.ReadNameOffset(false, typeof(uint)); uint Offset = reader.ReadUInt32(); if (Offset != 0) { reader.SeekBegin(Offset); Node node = new Node(); node.Name = NodeName; node.Read(reader); LowerNodes.Add(node); } } } if (FirstNodeOffset != 0) { reader.SeekBegin(FirstNodeOffset); uint NodeOffset = reader.ReadUInt32(); reader.SeekBegin(NodeOffset); Node node = new Node(); node.Name = GetNodeName(LowerNodes, node); node.Read(reader); LoadChildern(LowerNodes, node, root); } Skeleton.update(); Skeleton.reset(); } private void LoadChildern(List NodeLookup, Node Node, TreeNode root) { var NewNode = SetWrapperNode(Node); root.Nodes.Add(NewNode); for (int i = 0; i < Node.Children.Count; i++) { Node.Children[i].Name = GetNodeName(NodeLookup, Node.Children[i]); var newChild = SetWrapperNode(Node.Children[i]); NewNode.Nodes.Add(newChild); LoadChildern(NodeLookup, Node.Children[i], newChild); } } private TreeNode SetWrapperNode(Node Node) { if (Node.IsBone) { STBone boneNode = new STBone(Skeleton); boneNode.RotationType = STBone.BoneRotationType.Euler; boneNode.Checked = true; boneNode.Text = Node.Name; boneNode.position = new float[3]; boneNode.scale = new float[3]; boneNode.rotation = new float[4]; boneNode.position[0] = Node.Translation.X; boneNode.position[1] = Node.Translation.Y; boneNode.position[2] = Node.Translation.Z; boneNode.rotation[0] = Node.Rotation.X; boneNode.rotation[1] = Node.Rotation.Y; boneNode.rotation[2] = Node.Rotation.Z; boneNode.scale[0] = Node.Scale.X; boneNode.scale[1] = Node.Scale.Y; boneNode.scale[2] = Node.Scale.Z; if (Node.IsBone) Skeleton.bones.Add(boneNode); return boneNode; } else if (Node.IsMesh) { MeshWrapper meshNode = new MeshWrapper(); meshNode.ImageKey = "mesh"; meshNode.SelectedImageKey = "mesh"; int i = 0; meshNode.lodMeshes = new List(); var msh = new MeshWrapper.LOD_Mesh(); msh.PrimitiveType = STPolygonType.Triangle; msh.FirstVertex = 0; msh.faces = Node.SubMeshes[0].Faces; meshNode.vertices = Node.SubMeshes[0].Vertices; meshNode.lodMeshes.Add(msh); foreach (SubMesh subMesh in Node.SubMeshes) { if (i > 0) { MeshWrapper subMeshNode = new MeshWrapper(); subMeshNode.ImageKey = "mesh"; subMeshNode.SelectedImageKey = "mesh"; subMeshNode.lodMeshes = new List(); var submsh = new MeshWrapper.LOD_Mesh(); submsh.PrimitiveType = STPolygonType.Triangle; submsh.FirstVertex = 0; submsh.faces = subMesh.Faces; subMeshNode.lodMeshes.Add(submsh); subMeshNode.vertices = subMesh.Vertices; DrawableRenderer.Meshes.Add(subMeshNode); } i++; } meshNode.Checked = true; meshNode.Text = Node.Name; DrawableRenderer.Meshes.Add(meshNode); return meshNode; } else return new TreeNode(Node.Name); } private static string GetNodeName(List NodeLookup, Node node) { for (int i = 0; i < NodeLookup.Count; i++) { if (NodeLookup[i].Position == node.Position) return NodeLookup[i].Name; } return ""; } } public class Material { public Vector4 Ambient; public Vector4 Diffuse; public Vector4 Specular; public Vector4 Ambience; public float Shiny; public Vector4 Transparency; public float TransGlossy; public float TransparencySamples; public Vector4 Reflectivity; public float ReflectGlossy; public float ReflectSample; public float IndexRefreaction; public float Translucency; public float Unknown; public short[] TextureIndices; public uint[] Unknowns; public Material() { Ambient = new Vector4(0.3f, 0.3f, 0.3f,1.0f); Diffuse = new Vector4(0.7f, 0.7f, 0.7f, 1.0f); Specular = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); Ambience = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); Shiny = 50; Transparency = new Vector4(0.0f, 0.0f, 0.0f, 1.0f); TextureIndices = new short[10]; } public void Read(FileReader reader) { Ambient = reader.ReadVec4(); Diffuse = reader.ReadVec4(); Specular = reader.ReadVec4(); Ambience = reader.ReadVec4(); Shiny = reader.ReadSingle(); Transparency = reader.ReadVec4(); TransGlossy = reader.ReadSingle(); TransparencySamples = reader.ReadSingle(); Reflectivity = reader.ReadVec4(); ReflectGlossy = reader.ReadSingle(); ReflectSample = reader.ReadSingle(); IndexRefreaction = reader.ReadSingle(); Translucency = reader.ReadSingle(); Unknown = reader.ReadSingle(); TextureIndices = reader.ReadInt16s(6); Unknowns = reader.ReadUInt32s(10); } } public class SubMesh { public Node ParentNode { get; set; } public Material Material { get; set; } public List Vertices = new List(); public List Faces = new List(); public SubMesh(Node parentNode) { ParentNode = parentNode; } public void Read(FileReader reader) { uint Padding = reader.ReadUInt32(); uint FaceCount = reader.ReadUInt32(); uint[] Unknowns = reader.ReadUInt32s(5); uint VertexCount = reader.ReadUInt32(); uint VertexPositionOffset = reader.ReadUInt32(); uint VertexNormalOffset = reader.ReadUInt32(); uint UnknownOffset = reader.ReadUInt32(); uint TexCoord0Offset = reader.ReadUInt32(); uint TexCoord1Offset = reader.ReadUInt32(); uint Unknown1Offset = reader.ReadUInt32(); uint Unknown2Offset = reader.ReadUInt32(); uint FaceOffset = reader.ReadUInt32(); uint SkinCount = reader.ReadUInt32(); //Unsure uint Unknown = reader.ReadUInt32(); //Something related to count uint WeightOffset = reader.ReadUInt32(); uint Unknown2 = reader.ReadUInt32(); for (int i = 0; i < VertexCount; i++) { Vertex vertex = new Vertex(); Vertices.Add(vertex); if (VertexPositionOffset != 0) { reader.SeekBegin(VertexPositionOffset + (i * 12)); vertex.pos = reader.ReadVec3(); vertex.pos = Vector3.TransformPosition(vertex.pos, ParentNode.Transform); } if (VertexNormalOffset != 0) { reader.SeekBegin(VertexNormalOffset + (i * 12)); vertex.nrm = reader.ReadVec3(); vertex.nrm = Vector3.TransformNormal(vertex.nrm, ParentNode.Transform); } if (TexCoord0Offset != 0) { reader.SeekBegin(TexCoord0Offset + (i * 8)); vertex.uv0 = reader.ReadVec2(); } } reader.SeekBegin(FaceOffset); for (int i = 0; i < FaceCount * 3; i++) { Faces.Add(reader.ReadUInt16()); } } } public class Node { public string Name { get; set; } public bool Visible { get; set; } public Vector3 Scale { get; set; } public Vector3 Rotation { get; set; } public Vector3 Translation { get; set; } public byte[] Unknowns; public List Children = new List(); public List SubMeshes = new List(); internal long Position; public bool IsBone { get { return Name.Contains("jnt") || Name.Contains("center"); } } public bool IsMesh { get { return SubMeshes.Count > 0; } } public Matrix4 Transform { get; set; } public void Read(FileReader reader) { Position = reader.Position; Visible = reader.ReadUInt32() == 1; Scale = reader.ReadVec3(); Rotation = reader.ReadVec3(); Translation = reader.ReadVec3(); Unknowns = reader.ReadBytes(16); uint SubMeshArrayOffsetPtr = reader.ReadUInt32(); uint ChildNodeOffsetPtr = reader.ReadUInt32(); Matrix4 TranslateMat = Matrix4.CreateTranslation(Translation); Matrix4 RotXMat = Matrix4.CreateRotationX(Rotation.X); Matrix4 RotYMat = Matrix4.CreateRotationY(Rotation.Y); Matrix4 RotZMat = Matrix4.CreateRotationZ(Rotation.Z); Matrix4 ScaleMat = Matrix4.CreateTranslation(Scale); Transform = ScaleMat * (RotXMat * RotYMat * RotZMat) * TranslateMat; if (SubMeshArrayOffsetPtr != 0) { //4 possible sub meshes reader.SeekBegin(SubMeshArrayOffsetPtr); uint SubMeshArrayOffset1 = reader.ReadUInt32(); uint SubMeshArrayOffset2 = reader.ReadUInt32(); uint SubMeshArrayOffset3 = reader.ReadUInt32(); uint SubMeshArrayOffset4 = reader.ReadUInt32(); if (SubMeshArrayOffset1 != 0) { reader.SeekBegin(SubMeshArrayOffset1); SubMesh subMesh = new SubMesh(this); subMesh.Read(reader); SubMeshes.Add(subMesh); } if (SubMeshArrayOffset2 != 0) { reader.SeekBegin(SubMeshArrayOffset2); SubMesh subMesh = new SubMesh(this); subMesh.Read(reader); SubMeshes.Add(subMesh); } if (SubMeshArrayOffset3 != 0) { reader.SeekBegin(SubMeshArrayOffset3); SubMesh subMesh = new SubMesh(this); subMesh.Read(reader); SubMeshes.Add(subMesh); } if (SubMeshArrayOffset4 != 0) { reader.SeekBegin(SubMeshArrayOffset4); SubMesh subMesh = new SubMesh(this); subMesh.Read(reader); SubMeshes.Add(subMesh); } } if (ChildNodeOffsetPtr != 0) { //4 possible children reader.SeekBegin(ChildNodeOffsetPtr); uint ChildNodeOffset1 = reader.ReadUInt32(); uint ChildNodeOffset2 = reader.ReadUInt32(); uint ChildNodeOffset3 = reader.ReadUInt32(); uint ChildNodeOffset4 = reader.ReadUInt32(); if (ChildNodeOffset1 != 0) { reader.SeekBegin(ChildNodeOffset1); Node ChildNode = new Node(); ChildNode.Read(reader); Children.Add(ChildNode); } if (ChildNodeOffset2 != 0) { reader.SeekBegin(ChildNodeOffset2); Node ChildNode = new Node(); ChildNode.Read(reader); Children.Add(ChildNode); } if (ChildNodeOffset3 != 0) { reader.SeekBegin(ChildNodeOffset3); Node ChildNode = new Node(); ChildNode.Read(reader); Children.Add(ChildNode); } if (ChildNodeOffset4 != 0) { reader.SeekBegin(ChildNodeOffset4); Node ChildNode = new Node(); ChildNode.Read(reader); Children.Add(ChildNode); } } //After repeats a fairly similar structure, with SRT values //Unsure what it's used for? } } } }