92 lines
2.9 KiB
C#
92 lines
2.9 KiB
C#
// src/AcDream.App/Rendering/TextureCache.cs
|
|
using AcDream.Core.Textures;
|
|
using DatReaderWriter;
|
|
using DatReaderWriter.DBObjs;
|
|
using Silk.NET.OpenGL;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
public sealed unsafe class TextureCache : IDisposable
|
|
{
|
|
private readonly GL _gl;
|
|
private readonly DatCollection _dats;
|
|
private readonly Dictionary<uint, uint> _handlesBySurfaceId = new();
|
|
private uint _magentaHandle;
|
|
|
|
public TextureCache(GL gl, DatCollection dats)
|
|
{
|
|
_gl = gl;
|
|
_dats = dats;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get or upload the GL texture handle for a Surface id. Returns a
|
|
/// 1x1 magenta fallback if the Surface or its RenderSurface chain is
|
|
/// missing or uses an unsupported format.
|
|
/// </summary>
|
|
public uint GetOrUpload(uint surfaceId)
|
|
{
|
|
if (_handlesBySurfaceId.TryGetValue(surfaceId, out var h))
|
|
return h;
|
|
|
|
var decoded = DecodeFromDats(surfaceId);
|
|
h = UploadRgba8(decoded);
|
|
_handlesBySurfaceId[surfaceId] = h;
|
|
return h;
|
|
}
|
|
|
|
private DecodedTexture DecodeFromDats(uint surfaceId)
|
|
{
|
|
var surface = _dats.Get<Surface>(surfaceId);
|
|
if (surface is null)
|
|
return DecodedTexture.Magenta;
|
|
|
|
var surfaceTexture = _dats.Get<SurfaceTexture>((uint)surface.OrigTextureId);
|
|
if (surfaceTexture is null || surfaceTexture.Textures.Count == 0)
|
|
return DecodedTexture.Magenta;
|
|
|
|
var rs = _dats.Get<RenderSurface>((uint)surfaceTexture.Textures[0]);
|
|
if (rs is null)
|
|
return DecodedTexture.Magenta;
|
|
|
|
return SurfaceDecoder.DecodeRenderSurface(rs);
|
|
}
|
|
|
|
private uint UploadRgba8(DecodedTexture decoded)
|
|
{
|
|
uint tex = _gl.GenTexture();
|
|
_gl.BindTexture(TextureTarget.Texture2D, tex);
|
|
|
|
fixed (byte* p = decoded.Rgba8)
|
|
_gl.TexImage2D(
|
|
TextureTarget.Texture2D,
|
|
0,
|
|
InternalFormat.Rgba8,
|
|
(uint)decoded.Width,
|
|
(uint)decoded.Height,
|
|
0,
|
|
PixelFormat.Rgba,
|
|
PixelType.UnsignedByte,
|
|
p);
|
|
|
|
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
|
_gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
|
|
|
_gl.BindTexture(TextureTarget.Texture2D, 0);
|
|
return tex;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach (var h in _handlesBySurfaceId.Values)
|
|
_gl.DeleteTexture(h);
|
|
_handlesBySurfaceId.Clear();
|
|
if (_magentaHandle != 0)
|
|
{
|
|
_gl.DeleteTexture(_magentaHandle);
|
|
_magentaHandle = 0;
|
|
}
|
|
}
|
|
}
|