mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-05-18 19:31:17 +03:00
texture viewer
This commit is contained in:
parent
08c8d07d91
commit
5f84617567
@ -47,6 +47,9 @@ public class App
|
||||
serviceCollection.AddSingleton(type);
|
||||
}
|
||||
|
||||
serviceCollection.AddSingleton(openGl);
|
||||
serviceCollection.AddSingleton(window);
|
||||
|
||||
serviceCollection.AddSingleton(new NResExplorerViewModel());
|
||||
serviceCollection.AddSingleton(new TexmExplorerViewModel());
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
152
NResUI/OpenGlTexture.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user