1
0
mirror of synced 2024-12-04 11:58:03 +01:00
Switch-Toolbox/Switch_FileFormatsMain/GUI/TextureUI/TextureImporterSettings.cs
KillzXGaming 50ea4183ef Tons of stuff!
- Compress BC1 with alpha.
- Add GFPAK rebuilding.
- Fix some lag issues with bntx textures loading
- Fix saving multiple files.
- Support DDS cubemap importing
- Support rigged DAE/FBX files.
- Support animation playing thanks to smash forge.
- Some minor stuff to prepare custom animations.
- Many bug fixes.
2018-11-22 10:20:12 -05:00

450 lines
16 KiB
C#

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Syroot.NintenTools.NSW.Bntx;
using Syroot.NintenTools.NSW.Bntx.GFX;
using System.Runtime.InteropServices;
using Switch_Toolbox.Library;
using Switch_Toolbox.Library.IO;
namespace FirstPlugin
{
public class TextureImporterSettings
{
public TextureImporterSettings()
{
}
public string TexName;
public uint arrayLength = 1;
public uint AccessFlags = 0x20;
public uint MipCount;
public uint Depth = 1;
public uint TexWidth;
public uint TexHeight;
public uint Flags;
public uint Swizzle;
public uint SampleCount = 1;
public uint Pitch = 32;
public uint[] Regs;
public SurfaceFormat Format;
public SurfaceDim SurfaceDim = SurfaceDim.Dim2D;
public TileMode TileMode = TileMode.Default;
public Dim Dim = Dim.Dim2D;
public ChannelType RedComp = ChannelType.Red;
public ChannelType GreenComp = ChannelType.Green;
public ChannelType BlueComp = ChannelType.Blue;
public ChannelType AlphaComp = ChannelType.Alpha;
public TextureData textureData;
public uint TextureLayout;
public uint TextureLayout2 = 0x010007;
public uint bpp;
public List<byte[]> DataBlockOutput = new List<byte[]>();
public List<byte[]> DecompressedData = new List<byte[]>();
public BntxFile bntx;
public int sparseResidency = 0; //false
public int sparseBinding = 0; //false
public bool IsSRGB = true;
public bool GenerateMipmaps = false; //If bitmap and count more that 1 then geenrate
private SurfaceFormat LoadDDSFormat(string fourCC, DDS dds = null, bool IsSRGB = false)
{
bool IsDX10 = false;
switch (fourCC)
{
case "DXT1":
if (IsSRGB)
return SurfaceFormat.BC1_SRGB;
else
return SurfaceFormat.BC1_UNORM;
case "DXT3":
if (IsSRGB)
return SurfaceFormat.BC2_SRGB;
else
return SurfaceFormat.BC2_UNORM;
case "DXT5":
if (IsSRGB)
return SurfaceFormat.BC3_SRGB;
else
return SurfaceFormat.BC3_UNORM;
case "BC4U":
return SurfaceFormat.BC4_UNORM;
case "BC4S":
return SurfaceFormat.BC4_SNORM;
case "ATI1":
return SurfaceFormat.BC4_UNORM;
case "ATI2":
return SurfaceFormat.BC5_UNORM;
case "BC5U":
return SurfaceFormat.BC5_UNORM;
case "BC5S":
return SurfaceFormat.BC5_SNORM;
case "DX10":
IsDX10 = true;
break;
default:
return SurfaceFormat.R8_G8_B8_A8_SRGB;
}
if (IsDX10)
{
switch (dds.DX10header.DXGI_Format)
{
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
return SurfaceFormat.BC4_UNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
return SurfaceFormat.BC4_SNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
return SurfaceFormat.BC4_UNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
return SurfaceFormat.BC5_UNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
return SurfaceFormat.BC5_SNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
return SurfaceFormat.BC5_UNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
return SurfaceFormat.BC6_FLOAT;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
return SurfaceFormat.BC6_UFLOAT;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
return SurfaceFormat.BC7_UNORM;
case DDS.DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
return SurfaceFormat.BC7_SRGB;
default:
throw new Exception($"Format {dds.DX10header.DXGI_Format} not supported!");
}
}
throw new Exception($"This shouldn't happen :(");
}
public void LoadDDS(string FileName, BntxFile bntxFile, byte[] FileData = null, TextureData tree = null)
{
TexName = Path.GetFileNameWithoutExtension(FileName);
DDS dds = new DDS();
if (FileData != null)
dds.Load(new FileReader(new MemoryStream(FileData)));
else
dds.Load(new FileReader(FileName));
MipCount = dds.header.mipmapCount;
TexWidth = dds.header.width;
TexHeight = dds.header.height;
arrayLength = 1;
if (dds.header.caps2 == (uint)DDS.DDSCAPS2.CUBEMAP_ALLFACES)
{
arrayLength = 6;
}
DataBlockOutput.Add(dds.bdata);
Format = LoadDDSFormat(dds.header.ddspf.fourCC, dds, IsSRGB);
Texture tex = FromBitMap(DataBlockOutput[0], this);
if (tree != null)
{
tree.LoadTexture(tex, 1);
}
else
{
textureData = new TextureData(tex, bntxFile);
}
}
public void LoadBitMap(string FileName, BntxFile bntxFile)
{
DecompressedData.Clear();
TexName = Path.GetFileNameWithoutExtension(FileName);
bntx = bntxFile;
Format = SurfaceFormat.BC1_SRGB;
GenerateMipmaps = true;
Bitmap Image = new Bitmap(FileName);
Image = TextureData.SwapBlueRedChannels(Image);
TexWidth = (uint)Image.Width;
TexHeight = (uint)Image.Height;
MipCount = (uint)GetTotalMipCount();
DecompressedData.Add(BitmapExtension.ImageToByte(Image));
Image.Dispose();
if (DecompressedData.Count == 0)
{
throw new Exception("Failed to load " + Format);
}
}
public int GetTotalMipCount()
{
int MipmapNum = 0;
uint num = Math.Max(TexHeight, TexWidth);
int width = (int)TexWidth;
int height = (int)TexHeight;
while (true)
{
num >>= 1;
width = width / 2;
height = height / 2;
if (width <= 0 || height <= 0)
break;
if (num > 0)
++MipmapNum;
else
break;
}
return MipmapNum;
}
public byte[] GenerateMips(int SurfaceLevel = 0)
{
Bitmap Image = BitmapExtension.GetBitmap(DecompressedData[SurfaceLevel], (int)TexWidth, (int)TexHeight);
List<byte[]> mipmaps = new List<byte[]>();
mipmaps.Add(TextureData.CompressBlock(DecompressedData[SurfaceLevel], (int)TexWidth, (int)TexHeight, Format));
//while (Image.Width / 2 > 0 && Image.Height / 2 > 0)
// for (int mipLevel = 0; mipLevel < MipCount; mipLevel++)
for (int mipLevel = 0; mipLevel < MipCount; mipLevel++)
{
Image = BitmapExtension.Resize(Image, Image.Width / 2, Image.Height / 2);
mipmaps.Add(TextureData.CompressBlock(BitmapExtension.ImageToByte(Image), Image.Width, Image.Height, Format));
}
Image.Dispose();
return Utils.CombineByteArray(mipmaps.ToArray());
}
public void Compress()
{
DataBlockOutput.Clear();
foreach (var surface in DecompressedData)
{
DataBlockOutput.Add(TextureData.CompressBlock(surface, (int)TexWidth, (int)TexHeight, Format));
}
}
public static uint DIV_ROUND_UP(uint value1, uint value2)
{
return TegraX1Swizzle.DIV_ROUND_UP(value1, value2);
}
public static byte[] SubArray(byte[] data, uint offset, uint length)
{
return data.Skip((int)offset).Take((int)length).ToArray();
}
private static Tuple<uint, uint> GetCurrentMipSize(uint width, uint height, uint blkHeight, uint blkWidth, uint bpp, int CurLevel)
{
uint offset = 0;
uint width_ = 0;
uint height_ = 0;
for (int mipLevel = 0; mipLevel < CurLevel; mipLevel++)
{
width_ = DIV_ROUND_UP(Math.Max(1, width >> mipLevel), blkWidth);
height_ = DIV_ROUND_UP(Math.Max(1, height >> mipLevel), blkHeight);
offset += width_ * height_ * bpp;
}
width_ = DIV_ROUND_UP(Math.Max(1, width >> CurLevel), blkWidth);
height_ = DIV_ROUND_UP(Math.Max(1, height >> CurLevel), blkHeight);
uint size = width_ * height_ * bpp;
return Tuple.Create(offset, size);
}
public Texture FromBitMap(byte[] data, TextureImporterSettings settings)
{
Texture tex = new Texture();
tex.Height = (uint)settings.TexHeight;
tex.Width = (uint)settings.TexWidth;
tex.Format = Format;
tex.Name = settings.TexName;
tex.Path = "";
tex.TextureData = new List<List<byte[]>>();
if (settings.MipCount == 0)
settings.MipCount = 1;
tex.sparseBinding = settings.sparseBinding;
tex.sparseResidency = settings.sparseResidency;
tex.AccessFlags = settings.AccessFlags;
tex.ArrayLength = settings.arrayLength;
tex.MipCount = settings.MipCount;
tex.ChannelRed = settings.RedComp;
tex.ChannelGreen = settings.GreenComp;
tex.ChannelBlue = settings.BlueComp;
tex.ChannelAlpha = settings.AlphaComp;
tex.Depth = settings.Depth;
tex.Dim = settings.Dim;
tex.Flags = settings.Flags;
tex.TileMode = settings.TileMode;
tex.textureLayout = settings.TextureLayout;
tex.textureLayout2 = settings.TextureLayout2;
tex.Swizzle = settings.Swizzle;
tex.SurfaceDim = settings.SurfaceDim;
tex.SampleCount = settings.SampleCount;
tex.Regs = settings.Regs;
tex.Pitch = settings.Pitch;
tex.MipOffsets = new long[tex.MipCount];
List<byte[]> arrayFaces = new List<byte[]>();
if (tex.ArrayLength > 1)
arrayFaces = DDS.GetArrayFaces(data, tex.ArrayLength);
else
arrayFaces.Add(data);
for (int i = 0; i < tex.ArrayLength; i++)
{
List<byte[]> mipmaps = SwizzleSurfaceMipMaps(tex, arrayFaces[i], tex.TileMode);
tex.TextureData.Add(mipmaps);
byte[] test = Combine(mipmaps);
tex.TextureData[i][0] = test;
}
return tex;
}
public static List<byte[]> SwizzleSurfaceMipMaps(FTEX tex, byte[] data)
{
throw new Exception("Unimplemented");
}
public static List<byte[]> SwizzleSurfaceMipMaps(Texture tex,byte[] data, TileMode TileMode)
{
int blockHeightShift = 0;
int target = 1;
uint Pitch = 0;
uint SurfaceSize = 0;
uint blockHeight = 0;
uint blk_dim = Formats.blk_dims((uint)((int)tex.Format >> 8));
uint blkWidth = blk_dim >> 4;
uint blkHeight = blk_dim & 0xF;
uint linesPerBlockHeight = 0;
uint bpp = Formats.bpps((uint)((int)tex.Format >> 8));
if ((int)TileMode == 1)
{
blockHeight = 1;
tex.BlockHeightLog2 = 0;
tex.Alignment = 1;
linesPerBlockHeight = 1;
tex.ReadTextureLayout = 0;
}
else
{
blockHeight = TegraX1Swizzle.GetBlockHeight(DIV_ROUND_UP(tex.Height, blkHeight));
tex.BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1;
Console.WriteLine("BlockHeightLog2 " + tex.BlockHeightLog2);
Console.WriteLine("blockHeight " + blockHeight);
tex.Alignment = 512;
tex.ReadTextureLayout = 1;
linesPerBlockHeight = blockHeight * 8;
}
List<byte[]> mipmaps = new List<byte[]>();
for (int mipLevel = 0; mipLevel < tex.MipCount; mipLevel++)
{
var result = GetCurrentMipSize(tex.Width, tex.Height, blkWidth, blkHeight, bpp, mipLevel);
uint offset = result.Item1;
uint size = result.Item2;
byte[] data_ = SubArray(data, offset, size);
uint width_ = Math.Max(1, tex.Width >> mipLevel);
uint height_ = Math.Max(1, tex.Height >> mipLevel);
uint width__ = DIV_ROUND_UP(width_, blkWidth);
uint height__ = DIV_ROUND_UP(height_, blkHeight);
byte[] AlignedData = new byte[(TegraX1Swizzle.round_up(SurfaceSize, (uint)tex.Alignment) - SurfaceSize)];
SurfaceSize += (uint)AlignedData.Length;
Console.WriteLine("SurfaceSize Aligned " + AlignedData);
Console.WriteLine("MipOffsets " + SurfaceSize);
tex.MipOffsets[mipLevel] = SurfaceSize;
if (tex.TileMode == TileMode.LinearAligned)
{
Pitch = width__ * bpp;
Console.WriteLine("Pitch 1 " + Pitch);
if (target == 1)
{
Pitch = TegraX1Swizzle.round_up(width__ * bpp, 32);
Console.WriteLine("Pitch 2 " + Pitch);
}
SurfaceSize += Pitch * height__;
}
else
{
if (TegraX1Swizzle.pow2_round_up(height__) < linesPerBlockHeight)
blockHeightShift += 1;
Pitch = TegraX1Swizzle.round_up(width__ * bpp, 64);
Console.WriteLine("Pitch 1 " + Pitch);
Console.WriteLine("blockHeightShift " + blockHeightShift);
SurfaceSize += Pitch * TegraX1Swizzle.round_up(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8);
Console.WriteLine("SurfaceSize " + SurfaceSize);
byte[] SwizzledData = TegraX1Swizzle.swizzle(width_, height_, blkWidth, blkHeight, target, bpp, (uint)tex.TileMode, (int)Math.Max(0, tex.BlockHeightLog2 - blockHeightShift), data_);
mipmaps.Add(AlignedData.Concat(SwizzledData).ToArray());
}
}
tex.ImageSize = SurfaceSize;
return mipmaps;
}
private byte[] Combine(List<byte[]> arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays)
{
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
private void button1_Click(object sender, EventArgs e)
{
if (DataBlockOutput != null)
{
Texture tex = FromBitMap(DataBlockOutput[0], this);
if (textureData != null)
{
textureData.LoadTexture(tex, 1);
}
else
{
textureData = new TextureData(tex, bntx);
}
}
else
{
MessageBox.Show("Something went wrong???");
}
}
}
}