mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
229 lines
7.8 KiB
C#
229 lines
7.8 KiB
C#
using Dalamud.Interface.Components;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Bindings.ImGui;
|
|
using ImSharp;
|
|
using OtterGui;
|
|
using OtterGui.Raii;
|
|
using OtterGui.Text;
|
|
using OtterGui.Widgets;
|
|
using Penumbra.GameData.Files;
|
|
using Penumbra.GameData.Files.MaterialStructs;
|
|
using Penumbra.GameData.Interop;
|
|
using Penumbra.Interop.Hooks.Objects;
|
|
using Penumbra.Interop.ResourceTree;
|
|
using Penumbra.Services;
|
|
using Penumbra.String;
|
|
using Penumbra.UI.Classes;
|
|
|
|
namespace Penumbra.UI.AdvancedWindow.Materials;
|
|
|
|
public sealed partial class MtrlTab : IWritable, IDisposable
|
|
{
|
|
private const int ShpkPrefixLength = 16;
|
|
|
|
private static readonly CiByteString ShpkPrefix = CiByteString.FromSpanUnsafe("shader/sm5/shpk/"u8, true, true, true);
|
|
|
|
private readonly IDataManager _gameData;
|
|
private readonly IFramework _framework;
|
|
private readonly ObjectManager _objects;
|
|
private readonly CharacterBaseDestructor _characterBaseDestructor;
|
|
private readonly StainService _stainService;
|
|
private readonly ResourceTreeFactory _resourceTreeFactory;
|
|
private readonly FileDialogService _fileDialog;
|
|
private readonly MaterialTemplatePickers _materialTemplatePickers;
|
|
private readonly Configuration _config;
|
|
|
|
private readonly ModEditWindow _edit;
|
|
public readonly MtrlFile Mtrl;
|
|
public readonly string FilePath;
|
|
public readonly bool Writable;
|
|
|
|
private bool _updateOnNextFrame;
|
|
|
|
public MtrlTab(IDataManager gameData, IFramework framework, ObjectManager objects, CharacterBaseDestructor characterBaseDestructor,
|
|
StainService stainService, ResourceTreeFactory resourceTreeFactory, FileDialogService fileDialog,
|
|
MaterialTemplatePickers materialTemplatePickers,
|
|
Configuration config, ModEditWindow edit, MtrlFile file, string filePath, bool writable)
|
|
{
|
|
_gameData = gameData;
|
|
_framework = framework;
|
|
_objects = objects;
|
|
_characterBaseDestructor = characterBaseDestructor;
|
|
_stainService = stainService;
|
|
_resourceTreeFactory = resourceTreeFactory;
|
|
_fileDialog = fileDialog;
|
|
_materialTemplatePickers = materialTemplatePickers;
|
|
_config = config;
|
|
|
|
_edit = edit;
|
|
Mtrl = file;
|
|
FilePath = filePath;
|
|
Writable = writable;
|
|
_samplersPinned = true;
|
|
_associatedBaseDevkit = TryLoadShpkDevkit("_base", out _loadedBaseDevkitPathName);
|
|
Update();
|
|
LoadShpk(FindAssociatedShpk(out _, out _));
|
|
if (writable)
|
|
{
|
|
_characterBaseDestructor.Subscribe(UnbindFromDrawObjectMaterialInstances, CharacterBaseDestructor.Priority.MtrlTab);
|
|
BindToMaterialInstances();
|
|
}
|
|
}
|
|
|
|
public bool DrawVersionUpdate(bool disabled)
|
|
{
|
|
if (disabled || Mtrl.IsDawntrail)
|
|
return false;
|
|
|
|
if (!ImUtf8.ButtonEx("Update MTRL Version to Dawntrail"u8,
|
|
"Try using this if the material can not be loaded or should use legacy shaders.\n\nThis is not revertible."u8,
|
|
new Vector2(-0.1f, 0), false, 0, Colors.PressEnterWarningBg))
|
|
return false;
|
|
|
|
Mtrl.MigrateToDawntrail();
|
|
Update();
|
|
LoadShpk(FindAssociatedShpk(out _, out _));
|
|
return true;
|
|
}
|
|
|
|
public bool DrawPanel(bool disabled)
|
|
{
|
|
if (_updateOnNextFrame)
|
|
{
|
|
_updateOnNextFrame = false;
|
|
Update();
|
|
}
|
|
|
|
DrawMaterialLivePreviewRebind(disabled);
|
|
|
|
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
|
|
var ret = DrawBackFaceAndTransparency(disabled);
|
|
|
|
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
|
|
ret |= DrawShaderSection(disabled);
|
|
|
|
ret |= DrawTextureSection(disabled);
|
|
ret |= DrawColorTableSection(disabled);
|
|
ret |= DrawConstantsSection(disabled);
|
|
|
|
ImGui.Dummy(new Vector2(Im.Style.TextHeight / 2));
|
|
DrawOtherMaterialDetails(disabled);
|
|
|
|
return !disabled && ret;
|
|
}
|
|
|
|
private bool DrawBackFaceAndTransparency(bool disabled)
|
|
{
|
|
ref var shaderFlags = ref ShaderFlags.Wrap(ref Mtrl.ShaderPackage.Flags);
|
|
|
|
var ret = false;
|
|
|
|
using var dis = ImRaii.Disabled(disabled);
|
|
|
|
var tmp = shaderFlags.EnableTransparency;
|
|
|
|
// guardrail: the game crashes if transparency is enabled on characterstockings.shpk
|
|
var disallowTransparency = Mtrl.ShaderPackage.Name == "characterstockings.shpk";
|
|
using (ImRaii.Disabled(disallowTransparency))
|
|
{
|
|
if (ImUtf8.Checkbox("Enable Transparency"u8, ref tmp))
|
|
{
|
|
shaderFlags.EnableTransparency = tmp;
|
|
ret = true;
|
|
SetShaderPackageFlags(Mtrl.ShaderPackage.Flags);
|
|
}
|
|
}
|
|
|
|
if (disallowTransparency)
|
|
{
|
|
ImGuiComponents.HelpMarker("Enabling transparency for shader package characterstockings.shpk will crash the game.");
|
|
}
|
|
|
|
ImGui.SameLine(200 * Im.Style.GlobalScale + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X);
|
|
tmp = shaderFlags.HideBackfaces;
|
|
if (ImUtf8.Checkbox("Hide Backfaces"u8, ref tmp))
|
|
{
|
|
shaderFlags.HideBackfaces = tmp;
|
|
ret = true;
|
|
SetShaderPackageFlags(Mtrl.ShaderPackage.Flags);
|
|
}
|
|
|
|
if (_shpkLoading)
|
|
{
|
|
ImGui.SameLine(400 * Im.Style.GlobalScale + 2 * ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X);
|
|
|
|
ImUtf8.Text("Loading shader (.shpk) file. Some functionality will only be available after this is done."u8,
|
|
ImGuiUtil.HalfBlendText(0x808000u)); // Half cyan
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private void DrawOtherMaterialDetails(bool _)
|
|
{
|
|
if (!ImUtf8.CollapsingHeader("Further Content"u8))
|
|
return;
|
|
|
|
using (var sets = ImUtf8.TreeNode("UV Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
|
|
{
|
|
if (sets)
|
|
foreach (var set in Mtrl.UvSets)
|
|
ImUtf8.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose();
|
|
}
|
|
|
|
using (var sets = ImUtf8.TreeNode("Color Sets"u8, ImGuiTreeNodeFlags.DefaultOpen))
|
|
{
|
|
if (sets)
|
|
foreach (var set in Mtrl.ColorSets)
|
|
ImUtf8.TreeNode($"#{set.Index:D2} - {set.Name}", ImGuiTreeNodeFlags.Leaf).Dispose();
|
|
}
|
|
|
|
if (Mtrl.AdditionalData.Length <= 0)
|
|
return;
|
|
|
|
using var t = ImUtf8.TreeNode($"Additional Data (Size: {Mtrl.AdditionalData.Length})###AdditionalData");
|
|
if (t)
|
|
Widget.DrawHexViewer(Mtrl.AdditionalData);
|
|
}
|
|
|
|
private void UnpinResources(bool all)
|
|
{
|
|
_samplersPinned = false;
|
|
|
|
if (!all)
|
|
return;
|
|
|
|
var keys = Mtrl.ShaderPackage.ShaderKeys;
|
|
for (var i = 0; i < keys.Length; i++)
|
|
keys[i].Pinned = false;
|
|
|
|
var constants = Mtrl.ShaderPackage.Constants;
|
|
for (var i = 0; i < constants.Length; i++)
|
|
constants[i].Pinned = false;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
UpdateShaders();
|
|
UpdateTextures();
|
|
UpdateConstants();
|
|
}
|
|
|
|
public unsafe void Dispose()
|
|
{
|
|
UnbindFromMaterialInstances();
|
|
if (Writable)
|
|
_characterBaseDestructor.Unsubscribe(UnbindFromDrawObjectMaterialInstances);
|
|
}
|
|
|
|
public bool Valid
|
|
=> Mtrl.Valid; // FIXME This should be _shadersKnown && Mtrl.Valid but the algorithm for _shadersKnown is flawed as of 7.2.
|
|
|
|
public byte[] Write()
|
|
{
|
|
var output = Mtrl.Clone();
|
|
output.GarbageCollect(_associatedShpk, TextureIds);
|
|
|
|
return output.Write();
|
|
}
|
|
}
|