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)
{
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[] File003Data = fileEntries[3].GetData(); //Get the fourth file
LuigisMansion3.LM3_DICT.LoadHashes();
int chunkId = 0;
uint ImageHeaderIndex = 0;
uint modelIndex = 0;
@ -212,7 +214,13 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
for (int i = 0; i < numBones; i++)
{
boneReader.SeekBegin(i * 68);
uint hash = boneReader.ReadUInt32();
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.rotation = new float[4] { 0, 0, 0, 1 };
bone.scale = new float[3] { 0.2f, 0.2f, 0.2f };
@ -266,6 +274,43 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
}
}
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:
using (var matReader = new FileReader(chunkEntry.FileData))
{

View file

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

View file

@ -151,6 +151,15 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
{
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()))
{
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++)
polyGroup.faces.Add(reader.ReadUInt16());
break;
/* case IndexFormat.Index_32:
for (int f = 0; f < mesh.IndexCount; f++)
polyGroup.faces.Add((int)reader.ReadUInt32());
break;*/
case IndexFormat.Index_16_0x2:
for (int f = 0; f < mesh.IndexCount * 4; f++)
polyGroup.faces.Add(reader.ReadUInt16());
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}");
@ -596,7 +600,7 @@ namespace FirstPlugin.LuigisMansion3
public class LM3_Mesh
{
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 ushort BufferPtrOffset { get; private set; }
@ -620,10 +624,10 @@ namespace FirstPlugin.LuigisMansion3
HashID = reader.ReadUInt32();
IndexStartOffset = reader.ReadUInt32();
IndexCount = reader.ReadUInt16();
IndexFormat = reader.ReadEnum<IndexFormat>(false);
if (IndexFormat != (IndexFormat)0x8000 && IndexFormat != 0)
IndexFormat = IndexFormat.Index_16;
IndexCount = reader.ReadUInt32();
// IndexFormat = reader.ReadEnum<IndexFormat>(false);
// if (IndexFormat != (IndexFormat)0x8000 && IndexFormat != 0 && IndexFormat != IndexFormat.Index_16_0x2)
IndexFormat = IndexFormat.Index_16;
VertexCount = reader.ReadUInt32();
reader.ReadUInt32(); //unknown

View file

@ -12,6 +12,8 @@ namespace FirstPlugin.LuigisMansion3
{
public class TexturePOWE : STGenericTexture
{
public long DataOffset;
public static readonly uint Identifier = 0xE977D350;
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")}");
// ID = reader.ReadUInt32();
CanReplace = true;
CanRename = false;
CanDelete = false;
ID2 = reader.ReadUInt32();
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
@ -112,7 +118,11 @@ namespace FirstPlugin.LuigisMansion3
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));
if (editor == null)
@ -127,11 +137,73 @@ namespace FirstPlugin.LuigisMansion3
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)
{
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)
@ -159,19 +231,25 @@ namespace FirstPlugin.LuigisMansion3
{
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,
};
TEX_FORMAT.R8G8B8A8_UNORM,
TEX_FORMAT.R8G8B8A8_UNORM_SRGB,
TEX_FORMAT.BC1_UNORM,
TEX_FORMAT.BC1_UNORM_SRGB,
TEX_FORMAT.BC2_UNORM,
TEX_FORMAT.BC3_UNORM,
TEX_FORMAT.BC4_UNORM,
TEX_FORMAT.BC5_SNORM,
TEX_FORMAT.BC6H_UF16,
TEX_FORMAT.BC7_UNORM,
TEX_FORMAT.ASTC_4x4_UNORM,
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.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Toolbox.Library.IO;
namespace FirstPlugin
{
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)
{
//From (Works as tested comparing hashbin strings/hashes

View file

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

View file

@ -429,6 +429,10 @@ namespace Toolbox.Library.Rendering
{
DrawModelSelection(group, shader);
}
else if (Runtime.RenderModelWireframe)
{
DrawModelWireframe(group, shader);
}
else
{
if (Runtime.RenderModels)
@ -453,6 +457,10 @@ namespace Toolbox.Library.Rendering
{
DrawModelSelection(m, shader);
}
else if (Runtime.RenderModelWireframe)
{
DrawModelWireframe(m, shader);
}
else
{
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)
{