mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
117 lines
3.8 KiB
C#
117 lines
3.8 KiB
C#
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
|
using Penumbra.GameData.Interop;
|
|
using Penumbra.Interop.SafeHandles;
|
|
|
|
namespace Penumbra.Interop.MaterialPreview;
|
|
|
|
public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
|
|
{
|
|
private readonly IFramework _framework;
|
|
|
|
private readonly Texture** _colorTableTexture;
|
|
private readonly SafeTextureHandle _originalColorTableTexture;
|
|
|
|
private bool _updatePending;
|
|
|
|
public int Width { get; }
|
|
public int Height { get; }
|
|
|
|
public Half[] ColorTable { get; }
|
|
|
|
public LiveColorTablePreviewer(ObjectManager objects, IFramework framework, MaterialInfo materialInfo)
|
|
: base(objects, materialInfo)
|
|
{
|
|
_framework = framework;
|
|
|
|
var mtrlHandle = Material->MaterialResourceHandle;
|
|
if (mtrlHandle == null)
|
|
throw new InvalidOperationException("Material doesn't have a resource handle");
|
|
|
|
var colorSetTextures = DrawObject->ColorTableTextures;
|
|
if (colorSetTextures == null)
|
|
throw new InvalidOperationException("Draw object doesn't have color table textures");
|
|
|
|
_colorTableTexture = colorSetTextures + (MaterialInfo.ModelSlot * CharacterBase.MaterialsPerSlot + MaterialInfo.MaterialSlot);
|
|
|
|
|
|
_originalColorTableTexture = new SafeTextureHandle(*_colorTableTexture, true);
|
|
if (_originalColorTableTexture.Texture == null)
|
|
throw new InvalidOperationException("Material doesn't have a color table");
|
|
|
|
Width = (int)_originalColorTableTexture.Texture->ActualWidth;
|
|
Height = (int)_originalColorTableTexture.Texture->ActualHeight;
|
|
ColorTable = new Half[Width * Height * 4];
|
|
_updatePending = true;
|
|
|
|
framework.Update += OnFrameworkUpdate;
|
|
}
|
|
|
|
public Span<Half> GetColorRow(int i)
|
|
=> ColorTable.AsSpan().Slice(Width * 4 * i, Width * 4);
|
|
|
|
protected override void Clear(bool disposing, bool reset)
|
|
{
|
|
_framework.Update -= OnFrameworkUpdate;
|
|
|
|
base.Clear(disposing, reset);
|
|
|
|
if (reset)
|
|
_originalColorTableTexture.Exchange(ref *(nint*)_colorTableTexture);
|
|
|
|
_originalColorTableTexture.Dispose();
|
|
}
|
|
|
|
public void ScheduleUpdate()
|
|
{
|
|
_updatePending = true;
|
|
}
|
|
|
|
[SkipLocalsInit]
|
|
private void OnFrameworkUpdate(IFramework _)
|
|
{
|
|
if (!_updatePending)
|
|
return;
|
|
|
|
_updatePending = false;
|
|
|
|
if (!CheckValidity())
|
|
return;
|
|
|
|
var textureSize = stackalloc int[2];
|
|
textureSize[0] = Width;
|
|
textureSize[1] = Height;
|
|
|
|
using var texture =
|
|
new SafeTextureHandle(
|
|
Device.Instance()->CreateTexture2D(textureSize, 1, TextureFormat.R16G16B16A16_FLOAT,
|
|
TextureFlags.TextureNoSwizzle | TextureFlags.Immutable | TextureFlags.Managed, 7), false);
|
|
if (texture.IsInvalid)
|
|
return;
|
|
|
|
bool success;
|
|
lock (ColorTable)
|
|
{
|
|
fixed (Half* colorTable = ColorTable)
|
|
{
|
|
success = texture.Texture->InitializeContents(colorTable);
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
texture.Exchange(ref *(nint*)_colorTableTexture);
|
|
}
|
|
|
|
protected override bool IsStillValid()
|
|
{
|
|
if (!base.IsStillValid())
|
|
return false;
|
|
|
|
var colorSetTextures = DrawObject->ColorTableTextures;
|
|
if (colorSetTextures == null)
|
|
return false;
|
|
|
|
return _colorTableTexture == colorSetTextures + (MaterialInfo.ModelSlot * CharacterBase.MaterialsPerSlot + MaterialInfo.MaterialSlot);
|
|
}
|
|
}
|