Further improvements.

This commit is contained in:
Ottermandias 2024-02-16 17:48:40 +01:00
parent 5f74f4b4d9
commit 59529476eb
15 changed files with 342 additions and 161 deletions

View file

@ -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;

View file

@ -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>

View file

@ -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);
}
}
}

View file

@ -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()

View file

@ -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();
}

View file

@ -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;
}
}
}

View 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;
}
}
}

View file

@ -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);

View file

@ -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()

View file

@ -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();

View file

@ -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;

View file

@ -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>

View file

@ -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>
{

View file

@ -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)
{

View file

@ -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)
{