mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-01-03 14:23:43 +01:00
Further improvements.
This commit is contained in:
parent
5f74f4b4d9
commit
59529476eb
15 changed files with 342 additions and 161 deletions
|
|
@ -36,6 +36,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool OpenWindowAtStart { get; set; } = false;
|
||||
public bool UseAdvancedParameters { get; set; } = true;
|
||||
public bool UseAdvancedDyes { get; set; } = false;
|
||||
public bool KeepAdvancedDyesAttached { get; set; } = true;
|
||||
public bool ShowRevertAdvancedParametersButton { get; set; } = true;
|
||||
public bool ShowPalettePlusImport { get; set; } = true;
|
||||
public bool UseFloatForColors { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace Glamourer.Events
|
|||
/// <summary> A characters saved state had its customize parameter changed. Data is the old value, the new value and the type [(CustomizeParameterValue, CustomizeParameterValue, CustomizeParameterFlag)]. </summary>
|
||||
Parameter,
|
||||
|
||||
/// <summary> A characters saved state had a material color table value changed. Data is the old value, the new value and the index [(Vector3, Vector3, MaterialValueIndex)]. </summary>
|
||||
/// <summary> A characters saved state had a material color table value changed. Data is the old value, the new value and the index [(Vector3, Vector3, MaterialValueIndex)] or just the index for resets. </summary>
|
||||
MaterialValue,
|
||||
|
||||
/// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Gui.Materials;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
|
|
@ -27,12 +28,13 @@ public class EquipmentDrawer
|
|||
private readonly TextureService _textures;
|
||||
private readonly Configuration _config;
|
||||
private readonly GPoseService _gPose;
|
||||
private readonly AdvancedDyePopup _advancedDyes;
|
||||
|
||||
private float _requiredComboWidthUnscaled;
|
||||
private float _requiredComboWidth;
|
||||
|
||||
public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, CodeService codes, TextureService textures,
|
||||
Configuration config, GPoseService gPose)
|
||||
Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes)
|
||||
{
|
||||
_items = items;
|
||||
_codes = codes;
|
||||
|
|
@ -283,6 +285,10 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
DrawApplyStain(equipDrawData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_advancedDyes.DrawButton(equipDrawData.Slot);
|
||||
}
|
||||
|
||||
if (VerifyRestrictedGear(equipDrawData))
|
||||
label += " (Restricted)";
|
||||
|
|
@ -303,6 +309,10 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
DrawApplyStain(mainhand);
|
||||
}
|
||||
else
|
||||
{
|
||||
_advancedDyes.DrawButton(EquipSlot.MainHand);
|
||||
}
|
||||
|
||||
if (allWeapons)
|
||||
mainhandLabel += $" ({mainhand.CurrentItem.Type.ToName()})";
|
||||
|
|
@ -321,6 +331,10 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
DrawApplyStain(offhand);
|
||||
}
|
||||
else
|
||||
{
|
||||
_advancedDyes.DrawButton(EquipSlot.OffHand);
|
||||
}
|
||||
|
||||
WeaponHelpMarker(offhandLabel);
|
||||
}
|
||||
|
|
@ -351,6 +365,10 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
DrawApplyStain(equipDrawData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_advancedDyes.DrawButton(equipDrawData.Slot);
|
||||
}
|
||||
|
||||
if (VerifyRestrictedGear(equipDrawData))
|
||||
{
|
||||
|
|
@ -384,6 +402,10 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
DrawApplyStain(mainhand);
|
||||
}
|
||||
else
|
||||
{
|
||||
_advancedDyes.DrawButton(EquipSlot.MainHand);
|
||||
}
|
||||
}
|
||||
|
||||
if (offhand.CurrentItem.Type is FullEquipType.Unknown)
|
||||
|
|
@ -410,6 +432,10 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
DrawApplyStain(offhand);
|
||||
}
|
||||
else
|
||||
{
|
||||
_advancedDyes.DrawButton(EquipSlot.OffHand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Gui.Materials;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
|
|
@ -11,11 +12,12 @@ namespace Glamourer.Gui;
|
|||
public class GenericPopupWindow : Window
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly AdvancedDyePopup _advancedDye;
|
||||
private readonly ICondition _condition;
|
||||
private readonly IClientState _state;
|
||||
public bool OpenFestivalPopup { get; internal set; } = false;
|
||||
|
||||
public GenericPopupWindow(Configuration config, IClientState state, ICondition condition)
|
||||
public GenericPopupWindow(Configuration config, IClientState state, ICondition condition, AdvancedDyePopup advancedDye)
|
||||
: base("Glamourer Popups",
|
||||
ImGuiWindowFlags.NoBringToFrontOnFocus
|
||||
| ImGuiWindowFlags.NoDecoration
|
||||
|
|
@ -29,6 +31,7 @@ public class GenericPopupWindow : Window
|
|||
_config = config;
|
||||
_state = state;
|
||||
_condition = condition;
|
||||
_advancedDye = advancedDye;
|
||||
DisableWindowSounds = true;
|
||||
IsOpen = true;
|
||||
}
|
||||
|
|
@ -42,6 +45,7 @@ public class GenericPopupWindow : Window
|
|||
}
|
||||
|
||||
DrawFestivalPopup();
|
||||
//_advancedDye.Draw();
|
||||
}
|
||||
|
||||
private bool CheckFestivalPopupConditions()
|
||||
|
|
|
|||
|
|
@ -13,10 +13,18 @@ using Glamourer.Gui.Tabs.SettingsTab;
|
|||
using Glamourer.Gui.Tabs.UnlocksTab;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Custom;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public class MainWindowPosition : IService
|
||||
{
|
||||
public bool IsOpen { get; set; }
|
||||
public Vector2 Position { get; set; }
|
||||
public Vector2 Size { get; set; }
|
||||
}
|
||||
|
||||
public class MainWindow : Window, IDisposable
|
||||
{
|
||||
public enum TabType
|
||||
|
|
@ -35,6 +43,7 @@ public class MainWindow : Window, IDisposable
|
|||
private readonly Configuration _config;
|
||||
private readonly DesignQuickBar _quickBar;
|
||||
private readonly TabSelected _event;
|
||||
private readonly MainWindowPosition _position;
|
||||
private readonly ITab[] _tabs;
|
||||
|
||||
public readonly SettingsTab Settings;
|
||||
|
|
@ -50,7 +59,7 @@ public class MainWindow : Window, IDisposable
|
|||
|
||||
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
|
||||
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, MessagesTab messages, DesignQuickBar quickBar,
|
||||
NpcTab npcs)
|
||||
NpcTab npcs, MainWindowPosition position)
|
||||
: base(GetLabel())
|
||||
{
|
||||
pi.UiBuilder.DisableGposeUiHide = true;
|
||||
|
|
@ -69,6 +78,7 @@ public class MainWindow : Window, IDisposable
|
|||
Messages = messages;
|
||||
_quickBar = quickBar;
|
||||
Npcs = npcs;
|
||||
_position = position;
|
||||
_config = config;
|
||||
_tabs =
|
||||
[
|
||||
|
|
@ -90,6 +100,7 @@ public class MainWindow : Window, IDisposable
|
|||
Flags = _config.Ephemeral.LockMainWindow
|
||||
? Flags | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize
|
||||
: Flags & ~(ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize);
|
||||
_position.IsOpen = IsOpen;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -98,9 +109,14 @@ public class MainWindow : Window, IDisposable
|
|||
public override void Draw()
|
||||
{
|
||||
var yPos = ImGui.GetCursorPosY();
|
||||
_position.Size = ImGui.GetWindowSize();
|
||||
_position.Position = ImGui.GetWindowPos();
|
||||
if (TabBar.Draw("##tabs", ImGuiTabBarFlags.None, ToLabel(SelectTab), out var currentTab, () => { }, _tabs))
|
||||
{
|
||||
SelectTab = TabType.None;
|
||||
var tab = FromLabel(currentTab);
|
||||
|
||||
if (tab != _config.Ephemeral.SelectedTab)
|
||||
{
|
||||
_config.Ephemeral.SelectedTab = FromLabel(currentTab);
|
||||
_config.Ephemeral.Save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Dalamud.Interface;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.Interop;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui.Tabs.ActorTab;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.State;
|
||||
|
|
@ -9,148 +11,145 @@ using ImGuiNET;
|
|||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Materials;
|
||||
|
||||
public sealed unsafe class AdvancedDyePopup(
|
||||
MainWindowPosition mainPosition,
|
||||
Configuration config,
|
||||
StateManager stateManager,
|
||||
ActorSelector actorSelector,
|
||||
MaterialDrawer materials,
|
||||
LiveColorTablePreviewer preview) : IService
|
||||
{
|
||||
private MaterialValueIndex? _drawIndex;
|
||||
private ActorIdentifier _identifier;
|
||||
private ActorState? _state;
|
||||
private ActorState _state = null!;
|
||||
private Actor _actor;
|
||||
private byte _selectedMaterial = byte.MaxValue;
|
||||
|
||||
private bool ShouldBeDrawn()
|
||||
{
|
||||
if (!mainPosition.IsOpen)
|
||||
return false;
|
||||
|
||||
if (!config.UseAdvancedDyes)
|
||||
return false;
|
||||
|
||||
if (config.Ephemeral.SelectedTab is not MainWindow.TabType.Actors)
|
||||
if (_drawIndex is not { Valid: true })
|
||||
return false;
|
||||
|
||||
if (!_drawIndex.HasValue)
|
||||
return false;
|
||||
|
||||
if (actorSelector.Selection.Identifier != _identifier || !_identifier.IsValid)
|
||||
return false;
|
||||
|
||||
if (_state == null)
|
||||
return false;
|
||||
|
||||
_actor = actorSelector.Selection.Data.Valid ? actorSelector.Selection.Data.Objects[0] : Actor.Null;
|
||||
if (!_actor.Valid || !_actor.Model.IsCharacterBase)
|
||||
if (!_actor.IsCharacter || !_state.ModelData.IsHuman || !_actor.Model.IsHuman)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DrawButton(ActorIdentifier identifier, ActorState state, MaterialValueIndex index)
|
||||
public void DrawButton(EquipSlot slot)
|
||||
=> DrawButton(MaterialValueIndex.FromSlot(slot));
|
||||
|
||||
private void DrawButton(MaterialValueIndex index)
|
||||
{
|
||||
if (!config.UseAdvancedDyes)
|
||||
return;
|
||||
|
||||
ImGui.SameLine();
|
||||
using var id = ImRaii.PushId(index.SlotIndex | ((int)index.DrawObject << 8));
|
||||
var isOpen = identifier == _identifier && state == _state && index == _drawIndex;
|
||||
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), isOpen))
|
||||
var isOpen = index == _drawIndex;
|
||||
|
||||
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), isOpen)
|
||||
.Push(ImGuiCol.Text, ColorId.HeaderButtons.Value(), isOpen)
|
||||
.Push(ImGuiCol.Border, ColorId.HeaderButtons.Value(), isOpen))
|
||||
{
|
||||
using var frame = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, isOpen);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Palette.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||
"Open advanced dyes for this slot.", false, true))
|
||||
string.Empty, false, true))
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
_drawIndex = null;
|
||||
_identifier = ActorIdentifier.Invalid;
|
||||
_state = null;
|
||||
_selectedMaterial = byte.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_drawIndex = index;
|
||||
_identifier = identifier;
|
||||
_state = state;
|
||||
_selectedMaterial = byte.MaxValue;
|
||||
}
|
||||
}
|
||||
_drawIndex = isOpen ? null : index;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Draw()
|
||||
{
|
||||
if (!ShouldBeDrawn())
|
||||
return;
|
||||
ImGuiUtil.HoverTooltip("Open advanced dyes for this slot.");
|
||||
}
|
||||
|
||||
var position = mainPosition.Position;
|
||||
position.X += mainPosition.Size.X;
|
||||
position.Y += ImGui.GetFrameHeightWithSpacing() * 3;
|
||||
var size = new Vector2(3 * ImGui.GetFrameHeight() + 300 * ImGuiHelpers.GlobalScale, 18.5f * ImGui.GetFrameHeightWithSpacing());
|
||||
ImGui.SetNextWindowPos(position);
|
||||
ImGui.SetNextWindowSize(size);
|
||||
var window = ImGui.Begin("###Glamourer Advanced Dyes",
|
||||
ImGuiWindowFlags.NoFocusOnAppearing
|
||||
| ImGuiWindowFlags.NoCollapse
|
||||
| ImGuiWindowFlags.NoDecoration
|
||||
| ImGuiWindowFlags.NoMove
|
||||
| ImGuiWindowFlags.NoResize);
|
||||
try
|
||||
{
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
private void DrawTabBar(ReadOnlySpan<Pointer<Texture>> textures, ref bool firstAvailable)
|
||||
{
|
||||
using var bar = ImRaii.TabBar("tabs");
|
||||
if (!bar)
|
||||
return;
|
||||
|
||||
var model = _actor.Model.AsCharacterBase;
|
||||
var firstAvailable = true;
|
||||
Span<byte> label = stackalloc byte[12];
|
||||
label[0] = (byte)'M';
|
||||
label[1] = (byte)'a';
|
||||
label[2] = (byte)'t';
|
||||
label[3] = (byte)'e';
|
||||
label[4] = (byte)'r';
|
||||
label[5] = (byte)'i';
|
||||
label[6] = (byte)'a';
|
||||
label[7] = (byte)'l';
|
||||
label[8] = (byte)' ';
|
||||
label[9] = (byte)'#';
|
||||
label[11] = 0;
|
||||
|
||||
for (byte i = 0; i < MaterialService.MaterialsPerModel; ++i)
|
||||
{
|
||||
var texture = model->ColorTableTextures + _drawIndex!.Value.SlotIndex * MaterialService.MaterialsPerModel + i;
|
||||
var index = _drawIndex!.Value with { MaterialIndex = i };
|
||||
var available = *texture != null && DirectXTextureHelper.TryGetColorTable(*texture, out var table);
|
||||
var available = index.TryGetTexture(textures, out var texture) && index.TryGetColorTable(texture, out var table);
|
||||
if (index == preview.LastValueIndex with { RowIndex = 0 })
|
||||
table = preview.LastOriginalColorTable;
|
||||
|
||||
using var disable = ImRaii.Disabled(!available);
|
||||
label[10] = (byte)('1' + i);
|
||||
var select = available && (_selectedMaterial == i || firstAvailable && _selectedMaterial == byte.MaxValue)
|
||||
var select = available && firstAvailable && _selectedMaterial == byte.MaxValue
|
||||
? ImGuiTabItemFlags.SetSelected
|
||||
: ImGuiTabItemFlags.None;
|
||||
|
||||
if (available)
|
||||
firstAvailable = false;
|
||||
if (select is ImGuiTabItemFlags.SetSelected)
|
||||
_selectedMaterial = i;
|
||||
|
||||
|
||||
fixed (byte* labelPtr = label)
|
||||
using var tab = _label.TabItem(i, select);
|
||||
if (!available)
|
||||
{
|
||||
using var tab = ImRaii.TabItem(labelPtr, select);
|
||||
if (tab.Success && available)
|
||||
using var disabled = ImRaii.Enabled();
|
||||
ImGuiUtil.HoverTooltip("This material does not exist or does not have an associated color set.",
|
||||
ImGuiHoveredFlags.AllowWhenDisabled);
|
||||
}
|
||||
|
||||
if ((tab.Success || select is ImGuiTabItemFlags.SetSelected) && available)
|
||||
{
|
||||
_selectedMaterial = i;
|
||||
DrawTable(index, table);
|
||||
}
|
||||
}
|
||||
|
||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||
{
|
||||
if (ImGui.TabItemButton($"{FontAwesomeIcon.Times.ToIconString()} ", ImGuiTabItemFlags.NoTooltip))
|
||||
_drawIndex = null;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip("Close the advanced dye window.");
|
||||
}
|
||||
|
||||
private void DrawContent(ReadOnlySpan<Pointer<Texture>> textures)
|
||||
{
|
||||
var firstAvailable = true;
|
||||
DrawTabBar(textures, ref firstAvailable);
|
||||
|
||||
if (firstAvailable)
|
||||
ImGui.TextUnformatted("No Editable Materials available.");
|
||||
}
|
||||
|
||||
private void DrawWindow(ReadOnlySpan<Pointer<Texture>> textures)
|
||||
{
|
||||
var flags = ImGuiWindowFlags.NoFocusOnAppearing
|
||||
| ImGuiWindowFlags.NoCollapse
|
||||
| ImGuiWindowFlags.NoDecoration
|
||||
| ImGuiWindowFlags.NoResize;
|
||||
|
||||
// Set position to the right of the main window when attached
|
||||
// The downwards offset is implicit through child position.
|
||||
if (config.KeepAdvancedDyesAttached)
|
||||
{
|
||||
var position = ImGui.GetWindowPos();
|
||||
position.X += ImGui.GetWindowSize().X;
|
||||
ImGui.SetNextWindowPos(position);
|
||||
flags |= ImGuiWindowFlags.NoMove;
|
||||
}
|
||||
|
||||
var size = new Vector2(7 * ImGui.GetFrameHeight() + 3 * ImGui.GetStyle().ItemInnerSpacing.X + 300 * ImGuiHelpers.GlobalScale,
|
||||
17f * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y);
|
||||
ImGui.SetNextWindowSize(size);
|
||||
|
||||
|
||||
var window = ImGui.Begin("###Glamourer Advanced Dyes", flags);
|
||||
try
|
||||
{
|
||||
if (window)
|
||||
DrawContent(textures);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -158,37 +157,55 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
}
|
||||
}
|
||||
|
||||
public unsafe void Draw(Actor actor, ActorState state)
|
||||
{
|
||||
_actor = actor;
|
||||
_state = state;
|
||||
if (!ShouldBeDrawn())
|
||||
return;
|
||||
|
||||
if (_drawIndex!.Value.TryGetTextures(actor, out var textures))
|
||||
DrawWindow(textures);
|
||||
}
|
||||
|
||||
private void DrawTable(MaterialValueIndex materialIndex, in MtrlFile.ColorTable table)
|
||||
{
|
||||
using var disabled = ImRaii.Disabled(_state.IsLocked);
|
||||
for (byte i = 0; i < MtrlFile.ColorTable.NumRows; ++i)
|
||||
{
|
||||
var index = materialIndex with { RowIndex = i };
|
||||
ref var row = ref table[i];
|
||||
DrawRow(ref row, CharacterWeapon.Empty, index, table);
|
||||
DrawRow(ref row, index, table);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRow(ref MtrlFile.ColorTable.Row row, CharacterWeapon drawData, MaterialValueIndex index, in MtrlFile.ColorTable table)
|
||||
private void DrawRow(ref MtrlFile.ColorTable.Row row, MaterialValueIndex index, in MtrlFile.ColorTable table)
|
||||
{
|
||||
using var id = ImRaii.PushId(index.RowIndex);
|
||||
var changed = _state!.Materials.TryGetValue(index, out var value);
|
||||
if (!changed)
|
||||
{
|
||||
var internalRow = new ColorRow(row);
|
||||
value = new MaterialValueState(internalRow, internalRow, drawData, StateSource.Manual);
|
||||
var slot = index.ToSlot();
|
||||
var weapon = slot is EquipSlot.MainHand or EquipSlot.OffHand
|
||||
? _state.ModelData.Weapon(slot)
|
||||
: _state.ModelData.Armor(slot).ToWeapon(0);
|
||||
value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual);
|
||||
}
|
||||
|
||||
var buttonSize = new Vector2(ImGui.GetFrameHeight());
|
||||
ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), buttonSize, "Highlight the affected colors on the character.",
|
||||
false, true);
|
||||
if (ImGui.IsItemHovered())
|
||||
preview.OnHover(index, _actor.Index, table);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGui.TextUnformatted($"Row {index.RowIndex + 1:D2}");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), new Vector2(ImGui.GetFrameHeight()), "Locate", false, true);
|
||||
if (ImGui.IsItemHovered())
|
||||
preview.OnHover(index, _actor.Index, table);
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemSpacing.X * 2);
|
||||
var applied = ImGuiUtil.ColorPicker("##diffuse", "Change the diffuse value for this row.", value.Model.Diffuse,
|
||||
v => value.Model.Diffuse = v, "D");
|
||||
|
|
@ -209,16 +226,54 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
applied |= ImGui.DragFloat("##Specular Strength", ref value.Model.SpecularStrength, 0.01f, float.MinValue, float.MaxValue, "%.3f SS");
|
||||
ImGuiUtil.HoverTooltip("Change the specular strength for this row.");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), buttonSize, "Export this row to your clipboard.", false,
|
||||
true))
|
||||
ColorRowClipboard.Row = value.Model;
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), buttonSize,
|
||||
"Import an exported row from your clipboard onto this row.", !ColorRowClipboard.IsSet, true))
|
||||
{
|
||||
value.Model = ColorRowClipboard.Row;
|
||||
applied = true;
|
||||
}
|
||||
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this row to game state.", !changed, true))
|
||||
stateManager.ResetMaterialValue(_state, index, ApplySettings.Game);
|
||||
|
||||
if (applied)
|
||||
stateManager.ChangeMaterialValue(_state!, index, value, ApplySettings.Manual);
|
||||
if (changed)
|
||||
}
|
||||
|
||||
private LabelStruct _label = new();
|
||||
|
||||
private struct LabelStruct
|
||||
{
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||
private fixed byte _label[12];
|
||||
|
||||
public ImRaii.IEndObject TabItem(byte materialIndex, ImGuiTabItemFlags flags)
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FavoriteStarOn.Value());
|
||||
ImGui.TextUnformatted(FontAwesomeIcon.UserEdit.ToIconString());
|
||||
}
|
||||
_label[10] = (byte)('1' + materialIndex);
|
||||
fixed (byte* ptr = _label)
|
||||
{
|
||||
return ImRaii.TabItem(ptr, flags | ImGuiTabItemFlags.NoTooltip);
|
||||
}
|
||||
}
|
||||
|
||||
public LabelStruct()
|
||||
{
|
||||
_label[0] = (byte)'M';
|
||||
_label[1] = (byte)'a';
|
||||
_label[2] = (byte)'t';
|
||||
_label[3] = (byte)'e';
|
||||
_label[4] = (byte)'r';
|
||||
_label[5] = (byte)'i';
|
||||
_label[6] = (byte)'a';
|
||||
_label[7] = (byte)'l';
|
||||
_label[8] = (byte)' ';
|
||||
_label[9] = (byte)'#';
|
||||
_label[11] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
20
Glamourer/Gui/Materials/ColorRowClipboard.cs
Normal file
20
Glamourer/Gui/Materials/ColorRowClipboard.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using Glamourer.Interop.Material;
|
||||
|
||||
namespace Glamourer.Gui.Materials;
|
||||
|
||||
public static class ColorRowClipboard
|
||||
{
|
||||
private static ColorRow _row;
|
||||
|
||||
public static bool IsSet { get; private set; }
|
||||
|
||||
public static ColorRow Row
|
||||
{
|
||||
get => _row;
|
||||
set
|
||||
{
|
||||
IsSet = true;
|
||||
_row = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,9 +32,10 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
if (!table)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupColumn("button", ImGuiTableColumnFlags.WidthFixed, buttonSize.X);
|
||||
ImGui.TableSetupColumn("buttons", ImGuiTableColumnFlags.WidthFixed, buttonSize.X * 3 + 2 * ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
ImGui.TableSetupColumn("enabled", ImGuiTableColumnFlags.WidthFixed, buttonSize.X);
|
||||
ImGui.TableSetupColumn("values", ImGuiTableColumnFlags.WidthFixed, ImGui.GetStyle().ItemInnerSpacing.X * 4 + 3 * buttonSize.X + 220 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("values", ImGuiTableColumnFlags.WidthFixed,
|
||||
ImGui.GetStyle().ItemInnerSpacing.X * 4 + 3 * buttonSize.X + 220 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("revert", ImGuiTableColumnFlags.WidthFixed, buttonSize.X + ImGui.CalcTextSize("Revertm").X);
|
||||
ImGui.TableSetupColumn("slot", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
|
|
@ -64,6 +65,17 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
--i;
|
||||
}
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), buttonSize, "Export this row to your clipboard.",
|
||||
false,
|
||||
true))
|
||||
ColorRowClipboard.Row = value.Value;
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), buttonSize,
|
||||
"Import an exported row from your clipboard onto this row.", !ColorRowClipboard.IsSet, true))
|
||||
_designManager.ChangeMaterialValue(design, key, ColorRowClipboard.Row);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var enabled = value.Enabled;
|
||||
if (ImGui.Checkbox("Enabled", ref enabled))
|
||||
|
|
@ -233,16 +245,20 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
}
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemSpacing.X * 2);
|
||||
var applied = ImGuiUtil.ColorPicker("##diffuse", "Change the diffuse value for this row.", value.Model.Diffuse, v => value.Model.Diffuse = v, "D");
|
||||
var applied = ImGuiUtil.ColorPicker("##diffuse", "Change the diffuse value for this row.", value.Model.Diffuse,
|
||||
v => value.Model.Diffuse = v, "D");
|
||||
|
||||
var spacing = ImGui.GetStyle().ItemInnerSpacing;
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
applied |= ImGuiUtil.ColorPicker("##specular", "Change the specular value for this row.", value.Model.Specular, v => value.Model.Specular = v, "S");
|
||||
applied |= ImGuiUtil.ColorPicker("##specular", "Change the specular value for this row.", value.Model.Specular,
|
||||
v => value.Model.Specular = v, "S");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
applied |= ImGuiUtil.ColorPicker("##emissive", "Change the emissive value for this row.", value.Model.Emissive, v => value.Model.Emissive = v, "E");
|
||||
applied |= ImGuiUtil.ColorPicker("##emissive", "Change the emissive value for this row.", value.Model.Emissive,
|
||||
v => value.Model.Emissive = v, "E");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
applied |= ImGui.DragFloat("##Gloss", ref value.Model.GlossStrength, 0.01f, 0.001f, float.MaxValue, "%.3f G") && value.Model.GlossStrength > 0;
|
||||
applied |= ImGui.DragFloat("##Gloss", ref value.Model.GlossStrength, 0.01f, 0.001f, float.MaxValue, "%.3f G")
|
||||
&& value.Model.GlossStrength > 0;
|
||||
ImGuiUtil.HoverTooltip("Change the gloss strength for this row.");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ public class ActorPanel(
|
|||
ICondition _conditions,
|
||||
DictModelChara _modelChara,
|
||||
CustomizeParameterDrawer _parameterDrawer,
|
||||
MaterialDrawer _materialDrawer)
|
||||
MaterialDrawer _materialDrawer,
|
||||
AdvancedDyePopup _advancedDyes)
|
||||
{
|
||||
private ActorIdentifier _identifier;
|
||||
private string _actorName = string.Empty;
|
||||
|
|
@ -117,13 +118,12 @@ public class ActorPanel(
|
|||
|
||||
RevertButtons();
|
||||
|
||||
if (_config.UseAdvancedDyes && ImGui.CollapsingHeader("Material Shit"))
|
||||
_materialDrawer.DrawActorPanel(_actor);
|
||||
using var disabled = ImRaii.Disabled(transformationId != 0);
|
||||
if (_state.ModelData.IsHuman)
|
||||
DrawHumanPanel();
|
||||
else
|
||||
DrawMonsterPanel();
|
||||
_advancedDyes.Draw(_actor, _state);
|
||||
}
|
||||
|
||||
private void DrawHumanPanel()
|
||||
|
|
|
|||
|
|
@ -189,6 +189,11 @@ public class SettingsTab(
|
|||
PaletteImportButton();
|
||||
}
|
||||
|
||||
if (config.UseAdvancedDyes)
|
||||
Checkbox("Keep Advanced Dye Window Attached",
|
||||
"Keeps the advanced dye window expansion attached to the main window, or makes it freely movable.",
|
||||
config.KeepAdvancedDyesAttached, v => config.KeepAdvancedDyesAttached = v);
|
||||
|
||||
Checkbox("Debug Mode", "Show the debug tab. Only useful for debugging or advanced use. Not recommended in general.", config.DebugMode,
|
||||
v => config.DebugMode = v);
|
||||
ImGui.NewLine();
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable
|
|||
|
||||
if (_valueIndex.TryGetTexture(actor, out var texture))
|
||||
{
|
||||
Glamourer.Log.Information($"Set {_objectIndex} {_valueIndex}");
|
||||
var diffuse = CalculateDiffuse();
|
||||
var table = LastOriginalColorTable;
|
||||
table[_valueIndex.RowIndex].Diffuse = diffuse;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
}
|
||||
|
||||
foreach (var idx in deleteList)
|
||||
_stateManager.ChangeMaterialValue(state, idx, default, ApplySettings.Game);
|
||||
_stateManager.ResetMaterialValue(state, idx, ApplySettings.Game);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public readonly record struct MaterialValueIndex(
|
|||
byte RowIndex)
|
||||
{
|
||||
public static readonly MaterialValueIndex Invalid = new(DrawObjectType.Invalid, 0, 0, 0);
|
||||
|
||||
public uint Key
|
||||
=> ToKey(DrawObject, SlotIndex, MaterialIndex, RowIndex);
|
||||
|
||||
|
|
@ -27,6 +28,29 @@ public readonly record struct MaterialValueIndex(
|
|||
return index.Valid;
|
||||
}
|
||||
|
||||
public static MaterialValueIndex FromSlot(EquipSlot slot)
|
||||
{
|
||||
if (slot is EquipSlot.MainHand)
|
||||
return new MaterialValueIndex(DrawObjectType.Mainhand, 0, 0, 0);
|
||||
if (slot is EquipSlot.OffHand)
|
||||
return new MaterialValueIndex(DrawObjectType.Offhand, 0, 0, 0);
|
||||
|
||||
var idx = slot.ToIndex();
|
||||
if (idx < 10)
|
||||
return new MaterialValueIndex(DrawObjectType.Human, (byte)idx, 0, 0);
|
||||
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
public EquipSlot ToSlot()
|
||||
=> DrawObject switch
|
||||
{
|
||||
DrawObjectType.Human when SlotIndex < 10 => ((uint)SlotIndex).ToEquipSlot(),
|
||||
DrawObjectType.Mainhand when SlotIndex == 0 => EquipSlot.MainHand,
|
||||
DrawObjectType.Offhand when SlotIndex == 0 => EquipSlot.OffHand,
|
||||
_ => EquipSlot.Unknown,
|
||||
};
|
||||
|
||||
public unsafe bool TryGetModel(Actor actor, out Model model)
|
||||
{
|
||||
if (!actor.Valid)
|
||||
|
|
@ -154,14 +178,12 @@ public readonly record struct MaterialValueIndex(
|
|||
{ }
|
||||
|
||||
public override string ToString()
|
||||
=> DrawObject switch
|
||||
{
|
||||
DrawObjectType.Human when SlotIndex < 10 =>
|
||||
$"{((uint)SlotIndex).ToEquipSlot().ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
||||
DrawObjectType.Mainhand when SlotIndex == 0 => $"{EquipSlot.MainHand.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
||||
DrawObjectType.Offhand when SlotIndex == 0 => $"{EquipSlot.OffHand.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
||||
_ => $"{DrawObject} Slot {SlotIndex} Material #{MaterialIndex + 1} Row #{RowIndex + 1}",
|
||||
};
|
||||
var slot = ToSlot();
|
||||
return slot is EquipSlot.Unknown
|
||||
? $"{DrawObject} Slot {SlotIndex} Material #{MaterialIndex + 1} Row #{RowIndex + 1}"
|
||||
: $"{slot.ToName()} Material #{MaterialIndex + 1} Row #{RowIndex + 1}";
|
||||
}
|
||||
|
||||
private class Converter : JsonConverter<MaterialValueIndex>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -222,7 +222,8 @@ public class InternalStateEditor(
|
|||
}
|
||||
|
||||
/// <summary> Change the value of a single material color table entry. </summary>
|
||||
public bool ChangeMaterialValue(ActorState state, MaterialValueIndex index, in MaterialValueState newValue, StateSource source, out ColorRow? oldValue,
|
||||
public bool ChangeMaterialValue(ActorState state, MaterialValueIndex index, in MaterialValueState newValue, StateSource source,
|
||||
out ColorRow? oldValue,
|
||||
uint key = 0)
|
||||
{
|
||||
// We already have an existing value.
|
||||
|
|
@ -254,6 +255,10 @@ public class InternalStateEditor(
|
|||
return state.Materials.TryAddValue(index, newValue);
|
||||
}
|
||||
|
||||
/// <summary> Reset the value of a single material color table entry. </summary>
|
||||
public bool ResetMaterialValue(ActorState state, MaterialValueIndex index, uint key = 0)
|
||||
=> state.CanUnlock(key) && state.Materials.RemoveValue(index);
|
||||
|
||||
public bool ChangeMetaState(ActorState state, MetaIndex index, bool value, StateSource source, out bool oldValue,
|
||||
uint key = 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -177,6 +177,18 @@ public class StateEditor(
|
|||
StateChanged.Invoke(StateChanged.Type.MaterialValue, settings.Source, state, actors, (oldValue, newValue.Game, index));
|
||||
}
|
||||
|
||||
public void ResetMaterialValue(object data, MaterialValueIndex index, ApplySettings settings)
|
||||
{
|
||||
var state = (ActorState)data;
|
||||
if (!Editor.ResetMaterialValue(state, index, settings.Key))
|
||||
return;
|
||||
|
||||
var actors = Applier.ChangeMaterialValue(state, index, true);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Reset material value in state {state.Identifier.Incognito(null)} to game value. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChanged.Type.MaterialValue, settings.Source, state, actors, index);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeMetaState(object data, MetaIndex index, bool value, ApplySettings settings)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue