diff --git a/.vs/Toolbox/v15/.suo b/.vs/Toolbox/v15/.suo index 4223ac6c..c893c4a2 100644 Binary files a/.vs/Toolbox/v15/.suo and b/.vs/Toolbox/v15/.suo differ diff --git a/File_Format_Library/FileFormats/Archives/BEA.cs b/File_Format_Library/FileFormats/Archives/BEA.cs index 7e4d433e..19ee959f 100644 --- a/File_Format_Library/FileFormats/Archives/BEA.cs +++ b/File_Format_Library/FileFormats/Archives/BEA.cs @@ -254,10 +254,16 @@ namespace FirstPlugin node.Nodes.Insert(index, NewNode); } + + public static byte[] GetASSTData(FileEntry entry) { + return entry.CompressedData; + if (entry.IsCompressed) + { return STLibraryCompression.ZSTD.Decompress(entry.CompressedData); + } else return entry.CompressedData; } diff --git a/File_Format_Library/FileFormats/BMD/BMD.cs b/File_Format_Library/FileFormats/BMD/BMD.cs index 6dea6f67..9079542d 100644 --- a/File_Format_Library/FileFormats/BMD/BMD.cs +++ b/File_Format_Library/FileFormats/BMD/BMD.cs @@ -18,10 +18,8 @@ using OpenTK; namespace FirstPlugin { - public class BMD : TreeNodeFile, IFileFormat, IContextMenuNode, ITextureContainer, ITextureIconLoader + public class BMD : TreeNodeFile, IFileFormat, IContextMenuNode, ITextureContainer { - public List IconTextureList { get; set; } - public FileType FileType { get; set; } = FileType.Layout; public bool CanSave { get; set; } @@ -117,7 +115,6 @@ namespace FirstPlugin DrawableContainer.Drawables.Add(Skeleton); Textures = new Dictionary(); - IconTextureList = new List(); BMD_Renderer.TextureContainers.Add(this); @@ -278,7 +275,6 @@ namespace FirstPlugin var texWrapper = new BMDTextureWrapper(BMDFile.Textures.Textures[i]); TextureFolder.Nodes.Add(texWrapper); Renderer.TextureList.Add(texWrapper); - IconTextureList.Add(texWrapper); } } diff --git a/File_Format_Library/FileFormats/Grezzo/CMB.cs b/File_Format_Library/FileFormats/Grezzo/CMB.cs index cfa7b458..3f3add9f 100644 --- a/File_Format_Library/FileFormats/Grezzo/CMB.cs +++ b/File_Format_Library/FileFormats/Grezzo/CMB.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Toolbox; using System.Windows.Forms; +using Toolbox.Library.Forms; using Toolbox.Library; using Toolbox.Library.IO; +using Toolbox.Library.Rendering; +using Grezzo.CmbEnums; namespace FirstPlugin { @@ -38,11 +40,61 @@ namespace FirstPlugin } } + 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 CMB_Renderer Renderer; + public DrawableContainer DrawableContainer = new DrawableContainer(); + public Header header; STTextureFolder texFolder; + STSkeleton Skeleton; public void Load(System.IO.Stream stream) { + Renderer = new CMB_Renderer(); + DrawableContainer.Name = FileName; + DrawableContainer.Drawables.Add(Renderer); + Skeleton = new STSkeleton(); + //These models/skeletons come out massive so scale them with an overridden scale + Skeleton.PreviewScale = Renderer.PreviewScale; + DrawableContainer.Drawables.Add(Skeleton); + header = new Header(); header.Read(new FileReader(stream)); @@ -52,25 +104,247 @@ namespace FirstPlugin if (header.SectionData.TextureChunk != null) { texFolder = new TextureFolder("Texture"); - Nodes.Add(texFolder); + TreeNode meshFolder = new TreeNode("Meshes"); + TreeNode materialFolder = new TreeNode("Materials"); + TreeNode skeletonFolder = new TreeNode("Skeleton"); - int texIndex = 0; - foreach (var tex in header.SectionData.TextureChunk.Textures) + List materials = new List(); + + bool HasTextures = header.SectionData.TextureChunk != null && + header.SectionData.TextureChunk.Textures.Count != 0; + + bool HasMeshes = header.SectionData.SkeletalMeshChunk != null && + header.SectionData.SkeletalMeshChunk.ShapeChunk.SeperateShapes.Count != 0; + + bool HasSkeleton = header.SectionData.SkeletonChunk != null && + header.SectionData.SkeletonChunk.Bones.Count != 0; + + bool HasMaterials = header.SectionData.MaterialChunk != null && + header.SectionData.MaterialChunk.Materials.Count != 0; + + if (HasSkeleton) { - var texWrapper = new CTXB.TextureWrapper(); - texWrapper.Text = $"Texture {texIndex++}"; - texWrapper.ImageKey = "texture"; - texWrapper.SelectedImageKey = texWrapper.ImageKey; + foreach (var bone in header.SectionData.SkeletonChunk.Bones) + { + STBone genericBone = new STBone(Skeleton); + genericBone.parentIndex = bone.ParentIndex; + genericBone.position = new float[3]; + genericBone.scale = new float[3]; + genericBone.rotation = new float[4]; + genericBone.Checked = true; - if (tex.Name != string.Empty) - texWrapper.Text = tex.Name; + genericBone.Text = $"Bone {bone.ID}"; + genericBone.RotationType = STBone.BoneRotationType.Euler; - texWrapper.Width = tex.Width; - texWrapper.Height = tex.Height; - texWrapper.Format = CTR_3DS.ConvertPICAToGenericFormat(tex.PicaFormat); - texWrapper.ImageData = tex.ImageData; - texFolder.Nodes.Add(texWrapper); + genericBone.position[0] = bone.Translation.X; + genericBone.position[1] = bone.Translation.Y; + genericBone.position[2] = bone.Translation.Z; + + genericBone.scale[0] = bone.Scale.X; + genericBone.scale[1] = bone.Scale.Y; + genericBone.scale[2] = bone.Scale.Z; + + genericBone.rotation[0] = bone.Rotation.X; + genericBone.rotation[1] = bone.Rotation.Y; + genericBone.rotation[2] = bone.Rotation.Z; + + Skeleton.bones.Add(genericBone); + } + + foreach (var bone in Skeleton.bones) + { + if (bone.Parent == null) + skeletonFolder.Nodes.Add(bone); + } + + Skeleton.reset(); + Skeleton.update(); } + + if (HasTextures) + { + int texIndex = 0; + foreach (var tex in header.SectionData.TextureChunk.Textures) + { + var texWrapper = new CTXB.TextureWrapper(); + texWrapper.Text = $"Texture {texIndex++}"; + texWrapper.ImageKey = "texture"; + texWrapper.SelectedImageKey = texWrapper.ImageKey; + + if (tex.Name != string.Empty) + texWrapper.Text = tex.Name; + + texWrapper.Width = tex.Width; + texWrapper.Height = tex.Height; + texWrapper.Format = CTR_3DS.ConvertPICAToGenericFormat(tex.PicaFormat); + texWrapper.ImageData = tex.ImageData; + texFolder.Nodes.Add(texWrapper); + + Renderer.TextureList.Add(texWrapper); + } + } + + if (HasMaterials) + { + int materialIndex = 0; + foreach (var mat in header.SectionData.MaterialChunk.Materials) + { + STGenericMaterial material = new STGenericMaterial(); + material.Text = $"Material {materialIndex++}"; + materialFolder.Nodes.Add(material); + materials.Add(material); + + int index = 0; + foreach (var tex in mat.TextureMaps) + { + if (tex.TextureIndex != -1) + { + CMBTextureMapWrapper matTexture = new CMBTextureMapWrapper(); + matTexture.TextureIndex = tex.TextureIndex; + matTexture.wrapModeS = tex.WrapS; + matTexture.wrapModeT = tex.WrapT; + material.TextureMaps.Add(matTexture); + + if (index == 0) + matTexture.Type = STGenericMatTexture.TextureType.Diffuse; + + index++; + } + } + } + } + + if (HasMeshes) + { + int MeshIndex = 0; + foreach (var mesh in header.SectionData.SkeletalMeshChunk.MeshChunk.Meshes) + { + STGenericMaterial mat = new STGenericMaterial(); + if (materials.Count > mesh.MaterialIndex) //Incase materials for some reason are in a seperate file, check this + mat = materials[mesh.MaterialIndex]; + + CmbMeshWrapper genericMesh = new CmbMeshWrapper(mat); + genericMesh.Text = $"Mesh {MeshIndex++}"; + genericMesh.MaterialIndex = mesh.MaterialIndex; + + //Wow this is long + var shape = header.SectionData.SkeletalMeshChunk.ShapeChunk.SeperateShapes[(int)mesh.SepdIndex]; + genericMesh.Shape = shape; + + //Now load the vertex and face data + if (shape.Position.VertexData != null) + { + int VertexCount = shape.Position.VertexData.Length; + for (int v = 0; v < VertexCount; v++) + { + Vertex vert = new Vertex(); + vert.pos = new OpenTK.Vector3( + shape.Position.VertexData[v].X, + shape.Position.VertexData[v].Y, + shape.Position.VertexData[v].Z); + + if (shape.Normal != null) + { + vert.nrm = new OpenTK.Vector3( + shape.Normal.VertexData[v].X, + shape.Normal.VertexData[v].Y, + shape.Normal.VertexData[v].Z).Normalized(); + } + + if (shape.Color.VertexData != null) + { + vert.col = new OpenTK.Vector4( + shape.Color.VertexData[v].X, + shape.Color.VertexData[v].Y, + shape.Color.VertexData[v].Z, + shape.Color.VertexData[v].W).Normalized(); + } + + if (shape.TexCoord0 != null) + { + vert.uv0 = new OpenTK.Vector2( + shape.TexCoord0.VertexData[v].X, + shape.TexCoord0.VertexData[v].Y); + } + + if (shape.TexCoord1 != null) + { + + } + + if (shape.TexCoord2 != null) + { + + } + + for (int i = 0; i < 16; i++) + { + if (i < shape.Primatives[0].BoneIndexTable.Length) + { + int boneId = shape.Primatives[0].BoneIndexTable[i]; + + if (shape.Primatives[0].SkinningMode == SkinningMode.RIGID_SKINNING) + { + vert.pos = OpenTK.Vector3.TransformPosition(vert.pos, Skeleton.bones[boneId].Transform); + vert.nrm = OpenTK.Vector3.TransformNormal(vert.nrm, Skeleton.bones[boneId].Transform); + } + } + } + + bool HasSkinning = shape.Primatives[0].SkinningMode != SkinningMode.SINGLE_BONE + && shape.BoneIndices.Type == CmbDataType.UByte; //Noclip checks the type for ubyte so do the same + + bool HasWeights = shape.Primatives[0].SkinningMode == SkinningMode.SMOOTH_SKINNING; + + /* if (shape.BoneIndices != null && HasSkinning && shape.BoneIndices.VertexData.Length > v) + { + var BoneIndices = shape.BoneIndices.VertexData[v]; + for (int j = 0; j < shape.boneDimension; j++) + { + ushort index = shape.Primatives[0].BoneIndexTable[(uint)BoneIndices[j]]; + vert.boneIds.Add((int)index); + } + } + if (shape.BoneWeights != null && HasWeights && shape.BoneWeights.VertexData.Length > v) + { + var BoneWeights = shape.BoneWeights.VertexData[v]; + for (int j = 0; j < shape.boneDimension; j++) + { + vert.boneWeights.Add(BoneWeights[j]); + } + }*/ + + genericMesh.vertices.Add(vert); + } + } + + foreach (var prim in shape.Primatives) + { + STGenericPolygonGroup group = new STGenericPolygonGroup(); + genericMesh.PolygonGroups.Add(group); + + for (int i = 0; i < prim.Primatives[0].Indices.Length; i++) + { + group.faces.Add((int)prim.Primatives[0].Indices[i]); + } + } + + Renderer.Meshes.Add(genericMesh); + meshFolder.Nodes.Add(genericMesh); + } + } + + if (meshFolder.Nodes.Count > 0) + Nodes.Add(meshFolder); + + if (skeletonFolder.Nodes.Count > 0) + Nodes.Add(skeletonFolder); + + if (materialFolder.Nodes.Count > 0) + Nodes.Add(materialFolder); + + if (texFolder.Nodes.Count > 0) + Nodes.Add(texFolder); } } @@ -90,6 +364,32 @@ namespace FirstPlugin LM3DS, } + public class CMBMaterialWrapper : STGenericMaterial + { + + } + + public class CMBTextureMapWrapper : STGenericMatTexture + { + public int TextureIndex { get; set; } + } + + public class CmbMeshWrapper : GenericRenderedObject + { + public SeperateShape Shape { get; set; } + + STGenericMaterial material; + + public CmbMeshWrapper(STGenericMaterial mat) { + material = mat; + } + + public override STGenericMaterial GetMaterial() + { + return material; + } + } + private class TextureFolder : STTextureFolder, ITextureIconLoader { public List IconTextureList @@ -175,6 +475,8 @@ namespace FirstPlugin public LUTSChunk LUTSChunk; public VertexAttributesChunk VertexAttributesChunk; + public ushort[] Indices; + public void Read(FileReader reader, Header header) { uint numIndices = reader.ReadUInt32(); @@ -194,6 +496,58 @@ namespace FirstPlugin if (header.Version >= CMBVersion.MM3DS) reader.ReadUInt32(); //Padding? + if (VertexAttributesChunk != null) + { + long bufferStart = VertexAttributesChunk.StartPosition; + foreach (var shape in SkeletalMeshChunk.ShapeChunk.SeperateShapes) + { + ReadVertexDataFromSlice(reader, VertexAttributesChunk.PositionSlice, shape.Position, 3, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.NormalSlice, shape.Normal, 3, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.TangentSlice, shape.Tangent, 3, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.ColorSlice, shape.Color, 4, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.Texcoord0Slice, shape.TexCoord0, 2, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.Texcoord1Slice, shape.TexCoord1, 2, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.Texcoord2Slice, shape.TexCoord2, 2, bufferStart); + + ReadVertexDataFromSlice(reader, VertexAttributesChunk.BoneIndicesSlice, shape.BoneIndices, shape.boneDimension, bufferStart); + ReadVertexDataFromSlice(reader, VertexAttributesChunk.BoneWeightsSlice, shape.BoneWeights, shape.boneDimension, bufferStart); + } + } + + if (indexBufferOffset != 0) + { + foreach (var shape in SkeletalMeshChunk.ShapeChunk.SeperateShapes) + { + foreach (var prim in shape.Primatives) + { + foreach (var subprim in prim.Primatives) //Note 3DS usually only has one sub primative + { + subprim.Indices = new uint[subprim.IndexCount]; + + reader.SeekBegin(indexBufferOffset + subprim.Offset); + + switch (subprim.IndexType) + { + case CmbDataType.UByte: + for (int i = 0; i < subprim.IndexCount; i++) + subprim.Indices[i] = reader.ReadByte(); + break; + case CmbDataType.UShort: + for (int i = 0; i < subprim.IndexCount; i++) + subprim.Indices[i] = reader.ReadUInt16(); + break; + case CmbDataType.UInt: + for (int i = 0; i < subprim.IndexCount; i++) + subprim.Indices[i] = reader.ReadUInt32(); + break; + default: + throw new Exception("Unsupported index type! " + subprim.IndexType); + } + } + } + } + } + foreach (var tex in TextureChunk.Textures) { reader.SeekBegin(textureDataOffset + tex.DataOffset); @@ -201,20 +555,450 @@ namespace FirstPlugin } } + private static void ReadVertexDataFromSlice(FileReader reader, BufferSlice Slice, SepdVertexAttribute VertexAttribute, int elementCount, long bufferStart) + { + if (Slice == null || Slice.Size == 0) + return; + + reader.SeekBegin(bufferStart + VertexAttribute.StartPosition + Slice.Offset); + + int StrideSize = CalculateStrideSize(VertexAttribute.Type, elementCount); + int VertexCount = (int)Slice.Size / StrideSize; + + Console.WriteLine($"{VertexAttribute.GetType()} {VertexAttribute.Type} {elementCount} {StrideSize} {VertexCount}"); + + VertexAttribute.VertexData = new Syroot.Maths.Vector4F[VertexCount]; + for (int v = 0; v < VertexCount; v++) + { + VertexAttribute.VertexData[v] = ReadVertexBufferData(reader, VertexAttribute, elementCount); + } + } + + private static Syroot.Maths.Vector4F ReadVertexBufferData(FileReader reader, SepdVertexAttribute VertexAttribute, int elementCount) + { + List values = new List(); + + for (int i = 0; i < elementCount; i++) + { + switch (VertexAttribute.Type) + { + case CmbDataType.Byte: + values.Add(reader.ReadSByte()); + break; + case CmbDataType.Float: + values.Add(reader.ReadSingle()); + break; + case CmbDataType.Int: + values.Add(reader.ReadInt32()); + break; + case CmbDataType.Short: + values.Add(reader.ReadInt16()); + break; + case CmbDataType.UByte: + values.Add(reader.ReadByte()); + break; + case CmbDataType.UInt: + values.Add(reader.ReadUInt32()); + break; + case CmbDataType.UShort: + values.Add(reader.ReadUInt16()); + break; + default: throw new Exception("Unknwon format! " + VertexAttribute.Type); + } + } + + while (values.Count < 4) values.Add(0); + + return new Syroot.Maths.Vector4F( + values[0] * VertexAttribute.Scale, + values[1] * VertexAttribute.Scale, + values[2] * VertexAttribute.Scale, + values[3] * VertexAttribute.Scale); + } + + private static int CalculateStrideSize(CmbDataType type, int elementCount) + { + switch (type) + { + case CmbDataType.Byte: return elementCount * sizeof(sbyte); + case CmbDataType.Float: return elementCount * sizeof(float); + case CmbDataType.Int: return elementCount * sizeof(int); + case CmbDataType.Short: return elementCount * sizeof(short); + case CmbDataType.UByte: return elementCount * sizeof(byte); + case CmbDataType.UInt: return elementCount * sizeof(uint); + case CmbDataType.UShort: return elementCount * sizeof(ushort); + default: throw new Exception("Unknwon format! " + type); + } + } + public void Write(FileWriter writer, Header header) { + long pos = writer.Position; + writer.Write(Indices != null ? Indices.Length : 0); + //Reserve space for all the offses + writer.Write(0); //SkeletonChunk + if (header.Version >= CMBVersion.MM3DS) + writer.Write(0); //QuadTreeChunk + writer.Write(0); //MaterialChunk + writer.Write(0); //TextureChunk + writer.Write(0); //SkeletalMeshChunk + writer.Write(0); //LUTSChunk + writer.Write(0); //VertexAttributesChunk + writer.Write(0); //indexBufferOffset + writer.Write(0); //textureDataOffset + + if (header.Version >= CMBVersion.MM3DS) + writer.Write(0); //padding or unknown unused section + + //Write sections and offsets + int _offsetPos = 4; + if (SkeletonChunk != null) + { + writer.WriteUint32Offset(pos + _offsetPos); + SkeletonChunk.Write(writer, header); + } + + if (QuadTreeChunk != null && header.Version >= CMBVersion.MM3DS) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + QuadTreeChunk.Write(writer, header); + } + + if (MaterialChunk != null) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + MaterialChunk.Write(writer, header); + } + + if (TextureChunk != null) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + TextureChunk.Write(writer, header); + } + + if (SkeletalMeshChunk != null) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + SkeletalMeshChunk.Write(writer, header); + } + + if (LUTSChunk != null) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + LUTSChunk.Write(writer, header); + } + + if (VertexAttributesChunk != null) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + VertexAttributesChunk.Write(writer, header); + } + + if (Indices != null && Indices.Length > 0) + { + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + writer.Write(Indices); + } + + if (TextureChunk != null && TextureChunk.Textures.Count > 0) + { + long dataStart = writer.Position; + writer.WriteUint32Offset(pos + (_offsetPos += 4)); + //Save image data + foreach (var tex in TextureChunk.Textures) + { + writer.SeekBegin(tex.DataOffset + dataStart); + writer.Write(tex.ImageData); + } + } } } + //Connects all the meshes, vertex attributes, and shape data together public class SkeletalMeshChunk : IChunkCommon { private const string Magic = "sklm"; + public MeshesChunk MeshChunk { get; set; } + public ShapesChunk ShapeChunk { get; set; } + + public void Read(FileReader reader, Header header) + { + long pos = reader.Position; + + reader.ReadSignature(4, Magic); + uint sectionSize = reader.ReadUInt32(); + MeshChunk = ReadChunkSection(reader, header, pos); + ShapeChunk = ReadChunkSection(reader, header,pos); + } + + public void Write(FileWriter writer, Header header) + { + writer.WriteSignature(Magic); + writer.Write(uint.MaxValue);//SectionSize + } + } + + public class MeshesChunk : IChunkCommon + { + private const string Magic = "mshs"; + + public List Meshes = new List(); + public void Read(FileReader reader, Header header) { reader.ReadSignature(4, Magic); uint sectionSize = reader.ReadUInt32(); + uint meshCount = reader.ReadUInt32(); + uint unknown = reader.ReadUInt32(); + + long meshPos = reader.Position; + for (int i = 0; i < meshCount; i++) + { + if (header.Version == CMBVersion.OOT3DS) + reader.SeekBegin(meshPos + (i * 0x04)); + else if (header.Version == CMBVersion.MM3DS) + reader.SeekBegin(meshPos + (i * 0x0C)); + else if (header.Version >= CMBVersion.LM3DS) + reader.SeekBegin(meshPos + (i * 0x58)); + + Mesh mesh = new Mesh(); + mesh.SepdIndex = reader.ReadUInt16(); + mesh.MaterialIndex = reader.ReadByte(); + Meshes.Add(mesh); + } + } + + public void Write(FileWriter writer, Header header) + { + writer.WriteSignature(Magic); + writer.Write(uint.MaxValue);//SectionSize + writer.Write(Meshes.Count); + for (int i = 0; i < Meshes.Count; i++) + { + + } + } + + public class Mesh + { + public ushort SepdIndex { get; set; } + public byte MaterialIndex { get; set; } + } + } + + public class ShapesChunk : IChunkCommon + { + private const string Magic = "shp "; + + public uint Unknown; + + public List SeperateShapes = new List(); + + public void Read(FileReader reader, Header header) + { + long pos = reader.Position; + + reader.ReadSignature(4, Magic); + uint sectionSize = reader.ReadUInt32(); + uint sepdCount = reader.ReadUInt32(); + Unknown = reader.ReadUInt32(); + ushort[] offsets = reader.ReadUInt16s((int)sepdCount); + for (int i = 0; i < sepdCount; i++) + { + reader.SeekBegin(pos + offsets[i]); + var sepd= new SeperateShape(); + sepd.Read(reader, header); + SeperateShapes.Add(sepd); + } + } + + public void Write(FileWriter writer, Header header) + { + writer.WriteSignature(Magic); + writer.Write(uint.MaxValue);//SectionSize + + } + } + + public class SeperateShape : IChunkCommon + { + private const string Magic = "sepd"; + + public SepdVertexAttribute Position { get; set; } + public SepdVertexAttribute Normal { get; set; } + public SepdVertexAttribute Tangent { get; set; } + public SepdVertexAttribute Color { get; set; } + public SepdVertexAttribute TexCoord0 { get; set; } + public SepdVertexAttribute TexCoord1 { get; set; } + public SepdVertexAttribute TexCoord2 { get; set; } + public SepdVertexAttribute BoneIndices { get; set; } + public SepdVertexAttribute BoneWeights { get; set; } + + public List Primatives = new List(); + + public ushort boneDimension; + + public void Read(FileReader reader, Header header) + { + long pos = reader.Position; + + reader.ReadSignature(4, Magic); + uint sectionSize = reader.ReadUInt32(); + uint count = reader.ReadUInt16(); + + if (header.Version >= CMBVersion.LM3DS) + reader.SeekBegin(pos + 0x3C); + else + reader.SeekBegin(pos + 0x24); + + Position = ReadVertexAttrib(reader); + Normal = ReadVertexAttrib(reader); + if (header.Version >= CMBVersion.MM3DS) + Tangent = ReadVertexAttrib(reader); + + Color = ReadVertexAttrib(reader); + TexCoord0 = ReadVertexAttrib(reader); + TexCoord1 = ReadVertexAttrib(reader); + TexCoord2 = ReadVertexAttrib(reader); + BoneIndices = ReadVertexAttrib(reader); + BoneWeights = ReadVertexAttrib(reader); + + boneDimension = reader.ReadUInt16(); + reader.ReadUInt16(); //padding + + ushort[] Offsets = reader.ReadUInt16s((int)count); + + for (int i = 0; i < count; i++) + { + reader.SeekBegin(pos + Offsets[i]); + PrimativesChunk prim = new PrimativesChunk(); + prim.Read(reader, header); + Primatives.Add(prim); + } + } + + public void Write(FileWriter writer, Header header) + { + + } + + private SepdVertexAttribute ReadVertexAttrib(FileReader reader) + { + long pos = reader.Position; + + SepdVertexAttribute att = new SepdVertexAttribute(); + att.StartPosition = reader.ReadUInt32(); + att.Scale = reader.ReadSingle(); + att.Type = reader.ReadEnum(true); + att.Mode = reader.ReadEnum(true); + att.Constants = new float[4]; + att.Constants[0] = reader.ReadSingle(); + att.Constants[1] = reader.ReadSingle(); + att.Constants[2] = reader.ReadSingle(); + att.Constants[3] = reader.ReadSingle(); + + reader.SeekBegin(pos + 0x1C); + + return att; + } + } + + public class SepdVertexAttribute + { + public uint StartPosition { get; set; } + public float Scale { get; set; } + public CmbDataType Type { get; set; } + public SepdVertexAttribMode Mode { get; set; } + + public Syroot.Maths.Vector4F[] VertexData { get; set; } + + public float[] Constants { get; set; } + } + + public class PrimativesChunk : IChunkCommon + { + private const string Magic = "prms"; + + public SkinningMode SkinningMode; + + public List Primatives = new List(); + + public ushort[] BoneIndexTable { get; set; } + + public void Read(FileReader reader, Header header) + { + long pos = reader.Position; + + reader.ReadSignature(4, Magic); + uint sectionSize = reader.ReadUInt32(); + uint count = reader.ReadUInt32(); + SkinningMode = reader.ReadEnum(true); + ushort boneTableCount = reader.ReadUInt16(); + uint boneIndexOffset = reader.ReadUInt32(); + uint primativeOffset = reader.ReadUInt32(); + + reader.SeekBegin(pos + boneIndexOffset); + BoneIndexTable = reader.ReadUInt16s(boneTableCount); + + reader.SeekBegin(pos + primativeOffset); + for (int i = 0; i < count; i++) + { + SubPrimativeChunk prim = new SubPrimativeChunk(); + prim.Read(reader, header); + Primatives.Add(prim); + } + } + + public void Write(FileWriter writer, Header header) + { + writer.WriteSignature(Magic); + writer.Write(uint.MaxValue);//SectionSize + } + } + + public class SubPrimativeChunk : IChunkCommon + { + private const string Magic = "prm "; + + public SkinningMode SkinningMode { get; private set; } + + public CmbDataType IndexType { get; private set; } + + public ushort IndexCount { get; private set; } + + public uint Offset { get; private set; } + + private uint[] _indices; + public uint[] Indices + { + get + { + return _indices; + } + set + { + _indices = value; + } + } + + public void Read(FileReader reader, Header header) + { + long pos = reader.Position; + + reader.ReadSignature(4, Magic); + uint sectionSize = reader.ReadUInt32(); + uint unknown = reader.ReadUInt32(); + uint unknown2 = reader.ReadUInt32(); + IndexType = reader.ReadEnum(true); + reader.Seek(2); //padding + + IndexCount = reader.ReadUInt16(); + + //This value is the index, so we'll use it as an offset + //Despite the data type, this is always * 2 + Offset = (uint)reader.ReadUInt16() * sizeof(ushort); } public void Write(FileWriter writer, Header header) @@ -228,16 +1012,21 @@ namespace FirstPlugin { private const string Magic = "luts"; + private byte[] data; + public void Read(FileReader reader, Header header) { reader.ReadSignature(4, Magic); uint sectionSize = reader.ReadUInt32(); + + data = reader.getSection((uint)reader.Position, sectionSize); } public void Write(FileWriter writer, Header header) { writer.WriteSignature(Magic); writer.Write(uint.MaxValue);//SectionSize + writer.Write(data); } } @@ -245,10 +1034,37 @@ namespace FirstPlugin { private const string Magic = "vatr"; + public BufferSlice PositionSlice; + public BufferSlice NormalSlice; + public BufferSlice TangentSlice; //Used in MM3DS and newer + public BufferSlice ColorSlice; + public BufferSlice Texcoord0Slice; + public BufferSlice Texcoord1Slice; + public BufferSlice Texcoord2Slice; + public BufferSlice BoneIndicesSlice; + public BufferSlice BoneWeightsSlice; + + public long StartPosition; + public void Read(FileReader reader, Header header) { + StartPosition = reader.Position; + reader.ReadSignature(4, Magic); uint sectionSize = reader.ReadUInt32(); + uint maxIndex = reader.ReadUInt32(); + + PositionSlice = ReadSlice(reader); + NormalSlice = ReadSlice(reader); + if (header.Version >= CMBVersion.MM3DS) + TangentSlice = ReadSlice(reader); + + ColorSlice = ReadSlice(reader); + Texcoord0Slice = ReadSlice(reader); + Texcoord1Slice = ReadSlice(reader); + Texcoord2Slice = ReadSlice(reader); + BoneIndicesSlice = ReadSlice(reader); + BoneWeightsSlice = ReadSlice(reader); } public void Write(FileWriter writer, Header header) @@ -256,27 +1072,84 @@ namespace FirstPlugin writer.WriteSignature(Magic); writer.Write(uint.MaxValue);//SectionSize } + + private BufferSlice ReadSlice(FileReader reader) + { + BufferSlice slice = new BufferSlice(); + slice.Size = reader.ReadUInt32(); + slice.Offset = reader.ReadUInt32(); + return slice; + } } - + public class BufferSlice + { + public uint Offset; + public uint Size; + } public class SkeletonChunk : IChunkCommon { private const string Magic = "skl "; + public List Bones = new List(); + + public uint Unknown; + public void Read(FileReader reader, Header header) { reader.ReadSignature(4, Magic); uint sectionSize = reader.ReadUInt32(); + uint boneCount = reader.ReadUInt32(); + Unknown = reader.ReadUInt32(); + + for (int i = 0; i < boneCount; i++) + { + BoneChunk bone = new BoneChunk(); + bone.ID = reader.ReadInt16() & 0xFFFF; + bone.ParentIndex = reader.ReadInt16(); + bone.Scale = reader.ReadVec3SY(); + bone.Rotation = reader.ReadVec3SY(); + bone.Translation = reader.ReadVec3SY(); + if (header.Version >= CMBVersion.MM3DS) + bone.Unknown = reader.ReadInt32(); + + Bones.Add(bone); + } } public void Write(FileWriter writer, Header header) { writer.WriteSignature(Magic); writer.Write(uint.MaxValue);//SectionSize + writer.Write(Bones.Count); + writer.Write(Unknown); + for (int i = 0; i < Bones.Count; i++) + { + writer.Write(Bones[i].ID); + writer.Write(Bones[i].ParentIndex); + writer.Write(Bones[i].Scale); + writer.Write(Bones[i].Rotation); + writer.Write(Bones[i].Translation); + if (header.Version >= CMBVersion.MM3DS) + writer.Write(Bones[i].Unknown); + } } } + public class BoneChunk + { + public int ID { get; set; } + public int ParentIndex { get; set; } + + public Syroot.Maths.Vector3F Scale { get; set; } + public Syroot.Maths.Vector3F Rotation { get; set; } + public Syroot.Maths.Vector3F Translation { get; set; } + + //An unknown value used in versions MM3DS and newer + public int Unknown { get; set; } + } + public class QuadTreeChunk : IChunkCommon { private const string Magic = "qtrs"; @@ -298,10 +1171,27 @@ namespace FirstPlugin { private const string Magic = "mats"; + public List Materials = new List(); + public void Read(FileReader reader, Header header) { + long pos = reader.Position; + reader.ReadSignature(4, Magic); uint sectionSize = reader.ReadUInt32(); + uint count = reader.ReadUInt32(); + for (int i = 0; i < count; i++) + { + int materialSize = 0x15C; + if (header.Version >= CMBVersion.MM3DS) + materialSize = 0x16C; + + reader.SeekBegin(pos + 0xC + (i * materialSize)); + + Material mat = new Material(); + mat.Read(reader, header); + Materials.Add(mat); + } } public void Write(FileWriter writer, Header header) @@ -311,6 +1201,82 @@ namespace FirstPlugin } } + //Thanks for noclip for material RE stuff + //https://github.com/magcius/noclip.website/blob/9270b9e5022c691703689990f9c536cd9058e5cd/src/oot3d/cmb.ts#L232 + public class Material + { + public CullMode CullMode; + + public bool IsPolygonOffsetEnabled; + public uint PolygonOffset; + + public TextureMap[] TextureMaps; + public TextureMatrix[] TextureMaticies; + + public void Read(FileReader reader, Header header) + { + TextureMaps = new TextureMap[3]; + TextureMaticies = new TextureMatrix[3]; + + long pos = reader.Position; + + CullMode = reader.ReadEnum(true); //byte + IsPolygonOffsetEnabled = reader.ReadBoolean(); //byte + PolygonOffset = reader.ReadUInt32(); + PolygonOffset = IsPolygonOffsetEnabled ? PolygonOffset / 0x10000 : 0; + + //Texture bind data + reader.SeekBegin(pos + 0x10); + for (int j = 0; j < 3; j++) + { + TextureMaps[j] = new TextureMap(); + TextureMaps[j].TextureIndex = reader.ReadInt16(); + TextureMaps[j].MinFiler = reader.ReadUInt16(); + TextureMaps[j].MagFiler = reader.ReadUInt16(); + TextureMaps[j].WrapS = reader.ReadUInt16(); + TextureMaps[j].WrapT = reader.ReadUInt16(); + TextureMaps[j].MinLOD = reader.ReadSingle(); + TextureMaps[j].LodBias = reader.ReadSingle(); + TextureMaps[j].borderColorR = reader.ReadByte(); + TextureMaps[j].borderColorG = reader.ReadByte(); + TextureMaps[j].borderColorB = reader.ReadByte(); + TextureMaps[j].borderColorA = reader.ReadByte(); + } + + for (int j = 0; j < 3; j++) + { + TextureMaticies[j] = new TextureMatrix(); + TextureMaticies[j].Flags = reader.ReadUInt32(); + TextureMaticies[j].Scale = reader.ReadVec2SY(); + TextureMaticies[j].Translate = reader.ReadVec2SY(); + TextureMaticies[j].Rotate = reader.ReadSingle(); + } + } + } + + public class TextureMatrix + { + public uint Flags { get; set; } + public Syroot.Maths.Vector2F Scale { get; set; } + public Syroot.Maths.Vector2F Translate { get; set; } + public float Rotate { get; set; } + } + + public class TextureMap + { + public short TextureIndex { get; set; } + public ushort MinFiler { get; set; } + public ushort MagFiler { get; set; } + public ushort WrapS { get; set; } + public ushort WrapT { get; set; } + public float MinLOD { get; set; } + public float LodBias { get; set; } + public byte borderColorR { get; set; } + public byte borderColorG { get; set; } + public byte borderColorB { get; set; } + public byte borderColorA { get; set; } + } + public class TextureChunk : IChunkCommon { private const string Magic = "tex "; @@ -340,14 +1306,14 @@ namespace FirstPlugin } } - public static T ReadChunkSection(FileReader reader, Header header) + public static T ReadChunkSection(FileReader reader, Header header, long startPos = 0) where T : IChunkCommon, new() { long pos = reader.Position; //Read offset and seek it uint offset = reader.ReadUInt32(); - reader.SeekBegin(offset); + reader.SeekBegin(startPos + offset); //Create chunk instance T chunk = new T(); diff --git a/File_Format_Library/FileFormats/Grezzo/CMB_Enums.cs b/File_Format_Library/FileFormats/Grezzo/CMB_Enums.cs new file mode 100644 index 00000000..6735e764 --- /dev/null +++ b/File_Format_Library/FileFormats/Grezzo/CMB_Enums.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grezzo.CmbEnums +{ + //All enums from + //https://github.com/magcius/noclip.website/blob/master/src/oot3d/cmb.ts + public enum TextureFilter + { + NEAREST = 0x2600, + LINEAR = 0x2601, + NEAREST_MIPMAP_NEAREST = 0x2700, + LINEAR_MIPMAP_NEAREST = 0x2701, + NEAREST_MIPMAP_LINEAR = 0x2702, + LINEAR_MIPMAP_LINEAR = 0x2703, + } + + public enum SepdVertexAttribMode : ushort + { + ARRAY = 0, + CONSTANT = 1, + } + + public enum CmbDataType : ushort + { + Byte = 0x1400, + UByte = 0x1401, + Short = 0x1402, + UShort = 0x1403, + Int = 0x1404, + UInt = 0x1405, + Float = 0x1406, + } + + public enum CullMode : byte + { + FRONT_AND_BACK, + BACK, + FRONT, + NONE, + } + + public enum SkinningMode : ushort + { + SINGLE_BONE = 0x00, + RIGID_SKINNING = 0x01, + SMOOTH_SKINNING = 0x02, + } + + public enum TextureWrapMode + { + CLAMP = 0x2900, + REPEAT = 0x2901, + CLAMP_TO_EDGE = 0x812F, + MIRRORED_REPEAT = 0x8370, + } + + public enum CombineResultOpDMP + { + REPLACE = 0x1E01, + MODULATE = 0x2100, + ADD = 0x0104, + ADD_SIGNED = 0x8574, + INTERPOLATE = 0x8575, + SUBTRACT = 0x84E7, + DOT3_RGB = 0x86AE, + DOT3_RGBA = 0x86AF, + MULT_ADD = 0x6401, + ADD_MULT = 0x6402, + }; + + public enum CombineScaleDMP + { + _1 = 0x01, + _2 = 0x02, + _4 = 0x04, + }; + + public enum CombineBufferInputDMP + { + PREVIOUS = 0x8578, + PREVIOUS_BUFFER = 0x8579, + }; + + public enum CombineSourceDMP + { + TEXTURE0 = 0x84C0, + TEXTURE1 = 0x84C1, + TEXTURE2 = 0x84C2, + TEXTURE3 = 0x84C3, + CONSTANT = 0x8576, + PRIMARY_COLOR = 0x8577, + PREVIOUS = 0x8578, + PREVIOUS_BUFFER = 0x8579, + FRAGMENT_PRIMARY_COLOR = 0x6210, + FRAGMENT_SECONDARY_COLOR = 0x6211, + }; + + public enum CombineOpDMP + { + SRC_COLOR = 0x0300, + ONE_MINUS_SRC_COLOR = 0x0301, + SRC_ALPHA = 0x0302, + ONE_MINUS_SRC_ALPHA = 0x0303, + SRC_R = 0x8580, + SRC_G = 0x8581, + SRC_B = 0x8582, + ONE_MINUS_SRC_R = 0x8583, + ONE_MINUS_SRC_G = 0x8584, + ONE_MINUS_SRC_B = 0x8585, + }; + +} diff --git a/File_Format_Library/File_Format_Library.csproj b/File_Format_Library/File_Format_Library.csproj index f0a0857f..89750caa 100644 --- a/File_Format_Library/File_Format_Library.csproj +++ b/File_Format_Library/File_Format_Library.csproj @@ -206,6 +206,7 @@ + @@ -278,6 +279,7 @@ + diff --git a/File_Format_Library/GL/CMB_Renderer.cs b/File_Format_Library/GL/CMB_Renderer.cs new file mode 100644 index 00000000..dca62841 --- /dev/null +++ b/File_Format_Library/GL/CMB_Renderer.cs @@ -0,0 +1,45 @@ +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; + +namespace FirstPlugin +{ + public class CMB_Renderer : GenericModelRenderer + { + public override float PreviewScale { get; set; } = 0.01f; + + public List TextureList = new List(); + + public override void OnRender(GLControl control) + { + + } + + public override int BindTexture(STGenericMatTexture tex, ShaderProgram shader) + { + GL.ActiveTexture(TextureUnit.Texture0 + tex.textureUnit + 1); + GL.BindTexture(TextureTarget.Texture2D, RenderTools.defaultTex.RenderableTex.TexID); + + string activeTex = tex.Name; + + foreach (var texture in TextureList) + { + if (TextureList.IndexOf(texture) == ((CMB.CMBTextureMapWrapper)tex).TextureIndex) + { + BindGLTexture(tex, shader, TextureList[((CMB.CMBTextureMapWrapper)tex).TextureIndex]); + return tex.textureUnit + 1; + } + } + + return tex.textureUnit + 1; + } + } +} diff --git a/Switch_Toolbox_Library/Forms/Custom/TreeViewCustom.cs b/Switch_Toolbox_Library/Forms/Custom/TreeViewCustom.cs index 656171db..4c990346 100644 --- a/Switch_Toolbox_Library/Forms/Custom/TreeViewCustom.cs +++ b/Switch_Toolbox_Library/Forms/Custom/TreeViewCustom.cs @@ -272,7 +272,7 @@ namespace Toolbox.Library imgList.Images.Add("vec3", Properties.Resources.IconVec3); imgList.Images.Add("vec4", Properties.Resources.IconVec4); - Bitmap bmp = new Bitmap(32, 32); + Bitmap bmp = new Bitmap(Width, Height); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.Transparent); diff --git a/Switch_Toolbox_Library/Generics/STSkeleton.cs b/Switch_Toolbox_Library/Generics/STSkeleton.cs index 5faf7666..61fd6772 100644 --- a/Switch_Toolbox_Library/Generics/STSkeleton.cs +++ b/Switch_Toolbox_Library/Generics/STSkeleton.cs @@ -19,6 +19,8 @@ namespace Toolbox.Library { public class STSkeleton : EditableObject { + public virtual float PreviewScale { get; set; } = 1.0f; + public Vector3 position = new Vector3(0, 0, 0); protected bool Selected = false; @@ -200,7 +202,7 @@ namespace Toolbox.Library DrawBoundingBoxes(); control.UpdateModelMatrix( - Matrix4.CreateScale(Runtime.previewScale) * + Matrix4.CreateScale(Runtime.previewScale * PreviewScale) * Matrix4.CreateTranslation(Selected ? editorScene.CurrentAction.NewPos(position) : position)); diff --git a/Switch_Toolbox_Library/Rendering/GenericModelRenderer/GenericModelRenderer.cs b/Switch_Toolbox_Library/Rendering/GenericModelRenderer/GenericModelRenderer.cs index f9ceb3c0..64e0ec2f 100644 --- a/Switch_Toolbox_Library/Rendering/GenericModelRenderer/GenericModelRenderer.cs +++ b/Switch_Toolbox_Library/Rendering/GenericModelRenderer/GenericModelRenderer.cs @@ -14,6 +14,8 @@ namespace Toolbox.Library.Rendering { public class GenericModelRenderer : AbstractGlDrawable { + public virtual float PreviewScale { get; set; } = 1.0f; + public static List TextureContainers = new List(); public List Meshes = new List(); @@ -170,7 +172,7 @@ namespace Toolbox.Library.Rendering ShaderProgram shader = defaultShaderProgram; control.CurrentShader = shader; - control.UpdateModelMatrix(Matrix4.CreateScale(Runtime.previewScale) * ModelTransform); + control.UpdateModelMatrix(Matrix4.CreateScale(Runtime.previewScale * PreviewScale) * ModelTransform); OnRender(control); diff --git a/Switch_Toolbox_Library/Toolbox.Library.dll b/Switch_Toolbox_Library/Toolbox.Library.dll index 55c6cab7..4732e9e0 100644 Binary files a/Switch_Toolbox_Library/Toolbox.Library.dll and b/Switch_Toolbox_Library/Toolbox.Library.dll differ diff --git a/Switch_Toolbox_Library/Toolbox.Library.pdb b/Switch_Toolbox_Library/Toolbox.Library.pdb index 06240369..48d0e25c 100644 Binary files a/Switch_Toolbox_Library/Toolbox.Library.pdb and b/Switch_Toolbox_Library/Toolbox.Library.pdb differ