Penumbra/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewer.cs
2024-03-19 22:52:20 +01:00

138 lines
4.3 KiB
C#

using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using Penumbra.GameData.Interop;
namespace Penumbra.Interop.MaterialPreview;
public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase
{
private readonly ShaderPackage* _shaderPackage;
private readonly uint _originalShPkFlags;
private readonly float[] _originalMaterialParameter;
private readonly uint[] _originalSamplerFlags;
public LiveMaterialPreviewer(ObjectManager objects, MaterialInfo materialInfo)
: base(objects, materialInfo)
{
var mtrlHandle = Material->MaterialResourceHandle;
if (mtrlHandle == null)
throw new InvalidOperationException("Material doesn't have a resource handle");
var shpkHandle = mtrlHandle->ShaderPackageResourceHandle;
if (shpkHandle == null)
throw new InvalidOperationException("Material doesn't have a ShPk resource handle");
_shaderPackage = shpkHandle->ShaderPackage;
if (_shaderPackage == null)
throw new InvalidOperationException("Material doesn't have a shader package");
_originalShPkFlags = Material->ShaderFlags;
_originalMaterialParameter = Material->MaterialParameterCBuffer->TryGetBuffer().ToArray();
_originalSamplerFlags = new uint[Material->TextureCount];
for (var i = 0; i < _originalSamplerFlags.Length; ++i)
_originalSamplerFlags[i] = Material->Textures[i].SamplerFlags;
}
protected override void Clear(bool disposing, bool reset)
{
base.Clear(disposing, reset);
if (!reset)
return;
Material->ShaderFlags = _originalShPkFlags;
var materialParameter = Material->MaterialParameterCBuffer->TryGetBuffer();
if (!materialParameter.IsEmpty)
_originalMaterialParameter.AsSpan().CopyTo(materialParameter);
for (var i = 0; i < _originalSamplerFlags.Length; ++i)
Material->Textures[i].SamplerFlags = _originalSamplerFlags[i];
}
public void SetShaderPackageFlags(uint shPkFlags)
{
if (!CheckValidity())
return;
Material->ShaderFlags = shPkFlags;
}
public void SetMaterialParameter(uint parameterCrc, Index offset, Span<float> value)
{
if (!CheckValidity())
return;
var constantBuffer = Material->MaterialParameterCBuffer;
if (constantBuffer == null)
return;
var buffer = constantBuffer->TryGetBuffer();
if (buffer.IsEmpty)
return;
for (var i = 0; i < _shaderPackage->MaterialElementCount; ++i)
{
ref var parameter = ref _shaderPackage->MaterialElementsSpan[i];
if (parameter.CRC != parameterCrc)
continue;
if ((parameter.Offset & 0x3) != 0
|| (parameter.Size & 0x3) != 0
|| (parameter.Offset + parameter.Size) >> 2 > buffer.Length)
return;
value.TryCopyTo(buffer.Slice(parameter.Offset >> 2, parameter.Size >> 2)[offset..]);
return;
}
}
public void SetSamplerFlags(uint samplerCrc, uint samplerFlags)
{
if (!CheckValidity())
return;
var id = 0u;
var found = false;
var samplers = _shaderPackage->Samplers;
for (var i = 0; i < _shaderPackage->SamplerCount; ++i)
{
if (samplers[i].CRC != samplerCrc)
continue;
id = samplers[i].Id;
found = true;
break;
}
if (!found)
return;
for (var i = 0; i < Material->TextureCount; ++i)
{
if (Material->Textures[i].Id != id)
continue;
Material->Textures[i].SamplerFlags = (samplerFlags & 0xFFFFFDFF) | 0x000001C0;
break;
}
}
protected override bool IsStillValid()
{
if (!base.IsStillValid())
return false;
var mtrlHandle = Material->MaterialResourceHandle;
if (mtrlHandle == null)
return false;
var shpkHandle = mtrlHandle->ShaderPackageResourceHandle;
if (shpkHandle == null)
return false;
return _shaderPackage == shpkHandle->ShaderPackage;
}
}