mirror of
https://github.com/KillzXGaming/Switch-Toolbox
synced 2024-11-10 07:04:36 +00:00
Support Mega Man 11 and MHGU (Switch) model/texture loading.
This commit is contained in:
parent
38bd5f6bca
commit
02273a7501
13 changed files with 21645 additions and 0 deletions
59
File_Format_Library/FileFormats/MT/CRC32Hash.cs
Normal file
59
File_Format_Library/FileFormats/MT/CRC32Hash.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System.Text;
|
||||
|
||||
namespace CafeLibrary.M2
|
||||
{
|
||||
class CRC32Hash
|
||||
{
|
||||
private static uint[] Table = new uint[256];
|
||||
|
||||
private static bool IsInitialized = false;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
uint Polynomial = 0xedb88320;
|
||||
|
||||
for (uint Value, i = 0; i < Table.Length; i++)
|
||||
{
|
||||
Value = i;
|
||||
|
||||
for (int j = 8; j > 0; --j)
|
||||
{
|
||||
Value = (Value >> 1) ^ (Polynomial * (Value & 1));
|
||||
}
|
||||
|
||||
Table[i] = Value;
|
||||
}
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
public static uint Hash(byte[] Data)
|
||||
{
|
||||
if (!IsInitialized) Initialize();
|
||||
|
||||
uint CRC = 0xffffffff;
|
||||
|
||||
for (int i = 0; i < Data.Length; i++)
|
||||
{
|
||||
CRC = (CRC >> 8) ^ Table[(CRC & 0xff) ^ Data[i]];
|
||||
}
|
||||
|
||||
return CRC;
|
||||
}
|
||||
|
||||
public static uint Hash(string Text)
|
||||
{
|
||||
return Hash(Encoding.ASCII.GetBytes(Text));
|
||||
}
|
||||
|
||||
public static uint HashNegated(byte[] Data)
|
||||
{
|
||||
return ~Hash(Data);
|
||||
}
|
||||
|
||||
public static uint HashNegated(string Text)
|
||||
{
|
||||
return ~Hash(Text);
|
||||
}
|
||||
}
|
||||
}
|
66
File_Format_Library/FileFormats/MT/MT_Globals.cs
Normal file
66
File_Format_Library/FileFormats/MT/MT_Globals.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Toolbox.Library.Security.Cryptography;
|
||||
|
||||
namespace CafeLibrary.M2
|
||||
{
|
||||
public class MT_Globals
|
||||
{
|
||||
private static Dictionary<uint, Shader.AttributeGroup> attGroups;
|
||||
|
||||
public static Dictionary<uint, Shader.AttributeGroup> AttributeGroups
|
||||
{
|
||||
get
|
||||
{
|
||||
if (attGroups == null)
|
||||
Load();
|
||||
|
||||
return attGroups;
|
||||
}
|
||||
}
|
||||
public static void Load()
|
||||
{
|
||||
attGroups = new Dictionary<uint, Shader.AttributeGroup>();
|
||||
|
||||
|
||||
foreach (var file in Directory.GetFiles(Path.Combine("Lib", "MTVertexFormats")))
|
||||
{
|
||||
LoadPresets(file);
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadPresets(string filePath)
|
||||
{
|
||||
var shader = JsonConvert.DeserializeObject<Shader>(File.ReadAllText(filePath));
|
||||
foreach (var item in shader.AttributeGroups)
|
||||
if (!attGroups.ContainsKey(item.Key))
|
||||
attGroups.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public static uint Hash(string Text) {
|
||||
return CRC32Hash.Hash(Text);
|
||||
}
|
||||
|
||||
public static uint jamcrc(string name)
|
||||
{
|
||||
var crc = Crc32.Compute(name);
|
||||
return (crc ^ 0xffffffff) & 0x7fffffff;
|
||||
}
|
||||
|
||||
public static uint mfxcrc(string name, uint index)
|
||||
{
|
||||
var crc = Crc32.Compute(name);
|
||||
return (((crc ^ 0xffffffff) & 0x7fffffff) << 12) + index;
|
||||
}
|
||||
|
||||
public static uint crc32(string name)
|
||||
{
|
||||
var crc = Crc32.Compute(name);
|
||||
return crc;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
277
File_Format_Library/FileFormats/MT/MT_Model.cs
Normal file
277
File_Format_Library/FileFormats/MT/MT_Model.cs
Normal file
|
@ -0,0 +1,277 @@
|
|||
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.Forms;
|
||||
using Toolbox.Library.Rendering;
|
||||
using OpenTK;
|
||||
using CafeLibrary.M2;
|
||||
using System.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class MT_Model : TreeNodeFile, IFileFormat, IExportableModel
|
||||
{
|
||||
public FileType FileType { get; set; } = FileType.Model;
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "MT Model" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.mod" };
|
||||
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 FileReader(stream, true)) {
|
||||
return reader.CheckSignature(3, "MOD");
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
return types.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
//Check for the viewport in the object editor
|
||||
//This is attached to it to load multiple file formats within the object editor to the viewer
|
||||
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)
|
||||
{
|
||||
//Make sure opengl is enabled
|
||||
if (Runtime.UseOpenGL)
|
||||
{
|
||||
//Open the viewport
|
||||
if (viewport == null)
|
||||
{
|
||||
viewport = new Viewport(ObjectEditor.GetDrawableContainers());
|
||||
viewport.Dock = DockStyle.Fill;
|
||||
}
|
||||
|
||||
//Make sure to load the drawables only once so set it to true!
|
||||
if (!DrawablesLoaded)
|
||||
{
|
||||
ObjectEditor.AddContainer(DrawableContainer);
|
||||
DrawablesLoaded = true;
|
||||
}
|
||||
|
||||
//Reload which drawable to display
|
||||
viewport.ReloadDrawables(DrawableContainer);
|
||||
LibraryGUI.LoadEditor(viewport);
|
||||
|
||||
viewport.Text = Text;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<STGenericObject> ExportableMeshes => Model.Objects;
|
||||
public IEnumerable<STGenericMaterial> ExportableMaterials => Model.Materials;
|
||||
public IEnumerable<STGenericTexture> ExportableTextures => TextureList;
|
||||
public STSkeleton ExportableSkeleton => Model.GenericSkeleton;
|
||||
|
||||
public List<MT_TEX> TextureList { get; set; } = new List<MT_TEX>();
|
||||
|
||||
public MT_Renderer Renderer;
|
||||
|
||||
public DrawableContainer DrawableContainer = new DrawableContainer();
|
||||
|
||||
public Model ModelData { get; set; }
|
||||
|
||||
public STGenericModel Model;
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
Text = FileName;
|
||||
|
||||
Renderer = new MT_Renderer();
|
||||
ModelData = new Model(stream);
|
||||
ModelData.LoadMaterials(this.FilePath.Replace(".mod", ".mrl"));
|
||||
ModelData.LoadTextures(Path.GetDirectoryName(this.FilePath));
|
||||
|
||||
TextureList = ModelData.Textures;
|
||||
|
||||
TreeNode meshFolder = new TreeNode("Meshes");
|
||||
Nodes.Add(meshFolder);
|
||||
|
||||
TreeNode texFolder = new STTextureFolder("Textures");
|
||||
Nodes.Add(texFolder);
|
||||
|
||||
TreeNode skeletonFolder = new STTextureFolder("Skeleton");
|
||||
Nodes.Add(skeletonFolder);
|
||||
|
||||
foreach (var tex in ModelData.Textures)
|
||||
texFolder.Nodes.Add(tex);
|
||||
|
||||
Model = ToGeneric();
|
||||
|
||||
foreach (GenericRenderedObject mesh in Model.Objects)
|
||||
{
|
||||
Renderer.Meshes.Add(mesh);
|
||||
meshFolder.Nodes.Add(mesh);
|
||||
}
|
||||
Renderer.Skeleton = Model.GenericSkeleton;
|
||||
|
||||
foreach (var bone in Model.GenericSkeleton.bones)
|
||||
{
|
||||
if (bone.Parent == null)
|
||||
skeletonFolder.Nodes.Add(bone);
|
||||
}
|
||||
|
||||
foreach (var tex in TextureList)
|
||||
Renderer.Textures.Add(tex);
|
||||
|
||||
DrawableContainer = new DrawableContainer();
|
||||
DrawableContainer.Name = FileName;
|
||||
DrawableContainer.Drawables.Add(Renderer);
|
||||
DrawableContainer.Drawables.Add(Model.GenericSkeleton);
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Save(System.IO.Stream stream)
|
||||
{
|
||||
}
|
||||
|
||||
public STGenericModel ToGeneric()
|
||||
{
|
||||
var model = new STGenericModel();
|
||||
model.Name = this.FileName;
|
||||
|
||||
model.GenericSkeleton = new STSkeleton();
|
||||
foreach (var bn in ModelData.Bones)
|
||||
{
|
||||
model.GenericSkeleton.bones.Add(new STBone(model.GenericSkeleton)
|
||||
{
|
||||
Text = $"Bone_{bn.ID}",
|
||||
Position = bn.Position,
|
||||
Rotation = bn.LocalMatrix.ExtractRotation(),
|
||||
Scale = bn.LocalMatrix.ExtractScale(),
|
||||
parentIndex = bn.ParentIndex,
|
||||
});
|
||||
}
|
||||
model.GenericSkeleton.reset();
|
||||
model.GenericSkeleton.update();
|
||||
|
||||
List<STGenericMaterial> materials = new List<STGenericMaterial>();
|
||||
for (int i = 0; i < ModelData.MaterialNames.Length; i++)
|
||||
{
|
||||
STGenericMaterial mat = new STGenericMaterial();
|
||||
mat.Text = ModelData.MaterialNames[i];
|
||||
|
||||
var hash = MT_Globals.Hash(mat.Text);
|
||||
|
||||
if (ModelData.MaterialList != null && ModelData.MaterialList.Materials.ContainsKey(hash))
|
||||
{
|
||||
var m = ModelData.MaterialList.Materials[hash];
|
||||
if (m.DiffuseMap != null)
|
||||
{
|
||||
string tex = Path.GetFileName(m.DiffuseMap.Name);
|
||||
mat.TextureMaps.Add(new STGenericMatTexture()
|
||||
{
|
||||
Type = STGenericMatTexture.TextureType.Diffuse,
|
||||
Name = tex,
|
||||
});
|
||||
}
|
||||
if (m.NormalMap != null)
|
||||
{
|
||||
string tex = Path.GetFileName(m.NormalMap.Name);
|
||||
mat.TextureMaps.Add(new STGenericMatTexture()
|
||||
{
|
||||
Type = STGenericMatTexture.TextureType.Normal,
|
||||
Name = tex,
|
||||
});
|
||||
}
|
||||
if (m.SpecularMap != null)
|
||||
{
|
||||
string tex = Path.GetFileName(m.SpecularMap.Name);
|
||||
mat.TextureMaps.Add(new STGenericMatTexture()
|
||||
{
|
||||
Type = STGenericMatTexture.TextureType.Specular,
|
||||
Name = tex,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
materials.Add(mat);
|
||||
}
|
||||
|
||||
model.Materials = materials;
|
||||
|
||||
List<STGenericObject> meshes = new List<STGenericObject>();
|
||||
foreach (var mesh in ModelData.Meshes)
|
||||
{
|
||||
if (mesh.Vertices.Length == 0)
|
||||
continue;
|
||||
|
||||
var attributeGroup = MT_Globals.AttributeGroups[mesh.VertexFormatHash];
|
||||
|
||||
GenericRenderedObject genericMesh = new GenericRenderedObject();
|
||||
meshes.Add(genericMesh);
|
||||
|
||||
genericMesh.ImageKey = "mesh";
|
||||
genericMesh.SelectedImageKey = "mesh";
|
||||
|
||||
genericMesh.Text = $"Mesh_{meshes.Count - 1}";
|
||||
foreach (var vert in mesh.Vertices)
|
||||
{
|
||||
var genericVertex = new Vertex()
|
||||
{
|
||||
pos = new Vector3(vert.Position.X, vert.Position.Y, vert.Position.Z),
|
||||
nrm = new Vector3(vert.Normal.X, vert.Normal.Y, vert.Normal.Z),
|
||||
col = new Vector4(vert.Color.X, vert.Color.Y, vert.Color.Z, vert.Color.W),
|
||||
};
|
||||
if (vert.TexCoords?.Length > 0) genericVertex.uv0 = vert.TexCoords[0];
|
||||
if (vert.TexCoords?.Length > 1) genericVertex.uv1 = vert.TexCoords[1];
|
||||
if (vert.TexCoords?.Length > 2) genericVertex.uv2 = vert.TexCoords[2];
|
||||
|
||||
foreach (var boneID in vert.BoneIndices)
|
||||
{
|
||||
genericVertex.boneIds.Add(boneID);
|
||||
}
|
||||
foreach (var boneW in vert.BoneWeights)
|
||||
genericVertex.boneWeights.Add(boneW);
|
||||
|
||||
genericMesh.vertices.Add(genericVertex);
|
||||
}
|
||||
|
||||
STGenericPolygonGroup poly = new STGenericPolygonGroup();
|
||||
poly.PrimativeType = STPrimitiveType.Triangles;
|
||||
genericMesh.PolygonGroups.Add(poly);
|
||||
|
||||
poly.Material = materials[(int)mesh.MaterialID];
|
||||
|
||||
foreach (var ind in mesh.Indices)
|
||||
poly.faces.Add(ind);
|
||||
|
||||
genericMesh.CalculateNormals();
|
||||
}
|
||||
model.Objects = meshes.ToList();
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
28
File_Format_Library/FileFormats/MT/MT_Renderer.cs
Normal file
28
File_Format_Library/FileFormats/MT/MT_Renderer.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.Rendering;
|
||||
using GL_EditorFramework.GL_Core;
|
||||
using GL_EditorFramework.Interfaces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Toolbox.Library;
|
||||
using FirstPlugin;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class MT_Renderer : GenericModelRenderer
|
||||
{
|
||||
public override void OnRender(GLControl control)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void SetRenderData(STGenericMaterial mat, ShaderProgram shader, STGenericObject m)
|
||||
{
|
||||
shader.SetBoolToInt("NoSkinning", Skeleton.bones.Count == 0);
|
||||
}
|
||||
}
|
||||
}
|
194
File_Format_Library/FileFormats/MT/MT_TEX.cs
Normal file
194
File_Format_Library/FileFormats/MT/MT_TEX.cs
Normal file
|
@ -0,0 +1,194 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.Forms;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class MT_TEX : STGenericTexture, IFileFormat, IContextMenuNode
|
||||
{
|
||||
public FileType FileType { get; set; } = FileType.Image;
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "TEX (3DS)" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.tex" };
|
||||
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 FileReader(stream, true)) {
|
||||
return reader.CheckSignature(3, "TEX");
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
return types.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanEdit { get; set; } = false;
|
||||
|
||||
public override TEX_FORMAT[] SupportedFormats
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TEX_FORMAT[]
|
||||
{
|
||||
TEX_FORMAT.B5G6R5_UNORM,
|
||||
TEX_FORMAT.R8G8_UNORM,
|
||||
TEX_FORMAT.B5G5R5A1_UNORM,
|
||||
TEX_FORMAT.B4G4R4A4_UNORM,
|
||||
TEX_FORMAT.LA8,
|
||||
TEX_FORMAT.HIL08,
|
||||
TEX_FORMAT.L8,
|
||||
TEX_FORMAT.A8_UNORM,
|
||||
TEX_FORMAT.LA4,
|
||||
TEX_FORMAT.A4,
|
||||
TEX_FORMAT.ETC1_UNORM,
|
||||
TEX_FORMAT.ETC1_A4,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ImageData;
|
||||
|
||||
|
||||
public override void OnClick(TreeView treeView)
|
||||
{
|
||||
UpdateEditor();
|
||||
}
|
||||
|
||||
private void UpdateEditor()
|
||||
{
|
||||
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase));
|
||||
if (editor == null)
|
||||
{
|
||||
editor = new ImageEditorBase();
|
||||
editor.Dock = DockStyle.Fill;
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
}
|
||||
|
||||
editor.LoadProperties(GenericProperties);
|
||||
editor.LoadImage(this);
|
||||
}
|
||||
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
{
|
||||
List<ToolStripItem> Items = new List<ToolStripItem>();
|
||||
Items.Add(new ToolStripMenuItem("Save", null, SaveAction, Keys.Control | Keys.S));
|
||||
Items.AddRange(base.GetContextMenuItems());
|
||||
return Items.ToArray();
|
||||
}
|
||||
|
||||
private void SaveAction(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.Filter = Utils.GetAllFilters(this);
|
||||
sfd.FileName = FileName;
|
||||
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
STFileSaver.SaveFileFormat(this, sfd.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
public void FillEditor(UserControl control)
|
||||
{
|
||||
Properties prop = new Properties();
|
||||
prop.Width = Width;
|
||||
prop.Height = Height;
|
||||
prop.Depth = Depth;
|
||||
prop.MipCount = MipCount;
|
||||
prop.ArrayCount = ArrayCount;
|
||||
prop.ImageSize = (uint)ImageData.Length;
|
||||
prop.Format = Format;
|
||||
|
||||
((ImageEditorBase)control).LoadImage(this);
|
||||
((ImageEditorBase)control).LoadProperties(prop);
|
||||
}
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
PlatformSwizzle = PlatformSwizzle.Platform_Switch;
|
||||
CanSave = true;
|
||||
CanReplace = true;
|
||||
Text = FileName;
|
||||
this.ImageKey = "texture";
|
||||
this.SelectedImageKey = "texture";
|
||||
|
||||
using (var reader = new FileReader(stream))
|
||||
{
|
||||
reader.ReadInt32(); //magic
|
||||
int block1 = reader.ReadInt32();
|
||||
uint block2 = reader.ReadUInt32();
|
||||
uint block3 = reader.ReadUInt32();
|
||||
|
||||
int Version = (block1 >> 0) & 0xfff;
|
||||
int Shift = (block1 >> 24) & 0xf;
|
||||
Width = (block2 >> 6) & 0x1fff;
|
||||
Height = (block2 >> 19) & 0x1fff;
|
||||
uint format = (block3 >> 8) & 0xff;
|
||||
uint Aspect = (block3 >> 16) & 0x1fff;
|
||||
MipCount = (block2 & 0x3F);
|
||||
uint imageSize = reader.ReadUInt32();
|
||||
reader.ReadUInt32s((int)MipCount); //mip offsets
|
||||
|
||||
ImageData = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
|
||||
|
||||
Format = FormatList[format];
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(System.IO.Stream stream)
|
||||
{
|
||||
using (var writer = new FileWriter(stream, true))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override void Replace(string FileName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override void SetImageData(System.Drawing.Bitmap bitmap, int ArrayLevel)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0, int DepthLevel = 0)
|
||||
{
|
||||
return TegraX1Swizzle.GetImageData(this, ImageData, ArrayLevel, MipLevel, DepthLevel);
|
||||
}
|
||||
|
||||
Dictionary<uint, TEX_FORMAT> FormatList = new Dictionary<uint, TEX_FORMAT>()
|
||||
{
|
||||
{ 7, TEX_FORMAT.R8G8B8A8_UNORM_SRGB },
|
||||
|
||||
{ 19, TEX_FORMAT.BC1_UNORM_SRGB },
|
||||
{ 20, TEX_FORMAT.BC2_UNORM_SRGB },
|
||||
{ 23, TEX_FORMAT.BC3_UNORM_SRGB },
|
||||
{ 25, TEX_FORMAT.BC4_UNORM },
|
||||
{ 31, TEX_FORMAT.BC5_UNORM },
|
||||
|
||||
{ 33, TEX_FORMAT.BC1_UNORM },
|
||||
{ 39, TEX_FORMAT.BC3_UNORM },
|
||||
{ 42, TEX_FORMAT.BC3_UNORM },
|
||||
|
||||
{ 48, TEX_FORMAT.BC7_UNORM },
|
||||
};
|
||||
}
|
||||
}
|
137
File_Format_Library/FileFormats/MT/Model/MaterialList.cs
Normal file
137
File_Format_Library/FileFormats/MT/Model/MaterialList.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace CafeLibrary.M2
|
||||
{
|
||||
public class MaterialList
|
||||
{
|
||||
public uint Version { get; set; }
|
||||
|
||||
public Dictionary<uint, Material> Materials = new Dictionary<uint, Material>();
|
||||
|
||||
public List<Texture> Textures = new List<Texture>();
|
||||
|
||||
public uint ShaderHash { get; set; }
|
||||
|
||||
private bool _hasUint64Offsets;
|
||||
|
||||
public MaterialList(Stream stream, bool hasUint64Offsets)
|
||||
{
|
||||
_hasUint64Offsets = hasUint64Offsets;
|
||||
|
||||
using (var reader = new FileReader(stream))
|
||||
{
|
||||
Read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
reader.ReadUInt32(); //version
|
||||
Version = reader.ReadUInt32();
|
||||
uint materialCount = reader.ReadUInt32();
|
||||
uint textureCount = reader.ReadUInt32();
|
||||
ShaderHash = ReadUint(reader);
|
||||
uint textureOffset = ReadUint(reader);
|
||||
uint materialOffset = ReadUint(reader);
|
||||
|
||||
reader.SeekBegin(textureOffset);
|
||||
for (int i = 0; i < textureCount; i++)
|
||||
{
|
||||
Texture tex = new Texture();
|
||||
tex.Flags = ReadUint(reader);
|
||||
ReadUint(reader); //0
|
||||
ReadUint(reader); //0
|
||||
long pos = reader.Position;
|
||||
tex.Name = reader.ReadZeroTerminatedString();
|
||||
|
||||
reader.SeekBegin(pos + 64);
|
||||
Textures.Add(tex);
|
||||
}
|
||||
|
||||
reader.SeekBegin(materialOffset);
|
||||
for (int i = 0; i < materialCount; i++)
|
||||
{
|
||||
Material material = new Material();
|
||||
|
||||
uint id = ReadUint(reader);
|
||||
material.NameHash = reader.ReadUInt32();
|
||||
uint paramSize = reader.ReadUInt32();
|
||||
material.ShaderHash = reader.ReadUInt32();
|
||||
uint skinningHash = reader.ReadUInt32();
|
||||
reader.ReadUInt16();
|
||||
reader.ReadByte();
|
||||
|
||||
reader.ReadBytes(9);
|
||||
reader.ReadByte();
|
||||
reader.ReadBytes(19);
|
||||
uint paramOffset = ReadUint(reader);
|
||||
ReadUint(reader);
|
||||
|
||||
Materials.Add(material.NameHash, material);
|
||||
|
||||
using (reader.TemporarySeek(paramOffset, SeekOrigin.Begin))
|
||||
{
|
||||
if (_hasUint64Offsets)
|
||||
{
|
||||
ulong[] parameters = reader.ReadUInt64s((int)paramSize / 8);
|
||||
for (int j = 0; j < parameters.Length; j++)
|
||||
{
|
||||
if (parameters[j].ToString().StartsWith("34397848")) //tAlbedoMap
|
||||
material.DiffuseMap = this.Textures[(int)parameters[j - 1] - 1];
|
||||
if (parameters[j].ToString().StartsWith("577110")) //tNormalMap
|
||||
material.NormalMap = this.Textures[(int)parameters[j - 1] - 1];
|
||||
if (parameters[j].ToString().StartsWith("24862")) //tSpecularMap
|
||||
material.SpecularMap = this.Textures[(int)parameters[j - 1] - 1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint[] parameters = reader.ReadUInt32s((int)paramSize / 4);
|
||||
for (int j = 0; j < parameters.Length; j++)
|
||||
{
|
||||
if (parameters[j].ToString().StartsWith("34397848")) //tAlbedoMap
|
||||
material.DiffuseMap = this.Textures[(int)parameters[j - 1] - 1];
|
||||
if (parameters[j].ToString().StartsWith("5771109")) //tNormalMap
|
||||
material.NormalMap = this.Textures[(int)parameters[j - 1] - 1];
|
||||
if (parameters[j].ToString().StartsWith("2486240")) //tSpecularMap
|
||||
material.SpecularMap = this.Textures[(int)parameters[j - 1] - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint ReadUint(FileReader reader)
|
||||
{
|
||||
if (_hasUint64Offsets)
|
||||
{
|
||||
var v = (uint)reader.ReadInt32();
|
||||
reader.ReadInt32();
|
||||
return v;
|
||||
}
|
||||
return reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public class Material
|
||||
{
|
||||
public uint NameHash;
|
||||
public uint ShaderHash;
|
||||
|
||||
public Texture DiffuseMap;
|
||||
public Texture NormalMap;
|
||||
public Texture SpecularMap;
|
||||
}
|
||||
|
||||
public class Texture
|
||||
{
|
||||
public uint Flags;
|
||||
public string Name;
|
||||
}
|
||||
}
|
484
File_Format_Library/FileFormats/MT/Model/Model.cs
Normal file
484
File_Format_Library/FileFormats/MT/Model/Model.cs
Normal file
|
@ -0,0 +1,484 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenTK;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using FirstPlugin;
|
||||
|
||||
namespace CafeLibrary.M2
|
||||
{
|
||||
public class Model
|
||||
{
|
||||
public ushort Version { get; set; }
|
||||
|
||||
public string[] MaterialNames { get; set; }
|
||||
|
||||
public MaterialList MaterialList { get; set; }
|
||||
|
||||
public List<Mesh> Meshes { get; set; } = new List<Mesh>();
|
||||
|
||||
public List<Bone> Bones { get; set; } = new List<Bone>();
|
||||
|
||||
public List<MT_TEX> Textures { get; set; } = new List<MT_TEX>();
|
||||
|
||||
public List<Bounding> Boundings { get; set; } = new List<Bounding>();
|
||||
|
||||
public Vector3 BoundingCenter { get; set; }
|
||||
public float BoundingScale { get; set; }
|
||||
public Vector3 Min { get; set; }
|
||||
public Vector3 Max { get; set; }
|
||||
|
||||
public Model(Stream stream)
|
||||
{
|
||||
using (var reader = new MTFileReader(stream, this)) {
|
||||
Read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadMaterials(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
return;
|
||||
|
||||
MaterialList = new MaterialList(File.OpenRead(filePath), Version == 211);
|
||||
}
|
||||
|
||||
public void LoadTextures(string folder)
|
||||
{
|
||||
if (MaterialList == null)
|
||||
return;
|
||||
|
||||
foreach (var tex in MaterialList.Textures)
|
||||
{
|
||||
string filePath = Path.Combine(folder, Path.GetFileName(tex.Name) + ".tex");
|
||||
if (!File.Exists(filePath))
|
||||
continue;
|
||||
|
||||
MT_TEX texture = new MT_TEX();
|
||||
texture.FileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
texture.Load(File.OpenRead(filePath));
|
||||
Textures.Add(texture);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(MTFileReader reader)
|
||||
{
|
||||
reader.ReadUInt32(); //magic
|
||||
Version = reader.ReadUInt16();
|
||||
ushort boneCount = reader.ReadUInt16();
|
||||
ushort meshCount = reader.ReadUInt16();
|
||||
ushort matCount = reader.ReadUInt16();
|
||||
uint totalVertexCount = reader.ReadUInt32();
|
||||
uint totalIndexCount = reader.ReadUInt32();
|
||||
uint totalTrisCount = reader.ReadUInt32();
|
||||
uint vertexBufferSize = reader.ReadUInt32();
|
||||
reader.ReadUInt32(); //padding
|
||||
uint numBoundings = reader.ReadUInt32();
|
||||
if (Version == 211 || Version == 230)
|
||||
reader.ReadUInt32(); //padding
|
||||
|
||||
uint bonesOffset = reader.ReadOffset();
|
||||
uint boundingOffset = reader.ReadOffset();
|
||||
uint materialNamesOffset = reader.ReadOffset();
|
||||
uint meshListOffset = reader.ReadOffset();
|
||||
uint vertexBufferOffset = reader.ReadOffset();
|
||||
uint indexBufferOffset = reader.ReadOffset();
|
||||
uint unkOffset = reader.ReadOffset();
|
||||
BoundingCenter = reader.ReadVec3();
|
||||
BoundingScale = reader.ReadSingle();
|
||||
Min = reader.ReadVec3();
|
||||
reader.ReadSingle();
|
||||
Max = reader.ReadVec3();
|
||||
reader.ReadSingle();
|
||||
|
||||
reader.ReadUInt32s(5); //unknown
|
||||
|
||||
reader.SeekBegin(materialNamesOffset);
|
||||
|
||||
MaterialNames = new string[matCount];
|
||||
for (int i = 0; i < matCount; i++)
|
||||
MaterialNames[i] = reader.ReadString(128, true);
|
||||
|
||||
byte[] boneIDs = new byte[boneCount];
|
||||
|
||||
reader.SeekBegin(bonesOffset);
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
{
|
||||
Bone bone = new Bone();
|
||||
|
||||
bone.ID = reader.ReadByte();
|
||||
bone.ParentIndex = reader.ReadSByte();
|
||||
bone.MirrorIndex = reader.ReadSByte();
|
||||
bone.Unk = reader.ReadSByte();
|
||||
bone.ChildDistance = reader.ReadSingle();
|
||||
bone.ParentDistance = reader.ReadSingle();
|
||||
bone.Position = new Vector3(
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle());
|
||||
Bones.Add(bone);
|
||||
|
||||
boneIDs[i] = bone.ID;
|
||||
}
|
||||
//Local space
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
Bones[i].LocalMatrix = reader.ReadMatrix4();
|
||||
|
||||
//World space
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
Bones[i].WorldMatrix = reader.ReadMatrix4();
|
||||
|
||||
//Bone ID property to bone index
|
||||
byte[] remapTable = Version == 137 ? new byte[0x200] : new byte[0x100];
|
||||
remapTable = reader.ReadBytes(remapTable.Length);
|
||||
|
||||
Dictionary<int, int> boneIndices = new Dictionary<int, int>();
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
{
|
||||
if (Bones[i].ID < 255)
|
||||
{
|
||||
boneIDs[i] = (byte)remapTable[Bones[i].ID];
|
||||
|
||||
boneIndices.Add(boneIDs[i], i);
|
||||
|
||||
Console.WriteLine($"boneIDs {Bones[i].ID} -> {boneIDs[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
//Boundings after
|
||||
reader.SeekBegin(boundingOffset);
|
||||
for (int i = 0; i < numBoundings; i++)
|
||||
{
|
||||
Bounding bnd = new Bounding();
|
||||
bnd.ID = reader.ReadUInt32();
|
||||
bnd.Boundings = reader.ReadSingles(7);
|
||||
Boundings.Add(bnd);
|
||||
}
|
||||
|
||||
//Scaling as vertex data is scaled down for optmization
|
||||
Vector3 scale = Max - Min;
|
||||
if (this.Version >= 190 && Bones.Count > 0)
|
||||
scale = Bones[0].WorldMatrix.ExtractScale();
|
||||
|
||||
//Mesh data
|
||||
reader.SeekBegin(meshListOffset);
|
||||
for (int i = 0; i < meshCount; i++)
|
||||
{
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.Read(reader, this, vertexBufferOffset, indexBufferOffset);
|
||||
Meshes.Add(mesh);
|
||||
}
|
||||
|
||||
var mat = Matrix4.CreateScale(scale);
|
||||
foreach (var mesh in Meshes)
|
||||
{
|
||||
for (int i = 0; i < mesh.Vertices.Length; i++)
|
||||
{
|
||||
//Vertex positions use 16 SNorm format which is optmized to be smaller. Scale and offset by bounding size
|
||||
mesh.Vertices[i].Position = Min + Vector3.TransformPosition(mesh.Vertices[i].Position, mat);
|
||||
|
||||
var vertex = mesh.Vertices[i];
|
||||
if (vertex.BoneIndices.Count == 0)
|
||||
continue;
|
||||
|
||||
//Adjustments to single weights
|
||||
if (vertex.BoneIndices.Count > 0 && vertex.BoneWeights.Count == 0)
|
||||
vertex.BoneWeights.Add(1.0f);
|
||||
//Adjustments to non matching weights as these get optmized
|
||||
if (vertex.BoneIndices.Count != vertex.BoneWeights.Count)
|
||||
{
|
||||
var diff = vertex.BoneIndices.Count - vertex.BoneWeights.Count;
|
||||
if (diff > 0)
|
||||
{
|
||||
var totalToFill = 1.0f - vertex.BoneWeights.Sum(x => x);
|
||||
if (totalToFill < 0)
|
||||
throw new Exception();
|
||||
|
||||
for (int j = 0; j < diff; j++)
|
||||
vertex.BoneWeights.Add(totalToFill / diff);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < vertex.BoneIndices.Count; j++)
|
||||
{
|
||||
var boneID = (int)vertex.BoneIndices[j];
|
||||
var boneW = vertex.BoneWeights[j];
|
||||
|
||||
if (boneW == 0)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"boneID {boneID} boneW {boneW}");
|
||||
}
|
||||
|
||||
mesh.Vertices[i] = vertex;
|
||||
}
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public class Bounding
|
||||
{
|
||||
public uint ID;
|
||||
|
||||
public float[] Boundings = new float[7];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MTFileReader : FileReader
|
||||
{
|
||||
private Model Model;
|
||||
|
||||
public MTFileReader(Stream stream, Model model) : base(stream)
|
||||
{
|
||||
Model = model;
|
||||
}
|
||||
|
||||
public uint ReadOffset()
|
||||
{
|
||||
if (Model.Version == 211)
|
||||
return (uint)ReadUInt64();
|
||||
else
|
||||
return ReadUInt32();
|
||||
}
|
||||
}
|
||||
public class Bone
|
||||
{
|
||||
public Vector3 Position;
|
||||
|
||||
public Matrix4 WorldMatrix;
|
||||
public Matrix4 LocalMatrix;
|
||||
|
||||
public float ParentDistance;
|
||||
public float ChildDistance;
|
||||
|
||||
public byte ID;
|
||||
public sbyte ParentIndex;
|
||||
public sbyte Unk;
|
||||
public sbyte MirrorIndex;
|
||||
}
|
||||
|
||||
public class Mesh
|
||||
{
|
||||
public byte Order;
|
||||
|
||||
public uint BoundingBoxID;
|
||||
public uint MaterialID;
|
||||
public sbyte LOD;
|
||||
|
||||
public byte BoundingStartID;
|
||||
public ushort BatchID;
|
||||
|
||||
public ushort[] Indices;
|
||||
|
||||
public uint VertexFormatHash;
|
||||
|
||||
public Vertex[] Vertices = new Vertex[0];
|
||||
|
||||
public class Vertex
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Vector3 Normal;
|
||||
public Vector2[] TexCoords = new Vector2[1];
|
||||
public Vector4 Color = Vector4.One;
|
||||
public List<float> BoneWeights = new List<float>();
|
||||
public List<int> BoneIndices = new List<int>();
|
||||
}
|
||||
|
||||
public void Read(FileReader reader, Model model, uint vertexBufferOffset, uint indexBufferOffset)
|
||||
{
|
||||
reader.ReadUInt16(); //flags
|
||||
ushort vertexCount = reader.ReadUInt16();
|
||||
uint meshFlags = reader.ReadUInt32();
|
||||
reader.ReadByte();
|
||||
Order = reader.ReadByte();
|
||||
byte stride = reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
uint vertexID = reader.ReadUInt32();
|
||||
uint vertexDataOffset = reader.ReadUInt32();
|
||||
VertexFormatHash = reader.ReadUInt32();
|
||||
|
||||
uint indexID = reader.ReadUInt32();
|
||||
uint indexCount = reader.ReadUInt32();
|
||||
uint indexDataOffset = reader.ReadUInt32();
|
||||
byte unk = reader.ReadByte();
|
||||
BoundingStartID = reader.ReadByte();
|
||||
BatchID = reader.ReadUInt16();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
|
||||
BoundingBoxID = (meshFlags >> 0) & 0xfff;
|
||||
MaterialID = (meshFlags >> 12) & 0xfff;
|
||||
LOD = (sbyte)(meshFlags >> 24);
|
||||
|
||||
if (model.Version == 211)
|
||||
{
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
}
|
||||
|
||||
using (reader.TemporarySeek(vertexBufferOffset + vertexDataOffset + (vertexID * stride), SeekOrigin.Begin)) {
|
||||
ParseVertexBuffer(reader, VertexFormatHash, vertexCount, stride);
|
||||
}
|
||||
using (reader.TemporarySeek(indexBufferOffset + indexDataOffset + (indexID * 2), SeekOrigin.Begin)) {
|
||||
if (model.Version == 212 || model.Version == 211)
|
||||
ReadTriStrips(reader, indexCount, vertexID);
|
||||
else
|
||||
ReadTris(reader, indexCount, vertexID);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseVertexBuffer(FileReader reader, uint formatHash, ushort count, byte stride)
|
||||
{
|
||||
if (!MT_Globals.AttributeGroups.ContainsKey(formatHash))
|
||||
{
|
||||
// throw new Exception($"Unsupported attribute layout! {formatHash}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Vertices = new Vertex[count];
|
||||
var attributeGroup = MT_Globals.AttributeGroups[formatHash];
|
||||
long basePos = reader.Position;
|
||||
|
||||
Console.WriteLine($"ATTRIBUTE {attributeGroup.Name}");
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vertices[i] = new Vertex();
|
||||
|
||||
var maxUVSet = attributeGroup.Attributes.Where(x => x.Name.ToLower() == "texcoord").Sum(x => x.Set);
|
||||
Vertices[i].TexCoords = new Vector2[maxUVSet + 1];
|
||||
|
||||
foreach (var att in attributeGroup.Attributes)
|
||||
{
|
||||
reader.SeekBegin((uint)basePos + (i * stride) + att.Offset);
|
||||
switch (att.Name.ToLower())
|
||||
{
|
||||
case "position":
|
||||
Vertices[i].Position.X = Parse(reader, att.Format);
|
||||
Vertices[i].Position.Y = Parse(reader, att.Format);
|
||||
if (att.ElementCount > 2)
|
||||
Vertices[i].Position.Z = Parse(reader, att.Format);
|
||||
break;
|
||||
case "normal":
|
||||
Vertices[i].Normal.X = Parse(reader, att.Format);
|
||||
Vertices[i].Normal.Y = Parse(reader, att.Format);
|
||||
Vertices[i].Normal.Z = Parse(reader, att.Format);
|
||||
Vertices[i].Normal = Vertices[i].Normal.Normalized();
|
||||
break;
|
||||
case "texcoord":
|
||||
Vector2 uv = new Vector2();
|
||||
uv.X = Parse(reader, att.Format);
|
||||
uv.Y = Parse(reader, att.Format);
|
||||
Vertices[i].TexCoords[att.Set] = uv;
|
||||
break;
|
||||
case "vertexcolor":
|
||||
Vertices[i].Color.X = Parse(reader, att.Format);
|
||||
Vertices[i].Color.Y = Parse(reader, att.Format);
|
||||
Vertices[i].Color.Z = Parse(reader, att.Format);
|
||||
if (att.ElementCount == 4) //Alpha
|
||||
Vertices[i].Color.W = Parse(reader, att.Format);
|
||||
break;
|
||||
case "vertexalpha":
|
||||
Vertices[i].Color.W = Parse(reader, att.Format);
|
||||
break;
|
||||
case "weight":
|
||||
for (int j = 0; j < att.ElementCount; j++)
|
||||
Vertices[i].BoneWeights.Add(Parse(reader, att.Format));
|
||||
break;
|
||||
case "joint":
|
||||
for (int j = 0; j < att.ElementCount; j++)
|
||||
Vertices[i].BoneIndices.Add((int)Parse(reader, att.Format));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float Parse(FileReader reader, Shader.AttributeGroup.AttributeFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Shader.AttributeGroup.AttributeFormat.Float: return reader.ReadSingle();
|
||||
case Shader.AttributeGroup.AttributeFormat.HalfFloat: return reader.ReadHalfSingle();
|
||||
case Shader.AttributeGroup.AttributeFormat.UShort: return reader.ReadUInt16();
|
||||
case Shader.AttributeGroup.AttributeFormat.Short: return reader.ReadInt16();
|
||||
case Shader.AttributeGroup.AttributeFormat.S16N: return reader.ReadInt16() * (1f / short.MaxValue);
|
||||
case Shader.AttributeGroup.AttributeFormat.U16N: return reader.ReadUInt16() * (1f / ushort.MaxValue);
|
||||
case Shader.AttributeGroup.AttributeFormat.Sbyte: return reader.ReadSByte();
|
||||
case Shader.AttributeGroup.AttributeFormat.Byte: return reader.ReadByte();
|
||||
case Shader.AttributeGroup.AttributeFormat.S8N: return reader.ReadSByte() * (1f / sbyte.MaxValue);
|
||||
case Shader.AttributeGroup.AttributeFormat.U8N: return reader.ReadByte() * (1f / byte.MaxValue);
|
||||
case (Shader.AttributeGroup.AttributeFormat)11: return (reader.ReadByte() - 127) * 0.0078125f;
|
||||
case Shader.AttributeGroup.AttributeFormat.RGB: return reader.ReadByte();
|
||||
case Shader.AttributeGroup.AttributeFormat.RGBA: return reader.ReadByte();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void ReadTris(FileReader reader, uint indexCount, uint vertexID)
|
||||
{
|
||||
Indices = new ushort[indexCount / 3];
|
||||
for (int Index = 0; Index < Indices.Length; Index++)
|
||||
Indices[Index] = (ushort)(reader.ReadInt16() - vertexID); //Make sure index is relative rather than one big buffer
|
||||
}
|
||||
|
||||
private void ReadTriStrips(FileReader reader, uint indexCount, uint vertexID)
|
||||
{
|
||||
List<ushort> indices = new List<ushort>();
|
||||
|
||||
int startDirection = 1;
|
||||
//Start with triangle (f1, f2, f3)
|
||||
ushort f1 = reader.ReadUInt16();
|
||||
ushort f2 = reader.ReadUInt16();
|
||||
int faceDirection = startDirection;
|
||||
int p = 2;
|
||||
ushort f3;
|
||||
do
|
||||
{
|
||||
f3 = reader.ReadUInt16();
|
||||
p++;
|
||||
if (f3 == ushort.MaxValue) //Value after 0xFFFF is triangle (f1, f2, f3)
|
||||
{
|
||||
f1 = reader.ReadUInt16();
|
||||
f2 = reader.ReadUInt16();
|
||||
faceDirection = startDirection;
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Direction switches each strip
|
||||
faceDirection *= -1;
|
||||
//Skip single points
|
||||
if ((f1 != f2) && (f2 != f3) && (f3 != f1))
|
||||
{
|
||||
if (faceDirection > 0) //Determine strip face orientation
|
||||
{
|
||||
indices.Add(f3);
|
||||
indices.Add(f2);
|
||||
indices.Add(f1);
|
||||
}
|
||||
else
|
||||
{
|
||||
indices.Add(f2);
|
||||
indices.Add(f3);
|
||||
indices.Add(f1);
|
||||
}
|
||||
}
|
||||
//Remap indices for next strip
|
||||
f1 = f2;
|
||||
f2 = f3;
|
||||
}
|
||||
} while (p < indexCount);
|
||||
//Shift indices as indices in .mod index one global vertex table
|
||||
for (int i = 0; i < indices.Count; i++)
|
||||
indices[i] = (ushort)(indices[i] - vertexID);
|
||||
|
||||
Indices = indices.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
155
File_Format_Library/FileFormats/MT/Model/Shader.cs
Normal file
155
File_Format_Library/FileFormats/MT/Model/Shader.cs
Normal file
|
@ -0,0 +1,155 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using Toolbox.Library.Security.Cryptography;
|
||||
|
||||
namespace CafeLibrary.M2
|
||||
{
|
||||
public class Shader
|
||||
{
|
||||
public Dictionary<uint, AttributeGroup> AttributeGroups { get; set; } = new Dictionary<uint, AttributeGroup>();
|
||||
|
||||
public Shader() { }
|
||||
|
||||
public Shader(Stream stream)
|
||||
{
|
||||
using (var reader = new FileReader(stream)) {
|
||||
Read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
reader.ReadUInt32(); //MFX
|
||||
reader.Seek(8); //unks
|
||||
uint descCount = reader.ReadUInt32();
|
||||
uint stringTableOffset = reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
|
||||
long pos = reader.Position;
|
||||
for (int i = 0; i < descCount - 1; i++)
|
||||
{
|
||||
reader.SeekBegin(pos + (i * 4));
|
||||
uint ofs = reader.ReadUInt32();
|
||||
if (ofs == 0)
|
||||
continue;
|
||||
|
||||
reader.SeekBegin(ofs);
|
||||
|
||||
Descriptor desc = new Descriptor();
|
||||
desc.Name = ReadName(reader, stringTableOffset);
|
||||
desc.Type = ReadName(reader, stringTableOffset);
|
||||
ushort DescType = reader.ReadUInt16();
|
||||
ushort MapLength = reader.ReadUInt16(); //Actual length is value / 2? Not sure
|
||||
ushort MapIndex = reader.ReadUInt16();
|
||||
ushort DescIndex = reader.ReadUInt16();
|
||||
reader.ReadUInt32();
|
||||
uint MapAddress = reader.ReadUInt32(); //Not sure what this address actually points to
|
||||
|
||||
var crc = MT_Globals.crc32(desc.Name);
|
||||
var jcrc = MT_Globals.jamcrc(desc.Name);
|
||||
var mcrc = MT_Globals.mfxcrc(desc.Name, DescIndex);
|
||||
|
||||
|
||||
if (desc.Type == "__InputLayout")
|
||||
{
|
||||
Console.WriteLine($"attribute {desc.Name} DescIndex {DescIndex}");
|
||||
|
||||
AttributeGroups.Add(mcrc, new AttributeGroup(reader, stringTableOffset)
|
||||
{
|
||||
Name = desc.Name,
|
||||
});
|
||||
}
|
||||
}
|
||||
File.WriteAllText("Shader.json", JsonConvert.SerializeObject(this, Formatting.Indented));
|
||||
}
|
||||
private static string ReadName(FileReader reader, uint stringTableOffset)
|
||||
{
|
||||
uint ofs = reader.ReadUInt32();
|
||||
using (reader.TemporarySeek(stringTableOffset + ofs, SeekOrigin.Begin)) {
|
||||
return reader.ReadZeroTerminatedString();
|
||||
}
|
||||
}
|
||||
|
||||
class Descriptor
|
||||
{
|
||||
public string Name;
|
||||
public string Type;
|
||||
}
|
||||
|
||||
public class AttributeGroup
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public List<Attribute> Attributes { get; set; } = new List<Attribute>();
|
||||
|
||||
public AttributeGroup() { }
|
||||
|
||||
public AttributeGroup(FileReader reader, uint stringTableOffset)
|
||||
{
|
||||
ushort AttrCount = reader.ReadUInt16();
|
||||
byte Stride = reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadUInt32();
|
||||
|
||||
Stride *= 4;
|
||||
|
||||
for (int j = 0; j < AttrCount; j++)
|
||||
{
|
||||
string name = ReadName(reader, stringTableOffset);
|
||||
uint Format = reader.ReadUInt32();
|
||||
|
||||
uint AttrIndex = (Format >> 0) & 0x3f; //Used when attribute is repeated usually
|
||||
uint AttrFormat = (Format >> 6) & 0x1f; //See AttributeFormat enumerator
|
||||
uint AttrElems = (Format >> 11) & 0x7F; //2 = 2D, 3 = 3D, 4 = 4D, ...
|
||||
uint AttrOffset = (Format >> 24) & 0xff; //In Word Count (32-bits)
|
||||
var instancing = (Format >> 31) & 0x1;
|
||||
|
||||
//Color
|
||||
if (AttrFormat == 0xe || AttrFormat == 0xb || AttrFormat == 0xc)
|
||||
AttrElems = 4;
|
||||
|
||||
AttrOffset *= 4;
|
||||
|
||||
this.Attributes.Add(new Attribute()
|
||||
{
|
||||
Name = name,
|
||||
Format = (AttributeFormat)AttrFormat,
|
||||
ElementCount = AttrElems,
|
||||
Offset = AttrOffset,
|
||||
Set = AttrIndex,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public AttributeFormat Format { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
public uint ElementCount { get; set; }
|
||||
public uint Set { get; set; }
|
||||
}
|
||||
|
||||
public enum AttributeFormat
|
||||
{
|
||||
Float = 1,
|
||||
HalfFloat = 2,
|
||||
UShort = 3,
|
||||
Short = 4,
|
||||
S16N = 5,
|
||||
U16N = 6,
|
||||
Sbyte = 7,
|
||||
Byte = 8,
|
||||
S8N = 9,
|
||||
U8N = 10,
|
||||
RGB = 13,
|
||||
RGBA = 14
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -322,6 +322,14 @@
|
|||
<Compile Include="FileFormats\MarioParty\HSF.cs" />
|
||||
<Compile Include="FileFormats\Message\MSYT.cs" />
|
||||
<Compile Include="FileFormats\MKAGPDX\LM2_ARCADE_Model.cs" />
|
||||
<Compile Include="FileFormats\MT\CRC32Hash.cs" />
|
||||
<Compile Include="FileFormats\MT\Model\MaterialList.cs" />
|
||||
<Compile Include="FileFormats\MT\Model\Model.cs" />
|
||||
<Compile Include="FileFormats\MT\Model\Shader.cs" />
|
||||
<Compile Include="FileFormats\MT\MT_Globals.cs" />
|
||||
<Compile Include="FileFormats\MT\MT_Renderer.cs" />
|
||||
<Compile Include="FileFormats\MT\MT_Model.cs" />
|
||||
<Compile Include="FileFormats\MT\MT_TEX.cs" />
|
||||
<Compile Include="FileFormats\NKN.cs" />
|
||||
<Compile Include="FileFormats\NLG\LM2\LM2_Material.cs" />
|
||||
<Compile Include="FileFormats\NLG\LM3\LM3_ChunkTable.cs" />
|
||||
|
|
|
@ -341,6 +341,8 @@ namespace FirstPlugin
|
|||
{
|
||||
List<Type> Formats = new List<Type>();
|
||||
Formats.Add(typeof(BFRES));
|
||||
Formats.Add(typeof(MT_TEX));
|
||||
Formats.Add(typeof(MT_Model));
|
||||
Formats.Add(typeof(BCSV));
|
||||
Formats.Add(typeof(TVOL));
|
||||
Formats.Add(typeof(BTI));
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Toolbox.Library.Rendering
|
|||
public virtual float PreviewScale { get; set; } = 1.0f;
|
||||
|
||||
public static List<ITextureContainer> TextureContainers = new List<ITextureContainer>();
|
||||
public List<STGenericTexture> Textures = new List<STGenericTexture>();
|
||||
|
||||
public List<GenericRenderedObject> Meshes = new List<GenericRenderedObject>();
|
||||
public STSkeleton Skeleton = new STSkeleton();
|
||||
|
@ -333,6 +334,15 @@ namespace Toolbox.Library.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Textures?.Count; i++)
|
||||
{
|
||||
if (activeTex == Textures[i].Text)
|
||||
{
|
||||
BindGLTexture(tex, shader, Textures[i]);
|
||||
return tex.textureUnit + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return tex.textureUnit + 1;
|
||||
}
|
||||
|
||||
|
|
5652
Toolbox/Lib/MTVertexFormats/M11.json
Normal file
5652
Toolbox/Lib/MTVertexFormats/M11.json
Normal file
File diff suppressed because it is too large
Load diff
14573
Toolbox/Lib/MTVertexFormats/MHGU.json
Normal file
14573
Toolbox/Lib/MTVertexFormats/MHGU.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue