diff --git a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/api/Sm64dsModelImporter.cs b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/api/Sm64dsModelImporter.cs
index c56a9b297..d6fe5e1b4 100644
--- a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/api/Sm64dsModelImporter.cs
+++ b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/api/Sm64dsModelImporter.cs
@@ -1,7 +1,11 @@
-using fin.animation.keyframes;
+using System.Drawing;
+
+using fin.animation.keyframes;
using fin.compression;
using fin.data.dictionaries;
+using fin.data.lazy;
using fin.data.queues;
+using fin.image;
using fin.io;
using fin.math.transform;
using fin.model;
@@ -78,6 +82,33 @@ var firstChild
}
}
+ // Set up materials
+ var finMaterialManager = model.MaterialManager;
+ var lazyTextureDictionary
+ = new LazyDictionary<(Texture texture, Palette? palette), ITexture>(
+ textureAndPalette => {
+ var (sm64Texture, sm64Palette) = textureAndPalette;
+
+ var finTexture
+ = finMaterialManager.CreateTexture(
+ ImageReader.ReadImage(sm64Texture, sm64Palette));
+ finTexture.Name = sm64Texture.Name;
+
+ return finTexture;
+ });
+
+ foreach (var sm64Material in bmd.Materials) {
+ var textureId = sm64Material.TextureId;
+ var paletteId = sm64Material.TexturePaletteId;
+
+ if (textureId != -1) {
+ var sm64Texture = bmd.Textures[textureId];
+ var sm64Palette = paletteId != -1 ? bmd.Palettes[paletteId] : null;
+
+ var finTexture = lazyTextureDictionary[(sm64Texture, sm64Palette)];
+ }
+ }
+
// Set up mesh
{ }
diff --git a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Bmd.cs b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Bmd.cs
index 24e0cefcb..63405de67 100644
--- a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Bmd.cs
+++ b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Bmd.cs
@@ -41,6 +41,10 @@ public partial class Bmd : IBinaryConvertible {
[RSequenceLengthSource(nameof(TextureCount))]
public Texture[] Textures { get; set; }
+ [RAtPosition(nameof(TexturePalettesOffset))]
+ [RSequenceLengthSource(nameof(TexturePaletteCount))]
+ public Palette[] Palettes { get; set; }
+
[RAtPosition(nameof(MaterialsOffset))]
[RSequenceLengthSource(nameof(MaterialCount))]
public Material[] Materials { get; set; }
diff --git a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/ImageReader.cs b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/ImageReader.cs
new file mode 100644
index 000000000..9fcf33879
--- /dev/null
+++ b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/ImageReader.cs
@@ -0,0 +1,213 @@
+using System.Drawing;
+
+using fin.image;
+using fin.image.formats;
+using fin.util.asserts;
+using fin.util.color;
+
+using schema.binary;
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace sm64ds.schema.bmd;
+
+///
+/// Shamelessly stolen from:
+/// https://github.com/Arisotura/SM64DSe/blob/master/SM64DSFormats/BMD.NitroTexture.cs
+///
+public class ImageReader {
+ public static IImage ReadImage(Texture texture, Palette? palette) {
+ switch (texture.TextureType) {
+ case TextureType.A3_I5: break;
+ case TextureType.PALETTE_4:
+ return ReadPalette4_(texture, palette.AssertNonnull());
+ case TextureType.PALETTE_16:
+ return ReadPalette16_(texture, palette.AssertNonnull());
+ case TextureType.PALETTE_256:
+ return ReadPalette256_(texture, palette.AssertNonnull());
+ case TextureType.TEX_4X4: break;
+ case TextureType.A5_I3: break;
+ case TextureType.DIRECT: break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+
+ return FinImage.Create1x1FromColor(Color.Red);
+ }
+
+ private static IImage ReadPalette4_(Texture texture, Palette palette) {
+ var paletteColors = GetPaletteColors_(texture, palette);
+
+ var image = new Rgba32Image(texture.Width, texture.Height);
+ var fastLock = image.Lock();
+ var dst = fastLock.Pixels;
+ var dstI = 0;
+
+ using var textureBr = new SchemaBinaryReader(texture.Data);
+ while (!textureBr.Eof) {
+ var texel = textureBr.ReadByte();
+
+ for (var i = 0; i < 4; ++i) {
+ var subTexel = (texel >> (2 * i)) & 0x3;
+ dst[dstI++] = paletteColors[subTexel];
+ }
+ }
+
+ return image;
+ }
+
+ private static IImage ReadPalette16_(Texture texture, Palette palette) {
+ var paletteColors = GetPaletteColors_(texture, palette);
+
+ var image = new Rgba32Image(texture.Width, texture.Height);
+ var fastLock = image.Lock();
+ var dst = fastLock.Pixels;
+ var dstI = 0;
+
+ using var textureBr = new SchemaBinaryReader(texture.Data);
+ while (!textureBr.Eof) {
+ var texel = textureBr.ReadByte();
+
+ for (var i = 0; i < 2; ++i) {
+ var subTexel = (texel >> (4 * i)) & 0xF;
+ dst[dstI++] = paletteColors[subTexel];
+ }
+ }
+
+ return image;
+ }
+
+ private static IImage ReadPalette256_(Texture texture, Palette palette) {
+ var paletteColors = GetPaletteColors_(texture, palette);
+
+ var image = new Rgba32Image(texture.Width, texture.Height);
+ var fastLock = image.Lock();
+ var dst = fastLock.Pixels;
+ var dstI = 0;
+
+ using var textureBr = new SchemaBinaryReader(texture.Data);
+ while (!textureBr.Eof) {
+ var texel = textureBr.ReadByte();
+ dst[dstI++] = paletteColors[texel];
+ }
+
+ return image;
+ }
+
+ /*private static IImage ReadTex4x4_(Texture texture, Palette palette) {
+ var paletteColors = GetPaletteColors_(texture, palette);
+
+ int yOut = 0, xOut = 0;
+
+ var width = texture.Width;
+ var image = new Rgba32Image(width, texture.Height);
+ var fastLock = image.Lock();
+ var dst = fastLock.Pixels;
+
+ using var textureBr = new SchemaBinaryReader(texture.Data);
+
+ while (!textureBr.Eof) {
+ uint blox = Helper.BytesToUInt32(tex, _in);
+ ushort palidx_data = texture.
+ = Helper.BytesToUShort16(tex, m_TextureDataLength + (_in >> 1));
+
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ byte texel = (byte) (blox & 0x3);
+ blox >>= 2;
+
+ int pal_offset = (int) ((palidx_data & 0x3FFF) << 2);
+ ushort color_mode = (ushort) (palidx_data >> 14);
+ uint color = 0xFFFFFFFF;
+
+ switch (texel) {
+ case 0: color = Helper.BytesToUShort16(pal, pal_offset, 0); break;
+ case 1:
+ color = Helper.BytesToUShort16(pal, pal_offset + 2, 0); break;
+ case 2: {
+ switch (color_mode) {
+ case 0:
+ case 2:
+ color = Helper.BytesToUShort16(pal, pal_offset + 4, 0); break;
+ case 1: {
+ ushort c0 = Helper.BytesToUShort16(pal, pal_offset, 0);
+ ushort c1 = Helper.BytesToUShort16(pal, pal_offset + 2, 0);
+ color = Helper.BlendColorsBGR15(c0, 1, c1, 1);
+ }
+ break;
+ case 3: {
+ ushort c0 = Helper.BytesToUShort16(pal, pal_offset, 0);
+ ushort c1 = Helper.BytesToUShort16(pal, pal_offset + 2, 0);
+ color = Helper.BlendColorsBGR15(c0, 5, c1, 3);
+ }
+ break;
+ }
+ }
+ break;
+ case 3: {
+ switch (color_mode) {
+ case 0:
+ case 1: color = 0xFFFFFFFF; break;
+ case 2:
+ color = Helper.BytesToUShort16(pal, pal_offset + 6, 0); break;
+ case 3: {
+ ushort c0 = Helper.BytesToUShort16(pal, pal_offset, 0);
+ ushort c1 = Helper.BytesToUShort16(pal, pal_offset + 2, 0);
+ color = Helper.BlendColorsBGR15(c0, 3, c1, 5);
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ int _out = (int) (((y * width) + x) * 4);
+ int yoff = (int) (y * width * 4);
+ int xoff = (int) (x * 4);
+
+ var dstI = _out + yoff + xoff;
+
+ if (color == 0xFFFFFFFF) {
+ dst[dstI] = default;
+ } else {
+ dst[dstI] = ;
+ byte red = (byte) ((color & 0x001F) << 3);
+ byte green = (byte) ((color & 0x03E0) >> 2);
+ byte blue = (byte) ((color & 0x7C00) >> 7);
+
+ m_ARGB[_out + yoff + xoff] = blue;
+ m_ARGB[_out + yoff + xoff + 1] = green;
+ m_ARGB[_out + yoff + xoff + 2] = red;
+ m_ARGB[_out + yoff + xoff + 3] = 0xFF;
+ }
+ }
+ }
+
+ xOut += 4;
+ if (xOut >= width) {
+ xOut = 0;
+ yOut += 4;
+ }
+ }
+
+ return image;
+ }*/
+
+ // TODO: Optimize this to use stackalloc instead
+ public static Rgba32[] GetPaletteColors_(Texture texture, Palette palette) {
+ using var paletteBr = new SchemaBinaryReader(palette.Data);
+
+ var paletteColors = new Rgba32[palette.Data.Length >> 1];
+ for (var i = 0; i < paletteColors.Length; ++i) {
+ ColorUtil.SplitRgb5A1(paletteBr.ReadUInt16(),
+ out var b,
+ out var g,
+ out var r,
+ out _);
+ var a = (byte) (texture.UseTransparentColor0 && i == 0 ? 0 : 0xFF);
+
+ paletteColors[i] = new Rgba32(r, g, b, a);
+ }
+
+ return paletteColors;
+ }
+}
\ No newline at end of file
diff --git a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Material.cs b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Material.cs
index 7d8c14f68..bfdc24c90 100644
--- a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Material.cs
+++ b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Material.cs
@@ -15,8 +15,8 @@ public partial class Material : IBinaryConvertible {
[RAtPosition(nameof(nameOffset_))]
public string Name { get; set; }
- public uint TextureId { get; set; }
- public uint TexturePaletteId { get; set; }
+ public int TextureId { get; set; }
+ public int TexturePaletteId { get; set; }
public FixedPointVector2 TextureScale { get; set; }
public uint TextureRotation { get; set; }
diff --git a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Palette.cs b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Palette.cs
new file mode 100644
index 000000000..2d0f55823
--- /dev/null
+++ b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Palette.cs
@@ -0,0 +1,29 @@
+using fin.schema;
+
+using schema.binary;
+using schema.binary.attributes;
+
+namespace sm64ds.schema.bmd;
+
+///
+/// Shamelessly stolen from:
+/// https://kuribo64.net/get.php?id=KBNyhM0kmNiuUBb3
+///
+[BinarySchema]
+public partial class Palette : IBinaryConvertible {
+ private uint nameOffset_;
+
+ [NullTerminatedString]
+ [RAtPosition(nameof(nameOffset_))]
+ public string Name { get; set; }
+
+ private uint dataOffset_;
+ private uint dataLength_;
+
+ [RAtPosition(nameof(dataOffset_))]
+ [RSequenceLengthSource(nameof(dataLength_))]
+ public byte[] Data { get; set; }
+
+ [Unknown]
+ public uint Unknown { get; set; }
+}
\ No newline at end of file
diff --git a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Texture.cs b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Texture.cs
index 22ddb01fb..bf1e1f3fa 100644
--- a/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Texture.cs
+++ b/FinModelUtility/Games/SuperMario64Ds/SuperMario64Ds/src/schema/bmd/Texture.cs
@@ -1,8 +1,20 @@
-using schema.binary;
+using fin.math;
+
+using schema.binary;
using schema.binary.attributes;
namespace sm64ds.schema.bmd;
+public enum TextureType {
+ A3_I5 = 1,
+ PALETTE_4 = 2,
+ PALETTE_16 = 3,
+ PALETTE_256 = 4,
+ TEX_4X4 = 5,
+ A5_I3 = 6,
+ DIRECT = 7
+}
+
///
/// Shamelessly stolen from:
/// https://kuribo64.net/get.php?id=KBNyhM0kmNiuUBb3
@@ -26,4 +38,11 @@ public partial class Texture : IBinaryConvertible {
public ushort Height { get; set; }
public uint Parameters { get; set; }
+
+ [Skip]
+ public TextureType TextureType
+ => (TextureType) this.Parameters.ExtractFromRight(26, 3);
+
+ [Skip]
+ public bool UseTransparentColor0 => this.Parameters.GetBit(29);
}
\ No newline at end of file