0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-05-18 19:31:17 +03:00

texture viewer

This commit is contained in:
bird_egop 2024-11-19 02:13:14 +03:00
parent 08c8d07d91
commit 5f84617567
5 changed files with 285 additions and 28 deletions

View File

@ -47,6 +47,9 @@ public class App
serviceCollection.AddSingleton(type);
}
serviceCollection.AddSingleton(openGl);
serviceCollection.AddSingleton(window);
serviceCollection.AddSingleton(new NResExplorerViewModel());
serviceCollection.AddSingleton(new TexmExplorerViewModel());

View File

@ -1,16 +1,20 @@
using ImGuiNET;
using System.Numerics;
using ImGuiNET;
using NResUI.Abstractions;
using NResUI.Models;
using Silk.NET.OpenGL;
namespace NResUI.ImGuiUI;
public class TexmExplorer : IImGuiPanel
{
private readonly TexmExplorerViewModel _viewModel;
private readonly GL _openGl;
public TexmExplorer(TexmExplorerViewModel viewModel)
public TexmExplorer(TexmExplorerViewModel viewModel, GL openGl)
{
_viewModel = viewModel;
_openGl = openGl;
}
public void OnImGuiRender()
@ -49,7 +53,7 @@ public class TexmExplorer : IImGuiPanel
ImGui.SameLine();
ImGui.Text(_viewModel.TexmFile.Header.Stride.ToString());
ImGui.Text("Magic1: ");
ImGui.Text("Magic1 (possibly ddsCaps): ");
ImGui.SameLine();
ImGui.Text(_viewModel.TexmFile.Header.Magic1.ToString());
@ -64,6 +68,45 @@ public class TexmExplorer : IImGuiPanel
ImGui.Text("IsIndexed: ");
ImGui.SameLine();
ImGui.Text(_viewModel.TexmFile.IsIndexed.ToString());
ImGui.Checkbox("Включить чёрный фон", ref _viewModel.IsBlackBgEnabled);
ImGui.Checkbox("Включить белый фон", ref _viewModel.IsWhiteBgEnabled);
if (_viewModel.IsWhiteBgEnabled && _viewModel.IsBlackBgEnabled)
{
_viewModel.IsBlackBgEnabled = false;
_viewModel.IsWhiteBgEnabled = false;
}
_viewModel.GenerateGlTextures(_openGl);
var drawList = ImGui.GetWindowDrawList();
for (var index = 0; index < _viewModel.GlTextures.Count; index++)
{
var glTexture = _viewModel.GlTextures[index];
var screenPos = ImGui.GetCursorScreenPos();
if (_viewModel.IsBlackBgEnabled)
{
drawList.AddRectFilled(screenPos, screenPos + new Vector2(glTexture.Width, glTexture.Height), 0xFF000000);
}
else if (_viewModel.IsWhiteBgEnabled)
{
drawList.AddRectFilled(screenPos, screenPos + new Vector2(glTexture.Width, glTexture.Height), 0xFFFFFFFF);
}
ImGui.Image((IntPtr) glTexture.GlTexture, new Vector2(glTexture.Width, glTexture.Height));
ImGui.SameLine();
if (ImGui.IsItemHovered())
{
var mousePos = ImGui.GetMousePos();
var relativePos = mousePos - screenPos;
ImGui.Text("Hovering over: ");
ImGui.SameLine();
ImGui.Text(relativePos.ToString());
}
}
}
}

View File

@ -1,4 +1,5 @@
using TexmLib;
using Silk.NET.OpenGL;
using TexmLib;
namespace NResUI.Models;
@ -8,9 +9,15 @@ public class TexmExplorerViewModel
public string? Error { get; set; }
public TexmFile? TexmFile { get; set; }
public string? Path { get; set; }
public List<OpenGlTexture> GlTextures { get; set; } = [];
private bool _glTexturesDirty = false;
public bool IsWhiteBgEnabled;
public bool IsBlackBgEnabled;
public void SetParseResult(TexmParseResult result, string path)
{
Error = result.Error;
@ -22,6 +29,38 @@ public class TexmExplorerViewModel
TexmFile = result.TexmFile;
Path = path;
_glTexturesDirty = true;
}
/// <summary>
/// Сгенерировать OpenGL текстуры из всех мипмапов Texm файла
/// </summary>
public void GenerateGlTextures(GL gl)
{
if (_glTexturesDirty && TexmFile is not null)
{
foreach (var glTexture in GlTextures)
{
glTexture.Dispose();
}
GlTextures.Clear();
for (var i = 0; i < TexmFile!.Header.MipmapCount; i++)
{
var bytes = TexmFile.GetRgba32BytesFromMipmap(i, out var width, out var height);
var glTexture = new OpenGlTexture(
gl,
width,
height,
bytes
);
GlTextures.Add(glTexture);
}
_glTexturesDirty = false;
}
}
}

152
NResUI/OpenGlTexture.cs Normal file
View File

@ -0,0 +1,152 @@
using System.Runtime.CompilerServices;
using Silk.NET.OpenGL;
namespace NResUI
{
public enum TextureCoordinate
{
S = TextureParameterName.TextureWrapS,
T = TextureParameterName.TextureWrapT,
R = TextureParameterName.TextureWrapR
}
public class OpenGlTexture : IDisposable
{
public const SizedInternalFormat Srgb8Alpha8 = (SizedInternalFormat)GLEnum.Srgb8Alpha8;
public const SizedInternalFormat Rgb32F = (SizedInternalFormat)GLEnum.Rgb32f;
public const GLEnum MaxTextureMaxAnisotropy = (GLEnum)0x84FF;
public static float? MaxAniso;
private readonly GL _gl;
public readonly string Name;
public readonly uint GlTexture;
public readonly uint Width, Height;
public readonly uint MipmapLevels;
public readonly SizedInternalFormat InternalFormat;
public OpenGlTexture(GL gl, int width, int height, IntPtr data, bool generateMipmaps = false, bool srgb = false)
{
_gl = gl;
MaxAniso ??= gl.GetFloat(MaxTextureMaxAnisotropy);
Width = (uint)width;
Height = (uint)height;
InternalFormat = srgb ? Srgb8Alpha8 : SizedInternalFormat.Rgba8;
MipmapLevels = (uint)(generateMipmaps == false ? 1 : (int)Math.Floor(Math.Log(Math.Max(Width, Height), 2)));
GlTexture = _gl.GenTexture();
Bind();
_gl.TexStorage2D(GLEnum.Texture2D, MipmapLevels, InternalFormat, Width, Height);
_gl.TexSubImage2D(GLEnum.Texture2D, 0, 0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, data);
if (generateMipmaps) _gl.GenerateTextureMipmap(GlTexture);
SetWrap(TextureCoordinate.S, TextureWrapMode.Repeat);
SetWrap(TextureCoordinate.T, TextureWrapMode.Repeat);
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLevel, MipmapLevels - 1);
}
public OpenGlTexture(GL gl, int width, int height, byte[] data, bool generateMipmaps = false, bool srgb = false)
{
_gl = gl;
MaxAniso ??= gl.GetFloat(MaxTextureMaxAnisotropy);
Width = (uint)width;
Height = (uint)height;
InternalFormat = srgb ? Srgb8Alpha8 : SizedInternalFormat.Rgba8;
MipmapLevels = (uint)(generateMipmaps == false ? 1 : (int)Math.Floor(Math.Log(Math.Max(Width, Height), 2)));
GlTexture = _gl.GenTexture();
Bind();
_gl.TexStorage2D(GLEnum.Texture2D, MipmapLevels, InternalFormat, Width, Height);
_gl.TexSubImage2D(GLEnum.Texture2D, 0, 0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, Unsafe.AsRef(ref data[0]));
if (generateMipmaps) _gl.GenerateTextureMipmap(GlTexture);
SetWrap(TextureCoordinate.S, TextureWrapMode.Repeat);
SetWrap(TextureCoordinate.T, TextureWrapMode.Repeat);
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLevel, MipmapLevels - 1);
}
public OpenGlTexture(GL gl, int width, int height, byte[] data, PixelFormat pixelFormat, bool generateMipmaps = false, bool srgb = false)
{
_gl = gl;
MaxAniso ??= gl.GetFloat(MaxTextureMaxAnisotropy);
Width = (uint)width;
Height = (uint)height;
InternalFormat = srgb ? Srgb8Alpha8 : SizedInternalFormat.Rgba8;
MipmapLevels = (uint)(generateMipmaps == false ? 1 : (int)Math.Floor(Math.Log(Math.Max(Width, Height), 2)));
GlTexture = _gl.GenTexture();
Bind();
_gl.TexStorage2D(GLEnum.Texture2D, MipmapLevels, InternalFormat, Width, Height);
_gl.TexSubImage2D(GLEnum.Texture2D, 0, 0, 0, Width, Height, pixelFormat, PixelType.UnsignedByte, Unsafe.AsRef(ref data[0]));
if (generateMipmaps) _gl.GenerateTextureMipmap(GlTexture);
SetWrap(TextureCoordinate.S, TextureWrapMode.Repeat);
SetWrap(TextureCoordinate.T, TextureWrapMode.Repeat);
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLevel, MipmapLevels - 1);
}
public void UpdateData(byte[] data, PixelFormat pixelFormat)
{
Bind();
_gl.TexSubImage2D(GLEnum.Texture2D, 0, 0, 0, Width, Height, pixelFormat, PixelType.UnsignedByte, Unsafe.AsRef(ref data[0]));
}
public byte[] DownloadData()
{
Bind();
byte[] data = new byte[Width * Height * 4];
_gl.GetTexImage(TextureTarget.Texture2D, 0, PixelFormat.Rgba, PixelType.UnsignedByte, out Unsafe.AsRef(ref data[0]));
return data;
}
public void Bind()
{
_gl.BindTexture(GLEnum.Texture2D, GlTexture);
}
public void SetMinFilter(TextureMinFilter filter)
{
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMinFilter, (int)filter);
}
public void SetMagFilter(TextureMagFilter filter)
{
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMagFilter, (int)filter);
}
// public void SetAnisotropy(float level)
// {
// const TextureParameterName textureMaxAnisotropy = (TextureParameterName)0x84FE;
// _gl.TexParameter(GLEnum.Texture2D, (GLEnum)textureMaxAnisotropy, Util.Clamp(level, 1, MaxAniso.GetValueOrDefault()));
// }
public void SetLod(int @base, int min, int max)
{
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureLodBias, @base);
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMinLod, min);
_gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLod, max);
}
public void SetWrap(TextureCoordinate coord, TextureWrapMode mode)
{
_gl.TexParameterI(GLEnum.Texture2D, (TextureParameterName)coord, (int)mode);
}
public void Dispose()
{
_gl.DeleteTexture(GlTexture);
}
}
}

View File

@ -114,6 +114,26 @@ public class TexmFile
}
}
public byte[] GetRgba32BytesFromMipmap(int index, out int mipWidth, out int mipHeight)
{
var mipmapBytes = MipmapBytes[index];
mipWidth = Header.Width / (int) Math.Pow(2, index);
mipHeight = Header.Height / (int) Math.Pow(2, index);
if (IsIndexed)
{
return ReinterpretIndexedMipmap(mipmapBytes, LookupColors);
}
return ReinterpretMipmapBytesAsRgba32(
mipmapBytes,
mipWidth,
mipHeight,
Header.Format
);
}
private byte[] ReinterpretIndexedMipmap(byte[] bytes, byte[] lookupColors)
{
var span = bytes.AsSpan();
@ -123,15 +143,15 @@ public class TexmFile
{
var index = span[i];
var a = lookupColors[index * 4 + 0];
var r = lookupColors[index * 4 + 1];
var g = lookupColors[index * 4 + 2];
var b = lookupColors[index * 4 + 3];
var r = lookupColors[index * 4 + 0];
var g = lookupColors[index * 4 + 1];
var b = lookupColors[index * 4 + 2];
var a = lookupColors[index * 4 + 3];
result[i * 4 + 0] = r;
result[i * 4 + 1] = g;
result[i * 4 + 2] = b;
result[i * 4 + 3] = a;
result[i * 4 + 3] = 255;
}
return result;
@ -161,8 +181,8 @@ public class TexmFile
var rawPixel = span.Slice(i, 2);
var r = (byte)(((rawPixel[0] >> 3) & 0b11111) / 32 * 255);
var g = (byte)(((rawPixel[0] & 0b111) << 3) | ((rawPixel[1] >> 5) & 0b111) / 64 * 255);
var b = (byte)((rawPixel[1] & 0b11111) / 32 * 255);
var b = (byte)(((rawPixel[0] & 0b111) << 3) | ((rawPixel[1] >> 5) & 0b111) / 64 * 255);
var g = (byte)((rawPixel[1] & 0b11111) / 32 * 255);
result[i / 2 * 4 + 0] = r;
result[i / 2 * 4 + 1] = g;
@ -182,10 +202,10 @@ public class TexmFile
{
var rawPixel = span.Slice(i, 2);
var a = (byte)((float)((rawPixel[0] >> 4) & 0b1111) / 16 * 255);
var r = (byte)((float)((rawPixel[0] >> 0) & 0b1111) / 16 * 255);
var g = (byte)((float)((rawPixel[1] >> 4) & 0b1111) / 16 * 255);
var b = (byte)((float)((rawPixel[1] >> 0) & 0b1111) / 16 * 255);
var r = (byte)((float)((rawPixel[0] >> 4) & 0b1111) * 17);
var g = (byte)((float)((rawPixel[0] >> 0) & 0b1111) * 17);
var b = (byte)((float)((rawPixel[1] >> 4) & 0b1111) * 17);
var a = (byte)((float)((rawPixel[1] >> 0) & 0b1111) * 17);
result[i / 2 * 4 + 0] = r;
result[i / 2 * 4 + 1] = g;
@ -205,14 +225,14 @@ public class TexmFile
{
var rawPixel = span.Slice(i, 4);
var x = rawPixel[0];
var y = rawPixel[1];
var z = rawPixel[2];
var r = rawPixel[0];
var g = rawPixel[1];
var b = rawPixel[2];
var w = rawPixel[3];
result[i + 0] = y;
result[i + 1] = z;
result[i + 2] = w;
result[i + 0] = r;
result[i + 1] = g;
result[i + 2] = b;
result[i + 3] = 255;
}
@ -228,10 +248,10 @@ public class TexmFile
{
var rawPixel = span.Slice(i, 4);
var a = rawPixel[0];
var r = rawPixel[1];
var g = rawPixel[2];
var b = rawPixel[3];
var b = rawPixel[0];
var g = rawPixel[1];
var r = rawPixel[2];
var a = rawPixel[3];
result[i + 0] = r;
result[i + 1] = g;