580 lines
21 KiB
C#
580 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Toolbox;
|
|
using System.Windows.Forms;
|
|
using Toolbox.Library;
|
|
using Toolbox.Library.IO;
|
|
using Toolbox.Library.Rendering;
|
|
using Toolbox.Library.Forms;
|
|
|
|
namespace FirstPlugin
|
|
{
|
|
public class GMX : TreeNodeFile, IFileFormat, IContextMenuNode
|
|
{
|
|
public FileType FileType { get; set; } = FileType.Model;
|
|
|
|
public bool CanSave { get; set; }
|
|
public string[] Description { get; set; } = new string[] { "GMX" };
|
|
public string[] Extension { get; set; } = new string[] { "*.gmx" };
|
|
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 Toolbox.Library.IO.FileReader(stream, true))
|
|
{
|
|
return reader.CheckSignature(4, "GMX2");
|
|
}
|
|
}
|
|
|
|
public Type[] Types
|
|
{
|
|
get
|
|
{
|
|
List<Type> types = new List<Type>();
|
|
return types.ToArray();
|
|
}
|
|
}
|
|
|
|
Viewport viewport
|
|
{
|
|
get
|
|
{
|
|
var editor = LibraryGUI.GetObjectEditor();
|
|
return editor.GetViewport();
|
|
}
|
|
set
|
|
{
|
|
var editor = LibraryGUI.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.LoadEditor(viewport);
|
|
|
|
viewport.Text = Text;
|
|
}
|
|
}
|
|
|
|
public ToolStripItem[] GetContextMenuItems()
|
|
{
|
|
List<ToolStripItem> Items = new List<ToolStripItem>();
|
|
Items.Add(new ToolStripMenuItem("Export", null, ExportAction, Keys.Control | Keys.E));
|
|
return Items.ToArray();
|
|
}
|
|
|
|
private void ExportAction(object sender, EventArgs e)
|
|
{
|
|
SaveFileDialog sfd = new SaveFileDialog();
|
|
sfd.Filter = "Supported Formats|*.dae;";
|
|
if (sfd.ShowDialog() == DialogResult.OK)
|
|
{
|
|
ExportModelSettings exportDlg = new ExportModelSettings();
|
|
if (exportDlg.ShowDialog() == DialogResult.OK)
|
|
ExportModel(sfd.FileName, exportDlg.Settings);
|
|
}
|
|
}
|
|
|
|
public void ExportModel(string fileName, DAE.ExportSettings settings)
|
|
{
|
|
var model = new STGenericModel();
|
|
model.Materials = new List<STGenericMaterial>();
|
|
model.Objects = Renderer.Meshes;
|
|
|
|
DAE.Export(fileName, settings, model, new List<STGenericTexture>(), new STSkeleton());
|
|
}
|
|
|
|
public Header GMXHeader;
|
|
public GMX_Renderer Renderer;
|
|
|
|
public DrawableContainer DrawableContainer = new DrawableContainer();
|
|
|
|
public void Load(System.IO.Stream stream)
|
|
{
|
|
CanSave = true;
|
|
|
|
//Set renderer
|
|
Renderer = new GMX_Renderer();
|
|
DrawableContainer.Name = FileName;
|
|
DrawableContainer.Drawables.Add(Renderer);
|
|
|
|
//Read file
|
|
GMXHeader = new Header();
|
|
GMXHeader.Read(new FileReader(stream));
|
|
|
|
int MeshIndex = 0;
|
|
for (int i = 0; i < GMXHeader.Meshes.Count; i++)
|
|
{
|
|
var renderedMesh = new GenericRenderedObject();
|
|
renderedMesh.ImageKey = "mesh";
|
|
renderedMesh.SelectedImageKey = "mesh";
|
|
renderedMesh.Checked = true;
|
|
|
|
if (GMXHeader.Meshes[i].VertexGroup != null)
|
|
{
|
|
renderedMesh.vertices = GMXHeader.Meshes[i].VertexGroup.Vertices;
|
|
}
|
|
|
|
if (GMXHeader.Meshes[i].IndexGroup != null)
|
|
{
|
|
renderedMesh.lodMeshes = new List<STGenericObject.LOD_Mesh>();
|
|
var msh = new STGenericObject.LOD_Mesh();
|
|
msh.PrimativeType = STPrimitiveType.Triangles;
|
|
msh.FirstVertex = 0;
|
|
|
|
/* int VertexID = 0;
|
|
for (int f = 0; f < GMXHeader.Meshes[i].VertexCount; f+= 3)
|
|
{
|
|
msh.faces.AddRange(new List<int>() { VertexID + 2, VertexID + 1, VertexID });
|
|
VertexID++;
|
|
}*/
|
|
|
|
for (int f = 0; f < GMXHeader.Meshes[i].IndexGroup.Indices.Length; f++)
|
|
{
|
|
msh.faces.AddRange(new int[3]
|
|
{
|
|
GMXHeader.Meshes[i].IndexGroup.Indices[f++],
|
|
GMXHeader.Meshes[i].IndexGroup.Indices[f++],
|
|
GMXHeader.Meshes[i].IndexGroup.Indices[f]
|
|
});
|
|
}
|
|
|
|
renderedMesh.lodMeshes.Add(msh);
|
|
}
|
|
|
|
renderedMesh.Tag = GMXHeader.Meshes[i];
|
|
|
|
if (GMXHeader.Meshes[i].VertexGroup != null)
|
|
{
|
|
renderedMesh.Text = $"Mesh ({MeshIndex++})";
|
|
Nodes.Add(renderedMesh);
|
|
Renderer.Meshes.Add(renderedMesh);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Unload()
|
|
{
|
|
|
|
}
|
|
|
|
public void Save(System.IO.Stream stream)
|
|
{
|
|
GMXHeader.Write(new FileWriter(stream));
|
|
}
|
|
|
|
public class Header
|
|
{
|
|
public uint HeaderSize;
|
|
|
|
public Header()
|
|
{
|
|
HeaderSize = 8;
|
|
}
|
|
|
|
public List<MESH> Meshes = new List<MESH>();
|
|
|
|
public void Read(FileReader reader)
|
|
{
|
|
reader.ReadSignature(4, "GMX2");
|
|
reader.SetByteOrder(true);
|
|
HeaderSize = reader.ReadUInt32();
|
|
|
|
while (reader.Position < reader.BaseStream.Length)
|
|
{
|
|
long pos = reader.Position;
|
|
|
|
string Magic = reader.ReadString(4);
|
|
uint SectionSize = reader.ReadUInt32();
|
|
|
|
Console.WriteLine($"Magic {Magic}");
|
|
reader.SeekBegin(pos);
|
|
switch (Magic)
|
|
{
|
|
case "PADX":
|
|
PADX padding = new PADX();
|
|
padding.Read(reader);
|
|
break;
|
|
case "INDX":
|
|
INDX indexGrp = new INDX();
|
|
indexGrp.Read(reader, GetLastMesh());
|
|
GetLastMesh().IndexGroup = indexGrp;
|
|
break;
|
|
case "VMAP":
|
|
VMAP vmap = new VMAP();
|
|
vmap.Read(reader);
|
|
GetLastMesh().VMapGroup = vmap;
|
|
break;
|
|
case "MESH":
|
|
MESH mesh = new MESH();
|
|
mesh.Read(reader);
|
|
Meshes.Add(mesh);
|
|
break;
|
|
case "VERT":
|
|
VERT vert = new VERT();
|
|
vert.Read(reader, GetLastMesh());
|
|
GetLastMesh().VertexGroup = vert;
|
|
break;
|
|
case "ENDX":
|
|
reader.ReadSignature(4, "ENDX");
|
|
uint EndSectionSize = reader.ReadUInt32();
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown section! " + Magic);
|
|
}
|
|
|
|
reader.SeekBegin(pos + SectionSize);
|
|
}
|
|
}
|
|
|
|
public MESH GetLastMesh()
|
|
{
|
|
return Meshes[Meshes.Count - 1];
|
|
}
|
|
|
|
public void Write(FileWriter writer)
|
|
{
|
|
writer.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
|
|
|
PADX padding = new PADX();
|
|
|
|
Console.WriteLine("Saving GMX");
|
|
writer.WriteSignature("GMX2");
|
|
writer.Write(HeaderSize);
|
|
for (int i = 0; i < Meshes.Count; i++)
|
|
{
|
|
//Write mesh header
|
|
padding.Write(writer, 32);
|
|
Meshes[i].Write(writer);
|
|
|
|
if (Meshes[i].VertexGroup != null)
|
|
{
|
|
//Write Vertices
|
|
padding.Write(writer, 64);
|
|
Meshes[i].VertexGroup.Write(writer, Meshes[i]);
|
|
}
|
|
|
|
if (Meshes[i].IndexGroup != null)
|
|
{
|
|
//Write Faces
|
|
padding.Write(writer, 32);
|
|
Meshes[i].IndexGroup.Write(writer);
|
|
}
|
|
|
|
if (Meshes[i].VMapGroup != null)
|
|
{
|
|
//Write VMAPS
|
|
padding.Write(writer, 32);
|
|
Meshes[i].VMapGroup.Write(writer);
|
|
}
|
|
}
|
|
writer.WriteSignature("ENDX");
|
|
writer.Write(8); //Last section size
|
|
writer.Close();
|
|
writer.Dispose();
|
|
}
|
|
|
|
public class VERT
|
|
{
|
|
public List<Vertex> Vertices = new List<Vertex>();
|
|
public List<uint> Unknowns = new List<uint>();
|
|
|
|
public void Read(FileReader reader, MESH mesh)
|
|
{
|
|
reader.ReadSignature(4, "VERT");
|
|
uint SectionSize = reader.ReadUInt32();
|
|
|
|
if (mesh.VertexSize == 32)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
vert.pos = reader.ReadVec3();
|
|
vert.nrm = reader.ReadVec3();
|
|
vert.uv0 = reader.ReadVec2();
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else if (mesh.VertexSize == 12)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
vert.pos = reader.ReadVec3();
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else if (mesh.VertexSize == 20)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
vert.pos = reader.ReadVec3();
|
|
vert.uv0 = reader.ReadVec2();
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else if (mesh.VertexSize == 24)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
vert.pos = reader.ReadVec3();
|
|
vert.nrm = reader.ReadVec3();
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else if (mesh.VertexSize == 36)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
if (mesh.Unknown1 != 0)
|
|
{
|
|
uint Unknown = reader.ReadUInt32(); //Bone index?
|
|
vert.pos = reader.ReadVec3();
|
|
vert.nrm = reader.ReadVec3();
|
|
vert.uv0 = reader.ReadVec2();
|
|
Unknowns.Add(Unknown);
|
|
}
|
|
else
|
|
{
|
|
vert.pos = reader.ReadVec3();
|
|
vert.nrm = reader.ReadVec3();
|
|
vert.col = ColorUtility.ToVector4(reader.ReadBytes(4));
|
|
vert.uv0 = reader.ReadVec2();
|
|
}
|
|
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else if (mesh.VertexSize == 40)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
if (mesh.Unknown1 != 0)
|
|
{
|
|
uint Unknown = reader.ReadUInt32(); //Bone index?
|
|
vert.pos = reader.ReadVec3();
|
|
vert.nrm = reader.ReadVec3();
|
|
vert.col = ColorUtility.ToVector4(reader.ReadBytes(4));
|
|
vert.uv0 = reader.ReadVec2();
|
|
}
|
|
else
|
|
throw new Exception($"Unsupported Vertex Size {mesh.VertexSize}");
|
|
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else if (mesh.VertexSize == 44)
|
|
{
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
Vertex vert = new Vertex();
|
|
vert.pos = reader.ReadVec3();
|
|
vert.nrm = reader.ReadVec3();
|
|
vert.col = ColorUtility.ToVector4(reader.ReadBytes(4));
|
|
vert.uv0 = reader.ReadVec2();
|
|
vert.uv1 = reader.ReadVec2();
|
|
|
|
Vertices.Add(vert);
|
|
}
|
|
}
|
|
else
|
|
throw new Exception($"Unsupported Vertex Size {mesh.VertexSize}");
|
|
}
|
|
|
|
public void Write(FileWriter writer, MESH mesh)
|
|
{
|
|
writer.WriteSignature("VERT");
|
|
writer.Write(Vertices.Count * mesh.VertexSize + 8);
|
|
for (int v = 0; v < mesh.VertexCount; v++)
|
|
{
|
|
if (mesh.VertexSize == 32)
|
|
{
|
|
writer.Write(Vertices[v].pos);
|
|
writer.Write(Vertices[v].nrm);
|
|
writer.Write(Vertices[v].uv0);
|
|
}
|
|
else if (mesh.VertexSize == 36)
|
|
{
|
|
writer.Write(Unknowns[v]);
|
|
writer.Write(Vertices[v].pos);
|
|
writer.Write(-Vertices[v].nrm);
|
|
writer.Write(Vertices[v].uv0);
|
|
}
|
|
else
|
|
throw new Exception($"Unsupported Vertex Size {mesh.VertexSize}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public class MESH
|
|
{
|
|
public INDX IndexGroup { get; set; }
|
|
public VERT VertexGroup { get; set; }
|
|
public VMAP VMapGroup { get; set; }
|
|
|
|
public ushort VertexSize { get; set; }
|
|
public ushort VertexCount { get; set; }
|
|
public uint FaceCount { get; set; }
|
|
|
|
public uint Unknown { get; set; }
|
|
public uint Unknown1 { get; set; }
|
|
public uint Unknown2 { get; set; }
|
|
public uint Unknown3 { get; set; }
|
|
|
|
public void Read(FileReader reader)
|
|
{
|
|
reader.ReadSignature(4, "MESH");
|
|
uint SectionSize = reader.ReadUInt32();
|
|
uint Padding = reader.ReadUInt32();
|
|
VertexSize = reader.ReadUInt16();
|
|
VertexCount = reader.ReadUInt16();
|
|
uint Padding2 = reader.ReadUInt32();
|
|
FaceCount = reader.ReadUInt32();
|
|
Unknown = reader.ReadUInt32();
|
|
Unknown1 = reader.ReadUInt32();
|
|
Unknown2 = reader.ReadUInt32();
|
|
Unknown3 = reader.ReadUInt32();
|
|
}
|
|
|
|
public void Write(FileWriter writer)
|
|
{
|
|
writer.WriteSignature("MESH");
|
|
writer.Write(40);
|
|
writer.Write(0);
|
|
writer.Write(VertexSize);
|
|
writer.Write(VertexCount);
|
|
writer.Write(0);
|
|
writer.Write(FaceCount);
|
|
writer.Write(Unknown);
|
|
writer.Write(Unknown1);
|
|
writer.Write(Unknown2);
|
|
writer.Write(Unknown3);
|
|
}
|
|
}
|
|
|
|
public class INDX
|
|
{
|
|
public ushort[] Indices;
|
|
|
|
public void Read(FileReader reader, MESH mesh)
|
|
{
|
|
reader.ReadSignature(4, "INDX");
|
|
uint SectionSize = reader.ReadUInt32();
|
|
|
|
Indices = new ushort[mesh.FaceCount];
|
|
for (int i = 0; i < mesh.FaceCount; i++)
|
|
{
|
|
Indices[i] = reader.ReadUInt16();
|
|
}
|
|
}
|
|
|
|
public void Write(FileWriter writer)
|
|
{
|
|
writer.WriteSignature("INDX");
|
|
writer.Write(Indices.Length * sizeof(ushort) + 8);
|
|
for (int i = 0; i < Indices.Length; i++)
|
|
writer.Write(Indices[i]);
|
|
}
|
|
}
|
|
|
|
public class VMAP
|
|
{
|
|
public ushort[] Indices;
|
|
|
|
public void Read(FileReader reader)
|
|
{
|
|
reader.ReadSignature(4, "VMAP");
|
|
uint SectionSize = reader.ReadUInt32();
|
|
uint FaceCount = (SectionSize - 8) / sizeof(ushort);
|
|
|
|
Indices = new ushort[FaceCount];
|
|
for (int i = 0; i < FaceCount; i++)
|
|
{
|
|
Indices[i] = reader.ReadUInt16();
|
|
}
|
|
}
|
|
|
|
public void Write(FileWriter writer)
|
|
{
|
|
writer.WriteSignature("VMAP");
|
|
writer.Write(Indices.Length * sizeof(ushort) + 8);
|
|
for (int i = 0; i < Indices.Length; i++)
|
|
writer.Write(Indices[i]);
|
|
}
|
|
}
|
|
|
|
public class PADX
|
|
{
|
|
public void Read(FileReader reader)
|
|
{
|
|
reader.ReadSignature(4, "PADX");
|
|
uint SectionSize = reader.ReadUInt32();
|
|
}
|
|
|
|
public void Write(FileWriter writer, uint Alignment)
|
|
{
|
|
long pos = writer.Position;
|
|
|
|
//Check if alignment is needed first!
|
|
using (writer.TemporarySeek(pos + 8, System.IO.SeekOrigin.Begin))
|
|
{
|
|
var startPos = writer.Position;
|
|
long position = writer.Seek((-writer.Position % Alignment + Alignment) % Alignment, System.IO.SeekOrigin.Current);
|
|
|
|
if (startPos == position)
|
|
return;
|
|
}
|
|
|
|
writer.WriteSignature("PADX");
|
|
writer.Write(uint.MaxValue);
|
|
Align(writer, (int)Alignment);
|
|
|
|
long endPos = writer.Position;
|
|
using (writer.TemporarySeek(pos + 4, System.IO.SeekOrigin.Begin))
|
|
{
|
|
writer.Write((uint)(endPos - pos));
|
|
}
|
|
}
|
|
|
|
private void Align(FileWriter writer, int alignment)
|
|
{
|
|
var startPos = writer.Position;
|
|
long position = writer.Seek((-writer.Position % alignment + alignment) % alignment, System.IO.SeekOrigin.Current);
|
|
|
|
writer.Seek(startPos, System.IO.SeekOrigin.Begin);
|
|
while (writer.Position != position)
|
|
{
|
|
writer.Write(byte.MaxValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|