From 556ae6e036ea4203774ad7ad657f86466eabf864 Mon Sep 17 00:00:00 2001 From: "Marcel K." <106357974+Ka1serM@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:01:01 +0200 Subject: [PATCH] Texture Changes & HDR Preview/Export (#559) * texture refactor * fix bgra in opengl Co-authored-by: Asval --- CUE4Parse | 2 +- FModel/Creator/Bases/FN/BaseIcon.cs | 2 +- FModel/Creator/Utils.cs | 2 +- FModel/MainWindow.xaml.cs | 1 + FModel/ViewModels/ApplicationViewModel.cs | 16 ++++ FModel/ViewModels/TabControlViewModel.cs | 91 ++++++++++++++++++----- FModel/Views/Snooper/Options.cs | 5 +- FModel/Views/Snooper/Shading/Texture.cs | 11 ++- README.md | 2 +- 9 files changed, 102 insertions(+), 30 deletions(-) diff --git a/CUE4Parse b/CUE4Parse index d512c176..72eaf410 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit d512c176b722af7345fd4240ffaeed05a5f49013 +Subproject commit 72eaf4101268bd971281f3cd8769a57be90caedb diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index 43fa2073..fc2fc211 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -159,7 +159,7 @@ public class BaseIcon : UCreator { if (uObject is UTexture2D texture2D) { - SeriesBackground = texture2D.Decode(); + SeriesBackground = texture2D.Decode().ToSkBitmap(); return; } diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index f9c22dc5..3d44c40e 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -130,7 +130,7 @@ public static class Utils public static SKBitmap GetB64Bitmap(string b64) => SKBitmap.Decode(new MemoryStream(Convert.FromBase64String(b64)) { Position = 0 }); public static SKBitmap GetBitmap(FSoftObjectPath softObjectPath) => GetBitmap(softObjectPath.Load()); public static SKBitmap GetBitmap(string fullPath) => TryLoadObject(fullPath, out UTexture2D texture) ? GetBitmap(texture) : null; - public static SKBitmap GetBitmap(UTexture2D texture) => texture.Decode(UserSettings.Default.CurrentDir.TexturePlatform); + public static SKBitmap GetBitmap(UTexture2D texture) => texture.Decode(UserSettings.Default.CurrentDir.TexturePlatform).ToSkBitmap(); public static SKBitmap GetBitmap(byte[] data) => SKBitmap.Decode(data); public static SKBitmap ResizeWithRatio(this SKBitmap me, double width, double height) diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index cf200e84..2f915233 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -63,6 +63,7 @@ public partial class MainWindow await ApplicationViewModel.InitOodle(); await ApplicationViewModel.InitZlib(); + await ApplicationViewModel.InitDetex(); await _applicationView.CUE4Parse.Initialize(); await _applicationView.AesManager.InitAes(); await _applicationView.UpdateProvider(true); diff --git a/FModel/ViewModels/ApplicationViewModel.cs b/FModel/ViewModels/ApplicationViewModel.cs index a67df7ad..e3faa892 100644 --- a/FModel/ViewModels/ApplicationViewModel.cs +++ b/FModel/ViewModels/ApplicationViewModel.cs @@ -6,6 +6,7 @@ using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using System.Windows; +using CUE4Parse_Conversion.Textures.BC; using CUE4Parse.Compression; using CUE4Parse.Encryption.Aes; using CUE4Parse.UE4.Objects.Core.Misc; @@ -258,4 +259,19 @@ public class ApplicationViewModel : ViewModel ZlibHelper.Initialize(zlibPath); } + + public static async ValueTask InitDetex() + { + var detexPath = Path.Combine(UserSettings.Default.OutputDirectory, ".data", DetexHelper.DLL_NAME); + if (File.Exists(DetexHelper.DLL_NAME)) + { + File.Move(DetexHelper.DLL_NAME, detexPath, true); + } + else if (!File.Exists(detexPath)) + { + await DetexHelper.LoadDllAsync(detexPath); + } + + DetexHelper.Initialize(detexPath); + } } diff --git a/FModel/ViewModels/TabControlViewModel.cs b/FModel/ViewModels/TabControlViewModel.cs index 39edaced..f9f9eb96 100644 --- a/FModel/ViewModels/TabControlViewModel.cs +++ b/FModel/ViewModels/TabControlViewModel.cs @@ -22,7 +22,8 @@ namespace FModel.ViewModels; public class TabImage : ViewModel { - public string ExportName { get; } + public string ExportName { get; set; } + public byte[] ImageBuffer { get; set; } public TabImage(string name, bool rnn, SKBitmap img) @@ -32,6 +33,13 @@ public class TabImage : ViewModel SetImage(img); } + public TabImage(string name, bool rnn, CTexture img) + { + ExportName = name; + RenderNearestNeighbor = rnn; + SetImage(img); + } + private BitmapImage _image; public BitmapImage Image { @@ -71,11 +79,42 @@ public class TabImage : ViewModel } _bmp = bitmap; - using var data = _bmp.Encode(NoAlpha ? ETextureFormat.Jpeg : UserSettings.Default.TextureExportFormat, 100); + ExportName += "." + (NoAlpha ? "jpg" : "png"); + using var data = _bmp.Encode(NoAlpha ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100); using var stream = new MemoryStream(ImageBuffer = data.ToArray(), false); - if (UserSettings.Default.TextureExportFormat == ETextureFormat.Tga) - return; + var image = new BitmapImage(); + image.BeginInit(); + image.CacheOption = BitmapCacheOption.OnLoad; + image.StreamSource = stream; + image.EndInit(); + image.Freeze(); + Image = image; + } + private void SetImage(CTexture bitmap) + { + if (bitmap is null) + { + ImageBuffer = null; + Image = null; + return; + } + + _bmp = bitmap.ToSkBitmap(); + byte[] imageData = _bmp.Encode(NoAlpha ? SKEncodedImageFormat.Jpeg : SKEncodedImageFormat.Png, 100).ToArray(); + + if (PixelFormatUtils.IsHDR(bitmap.PixelFormat) || (UserSettings.Default.TextureExportFormat != ETextureFormat.Jpeg && UserSettings.Default.TextureExportFormat != ETextureFormat.Png)) + { + ImageBuffer = bitmap.Encode(UserSettings.Default.TextureExportFormat, out var ext); + ExportName += "." + ext; + } + else + { + ImageBuffer = imageData; + ExportName += "." + (NoAlpha ? "jpg" : "png"); + } + + using var stream = new MemoryStream(imageData); var image = new BitmapImage(); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; @@ -253,7 +292,7 @@ public class TabItem : ViewModel public void AddImage(UTexture texture, bool save, bool updateUi) { var appendLayerNumber = false; - var img = new SKBitmap[1]; + var img = new CTexture[1]; if (texture is UTexture2DArray textureArray) { img = textureArray.DecodeTextureArray(UserSettings.Default.CurrentDir.TexturePlatform); @@ -264,7 +303,7 @@ public class TabItem : ViewModel img[0] = texture.Decode(UserSettings.Default.CurrentDir.TexturePlatform); if (texture is UTextureCube) { - img[0] = img[0]?.ToPanorama(); + img[0] = img[0].ToPanorama(); } } @@ -279,6 +318,29 @@ public class TabItem : ViewModel } } + public void AddImage(string name, bool rnn, CTexture[] img, bool save, bool updateUi, bool appendLayerNumber = false) + { + for (var i = 0; i < img.Length; i++) + { + AddImage($"{name}{(appendLayerNumber ? $"_{i}" : "")}", rnn, img[i], save, updateUi); + } + } + + public void AddImage(string name, bool rnn, CTexture img, bool save, bool updateUi) + { + Application.Current.Dispatcher.Invoke(() => + { + var t = new TabImage(name, rnn, img); + if (save) SaveImage(t, updateUi); + if (!updateUi) return; + + _images.Add(t); + SelectedImage ??= t; + RaisePropertyChanged("Page"); + RaisePropertyChanged("HasMultipleImages"); + }); + } + public void AddImage(string name, bool rnn, SKBitmap img, bool save, bool updateUi) { Application.Current.Dispatcher.Invoke(() => @@ -312,23 +374,14 @@ public class TabItem : ViewModel public void SaveImage() => SaveImage(SelectedImage, true); private void SaveImage(TabImage image, bool updateUi) { - if (image == null) return; + if (image == null) + return; - var ext = UserSettings.Default.TextureExportFormat switch - { - ETextureFormat.Png => ".png", - ETextureFormat.Jpeg => ".jpg", - ETextureFormat.Tga => ".tga", - _ => ".png" - }; - - var fileName = image.ExportName + ext; - var path = Path.Combine(UserSettings.Default.TextureDirectory, - UserSettings.Default.KeepDirectoryStructure ? Entry.Directory : "", fileName!).Replace('\\', '/'); + var path = Path.Combine(UserSettings.Default.TextureDirectory, UserSettings.Default.KeepDirectoryStructure ? Entry.Directory : "", image.ExportName).Replace('\\', '/'); Directory.CreateDirectory(path.SubstringBeforeLast('/')); - SaveImage(image, path, fileName, updateUi); + SaveImage(image, path, image.ExportName, updateUi); } private void SaveImage(TabImage image, string path, string fileName, bool updateUi) diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs index e597259f..0f55dd27 100644 --- a/FModel/Views/Snooper/Options.cs +++ b/FModel/Views/Snooper/Options.cs @@ -193,7 +193,7 @@ public class Options if (Textures.TryGetValue(guid, out texture)) return texture != null; if (o.Format == EPixelFormat.PF_BC6H) return false; // BC6H is not supported by Decode thus randomly crashes the app - SKBitmap bitmap = o switch + var bitmap = o switch { UTexture2D texture2D => texture2D.Decode(UserSettings.Default.PreviewMaxTextureSize, UserSettings.Default.CurrentDir.TexturePlatform), UTexture2DArray texture2DArray => texture2DArray.DecodeTextureArray(UserSettings.Default.CurrentDir.TexturePlatform)?.FirstOrDefault(), @@ -202,10 +202,9 @@ public class Options if (bitmap is not null) { - texture = new Texture(bitmap, o); + texture = new Texture(bitmap.ToSkBitmap(), o); if (fix) TextureHelper.FixChannels(_game, texture); Textures[guid] = texture; - bitmap.Dispose(); } return texture != null; diff --git a/FModel/Views/Snooper/Shading/Texture.cs b/FModel/Views/Snooper/Shading/Texture.cs index 650e3138..c425c5ee 100644 --- a/FModel/Views/Snooper/Shading/Texture.cs +++ b/FModel/Views/Snooper/Shading/Texture.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using System.Windows; +using CUE4Parse_Conversion.Textures; using CUE4Parse.UE4.Assets.Exports.Texture; using CUE4Parse.UE4.Objects.Core.Math; using CUE4Parse.UE4.Objects.Core.Misc; @@ -90,15 +91,16 @@ public class Texture : IDisposable Height = bitmap.Height; Bind(TextureUnit.Texture0); - var internalFormat = Format switch + var internalFormat = bitmap.ColorType switch { - EPixelFormat.PF_G8 => PixelInternalFormat.R8, + SKColorType.Gray8 => PixelInternalFormat.R8, _ => texture2D.SRGB ? PixelInternalFormat.Srgb : PixelInternalFormat.Rgb }; - var pixelFormat = Format switch + var pixelFormat = bitmap.ColorType switch { - EPixelFormat.PF_G8 => PixelFormat.Red, + SKColorType.Gray8 => PixelFormat.Red, + SKColorType.Bgra8888 => PixelFormat.Bgra, _ => PixelFormat.Rgba }; @@ -109,6 +111,7 @@ public class Texture : IDisposable GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8); GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); + bitmap.Dispose(); } public Texture(FLinearColor color) : this(TextureType.Normal) diff --git a/README.md b/README.md index 5bfdaa51..92b5501b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ FModel - An Unreal Engine Archives Explorer in C# [![CI Status](https://img.shields.io/github/actions/workflow/status/4sval/FModel/qa.yml?label=CI)](https://github.com/4sval/FModel/actions) [![Latest](https://img.shields.io/github/v/release/4sval/FModel?color=yellow)](https://fmodel.app/download) [![Donate](https://img.shields.io/badge/sponsor-DB61A2?logo=GitHub-Sponsors&logoColor=white)](https://fmodel.app/donate) -[![Discord](https://discordapp.com/api/guilds/637265123144237061/widget.png?style=shield)](https://fmodel.app/discord) +[![Discord](https://discord.com/api/guilds/637265123144237061/widget.png?style=shield)](https://fmodel.app/discord) *** ### Description: