diff --git a/Glamourer/Gui/Materials/AdvancedDyePopup.cs b/Glamourer/Gui/Materials/AdvancedDyePopup.cs index 43036e8..f91f352 100644 --- a/Glamourer/Gui/Materials/AdvancedDyePopup.cs +++ b/Glamourer/Gui/Materials/AdvancedDyePopup.cs @@ -26,6 +26,7 @@ public sealed unsafe class AdvancedDyePopup( private ActorState _state = null!; private Actor _actor; private byte _selectedMaterial = byte.MaxValue; + private bool _anyChanged = false; private bool ShouldBeDrawn() { @@ -162,7 +163,7 @@ public sealed unsafe class AdvancedDyePopup( } var size = new Vector2(7 * ImGui.GetFrameHeight() + 3 * ImGui.GetStyle().ItemInnerSpacing.X + 300 * ImGuiHelpers.GlobalScale, - 17f * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y); + 18 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + ImGui.GetStyle().ItemSpacing.Y); ImGui.SetNextWindowSize(size); @@ -192,12 +193,57 @@ public sealed unsafe class AdvancedDyePopup( private void DrawTable(MaterialValueIndex materialIndex, in MtrlFile.ColorTable table) { using var disabled = ImRaii.Disabled(_state.IsLocked); + _anyChanged = false; for (byte i = 0; i < MtrlFile.ColorTable.NumRows; ++i) { var index = materialIndex with { RowIndex = i }; ref var row = ref table[i]; DrawRow(ref row, index, table); } + + ImGui.Separator(); + DrawAllRow(materialIndex, table); + } + + private void DrawAllRow(MaterialValueIndex materialIndex, in MtrlFile.ColorTable table) + { + using var id = ImRaii.PushId(100); + var buttonSize = new Vector2(ImGui.GetFrameHeight()); + ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), buttonSize, "Highlight all affected colors on the character.", + false, true); + if (ImGui.IsItemHovered()) + preview.OnHover(materialIndex with { RowIndex = byte.MaxValue }, _actor.Index, table); + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + using (ImRaii.PushFont(UiBuilder.MonoFont)) + { + ImGui.TextUnformatted("All Color Rows"); + } + + var spacing = ImGui.GetStyle().ItemInnerSpacing.X; + ImGui.SameLine(ImGui.GetWindowSize().X - 3 * buttonSize.X - 3 * spacing); + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), buttonSize, "Export this table to your clipboard.", false, + true)) + ColorRowClipboard.Table = table; + ImGui.SameLine(0, spacing); + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), buttonSize, + "Import an exported table from your clipboard onto this table.", !ColorRowClipboard.IsTableSet, true)) + foreach (var (row, idx) in ColorRowClipboard.Table.WithIndex()) + { + var internalRow = new ColorRow(row); + var slot = materialIndex.ToEquipSlot(); + var weapon = slot is EquipSlot.MainHand or EquipSlot.OffHand + ? _state.ModelData.Weapon(slot) + : _state.ModelData.Armor(slot).ToWeapon(0); + var value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual); + stateManager.ChangeMaterialValue(_state!, materialIndex with { RowIndex = (byte)idx }, value, ApplySettings.Manual); + } + + ImGui.SameLine(0, spacing); + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this table to game state.", !_anyChanged, + true)) + for (byte i = 0; i < MtrlFile.ColorTable.NumRows; ++i) + stateManager.ResetMaterialValue(_state, materialIndex with { RowIndex = (byte)i }, ApplySettings.Game); } private void DrawRow(ref MtrlFile.ColorTable.Row row, MaterialValueIndex index, in MtrlFile.ColorTable table) @@ -213,6 +259,10 @@ public sealed unsafe class AdvancedDyePopup( : _state.ModelData.Armor(slot).ToWeapon(0); value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual); } + else + { + _anyChanged = true; + } var buttonSize = new Vector2(ImGui.GetFrameHeight()); ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), buttonSize, "Highlight the affected colors on the character.", diff --git a/Glamourer/Gui/Materials/ColorRowClipboard.cs b/Glamourer/Gui/Materials/ColorRowClipboard.cs index 74c1c68..f8310c3 100644 --- a/Glamourer/Gui/Materials/ColorRowClipboard.cs +++ b/Glamourer/Gui/Materials/ColorRowClipboard.cs @@ -1,13 +1,27 @@ using Glamourer.Interop.Material; +using Penumbra.GameData.Files; namespace Glamourer.Gui.Materials; public static class ColorRowClipboard { - private static ColorRow _row; + private static ColorRow _row; + private static MtrlFile.ColorTable _table; public static bool IsSet { get; private set; } + public static bool IsTableSet { get; private set; } + + public static MtrlFile.ColorTable Table + { + get => _table; + set + { + IsTableSet = true; + _table = value; + } + } + public static ColorRow Row { get => _row; diff --git a/Glamourer/Interop/Material/LiveColorTablePreviewer.cs b/Glamourer/Interop/Material/LiveColorTablePreviewer.cs index 7b10829..8cd2b78 100644 --- a/Glamourer/Interop/Material/LiveColorTablePreviewer.cs +++ b/Glamourer/Interop/Material/LiveColorTablePreviewer.cs @@ -29,21 +29,20 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable private void Reset() { - if (!LastValueIndex.Valid || _lastObjectIndex == ObjectIndex.AnyIndex) + if (LastValueIndex.DrawObject is MaterialValueIndex.DrawObjectType.Invalid || _lastObjectIndex == ObjectIndex.AnyIndex) return; var actor = (Actor)_objects.GetObjectAddress(_lastObjectIndex.Index); if (actor.IsCharacter && LastValueIndex.TryGetTexture(actor, out var texture)) MaterialService.ReplaceColorTable(texture, LastOriginalColorTable); - Glamourer.Log.Information($"Reset {_lastObjectIndex} {LastValueIndex}"); LastValueIndex = MaterialValueIndex.Invalid; _lastObjectIndex = ObjectIndex.AnyIndex; } private void OnFramework(IFramework _) { - if (!_valueIndex.Valid || _objectIndex == ObjectIndex.AnyIndex) + if (_valueIndex.DrawObject is MaterialValueIndex.DrawObjectType.Invalid || _objectIndex == ObjectIndex.AnyIndex) { Reset(); _valueIndex = MaterialValueIndex.Invalid; @@ -69,10 +68,24 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable if (_valueIndex.TryGetTexture(actor, out var texture)) { - var diffuse = CalculateDiffuse(); - var table = LastOriginalColorTable; - table[_valueIndex.RowIndex].Diffuse = diffuse; - table[_valueIndex.RowIndex].Emissive = diffuse / 8; + var diffuse = CalculateDiffuse(); + var emissive = diffuse / 8; + var table = LastOriginalColorTable; + if (_valueIndex.RowIndex != byte.MaxValue) + { + table[_valueIndex.RowIndex].Diffuse = diffuse; + table[_valueIndex.RowIndex].Emissive = emissive; + } + else + { + + for (var i = 0; i < MtrlFile.ColorTable.NumRows; ++i) + { + table[i].Diffuse = diffuse; + table[i].Emissive = emissive; + } + } + MaterialService.ReplaceColorTable(texture, table); } @@ -82,12 +95,12 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable public void OnHover(MaterialValueIndex index, ObjectIndex objectIndex, MtrlFile.ColorTable table) { - if (_valueIndex.Valid) + if (_valueIndex.DrawObject is not MaterialValueIndex.DrawObjectType.Invalid) return; _valueIndex = index; _objectIndex = objectIndex; - if (!LastValueIndex.Valid + if (LastValueIndex.DrawObject is MaterialValueIndex.DrawObjectType.Invalid || _lastObjectIndex == ObjectIndex.AnyIndex || LastValueIndex.MaterialIndex != _valueIndex.MaterialIndex || LastValueIndex.DrawObject != _valueIndex.DrawObject diff --git a/Glamourer/Interop/Material/PrepareColorSet.cs b/Glamourer/Interop/Material/PrepareColorSet.cs index 1fc2f68..0f5be0d 100644 --- a/Glamourer/Interop/Material/PrepareColorSet.cs +++ b/Glamourer/Interop/Material/PrepareColorSet.cs @@ -74,15 +74,16 @@ public sealed unsafe class PrepareColorSet public static bool TryGetColorTable(Actor actor, MaterialValueIndex index, out MtrlFile.ColorTable table) { var idx = index.SlotIndex * MaterialService.MaterialsPerModel + index.MaterialIndex; - var model = actor.Model.AsCharacterBase; - var handle = (MaterialResourceHandle*)model->Materials[idx]; + if (!index.TryGetModel(actor, out var model)) + return false; + var handle = (MaterialResourceHandle*)model.AsCharacterBase->Materials[idx]; if (handle == null) { table = default; return false; } - return TryGetColorTable(model, handle, GetStain(), out table); + return TryGetColorTable(model.AsCharacterBase, handle, GetStain(), out table); StainId GetStain() {