From 4b4fec9236a3fa37433183c05b973fe70f5770a4 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 10 Nov 2020 09:01:41 +0100 Subject: [PATCH 1/3] Add support for Texture2DArray --- AssetStudio/AssetStudio.csproj | 3 + AssetStudio/AssetsManager.cs | 3 + AssetStudio/Classes/GLTextureSettings.cs | 29 +++++ AssetStudio/Classes/StreamingInfo.cs | 25 ++++ AssetStudio/Classes/Texture.cs | 67 +++++++++++ AssetStudio/Classes/Texture2D.cs | 121 +------------------ AssetStudio/Classes/Texture2DArray.cs | 46 +++++++ AssetStudioGUI/AssetStudioGUIForm.cs | 140 ++++++++++++++-------- AssetStudioGUI/Exporter.cs | 106 ++++++++++------ AssetStudioGUI/Studio.cs | 6 + AssetStudioUtility/Texture2DConverter.cs | 14 +++ AssetStudioUtility/Texture2DExtensions.cs | 6 + 12 files changed, 362 insertions(+), 204 deletions(-) create mode 100644 AssetStudio/Classes/GLTextureSettings.cs create mode 100644 AssetStudio/Classes/StreamingInfo.cs create mode 100644 AssetStudio/Classes/Texture2DArray.cs diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index a457361b3..5703d3eb1 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -90,6 +90,7 @@ + @@ -109,9 +110,11 @@ + + diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index 762d3dcba..0f46ffe70 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -327,6 +327,9 @@ private void ReadAssets() case ClassIDType.Texture2D: obj = new Texture2D(objectReader); break; + case ClassIDType.Texture2DArray: + obj = new Texture2DArray(objectReader); + break; case ClassIDType.Transform: obj = new Transform(objectReader); break; diff --git a/AssetStudio/Classes/GLTextureSettings.cs b/AssetStudio/Classes/GLTextureSettings.cs new file mode 100644 index 000000000..d0c812078 --- /dev/null +++ b/AssetStudio/Classes/GLTextureSettings.cs @@ -0,0 +1,29 @@ +namespace AssetStudio +{ + public class GLTextureSettings + { + public int m_FilterMode; + public int m_Aniso; + public float m_MipBias; + public int m_WrapMode; + + public GLTextureSettings(ObjectReader reader) + { + var version = reader.version; + + m_FilterMode = reader.ReadInt32(); + m_Aniso = reader.ReadInt32(); + m_MipBias = reader.ReadSingle(); + if (version[0] >= 2017)//2017.x and up + { + m_WrapMode = reader.ReadInt32(); //m_WrapU + int m_WrapV = reader.ReadInt32(); + int m_WrapW = reader.ReadInt32(); + } + else + { + m_WrapMode = reader.ReadInt32(); + } + } + } +} \ No newline at end of file diff --git a/AssetStudio/Classes/StreamingInfo.cs b/AssetStudio/Classes/StreamingInfo.cs new file mode 100644 index 000000000..971b23dbf --- /dev/null +++ b/AssetStudio/Classes/StreamingInfo.cs @@ -0,0 +1,25 @@ +namespace AssetStudio +{ + public class StreamingInfo + { + public ulong offset; + public uint size; + public string path; + + public StreamingInfo(ObjectReader reader) + { + var version = reader.version; + + if (version[0] >= 2020) //2020.1 and up + { + offset = reader.ReadUInt64(); + } + else + { + offset = reader.ReadUInt32(); + } + size = reader.ReadUInt32(); + path = reader.ReadAlignedString(); + } + } +} \ No newline at end of file diff --git a/AssetStudio/Classes/Texture.cs b/AssetStudio/Classes/Texture.cs index bcd219690..b1a1ac14d 100644 --- a/AssetStudio/Classes/Texture.cs +++ b/AssetStudio/Classes/Texture.cs @@ -17,4 +17,71 @@ protected Texture(ObjectReader reader) : base(reader) } } } + + public enum TextureFormat + { + Alpha8 = 1, + ARGB4444, + RGB24, + RGBA32, + ARGB32, + RGB565 = 7, + R16 = 9, + DXT1, + DXT5 = 12, + RGBA4444, + BGRA32, + RHalf, + RGHalf, + RGBAHalf, + RFloat, + RGFloat, + RGBAFloat, + YUY2, + RGB9e5Float, + BC4 = 26, + BC5, + BC6H = 24, + BC7, + DXT1Crunched = 28, + DXT5Crunched, + PVRTC_RGB2, + PVRTC_RGBA2, + PVRTC_RGB4, + PVRTC_RGBA4, + ETC_RGB4, + ATC_RGB4, + ATC_RGBA8, + EAC_R = 41, + EAC_R_SIGNED, + EAC_RG, + EAC_RG_SIGNED, + ETC2_RGB, + ETC2_RGBA1, + ETC2_RGBA8, + ASTC_RGB_4x4, + ASTC_RGB_5x5, + ASTC_RGB_6x6, + ASTC_RGB_8x8, + ASTC_RGB_10x10, + ASTC_RGB_12x12, + ASTC_RGBA_4x4, + ASTC_RGBA_5x5, + ASTC_RGBA_6x6, + ASTC_RGBA_8x8, + ASTC_RGBA_10x10, + ASTC_RGBA_12x12, + ETC_RGB4_3DS, + ETC_RGBA8_3DS, + RG16, + R8, + ETC_RGB4Crunched, + ETC2_RGBA8Crunched, + ASTC_HDR_4x4, + ASTC_HDR_5x5, + ASTC_HDR_6x6, + ASTC_HDR_8x8, + ASTC_HDR_10x10, + ASTC_HDR_12x12, + } } diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index 583f9c71a..862fb8569 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -1,57 +1,5 @@ -using System; - -namespace AssetStudio +namespace AssetStudio { - public class StreamingInfo - { - public ulong offset; - public uint size; - public string path; - - public StreamingInfo(ObjectReader reader) - { - var version = reader.version; - - if (version[0] >= 2020) //2020.1 and up - { - offset = reader.ReadUInt64(); - } - else - { - offset = reader.ReadUInt32(); - } - size = reader.ReadUInt32(); - path = reader.ReadAlignedString(); - } - } - - public class GLTextureSettings - { - public int m_FilterMode; - public int m_Aniso; - public float m_MipBias; - public int m_WrapMode; - - public GLTextureSettings(ObjectReader reader) - { - var version = reader.version; - - m_FilterMode = reader.ReadInt32(); - m_Aniso = reader.ReadInt32(); - m_MipBias = reader.ReadSingle(); - if (version[0] >= 2017)//2017.x and up - { - m_WrapMode = reader.ReadInt32(); //m_WrapU - int m_WrapV = reader.ReadInt32(); - int m_WrapW = reader.ReadInt32(); - } - else - { - m_WrapMode = reader.ReadInt32(); - } - } - } - public sealed class Texture2D : Texture { public int m_Width; @@ -138,71 +86,4 @@ public Texture2D(ObjectReader reader) : base(reader) image_data = resourceReader; } } - - public enum TextureFormat - { - Alpha8 = 1, - ARGB4444, - RGB24, - RGBA32, - ARGB32, - RGB565 = 7, - R16 = 9, - DXT1, - DXT5 = 12, - RGBA4444, - BGRA32, - RHalf, - RGHalf, - RGBAHalf, - RFloat, - RGFloat, - RGBAFloat, - YUY2, - RGB9e5Float, - BC4 = 26, - BC5, - BC6H = 24, - BC7, - DXT1Crunched = 28, - DXT5Crunched, - PVRTC_RGB2, - PVRTC_RGBA2, - PVRTC_RGB4, - PVRTC_RGBA4, - ETC_RGB4, - ATC_RGB4, - ATC_RGBA8, - EAC_R = 41, - EAC_R_SIGNED, - EAC_RG, - EAC_RG_SIGNED, - ETC2_RGB, - ETC2_RGBA1, - ETC2_RGBA8, - ASTC_RGB_4x4, - ASTC_RGB_5x5, - ASTC_RGB_6x6, - ASTC_RGB_8x8, - ASTC_RGB_10x10, - ASTC_RGB_12x12, - ASTC_RGBA_4x4, - ASTC_RGBA_5x5, - ASTC_RGBA_6x6, - ASTC_RGBA_8x8, - ASTC_RGBA_10x10, - ASTC_RGBA_12x12, - ETC_RGB4_3DS, - ETC_RGBA8_3DS, - RG16, - R8, - ETC_RGB4Crunched, - ETC2_RGBA8Crunched, - ASTC_HDR_4x4, - ASTC_HDR_5x5, - ASTC_HDR_6x6, - ASTC_HDR_8x8, - ASTC_HDR_10x10, - ASTC_HDR_12x12, - } } \ No newline at end of file diff --git a/AssetStudio/Classes/Texture2DArray.cs b/AssetStudio/Classes/Texture2DArray.cs new file mode 100644 index 000000000..0958b26ed --- /dev/null +++ b/AssetStudio/Classes/Texture2DArray.cs @@ -0,0 +1,46 @@ +namespace AssetStudio +{ + public sealed class Texture2DArray : Texture + { + public int m_Width; + public int m_Height; + public int m_Depth; + public TextureFormat m_TextureFormat; + public int m_MipCount; + public uint m_DataSize; + public GLTextureSettings m_TextureSettings; + public int m_ColorSpace; + public bool m_IsReadable; + public ResourceReader image_data; + public StreamingInfo m_StreamData; + + public Texture2DArray(ObjectReader reader) : base(reader) + { + m_Width = reader.ReadInt32(); + m_Height = reader.ReadInt32(); + m_Depth = reader.ReadInt32(); + m_TextureFormat = (TextureFormat)reader.ReadInt32(); + m_MipCount = reader.ReadInt32(); + m_DataSize = reader.ReadUInt32(); + m_TextureSettings = new GLTextureSettings(reader); + m_ColorSpace = reader.ReadInt32(); + m_IsReadable = reader.ReadBoolean(); + reader.AlignStream(); + + var image_data_size = reader.ReadInt32(); + if (image_data_size == 0) + { + m_StreamData = new StreamingInfo(reader); + } + + if (!string.IsNullOrEmpty(m_StreamData?.path)) + { + image_data = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size); + } + else + { + image_data = new ResourceReader(reader, reader.BaseStream.Position, image_data_size); + } + } + } +} diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 36d059b6a..216b029b7 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -42,6 +42,7 @@ partial class AssetStudioGUIForm : Form #region TexControl private static char[] textureChannelNames = new[] { 'B', 'G', 'R', 'A' }; private bool[] textureChannels = new[] { true, true, true, true }; + private static int textureArrayLayer = 0; #endregion #region GLControl @@ -321,6 +322,14 @@ private void AssetStudioForm_KeyDown(object sender, KeyEventArgs e) textureChannels[3] = !textureChannels[3]; need = true; break; + case Keys.Left: + textureArrayLayer--; + need = true; + break; + case Keys.Right: + textureArrayLayer++; + need = true; + break; } if (need) { @@ -374,6 +383,7 @@ private void enablePreview_Check(object sender, EventArgs e) switch (lastSelectedItem.Type) { case ClassIDType.Texture2D: + case ClassIDType.Texture2DArray: case ClassIDType.Sprite: { if (enablePreview.Checked && imageTexture != null) @@ -689,6 +699,9 @@ private void PreviewAsset(AssetItem assetItem) case Texture2D m_Texture2D: PreviewTexture2D(assetItem, m_Texture2D); break; + case Texture2DArray m_Texture2DArray: + PreviewTexture2DArray(assetItem, m_Texture2DArray); + break; case AudioClip m_AudioClip: PreviewAudioClip(assetItem, m_AudioClip); break; @@ -736,64 +749,91 @@ private void PreviewAsset(AssetItem assetItem) } } - private void PreviewTexture2D(AssetItem assetItem, Texture2D m_Texture2D) + private void PreviewTexture2D(AssetItem assetItem, Texture2D texture2D) { - var bitmap = m_Texture2D.ConvertToBitmap(true); + var bitmap = texture2D.ConvertToBitmap(true); if (bitmap != null) { - assetItem.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}"; - switch (m_Texture2D.m_TextureSettings.m_FilterMode) - { - case 0: assetItem.InfoText += "\nFilter Mode: Point "; break; - case 1: assetItem.InfoText += "\nFilter Mode: Bilinear "; break; - case 2: assetItem.InfoText += "\nFilter Mode: Trilinear "; break; - } - assetItem.InfoText += $"\nAnisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}\nMip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}"; - switch (m_Texture2D.m_TextureSettings.m_WrapMode) + assetItem.InfoText = $"Width: {texture2D.m_Width}\nHeight: {texture2D.m_Height}\nFormat: {texture2D.m_TextureFormat}"; + PreviewTextureSettings(assetItem, texture2D.m_TextureSettings); + PreviewBitmapWithChannels(assetItem, bitmap); + StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle"); + } + else + { + StatusStripUpdate("Unsupported image for preview"); + } + } + + private void PreviewTexture2DArray(AssetItem assetItem, Texture2DArray texture2DArray) + { + textureArrayLayer %= texture2DArray.m_Depth; + if (textureArrayLayer < 0) textureArrayLayer += texture2DArray.m_Depth; + + var bitmap = texture2DArray.ConvertToBitmap(true, textureArrayLayer); + if (bitmap != null) + { + assetItem.InfoText = $"Width: {texture2DArray.m_Width}\nHeight: {texture2DArray.m_Height}\nDepth: {texture2DArray.m_Depth} (Currently showing: {textureArrayLayer})\nFormat: {texture2DArray.m_TextureFormat}"; + PreviewTextureSettings(assetItem, texture2DArray.m_TextureSettings); + PreviewBitmapWithChannels(assetItem, bitmap); + StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle, 'Ctrl'+Left/Right Arrow to Switch Layer"); + } + else + { + StatusStripUpdate("Unsupported image for preview"); + } + } + + private void PreviewTextureSettings(AssetItem assetItem, GLTextureSettings textureSettings) + { + switch (textureSettings.m_FilterMode) + { + case 0: assetItem.InfoText += "\nFilter Mode: Point "; break; + case 1: assetItem.InfoText += "\nFilter Mode: Bilinear "; break; + case 2: assetItem.InfoText += "\nFilter Mode: Trilinear "; break; + } + assetItem.InfoText += $"\nAnisotropic level: {textureSettings.m_Aniso}\nMip map bias: {textureSettings.m_MipBias}"; + switch (textureSettings.m_WrapMode) + { + case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break; + case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break; + } + } + + private void PreviewBitmapWithChannels(AssetItem assetItem, Bitmap bitmap) { + assetItem.InfoText += "\nChannels: "; + int validChannel = 0; + for (int i = 0; i < 4; i++) + { + if (textureChannels[i]) { - case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break; - case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break; + assetItem.InfoText += textureChannelNames[i]; + validChannel++; } - assetItem.InfoText += "\nChannels: "; - int validChannel = 0; - for (int i = 0; i < 4; i++) + } + if (validChannel == 0) + assetItem.InfoText += "None"; + if (validChannel != 4) + { + var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + var bytes = new byte[bitmap.Width * bitmap.Height * 4]; + Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length); + for (int i = 0; i < bmpData.Height; i++) { - if (textureChannels[i]) + int offset = Math.Abs(bmpData.Stride) * i; + for (int j = 0; j < bmpData.Width; j++) { - assetItem.InfoText += textureChannelNames[i]; - validChannel++; + bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; + bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; + bytes[offset + 2] = textureChannels[2] ? bytes[offset + 2] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; + bytes[offset + 3] = textureChannels[3] ? bytes[offset + 3] : byte.MaxValue; + offset += 4; } } - if (validChannel == 0) - assetItem.InfoText += "None"; - if (validChannel != 4) - { - var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - var bytes = new byte[bitmap.Width * bitmap.Height * 4]; - Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length); - for (int i = 0; i < bmpData.Height; i++) - { - int offset = Math.Abs(bmpData.Stride) * i; - for (int j = 0; j < bmpData.Width; j++) - { - bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; - bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; - bytes[offset + 2] = textureChannels[2] ? bytes[offset + 2] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; - bytes[offset + 3] = textureChannels[3] ? bytes[offset + 3] : byte.MaxValue; - offset += 4; - } - } - Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length); - bitmap.UnlockBits(bmpData); - } - PreviewTexture(bitmap); - - StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle"); - } - else - { - StatusStripUpdate("Unsupported image for preview"); + Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length); + bitmap.UnlockBits(bmpData); } + PreviewBitmap(bitmap); } private void PreviewAudioClip(AssetItem assetItem, AudioClip m_AudioClip) @@ -1147,7 +1187,7 @@ private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) { assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; - PreviewTexture(bitmap); + PreviewBitmap(bitmap); } else { @@ -1155,7 +1195,7 @@ private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) } } - private void PreviewTexture(Bitmap bitmap) + private void PreviewBitmap(Bitmap bitmap) { imageTexture?.Dispose(); imageTexture = bitmap; diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index 66b58e43b..9a2c99bc2 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Drawing.Imaging; using System.IO; using System.Linq; @@ -16,48 +17,83 @@ public static bool ExportTexture2D(AssetItem item, string exportPath) var m_Texture2D = (Texture2D)item.Asset; if (Properties.Settings.Default.convertTexture) { - var bitmap = m_Texture2D.ConvertToBitmap(true); - if (bitmap == null) - return false; - ImageFormat format = null; - var ext = Properties.Settings.Default.convertType; - bool tga = false; - switch (ext) - { - case "BMP": - format = ImageFormat.Bmp; - break; - case "PNG": - format = ImageFormat.Png; - break; - case "JPEG": - format = ImageFormat.Jpeg; - break; - case "TGA": - tga = true; - break; - } - if (!TryExportFile(exportPath, item, "." + ext.ToLower(), out var exportFullPath)) + return ExportBitmap(item, exportPath, m_Texture2D.ConvertToBitmap(true)); + } + else + { + if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath)) return false; - if (tga) + File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData()); + return true; + } + } + + public static bool ExportTexture2DArray(AssetItem item, string exportPath) + { + var m_Texture2DArray = (Texture2DArray)item.Asset; + + if (Properties.Settings.Default.convertTexture) + { + for (var i = 0; i < m_Texture2DArray.m_Depth; i++) { - var file = new TGA(bitmap); - file.Save(exportFullPath); + if (!ExportBitmap(item, exportPath + "_" + i, m_Texture2DArray.ConvertToBitmap(true, i))) + { + return false; + } } - else + } + else + { + // TODO: Should this export everything as a single file? If so, which file extension? + var data = m_Texture2DArray.image_data.GetData(); + var singleLength = m_Texture2DArray.m_Width * m_Texture2DArray.m_Height; + var single = new byte[singleLength]; + for (var i = 0; i < m_Texture2DArray.m_Depth; i++) { - bitmap.Save(exportFullPath, format); + if (!TryExportFile(exportPath + "_" + i, item, ".tex", out var exportFullPath)) + return false; + Array.Copy(data, i * singleLength, single, 0, singleLength); + File.WriteAllBytes(exportFullPath, single); } - bitmap.Dispose(); - return true; + } + return true; + } + + private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawing.Bitmap bitmap) + { + if (bitmap == null) + return false; + ImageFormat format = null; + var ext = Properties.Settings.Default.convertType; + bool tga = false; + switch (ext) + { + case "BMP": + format = ImageFormat.Bmp; + break; + case "PNG": + format = ImageFormat.Png; + break; + case "JPEG": + format = ImageFormat.Jpeg; + break; + case "TGA": + tga = true; + break; + } + if (!TryExportFile(exportPath, item, "." + ext.ToLower(), out var exportFullPath)) + return false; + if (tga) + { + var file = new TGA(bitmap); + file.Save(exportFullPath); } else { - if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath)) - return false; - File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData()); - return true; + bitmap.Save(exportFullPath, format); } + bitmap.Dispose(); + return true; } public static bool ExportAudioClip(AssetItem item, string exportPath) @@ -384,6 +420,8 @@ public static bool ExportConvertFile(AssetItem item, string exportPath) { case ClassIDType.Texture2D: return ExportTexture2D(item, exportPath); + case ClassIDType.Texture2DArray: + return ExportTexture2DArray(item, exportPath); case ClassIDType.AudioClip: return ExportAudioClip(item, exportPath); case ClassIDType.Shader: diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index fdbc260d7..5b79b3dfa 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -148,6 +148,12 @@ public static (string, List) BuildAssetData() assetItem.Text = m_Texture2D.m_Name; exportable = true; break; + case Texture2DArray m_Texture2DArray: + if (!string.IsNullOrEmpty(m_Texture2DArray.m_StreamData?.path)) + assetItem.FullSize = asset.byteSize + m_Texture2DArray.m_StreamData.size; + assetItem.Text = m_Texture2DArray.m_Name; + exportable = true; + break; case AudioClip m_AudioClip: if (!string.IsNullOrEmpty(m_AudioClip.m_Source)) assetItem.FullSize = asset.byteSize + m_AudioClip.m_Size; diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index b79a790cb..37bb07a69 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -28,6 +28,20 @@ public Texture2DConverter(Texture2D m_Texture2D) platform = m_Texture2D.platform; } + public Texture2DConverter(Texture2DArray m_Texture2DArray, int layer) + { + image_data_size = m_Texture2DArray.m_Width * m_Texture2DArray.m_Height; + image_data = new byte[image_data_size]; + + Array.Copy(m_Texture2DArray.image_data.GetData(), layer * image_data_size, image_data, 0, image_data_size); + + m_Width = m_Texture2DArray.m_Width; + m_Height = m_Texture2DArray.m_Height; + m_TextureFormat = m_Texture2DArray.m_TextureFormat; + version = m_Texture2DArray.version; + platform = m_Texture2DArray.platform; + } + public Bitmap ConvertToBitmap(bool flip) { if (image_data == null || image_data.Length == 0) diff --git a/AssetStudioUtility/Texture2DExtensions.cs b/AssetStudioUtility/Texture2DExtensions.cs index 132ca9171..409396a36 100644 --- a/AssetStudioUtility/Texture2DExtensions.cs +++ b/AssetStudioUtility/Texture2DExtensions.cs @@ -9,5 +9,11 @@ public static Bitmap ConvertToBitmap(this Texture2D m_Texture2D, bool flip) var converter = new Texture2DConverter(m_Texture2D); return converter.ConvertToBitmap(flip); } + + public static Bitmap ConvertToBitmap(this Texture2DArray m_Texture2DArray, bool flip, int layer) + { + var converter = new Texture2DConverter(m_Texture2DArray, layer); + return converter.ConvertToBitmap(flip); + } } } From 4db39e59c4b488bb285a1e6bdd477cd492da109c Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 16 Feb 2021 23:55:59 +0100 Subject: [PATCH 2/3] Fix Texture2DArray export naming --- AssetStudioGUI/Exporter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index 9a2c99bc2..8d3e8e0b6 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -36,7 +36,7 @@ public static bool ExportTexture2DArray(AssetItem item, string exportPath) { for (var i = 0; i < m_Texture2DArray.m_Depth; i++) { - if (!ExportBitmap(item, exportPath + "_" + i, m_Texture2DArray.ConvertToBitmap(true, i))) + if (!ExportBitmap(item, exportPath, m_Texture2DArray.ConvertToBitmap(true, i), "_" + i)) { return false; } @@ -50,7 +50,7 @@ public static bool ExportTexture2DArray(AssetItem item, string exportPath) var single = new byte[singleLength]; for (var i = 0; i < m_Texture2DArray.m_Depth; i++) { - if (!TryExportFile(exportPath + "_" + i, item, ".tex", out var exportFullPath)) + if (!TryExportFile(exportPath, item, "_" + i + ".tex", out var exportFullPath)) return false; Array.Copy(data, i * singleLength, single, 0, singleLength); File.WriteAllBytes(exportFullPath, single); @@ -59,7 +59,7 @@ public static bool ExportTexture2DArray(AssetItem item, string exportPath) return true; } - private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawing.Bitmap bitmap) + private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawing.Bitmap bitmap, string postfix = "") { if (bitmap == null) return false; @@ -81,7 +81,7 @@ private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawi tga = true; break; } - if (!TryExportFile(exportPath, item, "." + ext.ToLower(), out var exportFullPath)) + if (!TryExportFile(exportPath, item, postfix + "." + ext.ToLower(), out var exportFullPath)) return false; if (tga) { From 32fb4539caaed5506364448c2d202d5230c4219a Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 16 Feb 2021 23:56:22 +0100 Subject: [PATCH 3/3] Fix IndexOutOfRangeException when merge-exporting assets unrelated to any objects --- AssetStudioGUI/AssetStudioGUIForm.cs | 77 ++++++++++++++++------------ 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 216b029b7..b5d58110c 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -1356,27 +1356,26 @@ private void exportObjectswithAnimationClipMenuItem_Click(object sender, EventAr private void ExportObjects(bool animation) { - if (sceneTreeView.Nodes.Count > 0) + if (sceneTreeView.Nodes.Count == 0) { - var saveFolderDialog = new OpenFolderDialog(); - if (saveFolderDialog.ShowDialog(this) == DialogResult.OK) + StatusStripUpdate("No Objects available for export"); + return; + } + + var saveFolderDialog = new OpenFolderDialog(); + if (saveFolderDialog.ShowDialog(this) == DialogResult.OK) + { + var exportPath = saveFolderDialog.Folder + "\\GameObject\\"; + List animationList = null; + if (animation) { - var exportPath = saveFolderDialog.Folder + "\\GameObject\\"; - List animationList = null; - if (animation) + animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList(); + if (animationList.Count == 0) { - animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList(); - if (animationList.Count == 0) - { - animationList = null; - } + animationList = null; } - ExportObjectsWithAnimationClip(exportPath, sceneTreeView.Nodes, animationList); } - } - else - { - StatusStripUpdate("No Objects available for export"); + ExportObjectsWithAnimationClip(exportPath, sceneTreeView.Nodes, animationList); } } @@ -1392,28 +1391,38 @@ private void exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem_Click( private void ExportMergeObjects(bool animation) { - if (sceneTreeView.Nodes.Count > 0) + if (sceneTreeView.Nodes.Count == 0) + { + StatusStripUpdate("No Objects available for export"); + return; + } + + var gameObjects = new List(); + GetSelectedParentNode(sceneTreeView.Nodes, gameObjects); + + if (gameObjects.Count == 0) { - var gameObjects = new List(); - GetSelectedParentNode(sceneTreeView.Nodes, gameObjects); - var saveFileDialog = new SaveFileDialog(); - saveFileDialog.FileName = gameObjects[0].m_Name + " (merge).fbx"; - saveFileDialog.AddExtension = false; - saveFileDialog.Filter = "Fbx file (*.fbx)|*.fbx"; - if (saveFileDialog.ShowDialog() == DialogResult.OK) - { - var exportPath = saveFileDialog.FileName; - List animationList = null; - if (animation) + StatusStripUpdate("No Object can be exported."); + return; + } + + var saveFileDialog = new SaveFileDialog(); + saveFileDialog.FileName = gameObjects[0].m_Name + " (merge).fbx"; + saveFileDialog.AddExtension = false; + saveFileDialog.Filter = "Fbx file (*.fbx)|*.fbx"; + if (saveFileDialog.ShowDialog() == DialogResult.OK) + { + var exportPath = saveFileDialog.FileName; + List animationList = null; + if (animation) + { + animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList(); + if (animationList.Count == 0) { - animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList(); - if (animationList.Count == 0) - { - animationList = null; - } + animationList = null; } - ExportObjectsMergeWithAnimationClip(exportPath, gameObjects, animationList); } + ExportObjectsMergeWithAnimationClip(exportPath, gameObjects, animationList); } }