A few more fixes and additions.

Fix LM3 meshes with large amounts of faces (ie Luigi's cutscene model).
Improvements to hashing.
Start on LM3 saving.
Start to support texture replacing.
This commit is contained in:
KillzXGaming 2019-11-03 18:25:47 -05:00
parent 120a86f4f5
commit 30d4a538f1
8 changed files with 223 additions and 30 deletions

View file

@ -134,7 +134,7 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
} }
foreach (var chunk in ChunkTable.ChunkSubEntries) foreach (var chunk in ChunkTable.ChunkSubEntries)
{ {
list2.Nodes.Add($"ChunkType {chunk.ChunkType} ChunkSize {chunk.ChunkSize} Unknown {chunk.ChunkOffset}"); list2.Nodes.Add($"ChunkType {chunk.ChunkType} ChunkSize {chunk.ChunkSize} ChunkOffset {chunk.ChunkOffset}");
} }
} }
} }
@ -154,6 +154,8 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
byte[] File002Data = fileEntries[2].GetData(); //Get the third file byte[] File002Data = fileEntries[2].GetData(); //Get the third file
byte[] File003Data = fileEntries[3].GetData(); //Get the fourth file byte[] File003Data = fileEntries[3].GetData(); //Get the fourth file
LuigisMansion3.LM3_DICT.LoadHashes();
int chunkId = 0; int chunkId = 0;
uint ImageHeaderIndex = 0; uint ImageHeaderIndex = 0;
uint modelIndex = 0; uint modelIndex = 0;
@ -212,7 +214,13 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
for (int i = 0; i < numBones; i++) for (int i = 0; i < numBones; i++)
{ {
boneReader.SeekBegin(i * 68); boneReader.SeekBegin(i * 68);
uint hash = boneReader.ReadUInt32();
STBone bone = new STBone(currentModel.Skeleton); STBone bone = new STBone(currentModel.Skeleton);
bone.Text = hash.ToString("X");
if (LuigisMansion3.LM3_DICT.HashNames.ContainsKey(hash))
bone.Text = LuigisMansion3.LM3_DICT.HashNames[hash];
bone.position = new float[3] { 0, 0, 0 }; bone.position = new float[3] { 0, 0, 0 };
bone.rotation = new float[4] { 0, 0, 0, 1 }; bone.rotation = new float[4] { 0, 0, 0, 1 };
bone.scale = new float[3] { 0.2f, 0.2f, 0.2f }; bone.scale = new float[3] { 0.2f, 0.2f, 0.2f };
@ -266,6 +274,43 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
} }
} }
break; break;
case SubDataType.BoneHashes:
using (var chunkReader = new FileReader(chunkEntry.FileData))
{
while (chunkReader.Position <= chunkReader.BaseStream.Length - 4)
{
uint hash = chunkReader.ReadUInt32();
string strHash = hash.ToString("X");
if (LuigisMansion3.LM3_DICT.HashNames.ContainsKey(hash))
strHash = LuigisMansion3.LM3_DICT.HashNames[hash];
Console.WriteLine("Hash! T " + strHash);
}
}
break;
case (SubDataType)0x12017105:
using (var chunkReader = new FileReader(chunkEntry.FileData))
{
while (chunkReader.Position <= chunkReader.BaseStream.Length - 8)
{
uint hash = chunkReader.ReadUInt32();
uint unk = chunkReader.ReadUInt32();
string strHash = hash.ToString("X");
if (LuigisMansion3.LM3_DICT.HashNames.ContainsKey(hash))
strHash = LuigisMansion3.LM3_DICT.HashNames[hash];
foreach (var bone in currentModel.Skeleton.bones) {
if (bone.Text == strHash)
{
}
}
}
}
currentModel.Skeleton.reset();
currentModel.Skeleton.update();
break;
case SubDataType.MaterialName: case SubDataType.MaterialName:
using (var matReader = new FileReader(chunkEntry.FileData)) using (var matReader = new FileReader(chunkEntry.FileData))
{ {

View file

@ -33,6 +33,7 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
ModelTransform = 0x1301B001, //Matrix4x4. 0x40 in size ModelTransform = 0x1301B001, //Matrix4x4. 0x40 in size
MeshBuffers = 0x1301B005, //vertex and index buffer MeshBuffers = 0x1301B005, //vertex and index buffer
BoneData = 0x1201B102, BoneData = 0x1201B102,
BoneHashes = 0x1201B103,
MaterialName = 0x1201B333, MaterialName = 0x1201B333,
MeshIndexTable = 0x1201B007, MeshIndexTable = 0x1201B007,
MessageData = 0x12027020, MessageData = 0x12027020,

View file

@ -151,6 +151,15 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
{ {
Nodes.Clear(); Nodes.Clear();
TreeNode skeletonNode = new TreeNode("Skeleton");
for (int t = 0; t < Skeleton?.bones.Count; t++) {
if (Skeleton.bones[t].Parent == null)
skeletonNode.Nodes.Add(Skeleton.bones[t]);
}
if (skeletonNode.Nodes.Count > 0)
Nodes.Add(skeletonNode);
using (var reader = new FileReader(DataDictionary.GetFile003Data())) using (var reader = new FileReader(DataDictionary.GetFile003Data()))
{ {
for (int i = 0; i < VertexBufferPointers.Count; i++) for (int i = 0; i < VertexBufferPointers.Count; i++)

View file

@ -297,10 +297,14 @@ namespace FirstPlugin.LuigisMansion3
for (int f = 0; f < mesh.IndexCount; f++) for (int f = 0; f < mesh.IndexCount; f++)
polyGroup.faces.Add(reader.ReadUInt16()); polyGroup.faces.Add(reader.ReadUInt16());
break; break;
/* case IndexFormat.Index_32: case IndexFormat.Index_16_0x2:
for (int f = 0; f < mesh.IndexCount; f++) for (int f = 0; f < mesh.IndexCount * 4; f++)
polyGroup.faces.Add((int)reader.ReadUInt32()); polyGroup.faces.Add(reader.ReadUInt16());
break;*/ break;
/* case IndexFormat.Index_32:
for (int f = 0; f < mesh.IndexCount; f++)
polyGroup.faces.Add((int)reader.ReadUInt32());
break;*/
} }
Console.WriteLine($"Mesh {genericObj.Text} Format {formatInfo.Format} BufferLength {formatInfo.BufferLength}"); Console.WriteLine($"Mesh {genericObj.Text} Format {formatInfo.Format} BufferLength {formatInfo.BufferLength}");
@ -596,7 +600,7 @@ namespace FirstPlugin.LuigisMansion3
public class LM3_Mesh public class LM3_Mesh
{ {
public uint IndexStartOffset { get; private set; } //relative to buffer start public uint IndexStartOffset { get; private set; } //relative to buffer start
public ushort IndexCount { get; private set; } //divide by 3 to get face count public uint IndexCount { get; private set; } //divide by 3 to get face count
public IndexFormat IndexFormat { get; private set; } //0x0 - ushort, 0x8000 - byte public IndexFormat IndexFormat { get; private set; } //0x0 - ushort, 0x8000 - byte
public ushort BufferPtrOffset { get; private set; } public ushort BufferPtrOffset { get; private set; }
@ -620,10 +624,10 @@ namespace FirstPlugin.LuigisMansion3
HashID = reader.ReadUInt32(); HashID = reader.ReadUInt32();
IndexStartOffset = reader.ReadUInt32(); IndexStartOffset = reader.ReadUInt32();
IndexCount = reader.ReadUInt16(); IndexCount = reader.ReadUInt32();
IndexFormat = reader.ReadEnum<IndexFormat>(false); // IndexFormat = reader.ReadEnum<IndexFormat>(false);
if (IndexFormat != (IndexFormat)0x8000 && IndexFormat != 0) // if (IndexFormat != (IndexFormat)0x8000 && IndexFormat != 0 && IndexFormat != IndexFormat.Index_16_0x2)
IndexFormat = IndexFormat.Index_16; IndexFormat = IndexFormat.Index_16;
VertexCount = reader.ReadUInt32(); VertexCount = reader.ReadUInt32();
reader.ReadUInt32(); //unknown reader.ReadUInt32(); //unknown

View file

@ -12,6 +12,8 @@ namespace FirstPlugin.LuigisMansion3
{ {
public class TexturePOWE : STGenericTexture public class TexturePOWE : STGenericTexture
{ {
public long DataOffset;
public static readonly uint Identifier = 0xE977D350; public static readonly uint Identifier = 0xE977D350;
public uint Index { get; set; } public uint Index { get; set; }
@ -82,6 +84,10 @@ namespace FirstPlugin.LuigisMansion3
// throw new Exception($"Invalid texture header magic! Expected {Identifier.ToString("x")}. Got {Identifier.ToString("x")}"); // throw new Exception($"Invalid texture header magic! Expected {Identifier.ToString("x")}. Got {Identifier.ToString("x")}");
// ID = reader.ReadUInt32(); // ID = reader.ReadUInt32();
CanReplace = true;
CanRename = false;
CanDelete = false;
ID2 = reader.ReadUInt32(); ID2 = reader.ReadUInt32();
Width = reader.ReadUInt16(); Width = reader.ReadUInt16();
Height = reader.ReadUInt16(); Height = reader.ReadUInt16();
@ -112,7 +118,11 @@ namespace FirstPlugin.LuigisMansion3
properties.Format = Format; properties.Format = Format;
} }
public override void OnClick(TreeView treeview) public override void OnClick(TreeView treeview) {
UpdateEditor();
}
private void UpdateEditor()
{ {
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase)); ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase));
if (editor == null) if (editor == null)
@ -127,11 +137,73 @@ namespace FirstPlugin.LuigisMansion3
editor.LoadImage(this); editor.LoadImage(this);
} }
public override bool CanEdit { get; set; } = false; public override void Replace(string FileName)
{
var bntxFile = new BNTX();
var tex = new TextureData();
tex.Replace(FileName, MipCount, 0, Format);
//If it's null, the operation is cancelled
if (tex.Texture == null)
return;
var surfacesNew = tex.GetSurfaces();
var surfaces = GetSurfaces();
if (surfaces[0].mipmaps[0].Length != surfacesNew[0].mipmaps[0].Length)
throw new Exception($"Image must be the same size! {surfaces[0].mipmaps[0].Length}");
if (Width != tex.Texture.Width || Height != tex.Texture.Height)
throw new Exception("Image size must be the same!");
ImageData = tex.Texture.TextureData[0][0];
Width = tex.Texture.Width;
Height = tex.Texture.Height;
MipCount = tex.Texture.MipCount;
surfacesNew.Clear();
surfaces.Clear();
UpdateEditor();
}
public override bool CanEdit { get; set; } = true;
public override void SetImageData(Bitmap bitmap, int ArrayLevel) public override void SetImageData(Bitmap bitmap, int ArrayLevel)
{ {
throw new NotImplementedException(); var tex = new Syroot.NintenTools.NSW.Bntx.Texture();
tex.Height = (uint)bitmap.Height;
tex.Width = (uint)bitmap.Width;
tex.Format = TextureData.GenericToBntxSurfaceFormat(Format);
tex.Name = Text;
tex.Path = "";
tex.TextureData = new List<List<byte[]>>();
STChannelType[] channels = SetChannelsByFormat(Format);
tex.sparseBinding = 0; //false
tex.sparseResidency = 0; //false
tex.Flags = 0;
tex.Swizzle = 0;
tex.textureLayout = 0;
tex.Regs = new uint[0];
tex.AccessFlags = 0x20;
tex.ArrayLength = (uint)ArrayLevel;
tex.MipCount = MipCount;
tex.Depth = Depth;
tex.Dim = Syroot.NintenTools.NSW.Bntx.GFX.Dim.Dim2D;
tex.TileMode = Syroot.NintenTools.NSW.Bntx.GFX.TileMode.Default;
tex.textureLayout2 = 0x010007;
tex.SurfaceDim = Syroot.NintenTools.NSW.Bntx.GFX.SurfaceDim.Dim2D;
tex.SampleCount = 1;
tex.Pitch = 32;
tex.MipOffsets = new long[tex.MipCount];
var mipmaps = TextureImporterSettings.SwizzleSurfaceMipMaps(tex,
GenerateMipsAndCompress(bitmap, MipCount, Format), MipCount);
ImageData = Utils.CombineByteArray(mipmaps.ToArray());
} }
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0) public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0)
@ -159,19 +231,25 @@ namespace FirstPlugin.LuigisMansion3
{ {
return new TEX_FORMAT[] return new TEX_FORMAT[]
{ {
TEX_FORMAT.B5G6R5_UNORM, TEX_FORMAT.R8G8B8A8_UNORM,
TEX_FORMAT.R8G8_UNORM, TEX_FORMAT.R8G8B8A8_UNORM_SRGB,
TEX_FORMAT.B5G5R5A1_UNORM, TEX_FORMAT.BC1_UNORM,
TEX_FORMAT.B4G4R4A4_UNORM, TEX_FORMAT.BC1_UNORM_SRGB,
TEX_FORMAT.LA8, TEX_FORMAT.BC2_UNORM,
TEX_FORMAT.HIL08, TEX_FORMAT.BC3_UNORM,
TEX_FORMAT.L8, TEX_FORMAT.BC4_UNORM,
TEX_FORMAT.A8_UNORM, TEX_FORMAT.BC5_SNORM,
TEX_FORMAT.LA4, TEX_FORMAT.BC6H_UF16,
TEX_FORMAT.A4, TEX_FORMAT.BC7_UNORM,
TEX_FORMAT.ETC1_UNORM, TEX_FORMAT.ASTC_4x4_UNORM,
TEX_FORMAT.ETC1_A4, TEX_FORMAT.ASTC_5x4_UNORM,
}; TEX_FORMAT.ASTC_5x5_UNORM,
TEX_FORMAT.ASTC_6x5_UNORM,
TEX_FORMAT.ASTC_6x6_UNORM,
TEX_FORMAT.ASTC_8x5_UNORM,
TEX_FORMAT.ASTC_8x6_UNORM,
TEX_FORMAT.ASTC_8x8_UNORM,
};
} }
} }
} }

View file

@ -2,12 +2,37 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Windows.Forms;
using Toolbox.Library.IO;
namespace FirstPlugin namespace FirstPlugin
{ {
public class NLG_Common public class NLG_Common
{ {
public static void PrintHashIdBin()
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
using (var reader = new FileReader(ofd.FileName))
{
reader.SetByteOrder(true);
uint numHashes = reader.ReadUInt32();
uint stringTblPos = (numHashes * 8) + 4;
for (int i = 0; i < numHashes; i++)
{
uint hash = reader.ReadUInt32();
uint offset = reader.ReadUInt32();
using (reader.TemporarySeek(stringTblPos + offset, System.IO.SeekOrigin.Begin))
{
string name = reader.ReadZeroTerminatedString();
Console.WriteLine(name);
}
}
}
}
}
public static uint StringToHash(string name, bool caseSensative = false) public static uint StringToHash(string name, bool caseSensative = false)
{ {
//From (Works as tested comparing hashbin strings/hashes //From (Works as tested comparing hashbin strings/hashes

View file

@ -793,13 +793,12 @@ namespace Toolbox.Library
{ {
if (IsAtscFormat(Format)) if (IsAtscFormat(Format))
{ {
return "Supported Formats|*.dds; *.png;*.tga;*.jpg;*.tiff;*.astc|" + return "Supported Formats|*.astc; *.png;*.tga;*.jpg;*.tiff|" +
"Microsoft DDS |*.dds|" + "ASTC |*.astc|" +
"Portable Network Graphics |*.png|" + "Portable Network Graphics |*.png|" +
"Joint Photographic Experts Group |*.jpg|" + "Joint Photographic Experts Group |*.jpg|" +
"Bitmap Image |*.bmp|" + "Bitmap Image |*.bmp|" +
"Tagged Image File Format |*.tiff|" + "Tagged Image File Format |*.tiff|" +
"ASTC |*.astc|" +
"All files(*.*)|*.*"; "All files(*.*)|*.*";
} }
else else

View file

@ -429,6 +429,10 @@ namespace Toolbox.Library.Rendering
{ {
DrawModelSelection(group, shader); DrawModelSelection(group, shader);
} }
else if (Runtime.RenderModelWireframe)
{
DrawModelWireframe(group, shader);
}
else else
{ {
if (Runtime.RenderModels) if (Runtime.RenderModels)
@ -453,6 +457,10 @@ namespace Toolbox.Library.Rendering
{ {
DrawModelSelection(m, shader); DrawModelSelection(m, shader);
} }
else if (Runtime.RenderModelWireframe)
{
DrawModelWireframe(m, shader);
}
else else
{ {
if (Runtime.RenderModels) if (Runtime.RenderModels)
@ -464,6 +472,30 @@ namespace Toolbox.Library.Rendering
} }
private static void DrawModelWireframe(STGenericObject p, ShaderProgram shader)
{
// use vertex color for wireframe color
shader.SetInt("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);
shader.SetInt("colorOverride", 0);
}
private static void DrawModelWireframe(STGenericPolygonGroup p, ShaderProgram shader)
{
// use vertex color for wireframe color
shader.SetInt("colorOverride", 1);
GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
GL.Enable(EnableCap.LineSmooth);
GL.LineWidth(1.5f);
GL.DrawElements(PrimitiveType.Triangles, p.displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
shader.SetInt("colorOverride", 0);
}
private static void DrawModelSelection(STGenericObject p, ShaderProgram shader) private static void DrawModelSelection(STGenericObject p, ShaderProgram shader)
{ {