mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Improve advanced dye stuff.
This commit is contained in:
parent
a194f88903
commit
10962cac6c
16 changed files with 342 additions and 113 deletions
|
|
@ -35,6 +35,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool ShowQuickBarInTabs { get; set; } = true;
|
||||
public bool OpenWindowAtStart { get; set; } = false;
|
||||
public bool UseAdvancedParameters { get; set; } = true;
|
||||
public bool UseAdvancedDyes { get; set; } = false;
|
||||
public bool ShowRevertAdvancedParametersButton { get; set; } = true;
|
||||
public bool ShowPalettePlusImport { get; set; } = true;
|
||||
public bool UseFloatForColors { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -219,6 +219,68 @@ public class DesignEditor(
|
|||
DesignChanged.Invoke(DesignChanged.Type.Other, design, (metaIndex, false, value));
|
||||
}
|
||||
|
||||
public void ChangeMaterialRevert(Design design, MaterialValueIndex index, bool revert)
|
||||
{
|
||||
var materials = design.GetMaterialDataRef();
|
||||
if (!materials.TryGetValue(index, out var oldValue))
|
||||
return;
|
||||
|
||||
materials.AddOrUpdateValue(index, oldValue with { Revert = revert });
|
||||
Glamourer.Log.Debug($"Changed advanced dye value for {index} to {(revert ? "Revert." : "no longer Revert.")}");
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.MaterialRevert, design, index);
|
||||
}
|
||||
|
||||
public void ChangeMaterialValue(Design design, MaterialValueIndex index, ColorRow? row)
|
||||
{
|
||||
var materials = design.GetMaterialDataRef();
|
||||
if (materials.TryGetValue(index, out var oldValue))
|
||||
{
|
||||
if (!row.HasValue)
|
||||
{
|
||||
materials.RemoveValue(index);
|
||||
Glamourer.Log.Debug($"Removed advanced dye value for {index}.");
|
||||
}
|
||||
else if (!row.Value.NearEqual(oldValue.Value))
|
||||
{
|
||||
materials.UpdateValue(index, new MaterialValueDesign(row.Value, oldValue.Enabled, oldValue.Revert), out _);
|
||||
Glamourer.Log.Debug($"Updated advanced dye value for {index} to new value.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!row.HasValue)
|
||||
return;
|
||||
if (!materials.TryAddValue(index, new MaterialValueDesign(row.Value, true, false)))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Added new advanced dye value for {index}.");
|
||||
}
|
||||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.DelaySave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Material, design, (oldValue, row, index));
|
||||
}
|
||||
|
||||
public void ChangeApplyMaterialValue(Design design, MaterialValueIndex index, bool value)
|
||||
{
|
||||
var materials = design.GetMaterialDataRef();
|
||||
if (!materials.TryGetValue(index, out var oldValue) || oldValue.Enabled == value)
|
||||
return;
|
||||
|
||||
materials.AddOrUpdateValue(index, oldValue with { Enabled = value });
|
||||
Glamourer.Log.Debug($"Changed application of advanced dye for {index} to {value}.");
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyMaterial, design, index);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ApplyDesign(object data, MergedDesign other, ApplySettings _ = default)
|
||||
=> ApplyDesign(data, other.Design);
|
||||
|
|
@ -262,7 +324,8 @@ public class DesignEditor(
|
|||
}
|
||||
|
||||
/// <summary> Change a mainhand weapon and either fix or apply appropriate offhand and potentially gauntlets. </summary>
|
||||
private bool ChangeMainhandPeriphery(DesignBase design, EquipItem currentMain, EquipItem currentOff, EquipItem newMain, out EquipItem? newOff,
|
||||
private bool ChangeMainhandPeriphery(DesignBase design, EquipItem currentMain, EquipItem currentOff, EquipItem newMain,
|
||||
out EquipItem? newOff,
|
||||
out EquipItem? newGauntlets)
|
||||
{
|
||||
newOff = null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Unlocks;
|
||||
|
|
@ -46,6 +47,7 @@ public class DesignMerger(
|
|||
ReduceCrests(data, crestFlags, ret, source);
|
||||
ReduceParameters(data, parameterFlags, ret, source);
|
||||
ReduceMods(design as Design, ret, modAssociations);
|
||||
ReduceMaterials(design, ret);
|
||||
}
|
||||
|
||||
ApplyFixFlags(ret, fixFlags);
|
||||
|
|
@ -53,6 +55,15 @@ public class DesignMerger(
|
|||
}
|
||||
|
||||
|
||||
private static void ReduceMaterials(DesignBase? design, MergedDesign ret)
|
||||
{
|
||||
if (design == null)
|
||||
return;
|
||||
var materials = ret.Design.GetMaterialDataRef();
|
||||
foreach (var (key, value) in design.Materials.Where(p => p.Item2.Enabled))
|
||||
materials.TryAddValue(MaterialValueIndex.FromKey(key), value);
|
||||
}
|
||||
|
||||
private static void ReduceMods(Design? design, MergedDesign ret, bool modAssociations)
|
||||
{
|
||||
if (design == null || !modAssociations)
|
||||
|
|
|
|||
|
|
@ -74,10 +74,16 @@ public sealed class DesignChanged()
|
|||
/// <summary> An existing design had a customize parameter changed. Data is the old value, the new value and the flag [(CustomizeParameterValue, CustomizeParameterValue, CustomizeParameterFlag)]. </summary>
|
||||
Parameter,
|
||||
|
||||
/// <summary> An existing design had an advanced dye row added, changed, or deleted. Data is the old value, the new value and the index [(ColorRow?, ColorRow?, MaterialValueIndex)]. </summary>
|
||||
Material,
|
||||
|
||||
/// <summary> An existing design had an advanced dye rows Revert state changed. Data is the index [MaterialValueIndex]. </summary>
|
||||
MaterialRevert,
|
||||
|
||||
/// <summary> An existing design changed whether a specific customization is applied. Data is the type of customization [CustomizeIndex]. </summary>
|
||||
ApplyCustomize,
|
||||
|
||||
/// <summary> An existing design changed whether a specific equipment is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||
/// <summary> An existing design changed whether a specific equipment piece is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||
ApplyEquip,
|
||||
|
||||
/// <summary> An existing design changed whether a specific stain is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||
|
|
@ -89,6 +95,9 @@ public sealed class DesignChanged()
|
|||
/// <summary> An existing design changed whether a specific customize parameter is applied. Data is the flag for the parameter [CustomizeParameterFlag]. </summary>
|
||||
ApplyParameter,
|
||||
|
||||
/// <summary> An existing design changed whether an advanced dye row is applied. Data is the index [MaterialValueIndex]. </summary>
|
||||
ApplyMaterial,
|
||||
|
||||
/// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary>
|
||||
WriteProtection,
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public partial class CustomizationDrawer
|
|||
{
|
||||
var size = ImGui.GetItemRectSize();
|
||||
ImGui.GetWindowDrawList()
|
||||
.AddCircleFilled(ImGui.GetItemRectMin() + size / 2, size.X / 4, ImGuiUtil.ContrastColorBW(custom.Color));
|
||||
.AddCircleFilled(ImGui.GetItemRectMin() + size / 2, size.X / 4, ImGuiUtil.ContrastColorBw(custom.Color));
|
||||
}
|
||||
|
||||
if (i % 8 != 7)
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
|
||||
{
|
||||
available |= 1;
|
||||
tooltip = "Left-Click: Revert the advanced customizations of the player character to their game state.";
|
||||
tooltip = "Left-Click: Revert the advanced customizations and dyes of the player character to their game state.";
|
||||
}
|
||||
|
||||
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
|
||||
|
|
@ -248,7 +248,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
if (available != 0)
|
||||
tooltip += '\n';
|
||||
available |= 2;
|
||||
tooltip += $"Right-Click: Revert the advanced customizations of {_targetIdentifier} to their game state.";
|
||||
tooltip += $"Right-Click: Revert the advanced customizations and dyes of {_targetIdentifier} to their game state.";
|
||||
}
|
||||
|
||||
if (available == 0)
|
||||
|
|
|
|||
|
|
@ -10,20 +10,135 @@ using OtterGui;
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Gui;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Vortice.DXGI;
|
||||
|
||||
namespace Glamourer.Gui.Materials;
|
||||
|
||||
public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _designManager) : IService
|
||||
public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _designManager, Configuration _config) : IService
|
||||
{
|
||||
private static readonly IReadOnlyList<MaterialValueIndex.DrawObjectType> Types =
|
||||
[
|
||||
MaterialValueIndex.DrawObjectType.Human,
|
||||
MaterialValueIndex.DrawObjectType.Mainhand,
|
||||
MaterialValueIndex.DrawObjectType.Offhand,
|
||||
];
|
||||
|
||||
private ActorState? _state;
|
||||
private EquipSlot _newSlot = EquipSlot.Head;
|
||||
private int _newMaterialIdx;
|
||||
private int _newRowIdx;
|
||||
private MaterialValueIndex _newKey = MaterialValueIndex.Min();
|
||||
|
||||
public void DrawDesignPanel(Design design)
|
||||
{
|
||||
var buttonSize = new Vector2(ImGui.GetFrameHeight());
|
||||
using (var table = ImRaii.Table("table", 5, ImGuiTableFlags.RowBg))
|
||||
{
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
|
||||
ImGui.TableSetupColumn("button", ImGuiTableColumnFlags.WidthFixed, buttonSize.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("revert", ImGuiTableColumnFlags.WidthFixed, buttonSize.X + ImGui.CalcTextSize("Revertm").X);
|
||||
ImGui.TableSetupColumn("slot", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
|
||||
|
||||
for (var i = 0; i < design.Materials.Count; ++i)
|
||||
{
|
||||
var (idx, value) = design.Materials[i];
|
||||
var key = MaterialValueIndex.FromKey(idx);
|
||||
var name = key.DrawObject switch
|
||||
{
|
||||
MaterialValueIndex.DrawObjectType.Human => ((uint)key.SlotIndex).ToEquipSlot().ToName(),
|
||||
MaterialValueIndex.DrawObjectType.Mainhand => EquipSlot.MainHand.ToName(),
|
||||
MaterialValueIndex.DrawObjectType.Offhand => EquipSlot.OffHand.ToName(),
|
||||
_ => string.Empty,
|
||||
};
|
||||
if (name.Length == 0)
|
||||
continue;
|
||||
|
||||
name = $"{name} Material #{key.MaterialIndex + 1} Row #{key.RowIndex + 1}";
|
||||
using var id = ImRaii.PushId((int)idx);
|
||||
var deleteEnabled = _config.DeleteDesignModifier.IsActive();
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize,
|
||||
$"Delete this color row.{(deleteEnabled ? string.Empty : $"\nHold {_config.DeleteDesignModifier} to delete.")}",
|
||||
!deleteEnabled, true))
|
||||
{
|
||||
_designManager.ChangeMaterialValue(design, key, null);
|
||||
--i;
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var enabled = value.Enabled;
|
||||
if (ImGui.Checkbox("Enabled", ref enabled))
|
||||
_designManager.ChangeApplyMaterialValue(design, key, enabled);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var revert = value.Revert;
|
||||
using (ImRaii.Disabled(revert))
|
||||
{
|
||||
var row = value.Value;
|
||||
DrawRow(design, key, row);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
if (ImGui.Checkbox("Revert", ref revert))
|
||||
_designManager.ChangeMaterialRevert(design, key, revert);
|
||||
ImGuiUtil.HoverTooltip(
|
||||
"If this is checked, Glamourer will try to revert the advanced dye row to its game state instead of applying a specific row.");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(name);
|
||||
}
|
||||
}
|
||||
|
||||
var exists = design.GetMaterialDataRef().TryGetValue(_newKey, out _);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), buttonSize,
|
||||
exists ? "The selected advanced dye row already exists." : "Add the selected advanced dye row.", exists, true))
|
||||
_designManager.ChangeMaterialValue(design, _newKey, ColorRow.Empty);
|
||||
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
if (EquipSlotCombo.Draw("##slot", "Choose a slot for an advanced dye row.", ref _newSlot))
|
||||
_newKey = _newSlot switch
|
||||
{
|
||||
EquipSlot.MainHand => new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Mainhand, 0, (byte)_newMaterialIdx,
|
||||
(byte)_newRowIdx),
|
||||
EquipSlot.OffHand => new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Offhand, 0, (byte)_newMaterialIdx,
|
||||
(byte)_newRowIdx),
|
||||
_ => new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Human, (byte)_newSlot.ToIndex(), (byte)_newMaterialIdx,
|
||||
(byte)_newRowIdx),
|
||||
};
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
DrawMaterialIdxDrag();
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
DrawRowIdxDrag();
|
||||
}
|
||||
|
||||
private void DrawMaterialIdxDrag()
|
||||
{
|
||||
_newMaterialIdx += 1;
|
||||
ImGui.SetNextItemWidth(ImGui.CalcTextSize("Material #000").X);
|
||||
if (ImGui.DragInt("##Material", ref _newMaterialIdx, 0.01f, 1, MaterialService.MaterialsPerModel, "Material #%i"))
|
||||
{
|
||||
_newMaterialIdx = Math.Clamp(_newMaterialIdx, 1, MaterialService.MaterialsPerModel);
|
||||
_newKey = _newKey with { MaterialIndex = (byte)(_newMaterialIdx - 1) };
|
||||
}
|
||||
|
||||
_newMaterialIdx -= 1;
|
||||
}
|
||||
|
||||
private void DrawRowIdxDrag()
|
||||
{
|
||||
_newRowIdx += 1;
|
||||
ImGui.SetNextItemWidth(ImGui.CalcTextSize("Row #0000").X);
|
||||
if (ImGui.DragInt("##Row", ref _newRowIdx, 0.01f, 1, MtrlFile.ColorTable.NumRows, "Row #%i"))
|
||||
{
|
||||
_newRowIdx = Math.Clamp(_newRowIdx, 1, MtrlFile.ColorTable.NumRows);
|
||||
_newKey = _newKey with { RowIndex = (byte)(_newRowIdx - 1) };
|
||||
}
|
||||
|
||||
_newRowIdx -= 1;
|
||||
}
|
||||
|
||||
public void DrawActorPanel(Actor actor)
|
||||
{
|
||||
|
|
@ -41,20 +156,21 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
|
||||
{
|
||||
var item = model.GetArmor(slot).ToWeapon(0);
|
||||
DrawSlotMaterials(model, slot.ToName(), item, new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Human, (byte) idx, 0, 0));
|
||||
DrawSlotMaterials(model, slot.ToName(), item, new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Human, (byte)idx, 0, 0));
|
||||
}
|
||||
|
||||
var (mainhand, offhand, mh, oh) = actor.Model.GetWeapons(actor);
|
||||
if (mainhand.IsWeapon && mainhand.AsCharacterBase->SlotCount > 0)
|
||||
DrawSlotMaterials(mainhand, EquipSlot.MainHand.ToName(), mh, new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Mainhand, 0, 0, 0));
|
||||
DrawSlotMaterials(mainhand, EquipSlot.MainHand.ToName(), mh,
|
||||
new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Mainhand, 0, 0, 0));
|
||||
if (offhand.IsWeapon && offhand.AsCharacterBase->SlotCount > 0)
|
||||
DrawSlotMaterials(offhand, EquipSlot.OffHand.ToName(), oh, new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Offhand, 0, 0, 0));
|
||||
DrawSlotMaterials(offhand, EquipSlot.OffHand.ToName(), oh,
|
||||
new MaterialValueIndex(MaterialValueIndex.DrawObjectType.Offhand, 0, 0, 0));
|
||||
}
|
||||
|
||||
|
||||
private void DrawSlotMaterials(Model model, string name, CharacterWeapon drawData, MaterialValueIndex index)
|
||||
{
|
||||
var drawnMaterial = 1;
|
||||
for (byte materialIndex = 0; materialIndex < MaterialService.MaterialsPerModel; ++materialIndex)
|
||||
{
|
||||
var texture = model.AsCharacterBase->ColorTableTextures + index.SlotIndex * MaterialService.MaterialsPerModel + materialIndex;
|
||||
|
|
@ -64,11 +180,11 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
if (!DirectXTextureHelper.TryGetColorTable(*texture, out var table))
|
||||
continue;
|
||||
|
||||
using var tree = ImRaii.TreeNode($"{name} Material #{drawnMaterial++}###{name}{materialIndex}");
|
||||
using var tree = ImRaii.TreeNode($"{name} Material #{materialIndex + 1}###{name}{materialIndex}");
|
||||
if (!tree)
|
||||
continue;
|
||||
|
||||
DrawMaterial(ref table, drawData, index with { MaterialIndex = materialIndex} );
|
||||
DrawMaterial(ref table, drawData, index with { MaterialIndex = materialIndex });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +198,27 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawRow(Design design, MaterialValueIndex index, in ColorRow row)
|
||||
{
|
||||
var spacing = ImGui.GetStyle().ItemInnerSpacing;
|
||||
var tmp = row;
|
||||
var applied = ImGuiUtil.ColorPicker("##diffuse", "Change the diffuse value for this row.", row.Diffuse, v => tmp.Diffuse = v, "D");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
applied |= ImGuiUtil.ColorPicker("##specular", "Change the specular value for this row.", row.Specular, v => tmp.Specular = v, "S");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
applied |= ImGuiUtil.ColorPicker("##emissive", "Change the emissive value for this row.", row.Emissive, v => tmp.Emissive = v, "E");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
applied |= ImGui.DragFloat("##Gloss", ref tmp.GlossStrength, 0.01f, 0.001f, float.MaxValue, "%.3f G");
|
||||
ImGuiUtil.HoverTooltip("Change the gloss strength for this row.");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
ImGui.SetNextItemWidth(120 * ImGuiHelpers.GlobalScale);
|
||||
applied |= ImGui.DragFloat("##Specular Strength", ref tmp.SpecularStrength, 0.01f, float.MinValue, float.MaxValue, "%.3f SS");
|
||||
ImGuiUtil.HoverTooltip("Change the specular strength for this row.");
|
||||
if (applied)
|
||||
_designManager.ChangeMaterialValue(design, index, tmp);
|
||||
}
|
||||
|
||||
private void DrawRow(ref MtrlFile.ColorTable.Row row, CharacterWeapon drawData, MaterialValueIndex index)
|
||||
{
|
||||
using var id = ImRaii.PushId(index.RowIndex);
|
||||
|
|
@ -92,22 +229,33 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
value = new MaterialValueState(internalRow, internalRow, drawData, StateSource.Manual);
|
||||
}
|
||||
|
||||
var applied = ImGui.ColorEdit3("Diffuse", ref value.Model.Diffuse, ImGuiColorEditFlags.NoInputs);
|
||||
ImGui.SameLine();
|
||||
applied |= ImGui.ColorEdit3("Specular", ref value.Model.Specular, ImGuiColorEditFlags.NoInputs);
|
||||
ImGui.SameLine();
|
||||
applied |= ImGui.ColorEdit3("Emissive", ref value.Model.Emissive, ImGuiColorEditFlags.NoInputs);
|
||||
ImGui.SameLine();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||
{
|
||||
ImGui.TextUnformatted($"Row {index.RowIndex + 1:D2}");
|
||||
}
|
||||
|
||||
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 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");
|
||||
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");
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||
applied |= ImGui.DragFloat("Gloss", ref value.Model.GlossStrength, 0.1f);
|
||||
ImGui.SameLine();
|
||||
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);
|
||||
applied |= ImGui.DragFloat("Specular Strength", ref value.Model.SpecularStrength, 0.1f);
|
||||
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.");
|
||||
if (applied)
|
||||
_stateManager.ChangeMaterialValue(_state!, index, value, ApplySettings.Manual);
|
||||
if (changed)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.SameLine(0, spacing.X);
|
||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FavoriteStarOn.Value());
|
||||
|
|
@ -115,52 +263,4 @@ public unsafe class MaterialDrawer(StateManager _stateManager, DesignManager _de
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyList<string> SlotNames =
|
||||
[
|
||||
"Slot 1",
|
||||
"Slot 2",
|
||||
"Slot 3",
|
||||
"Slot 4",
|
||||
"Slot 5",
|
||||
"Slot 6",
|
||||
"Slot 7",
|
||||
"Slot 8",
|
||||
"Slot 9",
|
||||
"Slot 10",
|
||||
"Slot 11",
|
||||
"Slot 12",
|
||||
"Slot 13",
|
||||
"Slot 14",
|
||||
"Slot 15",
|
||||
"Slot 16",
|
||||
"Slot 17",
|
||||
"Slot 18",
|
||||
"Slot 19",
|
||||
"Slot 20",
|
||||
];
|
||||
|
||||
private static readonly IReadOnlyList<string> SlotNamesHuman =
|
||||
[
|
||||
"Head",
|
||||
"Body",
|
||||
"Hands",
|
||||
"Legs",
|
||||
"Feet",
|
||||
"Earrings",
|
||||
"Neck",
|
||||
"Wrists",
|
||||
"Right Finger",
|
||||
"Left Finger",
|
||||
"Slot 11",
|
||||
"Slot 12",
|
||||
"Slot 13",
|
||||
"Slot 14",
|
||||
"Slot 15",
|
||||
"Slot 16",
|
||||
"Slot 17",
|
||||
"Slot 18",
|
||||
"Slot 19",
|
||||
"Slot 20",
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,9 +117,8 @@ public class ActorPanel(
|
|||
|
||||
RevertButtons();
|
||||
|
||||
// TODO Materials
|
||||
//if (ImGui.CollapsingHeader("Material Shit"))
|
||||
// _materialDrawer.DrawActorPanel(_actor);
|
||||
if (_config.UseAdvancedDyes && ImGui.CollapsingHeader("Material Shit"))
|
||||
_materialDrawer.DrawActorPanel(_actor);
|
||||
using var disabled = ImRaii.Disabled(transformationId != 0);
|
||||
if (_state.ModelData.IsHuman)
|
||||
DrawHumanPanel();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Glamourer.Designs;
|
|||
using Glamourer.GameData;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Gui.Materials;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
|
|
@ -31,7 +32,8 @@ public class DesignPanel(
|
|||
ImportService _importService,
|
||||
MultiDesignPanel _multiDesignPanel,
|
||||
CustomizeParameterDrawer _parameterDrawer,
|
||||
DesignLinkDrawer _designLinkDrawer)
|
||||
DesignLinkDrawer _designLinkDrawer,
|
||||
MaterialDrawer _materials)
|
||||
{
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
|
||||
|
|
@ -176,21 +178,14 @@ public class DesignPanel(
|
|||
|
||||
private void DrawMaterialValues()
|
||||
{
|
||||
if (!_config.UseAdvancedParameters)
|
||||
if (!_config.UseAdvancedDyes)
|
||||
return;
|
||||
|
||||
using var h = ImRaii.CollapsingHeader("Advanced Dyes");
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
foreach (var ((key, value), i) in _selector.Selected!.Materials.WithIndex())
|
||||
{
|
||||
using var id = ImRaii.PushId(i);
|
||||
ImGui.TextUnformatted($"{key:X16}");
|
||||
ImGui.SameLine();
|
||||
var enabled = value.Enabled;
|
||||
ImGui.Checkbox("Enabled", ref enabled);
|
||||
}
|
||||
_materials.DrawDesignPanel(_selector.Selected!);
|
||||
}
|
||||
|
||||
private void DrawCustomizeApplication()
|
||||
|
|
@ -384,7 +379,7 @@ public class DesignPanel(
|
|||
DrawCustomize();
|
||||
DrawEquipment();
|
||||
DrawCustomizeParameters();
|
||||
//DrawMaterialValues(); TODO Materials
|
||||
DrawMaterialValues();
|
||||
_designDetails.Draw();
|
||||
DrawApplicationRules();
|
||||
_modAssociations.Draw();
|
||||
|
|
|
|||
|
|
@ -18,20 +18,21 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
private readonly StateManager _stateManager;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly ActorManager _actors;
|
||||
private readonly Configuration _config;
|
||||
|
||||
private int _lastSlot;
|
||||
|
||||
private readonly ThreadLocal<List<MaterialValueIndex>> _deleteList = new(() => []);
|
||||
|
||||
public MaterialManager(PrepareColorSet prepareColorSet, StateManager stateManager, ActorManager actors, PenumbraService penumbra)
|
||||
public MaterialManager(PrepareColorSet prepareColorSet, StateManager stateManager, ActorManager actors, PenumbraService penumbra,
|
||||
Configuration config)
|
||||
{
|
||||
_stateManager = stateManager;
|
||||
_actors = actors;
|
||||
_penumbra = penumbra;
|
||||
_config = config;
|
||||
_event = prepareColorSet;
|
||||
|
||||
// TODO Material
|
||||
//_event.Subscribe(OnPrepareColorSet, PrepareColorSet.Priority.MaterialManager);
|
||||
_event.Subscribe(OnPrepareColorSet, PrepareColorSet.Priority.MaterialManager);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -39,6 +40,9 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
|
||||
private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainId stain, ref nint ret)
|
||||
{
|
||||
if (!_config.UseAdvancedDyes)
|
||||
return;
|
||||
|
||||
var actor = _penumbra.GameObjectFromDrawObject(characterBase);
|
||||
var validType = FindType(characterBase, actor, out var type);
|
||||
var (slotId, materialId) = FindMaterial(characterBase, material);
|
||||
|
|
@ -176,7 +180,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
}
|
||||
|
||||
/// <summary> We need to get the temporary set, variant and stain that is currently being set if it is available. </summary>
|
||||
private CharacterWeapon GetTempSlot(Human* human, byte slotId)
|
||||
private static CharacterWeapon GetTempSlot(Human* human, byte slotId)
|
||||
{
|
||||
if (human->ChangedEquipData == null)
|
||||
return ((Model)human).GetArmor(((uint)slotId).ToEquipSlot()).ToWeapon(0);
|
||||
|
|
@ -188,7 +192,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
/// We need to get the temporary set, variant and stain that is currently being set if it is available.
|
||||
/// Weapons do not change in skeleton id without being reconstructed, so this is not changeable data.
|
||||
/// </summary>
|
||||
private CharacterWeapon GetTempSlot(Weapon* weapon)
|
||||
private static CharacterWeapon GetTempSlot(Weapon* weapon)
|
||||
{
|
||||
var changedData = *(void**)((byte*)weapon + 0x918);
|
||||
if (changedData == null)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using FFXIVClientStructs.Interop;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Newtonsoft.Json;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
|
||||
namespace Glamourer.Interop.Material;
|
||||
|
|
@ -150,6 +151,16 @@ public readonly record struct MaterialValueIndex(
|
|||
: this((DrawObjectType)(key >> 24), (byte)(key >> 16), (byte)(key >> 8), (byte)key)
|
||||
{ }
|
||||
|
||||
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}",
|
||||
};
|
||||
|
||||
private class Converter : JsonConverter<MaterialValueIndex>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, MaterialValueIndex value, JsonSerializer serializer)
|
||||
|
|
|
|||
|
|
@ -126,16 +126,26 @@ public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, floa
|
|||
}
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public struct MaterialValueDesign(ColorRow value, bool enabled)
|
||||
public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert)
|
||||
{
|
||||
public ColorRow Value = value;
|
||||
public bool Enabled = enabled;
|
||||
public bool Revert = revert;
|
||||
|
||||
public readonly bool Apply(ref MaterialValueState state)
|
||||
{
|
||||
if (!Enabled)
|
||||
return false;
|
||||
|
||||
if (revert)
|
||||
{
|
||||
if (state.Model.NearEqual(state.Game))
|
||||
return false;
|
||||
|
||||
state.Model = state.Game;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.Model.NearEqual(Value))
|
||||
return false;
|
||||
|
||||
|
|
@ -148,6 +158,8 @@ public struct MaterialValueDesign(ColorRow value, bool enabled)
|
|||
public override void WriteJson(JsonWriter writer, MaterialValueDesign value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("Revert");
|
||||
writer.WriteValue(value.Revert);
|
||||
writer.WritePropertyName("DiffuseR");
|
||||
writer.WriteValue(value.Value.Diffuse.X);
|
||||
writer.WritePropertyName("DiffuseG");
|
||||
|
|
@ -180,6 +192,7 @@ public struct MaterialValueDesign(ColorRow value, bool enabled)
|
|||
JsonSerializer serializer)
|
||||
{
|
||||
var obj = JObject.Load(reader);
|
||||
Set(ref existingValue.Revert, obj["Revert"]?.Value<bool>());
|
||||
Set(ref existingValue.Value.Diffuse.X, obj["DiffuseR"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Diffuse.Y, obj["DiffuseG"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Diffuse.Z, obj["DiffuseB"]?.Value<float>());
|
||||
|
|
@ -235,7 +248,7 @@ public struct MaterialValueState(
|
|||
&& Game.NearEqual(rhsRow);
|
||||
|
||||
public readonly MaterialValueDesign Convert()
|
||||
=> new(Model, true);
|
||||
=> new(Model, true, false);
|
||||
}
|
||||
|
||||
public readonly struct MaterialValueManager<T>
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ public class StateApplier(
|
|||
|
||||
public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force)
|
||||
{
|
||||
if (!force && !_config.UseAdvancedParameters)
|
||||
if (!force && !_config.UseAdvancedDyes)
|
||||
return;
|
||||
|
||||
foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true }))
|
||||
|
|
|
|||
|
|
@ -172,7 +172,8 @@ public class StateEditor(
|
|||
return;
|
||||
|
||||
var actors = Applier.ChangeMaterialValue(state, index, settings.Source.RequiresChange());
|
||||
Glamourer.Log.Verbose($"Set material value in state {state.Identifier.Incognito(null)} from {oldValue} to {newValue.Game}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set material value in state {state.Identifier.Incognito(null)} from {oldValue} to {newValue.Game}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChanged.Type.MaterialValue, settings.Source, state, actors, (oldValue, newValue.Game, index));
|
||||
}
|
||||
|
||||
|
|
@ -289,12 +290,24 @@ public class StateEditor(
|
|||
continue;
|
||||
|
||||
var idx = MaterialValueIndex.FromKey(key);
|
||||
// TODO
|
||||
//if (state.Materials.TryGetValue(idx, out var materialState))
|
||||
//{
|
||||
// if (!settings.RespectManual || materialState.Source.IsManual())
|
||||
// Editor.ChangeMaterialValue(state, idx, new MaterialValueState(materialState.Game, value.Value, materialState.DrawData));
|
||||
//}
|
||||
var source = settings.Source.SetPending();
|
||||
if (state.Materials.TryGetValue(idx, out var materialState))
|
||||
{
|
||||
if (settings.RespectManual && !materialState.Source.IsManual())
|
||||
continue;
|
||||
|
||||
if (value.Revert)
|
||||
Editor.ChangeMaterialValue(state, idx, default, StateSource.Game, out _, settings.Key);
|
||||
else
|
||||
Editor.ChangeMaterialValue(state, idx,
|
||||
new MaterialValueState(materialState.Game, value.Value, materialState.DrawData, source), settings.Source, out _,
|
||||
settings.Key);
|
||||
}
|
||||
else if (!value.Revert)
|
||||
{
|
||||
Editor.ChangeMaterialValue(state, idx, new MaterialValueState(ColorRow.Empty, value.Value, CharacterWeapon.Empty, source),
|
||||
settings.Source, out _, settings.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +334,8 @@ public class StateEditor(
|
|||
public void ApplyDesign(object data, DesignBase design, ApplySettings settings)
|
||||
{
|
||||
var merged = settings.MergeLinks && design is Design d
|
||||
? merger.Merge(d.AllLinks, ((ActorState)data).ModelData.Customize, ((ActorState)data).BaseData, false, Config.AlwaysApplyAssociatedMods)
|
||||
? merger.Merge(d.AllLinks, ((ActorState)data).ModelData.Customize, ((ActorState)data).BaseData, false,
|
||||
Config.AlwaysApplyAssociatedMods)
|
||||
: new MergedDesign(design);
|
||||
|
||||
ApplyDesign(data, merged, settings with
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Glamourer.Designs.Links;
|
|||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -11,6 +12,7 @@ using Penumbra.GameData.Actors;
|
|||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using System;
|
||||
|
||||
namespace Glamourer.State;
|
||||
|
||||
|
|
@ -265,9 +267,16 @@ public sealed class StateManager(
|
|||
|
||||
var actors = ActorData.Invalid;
|
||||
if (source is not StateSource.Game)
|
||||
{
|
||||
actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true);
|
||||
foreach (var (idx, mat) in state.Materials.Values)
|
||||
Applier.ChangeMaterialValue(actors, MaterialValueIndex.FromKey(idx), mat.Game, true);
|
||||
}
|
||||
|
||||
state.Materials.Clear();
|
||||
|
||||
Glamourer.Log.Verbose(
|
||||
$"Reset advanced customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
$"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChanged.Type.Reset, source, state, actors, null);
|
||||
}
|
||||
|
||||
|
|
|
|||
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 1a187f756f2e8823197bd43db1c3383231f5eaff
|
||||
Subproject commit 75c5a7b220e7f799f85741288e3de4a20af9bcf4
|
||||
Loading…
Add table
Add a link
Reference in a new issue