StoicGoose.Unity/Assets/Plugins/StoicGooseUnity/StoicGoose.Common/OpenGL/Texture.cs

170 lines
4.4 KiB
C#

using System;
using System.Runtime.InteropServices;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using StoicGoose.Common.Drawing;
namespace StoicGoose.Common.OpenGL
{
public sealed class Texture : IDisposable
{
const TextureMinFilter defaultMinFilter = TextureMinFilter.Nearest;
const TextureMagFilter defaultMagFilter = TextureMagFilter.Nearest;
const TextureWrapMode defaultWrapModeS = TextureWrapMode.Repeat;
const TextureWrapMode defaultWrapModeT = TextureWrapMode.Repeat;
public int Handle { get; } = GL.GenTexture();
public Vector2i Size { get; private set; } = Vector2i.Zero;
byte[] pixelData = default;
bool isDirty = false;
bool disposed = false;
public Texture(int width, int height) : this(width, height, 255, 255, 255, 255) { }
public Texture(int width, int height, byte r, byte g, byte b, byte a)
{
Size = new Vector2i(width, height);
var data = new byte[width * height * 4];
for (var i = 0; i < data.Length; i += 4)
{
data[i + 0] = r;
data[i + 1] = g;
data[i + 2] = b;
data[i + 3] = a;
}
SetInitialTexImage(data);
}
public Texture(RgbaFile rgbaFile)
{
Size = new Vector2i((int)rgbaFile.Width, (int)rgbaFile.Height);
SetInitialTexImage(rgbaFile.PixelData);
}
public Texture(int width, int height, byte[] data)
{
Size = new Vector2i(width, height);
SetInitialTexImage(data);
}
public Texture(int width, int height, IntPtr data)
{
Size = new Vector2i(width, height);
SetInitialTexImage(data);
}
~Texture()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
if (GL.IsTexture(Handle))
GL.DeleteTexture(Handle);
}
disposed = true;
}
private void SetInitialTexImage(byte[] data)
{
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var pointer = handle.AddrOfPinnedObject();
SetInitialTexImage(pointer);
handle.Free();
}
private void SetInitialTexImage(IntPtr pixels)
{
ChangeTextureParams(() =>
{
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, Size.X, Size.Y, 0, PixelFormat.Rgba, PixelType.UnsignedByte, pixels);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)defaultMinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)defaultMagFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)defaultWrapModeS);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)defaultWrapModeT);
});
}
public void SetTextureFilter(TextureMinFilter textureMinFilter, TextureMagFilter textureMagFilter)
{
ChangeTextureParams(() =>
{
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)textureMinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)textureMagFilter);
});
}
public void SetTextureWrapMode(TextureWrapMode textureWrapModeS, TextureWrapMode textureWrapModeT)
{
ChangeTextureParams(() =>
{
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)textureWrapModeS);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)textureWrapModeT);
});
}
private void ChangeTextureParams(Action action)
{
var lastTextureSet = GL.GetInteger(GetPName.Texture2D);
if (Handle != lastTextureSet) GL.BindTexture(TextureTarget.Texture2D, Handle);
action?.Invoke();
GL.BindTexture(TextureTarget.Texture2D, lastTextureSet);
}
public void Bind()
{
Bind(0);
}
public void Bind(int textureUnit)
{
GL.ActiveTexture(TextureUnit.Texture0 + textureUnit);
GL.BindTexture(TextureTarget.Texture2D, Handle);
if (isDirty)
{
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Size.X, Size.Y, PixelFormat.Rgba, PixelType.UnsignedByte, pixelData);
isDirty = false;
}
}
public void Update(byte[] data)
{
isDirty = true;
pixelData = data;
}
public void Fill(byte r, byte g, byte b, byte a)
{
isDirty = true;
var data = new byte[Size.X * Size.Y * 4];
for (var i = 0; i < data.Length; i += 4)
{
data[i + 0] = r;
data[i + 1] = g;
data[i + 2] = b;
data[i + 3] = a;
}
pixelData = data;
}
}
}