mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-13 12:14:18 +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 ShowQuickBarInTabs { get; set; } = true;
|
||||||
public bool OpenWindowAtStart { get; set; } = false;
|
public bool OpenWindowAtStart { get; set; } = false;
|
||||||
public bool UseAdvancedParameters { get; set; } = true;
|
public bool UseAdvancedParameters { get; set; } = true;
|
||||||
|
public bool UseAdvancedDyes { get; set; } = false;
|
||||||
public bool ShowRevertAdvancedParametersButton { get; set; } = true;
|
public bool ShowRevertAdvancedParametersButton { get; set; } = true;
|
||||||
public bool ShowPalettePlusImport { get; set; } = true;
|
public bool ShowPalettePlusImport { get; set; } = true;
|
||||||
public bool UseFloatForColors { get; set; } = true;
|
public bool UseFloatForColors { get; set; } = true;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public class DesignEditor(
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void ChangeCustomize(object data, CustomizeIndex idx, CustomizeValue value, ApplySettings _ = default)
|
public void ChangeCustomize(object data, CustomizeIndex idx, CustomizeValue value, ApplySettings _ = default)
|
||||||
{
|
{
|
||||||
var design = (Design)data;
|
var design = (Design)data;
|
||||||
var oldValue = design.DesignData.Customize[idx];
|
var oldValue = design.DesignData.Customize[idx];
|
||||||
switch (idx)
|
switch (idx)
|
||||||
{
|
{
|
||||||
|
|
@ -96,7 +96,7 @@ public class DesignEditor(
|
||||||
public void ChangeCustomizeParameter(object data, CustomizeParameterFlag flag, CustomizeParameterValue value, ApplySettings _ = default)
|
public void ChangeCustomizeParameter(object data, CustomizeParameterFlag flag, CustomizeParameterValue value, ApplySettings _ = default)
|
||||||
{
|
{
|
||||||
var design = (Design)data;
|
var design = (Design)data;
|
||||||
var old = design.DesignData.Parameters[flag];
|
var old = design.DesignData.Parameters[flag];
|
||||||
if (!design.GetDesignDataRef().Parameters.Set(flag, value))
|
if (!design.GetDesignDataRef().Parameters.Set(flag, value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -219,6 +219,68 @@ public class DesignEditor(
|
||||||
DesignChanged.Invoke(DesignChanged.Type.Other, design, (metaIndex, false, value));
|
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/>
|
/// <inheritdoc/>
|
||||||
public void ApplyDesign(object data, MergedDesign other, ApplySettings _ = default)
|
public void ApplyDesign(object data, MergedDesign other, ApplySettings _ = default)
|
||||||
=> ApplyDesign(data, other.Design);
|
=> 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>
|
/// <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)
|
out EquipItem? newGauntlets)
|
||||||
{
|
{
|
||||||
newOff = null;
|
newOff = null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Glamourer.Automation;
|
using Glamourer.Automation;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
|
using Glamourer.Interop.Material;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using Glamourer.Unlocks;
|
using Glamourer.Unlocks;
|
||||||
|
|
@ -46,6 +47,7 @@ public class DesignMerger(
|
||||||
ReduceCrests(data, crestFlags, ret, source);
|
ReduceCrests(data, crestFlags, ret, source);
|
||||||
ReduceParameters(data, parameterFlags, ret, source);
|
ReduceParameters(data, parameterFlags, ret, source);
|
||||||
ReduceMods(design as Design, ret, modAssociations);
|
ReduceMods(design as Design, ret, modAssociations);
|
||||||
|
ReduceMaterials(design, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyFixFlags(ret, fixFlags);
|
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)
|
private static void ReduceMods(Design? design, MergedDesign ret, bool modAssociations)
|
||||||
{
|
{
|
||||||
if (design == null || !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>
|
/// <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,
|
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>
|
/// <summary> An existing design changed whether a specific customization is applied. Data is the type of customization [CustomizeIndex]. </summary>
|
||||||
ApplyCustomize,
|
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,
|
ApplyEquip,
|
||||||
|
|
||||||
/// <summary> An existing design changed whether a specific stain is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
/// <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>
|
/// <summary> An existing design changed whether a specific customize parameter is applied. Data is the flag for the parameter [CustomizeParameterFlag]. </summary>
|
||||||
ApplyParameter,
|
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>
|
/// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary>
|
||||||
WriteProtection,
|
WriteProtection,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ public partial class CustomizationDrawer
|
||||||
{
|
{
|
||||||
var size = ImGui.GetItemRectSize();
|
var size = ImGui.GetItemRectSize();
|
||||||
ImGui.GetWindowDrawList()
|
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)
|
if (i % 8 != 7)
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
||||||
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
|
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
|
||||||
{
|
{
|
||||||
available |= 1;
|
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)
|
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
|
||||||
|
|
@ -248,7 +248,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
||||||
if (available != 0)
|
if (available != 0)
|
||||||
tooltip += '\n';
|
tooltip += '\n';
|
||||||
available |= 2;
|
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)
|
if (available == 0)
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,135 @@ using OtterGui;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Files;
|
using Penumbra.GameData.Files;
|
||||||
|
using Penumbra.GameData.Gui;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using Vortice.DXGI;
|
||||||
|
|
||||||
namespace Glamourer.Gui.Materials;
|
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 =
|
private ActorState? _state;
|
||||||
[
|
private EquipSlot _newSlot = EquipSlot.Head;
|
||||||
MaterialValueIndex.DrawObjectType.Human,
|
private int _newMaterialIdx;
|
||||||
MaterialValueIndex.DrawObjectType.Mainhand,
|
private int _newRowIdx;
|
||||||
MaterialValueIndex.DrawObjectType.Offhand,
|
private MaterialValueIndex _newKey = MaterialValueIndex.Min();
|
||||||
];
|
|
||||||
|
|
||||||
private ActorState? _state;
|
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)
|
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())
|
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
|
||||||
{
|
{
|
||||||
var item = model.GetArmor(slot).ToWeapon(0);
|
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);
|
var (mainhand, offhand, mh, oh) = actor.Model.GetWeapons(actor);
|
||||||
if (mainhand.IsWeapon && mainhand.AsCharacterBase->SlotCount > 0)
|
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)
|
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)
|
private void DrawSlotMaterials(Model model, string name, CharacterWeapon drawData, MaterialValueIndex index)
|
||||||
{
|
{
|
||||||
var drawnMaterial = 1;
|
|
||||||
for (byte materialIndex = 0; materialIndex < MaterialService.MaterialsPerModel; ++materialIndex)
|
for (byte materialIndex = 0; materialIndex < MaterialService.MaterialsPerModel; ++materialIndex)
|
||||||
{
|
{
|
||||||
var texture = model.AsCharacterBase->ColorTableTextures + index.SlotIndex * 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))
|
if (!DirectXTextureHelper.TryGetColorTable(*texture, out var table))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
using var tree = ImRaii.TreeNode($"{name} Material #{drawnMaterial++}###{name}{materialIndex}");
|
using var tree = ImRaii.TreeNode($"{name} Material #{materialIndex + 1}###{name}{materialIndex}");
|
||||||
if (!tree)
|
if (!tree)
|
||||||
continue;
|
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)
|
private void DrawRow(ref MtrlFile.ColorTable.Row row, CharacterWeapon drawData, MaterialValueIndex index)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(index.RowIndex);
|
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);
|
value = new MaterialValueState(internalRow, internalRow, drawData, StateSource.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
var applied = ImGui.ColorEdit3("Diffuse", ref value.Model.Diffuse, ImGuiColorEditFlags.NoInputs);
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.SameLine();
|
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||||
applied |= ImGui.ColorEdit3("Specular", ref value.Model.Specular, ImGuiColorEditFlags.NoInputs);
|
{
|
||||||
ImGui.SameLine();
|
ImGui.TextUnformatted($"Row {index.RowIndex + 1:D2}");
|
||||||
applied |= ImGui.ColorEdit3("Emissive", ref value.Model.Emissive, ImGuiColorEditFlags.NoInputs);
|
}
|
||||||
ImGui.SameLine();
|
|
||||||
|
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);
|
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
||||||
applied |= ImGui.DragFloat("Gloss", ref value.Model.GlossStrength, 0.1f);
|
applied |= ImGui.DragFloat("##Gloss", ref value.Model.GlossStrength, 0.01f, 0.001f, float.MaxValue, "%.3f G") && value.Model.GlossStrength > 0;
|
||||||
ImGui.SameLine();
|
ImGuiUtil.HoverTooltip("Change the gloss strength for this row.");
|
||||||
|
ImGui.SameLine(0, spacing.X);
|
||||||
ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
|
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)
|
if (applied)
|
||||||
_stateManager.ChangeMaterialValue(_state!, index, value, ApplySettings.Manual);
|
_stateManager.ChangeMaterialValue(_state!, index, value, ApplySettings.Manual);
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine(0, spacing.X);
|
||||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
{
|
{
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FavoriteStarOn.Value());
|
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();
|
RevertButtons();
|
||||||
|
|
||||||
// TODO Materials
|
if (_config.UseAdvancedDyes && ImGui.CollapsingHeader("Material Shit"))
|
||||||
//if (ImGui.CollapsingHeader("Material Shit"))
|
_materialDrawer.DrawActorPanel(_actor);
|
||||||
// _materialDrawer.DrawActorPanel(_actor);
|
|
||||||
using var disabled = ImRaii.Disabled(transformationId != 0);
|
using var disabled = ImRaii.Disabled(transformationId != 0);
|
||||||
if (_state.ModelData.IsHuman)
|
if (_state.ModelData.IsHuman)
|
||||||
DrawHumanPanel();
|
DrawHumanPanel();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using Glamourer.Designs;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Gui.Customization;
|
using Glamourer.Gui.Customization;
|
||||||
using Glamourer.Gui.Equipment;
|
using Glamourer.Gui.Equipment;
|
||||||
|
using Glamourer.Gui.Materials;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
@ -31,7 +32,8 @@ public class DesignPanel(
|
||||||
ImportService _importService,
|
ImportService _importService,
|
||||||
MultiDesignPanel _multiDesignPanel,
|
MultiDesignPanel _multiDesignPanel,
|
||||||
CustomizeParameterDrawer _parameterDrawer,
|
CustomizeParameterDrawer _parameterDrawer,
|
||||||
DesignLinkDrawer _designLinkDrawer)
|
DesignLinkDrawer _designLinkDrawer,
|
||||||
|
MaterialDrawer _materials)
|
||||||
{
|
{
|
||||||
private readonly FileDialogManager _fileDialog = new();
|
private readonly FileDialogManager _fileDialog = new();
|
||||||
|
|
||||||
|
|
@ -176,21 +178,14 @@ public class DesignPanel(
|
||||||
|
|
||||||
private void DrawMaterialValues()
|
private void DrawMaterialValues()
|
||||||
{
|
{
|
||||||
if (!_config.UseAdvancedParameters)
|
if (!_config.UseAdvancedDyes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using var h = ImRaii.CollapsingHeader("Advanced Dyes");
|
using var h = ImRaii.CollapsingHeader("Advanced Dyes");
|
||||||
if (!h)
|
if (!h)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var ((key, value), i) in _selector.Selected!.Materials.WithIndex())
|
_materials.DrawDesignPanel(_selector.Selected!);
|
||||||
{
|
|
||||||
using var id = ImRaii.PushId(i);
|
|
||||||
ImGui.TextUnformatted($"{key:X16}");
|
|
||||||
ImGui.SameLine();
|
|
||||||
var enabled = value.Enabled;
|
|
||||||
ImGui.Checkbox("Enabled", ref enabled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCustomizeApplication()
|
private void DrawCustomizeApplication()
|
||||||
|
|
@ -384,7 +379,7 @@ public class DesignPanel(
|
||||||
DrawCustomize();
|
DrawCustomize();
|
||||||
DrawEquipment();
|
DrawEquipment();
|
||||||
DrawCustomizeParameters();
|
DrawCustomizeParameters();
|
||||||
//DrawMaterialValues(); TODO Materials
|
DrawMaterialValues();
|
||||||
_designDetails.Draw();
|
_designDetails.Draw();
|
||||||
DrawApplicationRules();
|
DrawApplicationRules();
|
||||||
_modAssociations.Draw();
|
_modAssociations.Draw();
|
||||||
|
|
|
||||||
|
|
@ -18,20 +18,21 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
||||||
private readonly StateManager _stateManager;
|
private readonly StateManager _stateManager;
|
||||||
private readonly PenumbraService _penumbra;
|
private readonly PenumbraService _penumbra;
|
||||||
private readonly ActorManager _actors;
|
private readonly ActorManager _actors;
|
||||||
|
private readonly Configuration _config;
|
||||||
|
|
||||||
private int _lastSlot;
|
private int _lastSlot;
|
||||||
|
|
||||||
private readonly ThreadLocal<List<MaterialValueIndex>> _deleteList = new(() => []);
|
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;
|
_stateManager = stateManager;
|
||||||
_actors = actors;
|
_actors = actors;
|
||||||
_penumbra = penumbra;
|
_penumbra = penumbra;
|
||||||
|
_config = config;
|
||||||
_event = prepareColorSet;
|
_event = prepareColorSet;
|
||||||
|
_event.Subscribe(OnPrepareColorSet, PrepareColorSet.Priority.MaterialManager);
|
||||||
// TODO Material
|
|
||||||
//_event.Subscribe(OnPrepareColorSet, PrepareColorSet.Priority.MaterialManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
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)
|
private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainId stain, ref nint ret)
|
||||||
{
|
{
|
||||||
|
if (!_config.UseAdvancedDyes)
|
||||||
|
return;
|
||||||
|
|
||||||
var actor = _penumbra.GameObjectFromDrawObject(characterBase);
|
var actor = _penumbra.GameObjectFromDrawObject(characterBase);
|
||||||
var validType = FindType(characterBase, actor, out var type);
|
var validType = FindType(characterBase, actor, out var type);
|
||||||
var (slotId, materialId) = FindMaterial(characterBase, material);
|
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>
|
/// <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)
|
if (human->ChangedEquipData == null)
|
||||||
return ((Model)human).GetArmor(((uint)slotId).ToEquipSlot()).ToWeapon(0);
|
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.
|
/// 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.
|
/// Weapons do not change in skeleton id without being reconstructed, so this is not changeable data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private CharacterWeapon GetTempSlot(Weapon* weapon)
|
private static CharacterWeapon GetTempSlot(Weapon* weapon)
|
||||||
{
|
{
|
||||||
var changedData = *(void**)((byte*)weapon + 0x918);
|
var changedData = *(void**)((byte*)weapon + 0x918);
|
||||||
if (changedData == null)
|
if (changedData == null)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using FFXIVClientStructs.Interop;
|
using FFXIVClientStructs.Interop;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Files;
|
using Penumbra.GameData.Files;
|
||||||
|
|
||||||
namespace Glamourer.Interop.Material;
|
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)
|
: 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>
|
private class Converter : JsonConverter<MaterialValueIndex>
|
||||||
{
|
{
|
||||||
public override void WriteJson(JsonWriter writer, MaterialValueIndex value, JsonSerializer serializer)
|
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))]
|
[JsonConverter(typeof(Converter))]
|
||||||
public struct MaterialValueDesign(ColorRow value, bool enabled)
|
public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert)
|
||||||
{
|
{
|
||||||
public ColorRow Value = value;
|
public ColorRow Value = value;
|
||||||
public bool Enabled = enabled;
|
public bool Enabled = enabled;
|
||||||
|
public bool Revert = revert;
|
||||||
|
|
||||||
public readonly bool Apply(ref MaterialValueState state)
|
public readonly bool Apply(ref MaterialValueState state)
|
||||||
{
|
{
|
||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (revert)
|
||||||
|
{
|
||||||
|
if (state.Model.NearEqual(state.Game))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.Model = state.Game;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.Model.NearEqual(Value))
|
if (state.Model.NearEqual(Value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -148,6 +158,8 @@ public struct MaterialValueDesign(ColorRow value, bool enabled)
|
||||||
public override void WriteJson(JsonWriter writer, MaterialValueDesign value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, MaterialValueDesign value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteStartObject();
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("Revert");
|
||||||
|
writer.WriteValue(value.Revert);
|
||||||
writer.WritePropertyName("DiffuseR");
|
writer.WritePropertyName("DiffuseR");
|
||||||
writer.WriteValue(value.Value.Diffuse.X);
|
writer.WriteValue(value.Value.Diffuse.X);
|
||||||
writer.WritePropertyName("DiffuseG");
|
writer.WritePropertyName("DiffuseG");
|
||||||
|
|
@ -180,6 +192,7 @@ public struct MaterialValueDesign(ColorRow value, bool enabled)
|
||||||
JsonSerializer serializer)
|
JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
var obj = JObject.Load(reader);
|
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.X, obj["DiffuseR"]?.Value<float>());
|
||||||
Set(ref existingValue.Value.Diffuse.Y, obj["DiffuseG"]?.Value<float>());
|
Set(ref existingValue.Value.Diffuse.Y, obj["DiffuseG"]?.Value<float>());
|
||||||
Set(ref existingValue.Value.Diffuse.Z, obj["DiffuseB"]?.Value<float>());
|
Set(ref existingValue.Value.Diffuse.Z, obj["DiffuseB"]?.Value<float>());
|
||||||
|
|
@ -235,7 +248,7 @@ public struct MaterialValueState(
|
||||||
&& Game.NearEqual(rhsRow);
|
&& Game.NearEqual(rhsRow);
|
||||||
|
|
||||||
public readonly MaterialValueDesign Convert()
|
public readonly MaterialValueDesign Convert()
|
||||||
=> new(Model, true);
|
=> new(Model, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct MaterialValueManager<T>
|
public readonly struct MaterialValueManager<T>
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ public class StateApplier(
|
||||||
|
|
||||||
public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force)
|
public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force)
|
||||||
{
|
{
|
||||||
if (!force && !_config.UseAdvancedParameters)
|
if (!force && !_config.UseAdvancedDyes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true }))
|
foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true }))
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,8 @@ public class StateEditor(
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actors = Applier.ChangeMaterialValue(state, index, settings.Source.RequiresChange());
|
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));
|
StateChanged.Invoke(StateChanged.Type.MaterialValue, settings.Source, state, actors, (oldValue, newValue.Game, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,13 +289,25 @@ public class StateEditor(
|
||||||
if (!value.Enabled)
|
if (!value.Enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var idx = MaterialValueIndex.FromKey(key);
|
var idx = MaterialValueIndex.FromKey(key);
|
||||||
// TODO
|
var source = settings.Source.SetPending();
|
||||||
//if (state.Materials.TryGetValue(idx, out var materialState))
|
if (state.Materials.TryGetValue(idx, out var materialState))
|
||||||
//{
|
{
|
||||||
// if (!settings.RespectManual || materialState.Source.IsManual())
|
if (settings.RespectManual && !materialState.Source.IsManual())
|
||||||
// Editor.ChangeMaterialValue(state, idx, new MaterialValueState(materialState.Game, value.Value, materialState.DrawData));
|
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)
|
public void ApplyDesign(object data, DesignBase design, ApplySettings settings)
|
||||||
{
|
{
|
||||||
var merged = settings.MergeLinks && design is Design d
|
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);
|
: new MergedDesign(design);
|
||||||
|
|
||||||
ApplyDesign(data, merged, settings with
|
ApplyDesign(data, merged, settings with
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using Glamourer.Designs.Links;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
using Glamourer.Interop.Material;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
|
|
@ -11,6 +12,7 @@ using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.DataContainers;
|
using Penumbra.GameData.DataContainers;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
|
|
@ -265,9 +267,16 @@ public sealed class StateManager(
|
||||||
|
|
||||||
var actors = ActorData.Invalid;
|
var actors = ActorData.Invalid;
|
||||||
if (source is not StateSource.Game)
|
if (source is not StateSource.Game)
|
||||||
|
{
|
||||||
actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true);
|
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(
|
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);
|
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