mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-22 23:47:45 +01:00
Add DT material parameters to Advanced Dyes
This commit is contained in:
parent
4646fd57ab
commit
5efd8c5c88
12 changed files with 626 additions and 121 deletions
|
|
@ -82,6 +82,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool AllowDoubleClickToApply { get; set; } = false;
|
||||
public bool RespectManualOnAutomationUpdate { get; set; } = false;
|
||||
public bool PreventRandomRepeats { get; set; } = false;
|
||||
public bool? AlwaysEditAsRoughness { get; set; } = null;
|
||||
public string PcpFolder { get; set; } = "PCP";
|
||||
public string PcpColor { get; set; } = "";
|
||||
|
||||
|
|
|
|||
|
|
@ -256,7 +256,21 @@ public class DesignEditor(
|
|||
DesignChanged.Invoke(DesignChanged.Type.MaterialRevert, design, new MaterialRevertTransaction(index, !revert, revert));
|
||||
}
|
||||
|
||||
public void ChangeMaterialValue(Design design, MaterialValueIndex index, ColorRow? row)
|
||||
public void ChangeMaterialMode(Design design, MaterialValueIndex index, ColorRow.Mode mode)
|
||||
{
|
||||
var materials = design.GetMaterialDataRef();
|
||||
if (!materials.TryGetValue(index, out var oldValue))
|
||||
return;
|
||||
|
||||
var oldMode = oldValue.Mode;
|
||||
materials.AddOrUpdateValue(index, oldValue with { Mode = mode });
|
||||
Glamourer.Log.Debug($"Changed advanced dye value for {index} from {oldMode} to {mode} mode.");
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.MaterialMode, design, new MaterialModeTransaction(index, oldMode, mode));
|
||||
}
|
||||
|
||||
public void ChangeMaterialValue(Design design, MaterialValueIndex index, ColorRow? row, ColorRow.Mode? mode = null)
|
||||
{
|
||||
var materials = design.GetMaterialDataRef();
|
||||
if (materials.TryGetValue(index, out var oldValue))
|
||||
|
|
@ -268,9 +282,14 @@ public class DesignEditor(
|
|||
}
|
||||
else if (!row.Value.NearEqual(oldValue.Value))
|
||||
{
|
||||
materials.UpdateValue(index, new MaterialValueDesign(row.Value, oldValue.Enabled, oldValue.Revert), out _);
|
||||
materials.UpdateValue(index, new MaterialValueDesign(row.Value, oldValue.Enabled, oldValue.Revert, mode ?? oldValue.Mode), out _);
|
||||
Glamourer.Log.Debug($"Updated advanced dye value for {index} to new value.");
|
||||
}
|
||||
else if (mode.HasValue && mode.Value != oldValue.Mode)
|
||||
{
|
||||
ChangeMaterialMode(design, index, mode.Value);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
|
|
@ -280,7 +299,7 @@ public class DesignEditor(
|
|||
{
|
||||
if (!row.HasValue)
|
||||
return;
|
||||
if (!materials.TryAddValue(index, new MaterialValueDesign(row.Value, true, false)))
|
||||
if (!materials.TryAddValue(index, new MaterialValueDesign(row.Value, true, false, mode ?? ColorRow.Mode.Legacy)))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Added new advanced dye value for {index}.");
|
||||
|
|
@ -288,7 +307,8 @@ public class DesignEditor(
|
|||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.DelaySave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Material, design, new MaterialTransaction(index, oldValue.Value, row));
|
||||
DesignChanged.Invoke(DesignChanged.Type.Material, design,
|
||||
new MaterialTransaction(index, oldValue.Value, row, mode.HasValue ? oldValue.Mode : null, mode));
|
||||
}
|
||||
|
||||
public void ChangeApplyMaterialValue(Design design, MaterialValueIndex index, bool value)
|
||||
|
|
|
|||
|
|
@ -119,16 +119,18 @@ public readonly record struct ModUpdatedTransaction(Mod Mod, ModSettings Old, Mo
|
|||
}
|
||||
|
||||
/// <remarks> Only Designs. </remarks>
|
||||
public readonly record struct MaterialTransaction(MaterialValueIndex Index, ColorRow? Old, ColorRow? New)
|
||||
public readonly record struct MaterialTransaction(MaterialValueIndex Index, ColorRow? Old, ColorRow? New, ColorRow.Mode? OldMode, ColorRow.Mode? NewMode)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
=> older is MaterialTransaction other && Index == other.Index ? new MaterialTransaction(Index, other.Old, New) : null;
|
||||
=> older is MaterialTransaction other && Index == other.Index
|
||||
? new MaterialTransaction(Index, other.Old, New, other.OldMode ?? OldMode, NewMode ?? other.NewMode)
|
||||
: null;
|
||||
|
||||
public void Revert(IDesignEditor editor, object data)
|
||||
{
|
||||
if (editor is DesignManager e)
|
||||
e.ChangeMaterialValue((Design)data, Index, Old);
|
||||
e.ChangeMaterialValue((Design)data, Index, Old, OldMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +145,17 @@ public readonly record struct MaterialRevertTransaction(MaterialValueIndex Index
|
|||
=> ((DesignManager)editor).ChangeMaterialRevert((Design)data, Index, Old);
|
||||
}
|
||||
|
||||
/// <remarks> Only Designs. </remarks>
|
||||
public readonly record struct MaterialModeTransaction(MaterialValueIndex Index, ColorRow.Mode Old, ColorRow.Mode New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction other)
|
||||
=> null;
|
||||
|
||||
public void Revert(IDesignEditor editor, object data)
|
||||
=> ((DesignManager)editor).ChangeMaterialMode((Design)data, Index, Old);
|
||||
}
|
||||
|
||||
/// <remarks> Only Designs. </remarks>
|
||||
public readonly record struct ApplicationTransaction(object Index, bool Old, bool New)
|
||||
: ITransaction
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ public sealed class DesignChanged()
|
|||
/// <summary> An existing design had an advanced dye rows Revert state changed. </summary>
|
||||
MaterialRevert,
|
||||
|
||||
/// <summary> An existing design had an advanced dye rows mode changed. </summary>
|
||||
MaterialMode,
|
||||
|
||||
/// <summary> An existing design had changed whether it always forces a redraw or not. </summary>
|
||||
ForceRedraw,
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
private bool _anyChanged;
|
||||
private bool _forceFocus;
|
||||
|
||||
private const int RowsPerPage = 16;
|
||||
private int _rowOffset;
|
||||
private const int RowsPerPage = 16;
|
||||
private int _rowOffset;
|
||||
private bool _editSheen;
|
||||
|
||||
private bool ShouldBeDrawn()
|
||||
{
|
||||
|
|
@ -149,28 +150,51 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
if ((tab.Success || select is TabItemFlags.SetSelected) && available)
|
||||
{
|
||||
_selectedMaterial = i;
|
||||
DrawToggle();
|
||||
DrawToggles();
|
||||
DrawTable(index, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawToggle()
|
||||
private void DrawToggles()
|
||||
{
|
||||
var buttonWidth = new Vector2(Im.ContentRegion.Available.X / 2, 0);
|
||||
using var font = Im.Font.PushMono();
|
||||
using var hoverColor = ImGuiColor.ButtonHovered.Push(Im.Style[ImGuiColor.TabHovered]);
|
||||
var layerButtonWidth = _mode is ColorRow.Mode.Dawntrail ? Im.Font.Mono.GetCharacterAdvance(' ') * 5 + Im.Style.FramePadding.X * 2 : 0;
|
||||
var buttonWidth =
|
||||
(Im.ContentRegion.Available.X - (_mode is ColorRow.Mode.Dawntrail ? layerButtonWidth * 2 + Im.Style.ItemSpacing.X : 0)) / 2;
|
||||
var buttonSize = new Vector2(buttonWidth, 0);
|
||||
using var font = Im.Font.PushMono();
|
||||
using var hoverColor = ImGuiColor.ButtonHovered.Push(Im.Style[ImGuiColor.TabHovered]);
|
||||
|
||||
hoverColor.Push(ImGuiColor.Button, Im.Style[_rowOffset is 0 ? ImGuiColor.TabSelected : ImGuiColor.Tab]);
|
||||
if (ImEx.ButtonCorners("Row Pairs 1-8 "u8, buttonWidth, ButtonFlags.MouseButtonLeft, Corners.Left))
|
||||
if (ImEx.ButtonCorners("Row Pairs 1-8 "u8, buttonSize, ButtonFlags.MouseButtonLeft, Corners.Left))
|
||||
_rowOffset = 0;
|
||||
hoverColor.Pop();
|
||||
|
||||
Im.Line.NoSpacing();
|
||||
|
||||
hoverColor.Push(ImGuiColor.Button, Im.Style[_rowOffset is RowsPerPage ? ImGuiColor.TabSelected : ImGuiColor.Tab]);
|
||||
if (ImEx.ButtonCorners("Row Pairs 9-16"u8, buttonWidth, ButtonFlags.MouseButtonLeft, Corners.Right))
|
||||
if (ImEx.ButtonCorners("Row Pairs 9-16"u8, buttonSize, ButtonFlags.MouseButtonLeft, Corners.Right))
|
||||
_rowOffset = RowsPerPage;
|
||||
hoverColor.Pop();
|
||||
|
||||
if (_mode is ColorRow.Mode.Dawntrail)
|
||||
{
|
||||
Im.Line.Same();
|
||||
|
||||
buttonSize = new Vector2(layerButtonWidth, 0);
|
||||
|
||||
hoverColor.Push(ImGuiColor.Button, Im.Style[!_editSheen ? ImGuiColor.TabSelected : ImGuiColor.Tab]);
|
||||
if (ImEx.ButtonCorners("Base"u8, buttonSize, ButtonFlags.MouseButtonLeft, Corners.Left))
|
||||
_editSheen = false;
|
||||
hoverColor.Pop();
|
||||
|
||||
Im.Line.NoSpacing();
|
||||
|
||||
hoverColor.Push(ImGuiColor.Button, Im.Style[_editSheen ? ImGuiColor.TabSelected : ImGuiColor.Tab]);
|
||||
if (ImEx.ButtonCorners("Sheen"u8, buttonSize, ButtonFlags.MouseButtonLeft, Corners.Right))
|
||||
_editSheen = true;
|
||||
hoverColor.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawContent(ReadOnlySpan<FFXIVClientStructs.Interop.Pointer<Texture>> textures,
|
||||
|
|
@ -323,7 +347,8 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
Im.Line.Same(Im.Window.Size.X - 3 * Im.Style.FrameHeight - 2 * spacing - Im.Style.WindowPadding.X);
|
||||
if (ImEx.Icon.Button(LunaStyle.ToClipboardIcon, "Export this table to your clipboard."u8))
|
||||
{
|
||||
ColorRowClipboard.Table = table;
|
||||
ColorRowClipboard.Table = table;
|
||||
ColorRowClipboard.TableMode = _mode;
|
||||
CopyToClipboard(table);
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +358,7 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
for (var idx = 0; idx < ColorTable.NumRows; ++idx)
|
||||
{
|
||||
var row = newTable[idx];
|
||||
var internalRow = new ColorRow(row);
|
||||
var internalRow = ColorRow.From(row, _mode);
|
||||
var slot = materialIndex.ToEquipSlot();
|
||||
var weapon = slot is EquipSlot.MainHand or EquipSlot.OffHand
|
||||
? _state.ModelData.Weapon(slot)
|
||||
|
|
@ -354,7 +379,7 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
var changed = _state.Materials.TryGetValue(index, out var value);
|
||||
if (!changed)
|
||||
{
|
||||
var internalRow = new ColorRow(row);
|
||||
var internalRow = ColorRow.From(row, _mode);
|
||||
var slot = index.ToEquipSlot();
|
||||
var weapon = slot switch
|
||||
{
|
||||
|
|
@ -369,7 +394,9 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
else
|
||||
{
|
||||
_anyChanged = true;
|
||||
value = new MaterialValueState(value.Game, value.Model, value.DrawData, StateSource.Manual);
|
||||
value = new MaterialValueState(value.Game,
|
||||
value.Model.IsPartial(_mode) ? value.Model.MergeOnto(ColorRow.From(row, _mode)) : value.Model,
|
||||
value.DrawData, StateSource.Manual);
|
||||
}
|
||||
|
||||
ImEx.Icon.Button(LunaStyle.OnHoverIcon, "Highlight the affected colors on the character."u8);
|
||||
|
|
@ -385,45 +412,79 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
}
|
||||
|
||||
Im.Line.Same(0, Im.Style.ItemSpacing.X * 2);
|
||||
var applied = ImEx.ColorPickerButton("##diffuse"u8, "Change the diffuse value for this row."u8, value.Model.Diffuse,
|
||||
var applied = ImEx.ColorPickerButton("##diffuse"u8, "Change the diffuse color for this row."u8, value.Model.Diffuse,
|
||||
out value.Model.Diffuse, 'D');
|
||||
|
||||
var spacing = Im.Style.ItemInnerSpacing;
|
||||
Im.Line.Same(0, spacing.X);
|
||||
applied |= ImEx.ColorPickerButton("##specular"u8, "Change the specular value for this row."u8, value.Model.Specular,
|
||||
applied |= ImEx.ColorPickerButton("##specular"u8, "Change the specular color for this row."u8, value.Model.Specular,
|
||||
out value.Model.Specular, 'S');
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
applied |= ImEx.ColorPickerButton("##emissive"u8, "Change the emissive value for this row."u8, value.Model.Emissive,
|
||||
applied |= ImEx.ColorPickerButton("##emissive"u8, "Change the emissive color for this row."u8, value.Model.Emissive,
|
||||
out value.Model.Emissive, 'E');
|
||||
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
if (_mode is not ColorRow.Mode.Dawntrail)
|
||||
|
||||
if (_mode is ColorRow.Mode.Dawntrail && _editSheen)
|
||||
{
|
||||
Im.Item.SetNextWidthScaled(100);
|
||||
applied |= DragGloss(ref value.Model.GlossStrength);
|
||||
Im.Tooltip.OnHover("Change the gloss strength for this row."u8);
|
||||
// The other layout has 2 items of width 100*sc and one spacing, for a total of 200*sc + 1*sp.
|
||||
// This layout has 3 items and two spacings: 3*w + 2*sp = 200*sc + 1*sp.
|
||||
var allItemsWidth = 200 * Im.Style.GlobalScale - spacing.X;
|
||||
var itemWidth = MathF.Floor(allItemsWidth / 3);
|
||||
|
||||
Im.Item.SetNextWidth(allItemsWidth - itemWidth * 2);
|
||||
applied |= DragSheen(ref value.Model.Sheen, false);
|
||||
Im.Tooltip.OnHover("Change the sheen strength for this row."u8);
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
|
||||
Im.Item.SetNextWidth(itemWidth);
|
||||
applied |= DragSheenTint(ref value.Model.SheenTint, false);
|
||||
Im.Tooltip.OnHover("Change the sheen tint for this row."u8);
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
|
||||
Im.Item.SetNextWidth(itemWidth);
|
||||
applied |= DragSheenRoughness(ref value.Model.SheenAperture, false);
|
||||
Im.Tooltip.OnHover("Change the sheen roughness for this row."u8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Im.Dummy(new Vector2(100 * Im.Style.GlobalScale, 0));
|
||||
}
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
if (_mode is not ColorRow.Mode.Dawntrail)
|
||||
{
|
||||
Im.Item.SetNextWidthScaled(100);
|
||||
applied |= DragSpecularStrength(ref value.Model.SpecularStrength);
|
||||
Im.Tooltip.OnHover("Change the specular strength for this row."u8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Im.Dummy(new Vector2(100 * Im.Style.GlobalScale, 0));
|
||||
var editAsRoughness = config.AlwaysEditAsRoughness ?? _mode is ColorRow.Mode.Dawntrail;
|
||||
applied |= (_mode, editAsRoughness) switch
|
||||
{
|
||||
(ColorRow.Mode.Legacy, false) => DragGloss(ref value.Model.GlossStrength, false),
|
||||
(ColorRow.Mode.Legacy, true) => DragGlossAsRoughness(ref value.Model.GlossStrength, false),
|
||||
(ColorRow.Mode.Dawntrail, false) => DragRoughnessAsGloss(ref value.Model.Roughness, false),
|
||||
(ColorRow.Mode.Dawntrail, true) => DragRoughness(ref value.Model.Roughness, false),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
Im.Tooltip.OnHover(editAsRoughness ? "Change the roughness for this row."u8 : "Change the gloss strength for this row."u8);
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
if (_mode is not ColorRow.Mode.Dawntrail)
|
||||
{
|
||||
Im.Item.SetNextWidthScaled(100);
|
||||
applied |= DragSpecularStrength(ref value.Model.SpecularStrength, false);
|
||||
Im.Tooltip.OnHover("Change the specular strength for this row."u8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Im.Item.SetNextWidthScaled(100);
|
||||
applied |= DragMetalness(ref value.Model.Metalness, false);
|
||||
Im.Tooltip.OnHover("Change the metalness for this row."u8);
|
||||
}
|
||||
}
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
if (ImEx.Icon.Button(LunaStyle.ToClipboardIcon, "Export this row to your clipboard."u8))
|
||||
ColorRowClipboard.Row = value.Model;
|
||||
{
|
||||
ColorRowClipboard.Row = value.Model;
|
||||
ColorRowClipboard.RowMode = _mode;
|
||||
}
|
||||
|
||||
Im.Line.Same(0, spacing.X);
|
||||
if (ImEx.Icon.Button(LunaStyle.FromClipboardIcon, "Import an exported row from your clipboard onto this row."u8,
|
||||
!ColorRowClipboard.IsSet))
|
||||
|
|
@ -440,12 +501,12 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
stateManager.ChangeMaterialValue(_state, index, value, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public static bool DragGloss(ref float value)
|
||||
public static bool DragGloss(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = value;
|
||||
var tmp = float.IsNaN(value) ? ColorRow.DefaultGlossStrength : value;
|
||||
var minValue = Im.Io.KeyControl ? 0f : (float)Half.Epsilon;
|
||||
if (!Im.Drag("##Gloss"u8, ref tmp, "%.1f G"u8, 0.001f, minValue, Math.Max(0.01f, 0.005f * value), SliderFlags.AlwaysClamp))
|
||||
return false;
|
||||
if (!Im.Drag("##Gloss"u8, ref tmp, float.IsNaN(value) ? "\u2014 G"u8 : "%.1f G"u8, 0.001f, minValue, Math.Max(0.01f, 0.005f * value), SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, minValue, (float)Half.MaxValue);
|
||||
if (tmp2 == value)
|
||||
|
|
@ -455,12 +516,27 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
return true;
|
||||
}
|
||||
|
||||
public static bool DragSpecularStrength(ref float value)
|
||||
public static bool DragGlossAsRoughness(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = value * 100f;
|
||||
if (!Im.Drag("##SpecularStrength"u8, ref tmp, "%.0f%% SS"u8, 0f, (float)Half.MaxValue * 100f, 0.05f, SliderFlags.AlwaysClamp))
|
||||
var roughness = ColorTableRow.RoughnessFromShininess(float.IsNaN(value) ? ColorRow.DefaultGlossStrength : value);
|
||||
var tmp = roughness * 100f;
|
||||
if (!Im.Drag("##Gloss"u8, ref tmp, float.IsNaN(value) ? "\u2014 Rg"u8 : "%.0f%% Rg"u8, 0f, 100f, 0.25f, SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, 0f, 100f) / 100f;
|
||||
if (tmp2 == roughness)
|
||||
return false;
|
||||
|
||||
value = ColorTableRow.ShininessFromRoughness(tmp2);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragSpecularStrength(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = (float.IsNaN(value) ? ColorRow.DefaultSpecularStrength : value) * 100f;
|
||||
if (!Im.Drag("##SpecularStrength"u8, ref tmp, float.IsNaN(value) ? "\u2014 SS"u8 : "%.0f%% SS"u8, 0f, (float)Half.MaxValue * 100f, 0.05f, SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, 0f, (float)Half.MaxValue * 100f) / 100f;
|
||||
if (tmp2 == value)
|
||||
return false;
|
||||
|
|
@ -469,6 +545,102 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
return true;
|
||||
}
|
||||
|
||||
public static bool DragRoughness(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = (float.IsNaN(value) ? ColorRow.DefaultRoughness : value) * 100f;
|
||||
if (!Im.Drag("##Roughness"u8, ref tmp, float.IsNaN(value) ? "\u2014 Rg"u8 : "%.0f%% Rg"u8, 0f, 100f, 0.25f, SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, 0f, 100f) / 100f;
|
||||
if (tmp2 == value)
|
||||
return false;
|
||||
|
||||
value = tmp2;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragRoughnessAsGloss(ref float value, bool canUnset)
|
||||
{
|
||||
var gloss = ColorTableRow.ShininessFromRoughness(float.IsNaN(value) ? ColorRow.DefaultRoughness : value);
|
||||
var tmp = gloss;
|
||||
if (!Im.Drag("##Roughness"u8, ref tmp, float.IsNaN(value) ? "\u2014 G"u8 : "%.1f G"u8, 0.001f, (float)Half.Epsilon, Math.Max(0.01f, 0.005f * gloss), SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, (float)Half.Epsilon, (float)Half.MaxValue);
|
||||
if (tmp2 == gloss)
|
||||
return false;
|
||||
|
||||
value = ColorTableRow.RoughnessFromShininess(tmp2);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragMetalness(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = (float.IsNaN(value) ? ColorRow.DefaultMetalness : value) * 100f;
|
||||
if (!Im.Drag("##Metalness"u8, ref tmp, float.IsNaN(value) ? "\u2014 Mt"u8 : "%.0f%% Mt"u8, 0f, 100f, 0.25f, SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, 0f, 100f) / 100f;
|
||||
if (tmp2 == value)
|
||||
return false;
|
||||
|
||||
value = tmp2;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragSheen(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = (float.IsNaN(value) ? ColorRow.DefaultSheen : value) * 100f;
|
||||
if (!Im.Drag("##Sheen"u8, ref tmp, float.IsNaN(value) ? "\u2014 Sh"u8 : "%.0f%% Sh"u8, 0f, 100f * (float)Half.MaxValue, 0.25f, SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, 0f, 100f * (float)Half.MaxValue) / 100f;
|
||||
if (tmp2 == value)
|
||||
return false;
|
||||
|
||||
value = tmp2;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragSheenTint(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = (float.IsNaN(value) ? ColorRow.DefaultSheenTint : value) * 100f;
|
||||
if (!Im.Drag("##SheenTint"u8, ref tmp, float.IsNaN(value) ? "\u2014 ST"u8 : "%.0f%% ST"u8, -100f * (float)Half.MaxValue, 100f * (float)Half.MaxValue, 0.25f,
|
||||
SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(tmp, -100f * (float)Half.MaxValue, 100f * (float)Half.MaxValue) / 100f;
|
||||
if (tmp2 == value)
|
||||
return false;
|
||||
|
||||
value = tmp2;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragSheenRoughness(ref float value, bool canUnset)
|
||||
{
|
||||
var tmp = 100f / (float.IsNaN(value) ? ColorRow.DefaultSheenAperture : value);
|
||||
if (!Im.Drag("##SheenAperture"u8, ref tmp, float.IsNaN(value) ? "\u2014 SR"u8 : "%.0f%% SR"u8, 100f / (float)Half.MaxValue, 100f / (float)Half.Epsilon, 0.25f,
|
||||
SliderFlags.AlwaysClamp))
|
||||
return UnsetBehavior(ref value, canUnset);
|
||||
|
||||
var tmp2 = Math.Clamp(100f / tmp, (float)Half.Epsilon, (float)Half.MaxValue);
|
||||
if (tmp2 == value)
|
||||
return false;
|
||||
|
||||
value = tmp2;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool UnsetBehavior(ref float value, bool canUnset)
|
||||
{
|
||||
if (!(canUnset && Im.Item.RightClicked() && Im.Io.KeyControl))
|
||||
return false;
|
||||
|
||||
value = float.NaN;
|
||||
return true;
|
||||
}
|
||||
|
||||
private LabelStruct _label = new();
|
||||
|
||||
private struct LabelStruct
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ public static class ColorRowClipboard
|
|||
field = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static ColorRow.Mode TableMode { get; set; }
|
||||
|
||||
public static ColorRow Row
|
||||
{
|
||||
|
|
@ -28,4 +30,6 @@ public static class ColorRowClipboard
|
|||
field = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static ColorRow.Mode RowMode { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ namespace Glamourer.Gui.Materials;
|
|||
|
||||
public class MaterialDrawer(DesignManager designManager, Configuration config) : IService
|
||||
{
|
||||
public const float GlossWidth = 100;
|
||||
public const float SpecularStrengthWidth = 125;
|
||||
public const float SliderWidth = 90;
|
||||
public const float ModeWidth = 45;
|
||||
public const float SheenSliderWidth = 75; // Should satisfy 2*Slider + Mode = 3*SheenSlider
|
||||
|
||||
private int _newMaterialIdx;
|
||||
private int _newRowIdx;
|
||||
|
|
@ -25,17 +26,17 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
_spacing = Im.Style.ItemInnerSpacing.X;
|
||||
_buttonSize = new Vector2(Im.Style.FrameHeight);
|
||||
var colorWidth = 4 * _buttonSize.X
|
||||
+ (GlossWidth + SpecularStrengthWidth) * Im.Style.GlobalScale
|
||||
+ 6 * _spacing
|
||||
+ (SliderWidth * 2 + ModeWidth) * Im.Style.GlobalScale
|
||||
+ 7 * _spacing
|
||||
+ Im.Font.CalculateSize("Revert"u8).X;
|
||||
DrawMultiButtons(design);
|
||||
Im.Dummy(0);
|
||||
Im.Separator();
|
||||
Im.Dummy(0);
|
||||
if (available > 1.95 * colorWidth)
|
||||
if (available > 2.6f * colorWidth)
|
||||
DrawSingleRow(design);
|
||||
else
|
||||
DrawTwoRow(design);
|
||||
DrawMultipleRow(design);
|
||||
DrawNew(design);
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
private void DrawName(MaterialValueIndex index)
|
||||
{
|
||||
using var style = ImStyleDouble.ButtonTextAlign.Push(new Vector2(0.05f, 0.5f));
|
||||
ImEx.TextFramed($"{index}", new Vector2((GlossWidth + SpecularStrengthWidth) * Im.Style.GlobalScale + _spacing, 0),
|
||||
ImEx.TextFramed($"{index}", new Vector2((SliderWidth * 2 + ModeWidth) * Im.Style.GlobalScale + _spacing * 2, 0),
|
||||
borderColor: ImGuiColor.Text.Get());
|
||||
}
|
||||
|
||||
|
|
@ -91,20 +92,21 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
Im.Line.Same(0, _spacing);
|
||||
DeleteButton(design, key, ref i);
|
||||
Im.Line.Same(0, _spacing);
|
||||
CopyButton(value.Value);
|
||||
CopyButton(value.Value, value.Mode);
|
||||
Im.Line.Same(0, _spacing);
|
||||
PasteButton(design, key);
|
||||
Im.Line.Same(0, _spacing);
|
||||
using var disabled = Im.Disabled(design.WriteProtected());
|
||||
EnabledToggle(design, key, value.Enabled);
|
||||
Im.Line.Same(0, _spacing);
|
||||
DrawRow(design, key, value.Value, value.Revert);
|
||||
DrawRow(design, key, value.Value, value.Revert, value.Mode);
|
||||
DrawRowExtra(design, key, value.Value, value.Revert, value.Mode, true);
|
||||
Im.Line.Same(0, _spacing);
|
||||
RevertToggle(design, key, value.Revert);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTwoRow(Design design)
|
||||
private void DrawMultipleRow(Design design)
|
||||
{
|
||||
for (var i = 0; i < design.Materials.Count; ++i)
|
||||
{
|
||||
|
|
@ -116,16 +118,18 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
Im.Line.Same(0, _spacing);
|
||||
DeleteButton(design, key, ref i);
|
||||
Im.Line.Same(0, _spacing);
|
||||
CopyButton(value.Value);
|
||||
CopyButton(value.Value, value.Mode);
|
||||
Im.Line.Same(0, _spacing);
|
||||
PasteButton(design, key);
|
||||
Im.Line.Same(0, _spacing);
|
||||
using var disabled = Im.Disabled(design.WriteProtected());
|
||||
EnabledToggle(design, key, value.Enabled);
|
||||
|
||||
|
||||
DrawRow(design, key, value.Value, value.Revert);
|
||||
DrawRow(design, key, value.Value, value.Revert, value.Mode);
|
||||
Im.Line.Same(0, _spacing);
|
||||
RevertToggle(design, key, value.Revert);
|
||||
DrawRowExtra(design, key, value.Value, value.Revert, value.Mode, false);
|
||||
Im.Separator();
|
||||
}
|
||||
}
|
||||
|
|
@ -142,17 +146,20 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
--idx;
|
||||
}
|
||||
|
||||
private void CopyButton(in ColorRow row)
|
||||
private void CopyButton(in ColorRow row, ColorRow.Mode mode)
|
||||
{
|
||||
if (ImEx.Icon.Button(LunaStyle.ToClipboardIcon, "Export this row to your clipboard."u8))
|
||||
ColorRowClipboard.Row = row;
|
||||
{
|
||||
ColorRowClipboard.Row = row;
|
||||
ColorRowClipboard.RowMode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
private void PasteButton(Design design, MaterialValueIndex index)
|
||||
{
|
||||
if (ImEx.Icon.Button(LunaStyle.FromClipboardIcon, "Import an exported row from your clipboard onto this row."u8,
|
||||
!ColorRowClipboard.IsSet || design.WriteProtected()))
|
||||
designManager.ChangeMaterialValue(design, index, ColorRowClipboard.Row);
|
||||
designManager.ChangeMaterialValue(design, index, ColorRowClipboard.Row, ColorRowClipboard.RowMode);
|
||||
}
|
||||
|
||||
private void EnabledToggle(Design design, MaterialValueIndex index, bool enabled)
|
||||
|
|
@ -169,6 +176,39 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
"If this is checked, Glamourer will try to revert the advanced dye row to its game state instead of applying a specific row."u8);
|
||||
}
|
||||
|
||||
private void ModeToggle(Design design, MaterialValueIndex index, ColorRow.Mode mode)
|
||||
{
|
||||
if (Im.Button(ToCallsignString(mode), new Vector2(ModeWidth * Im.Style.GlobalScale, 0)))
|
||||
designManager.ChangeMaterialMode(design, index, GetNextMode(mode));
|
||||
Im.Tooltip.OnHover(ToTooltipString(mode));
|
||||
|
||||
return;
|
||||
|
||||
static ReadOnlySpan<byte> ToCallsignString(ColorRow.Mode mode)
|
||||
=> mode switch
|
||||
{
|
||||
ColorRow.Mode.Legacy => "Lgc###mode"u8,
|
||||
ColorRow.Mode.Dawntrail => "DT###mode"u8,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
static ColorRow.Mode GetNextMode(ColorRow.Mode mode)
|
||||
=> mode switch
|
||||
{
|
||||
ColorRow.Mode.Legacy => ColorRow.Mode.Dawntrail,
|
||||
ColorRow.Mode.Dawntrail => ColorRow.Mode.Legacy,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
static ReadOnlySpan<byte> ToTooltipString(ColorRow.Mode mode)
|
||||
=> mode switch
|
||||
{
|
||||
ColorRow.Mode.Legacy => "This color row currently contains Legacy material parameters.\nClick this button to switch it to Dawntrail parameters."u8,
|
||||
ColorRow.Mode.Dawntrail => "This color row currently contains Dawntrail material parameters.\nClick this button to switch it to Legacy parameters."u8,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class MaterialSlotCombo;
|
||||
|
||||
private void DrawSlotCombo()
|
||||
|
|
@ -245,23 +285,76 @@ public class MaterialDrawer(DesignManager designManager, Configuration config) :
|
|||
Im.Tooltip.OnHover("Drag this to the left or right to change its value."u8);
|
||||
}
|
||||
|
||||
private void DrawRow(Design design, MaterialValueIndex index, in ColorRow row, bool disabled)
|
||||
private void DrawRow(Design design, MaterialValueIndex index, in ColorRow row, bool disabled, ColorRow.Mode mode)
|
||||
{
|
||||
var tmp = row;
|
||||
using var _ = Im.Disabled(disabled);
|
||||
var applied = ImEx.ColorPickerButton("##diffuse"u8, "Change the diffuse value for this row."u8, row.Diffuse, out tmp.Diffuse, 'D');
|
||||
var applied = ImEx.ColorPickerButton("##diffuse"u8, "Change the diffuse color for this row."u8, row.Diffuse, out tmp.Diffuse, 'D');
|
||||
Im.Line.SameInner();
|
||||
applied |= ImEx.ColorPickerButton("##specular"u8, "Change the specular value for this row."u8, row.Specular, out tmp.Specular, 'S');
|
||||
applied |= ImEx.ColorPickerButton("##specular"u8, "Change the specular color for this row."u8, row.Specular, out tmp.Specular, 'S');
|
||||
Im.Line.SameInner();
|
||||
applied |= ImEx.ColorPickerButton("##emissive"u8, "Change the emissive value for this row."u8, row.Emissive, out tmp.Emissive, 'E');
|
||||
applied |= ImEx.ColorPickerButton("##emissive"u8, "Change the emissive color for this row."u8, row.Emissive, out tmp.Emissive, 'E');
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(GlossWidth * Im.Style.GlobalScale);
|
||||
applied |= AdvancedDyePopup.DragGloss(ref tmp.GlossStrength);
|
||||
Im.Tooltip.OnHover("Change the gloss strength for this row."u8);
|
||||
ModeToggle(design, index, mode);
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(SpecularStrengthWidth * Im.Style.GlobalScale);
|
||||
applied |= AdvancedDyePopup.DragSpecularStrength(ref tmp.SpecularStrength);
|
||||
Im.Tooltip.OnHover("Change the specular strength for this row."u8);
|
||||
Im.Item.SetNextWidth(SliderWidth * Im.Style.GlobalScale);
|
||||
var editAsRoughness = config.AlwaysEditAsRoughness ?? mode is ColorRow.Mode.Dawntrail;
|
||||
applied |= (mode, editAsRoughness) switch
|
||||
{
|
||||
(ColorRow.Mode.Legacy, false) => AdvancedDyePopup.DragGloss(ref tmp.GlossStrength, true),
|
||||
(ColorRow.Mode.Legacy, true) => AdvancedDyePopup.DragGlossAsRoughness(ref tmp.GlossStrength, true),
|
||||
(ColorRow.Mode.Dawntrail, false) => AdvancedDyePopup.DragRoughnessAsGloss(ref tmp.Roughness, true),
|
||||
(ColorRow.Mode.Dawntrail, true) => AdvancedDyePopup.DragRoughness(ref tmp.Roughness, true),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
Im.Tooltip.OnHover(editAsRoughness
|
||||
? "Change the roughness for this row.\nControl and Right-Click to unset."u8
|
||||
: "Change the gloss strength for this row.\nControl and Right-Click to unset."u8);
|
||||
if (mode is ColorRow.Mode.Dawntrail)
|
||||
{
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(SliderWidth * Im.Style.GlobalScale);
|
||||
applied |= AdvancedDyePopup.DragMetalness(ref tmp.Metalness, true);
|
||||
Im.Tooltip.OnHover("Change the metalness for this row.\nControl and Right-Click to unset."u8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(SliderWidth * Im.Style.GlobalScale);
|
||||
applied |= AdvancedDyePopup.DragSpecularStrength(ref tmp.SpecularStrength, true);
|
||||
Im.Tooltip.OnHover("Change the specular strength for this row.\nControl and Right-Click to unset."u8);
|
||||
}
|
||||
|
||||
if (applied)
|
||||
designManager.ChangeMaterialValue(design, index, tmp);
|
||||
}
|
||||
|
||||
private void DrawRowExtra(Design design, MaterialValueIndex index, in ColorRow row, bool disabled, ColorRow.Mode mode, bool compact)
|
||||
{
|
||||
if (mode is not ColorRow.Mode.Dawntrail)
|
||||
return;
|
||||
|
||||
var tmp = row;
|
||||
using var _ = Im.Disabled(disabled);
|
||||
|
||||
if (!compact)
|
||||
Im.Dummy(new Vector2(_buttonSize.X * 3 + _spacing * 2, _buttonSize.Y));
|
||||
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(SheenSliderWidth * Im.Style.GlobalScale);
|
||||
var applied = AdvancedDyePopup.DragSheen(ref tmp.Sheen, true);
|
||||
Im.Tooltip.OnHover("Change the sheen strength for this row.\nControl and Right-Click to unset."u8);
|
||||
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(SheenSliderWidth * Im.Style.GlobalScale);
|
||||
applied |= AdvancedDyePopup.DragSheenTint(ref tmp.SheenTint, true);
|
||||
Im.Tooltip.OnHover("Change the sheen tint for this row.\nControl and Right-Click to unset."u8);
|
||||
|
||||
Im.Line.SameInner();
|
||||
Im.Item.SetNextWidth(SheenSliderWidth * Im.Style.GlobalScale);
|
||||
applied |= AdvancedDyePopup.DragSheenRoughness(ref tmp.SheenAperture, true);
|
||||
Im.Tooltip.OnHover("Change the sheen roughness for this row.\nControl and Right-Click to unset."u8);
|
||||
|
||||
if (applied)
|
||||
designManager.ChangeMaterialValue(design, index, tmp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ public sealed class SettingsTab(
|
|||
Checkbox("Smaller Equip Display"u8, "Use single-line display without icons and small dye buttons instead of double-line display."u8,
|
||||
config.SmallEquip, v => config.SmallEquip = v);
|
||||
DrawHeightUnitSettings();
|
||||
DrawRoughnessSettings();
|
||||
Checkbox("Show Application Checkboxes"u8,
|
||||
"Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules."u8,
|
||||
!config.HideApplyCheckmarks, v => config.HideApplyCheckmarks = !v);
|
||||
|
|
@ -502,4 +503,34 @@ public sealed class SettingsTab(
|
|||
LunaStyle.DrawAlignedHelpMarkerLabel("Character Height Display Type"u8,
|
||||
"Select how to display the height of characters in real-world units, if at all."u8);
|
||||
}
|
||||
|
||||
private void DrawRoughnessSettings()
|
||||
{
|
||||
Im.Item.SetNextWidthScaled(300);
|
||||
using (var combo = Im.Combo.Begin("##alwaysEditAsRoughness"u8, ToRoughnessSettingString(config.AlwaysEditAsRoughness)))
|
||||
{
|
||||
if (combo)
|
||||
foreach (var type in (IEnumerable<bool?>)[null, true, false,])
|
||||
{
|
||||
if (Im.Selectable(ToRoughnessSettingString(type), config.AlwaysEditAsRoughness == type))
|
||||
{
|
||||
config.AlwaysEditAsRoughness = type;
|
||||
config.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LunaStyle.DrawAlignedHelpMarkerLabel("Gloss Strength and Roughness Display Type"u8,
|
||||
"Select how to display and edit Gloss Strength and Roughness values.\nThe conversion formula used is an approximation and does not account for all the subtleties of legacy vs PBR shaders."u8);
|
||||
|
||||
return;
|
||||
|
||||
static ReadOnlySpan<byte> ToRoughnessSettingString(bool? alwaysEditAsRoughness)
|
||||
=> alwaysEditAsRoughness switch
|
||||
{
|
||||
null => "As-Is"u8,
|
||||
true => "Always Roughness"u8,
|
||||
false => "Always Gloss Strength"u8,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
var idx = MaterialValueIndex.FromKey(values[i].Key);
|
||||
var materialValue = values[i].Value;
|
||||
ref var row = ref colorTable[idx.RowIndex];
|
||||
var newGame = new ColorRow(row);
|
||||
var newGame = ColorRow.From(row, mode);
|
||||
if (materialValue.EqualGame(newGame, drawData))
|
||||
materialValue.Model.Apply(ref row, mode);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using Penumbra.GameData.Structs;
|
|||
namespace Glamourer.Interop.Material;
|
||||
|
||||
/// <summary> Values are not squared. </summary>
|
||||
public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, float specularStrength, float glossStrength)
|
||||
public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, float specularStrength, float glossStrength, float roughness, float metalness, float sheen, float sheenTint, float sheenAperture)
|
||||
{
|
||||
public enum Mode
|
||||
{
|
||||
|
|
@ -19,26 +19,50 @@ public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, floa
|
|||
Dawntrail,
|
||||
}
|
||||
|
||||
public static readonly ColorRow Empty = new(Vector3.Zero, Vector3.Zero, Vector3.Zero, 1f, 1f);
|
||||
public const float DefaultSpecularStrength = 1f;
|
||||
public const float DefaultGlossStrength = 20f;
|
||||
public const float DefaultRoughness = 0.5f;
|
||||
public const float DefaultMetalness = 0f;
|
||||
public const float DefaultSheen = 0.1f;
|
||||
public const float DefaultSheenTint = 0.2f;
|
||||
public const float DefaultSheenAperture = 5f;
|
||||
|
||||
public static readonly ColorRow Empty = new(Vector3.Zero, Vector3.Zero, Vector3.Zero, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN,
|
||||
float.NaN, float.NaN);
|
||||
|
||||
public Vector3 Diffuse = diffuse;
|
||||
public Vector3 Specular = specular;
|
||||
public Vector3 Emissive = emissive;
|
||||
public float SpecularStrength = specularStrength;
|
||||
public float GlossStrength = glossStrength;
|
||||
public float Roughness = roughness;
|
||||
public float Metalness = metalness;
|
||||
public float Sheen = sheen;
|
||||
public float SheenTint = sheenTint;
|
||||
public float SheenAperture = sheenAperture;
|
||||
|
||||
public ColorRow(in ColorTableRow row)
|
||||
: this(Root((Vector3)row.DiffuseColor), Root((Vector3)row.SpecularColor), Root((Vector3)row.EmissiveColor),
|
||||
(float)row.LegacySpecularStrength(),
|
||||
(float)row.LegacyGloss())
|
||||
{ }
|
||||
public static ColorRow From(in ColorTableRow row, Mode mode)
|
||||
=> mode switch
|
||||
{
|
||||
Mode.Legacy => new(Root((Vector3)row.DiffuseColor), Root((Vector3)row.SpecularColor), Root((Vector3)row.EmissiveColor),
|
||||
(float)row.LegacySpecularStrength(), (float)row.LegacyGloss(), float.NaN, float.NaN, float.NaN, float.NaN, float.NaN),
|
||||
Mode.Dawntrail => new(Root((Vector3)row.DiffuseColor), Root((Vector3)row.SpecularColor), Root((Vector3)row.EmissiveColor),
|
||||
float.NaN, float.NaN, (float)row.DawntrailRoughness(), (float)row.DawntrailMetalness(), (float)row.DawntrailSheen(),
|
||||
(float)row.DawntrailSheenTint(), (float)row.DawntrailSheenAperture()),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
public readonly bool NearEqual(in ColorRow rhs)
|
||||
public readonly bool NearEqual(in ColorRow rhs, bool skipEmpty = false)
|
||||
=> Diffuse.NearEqual(rhs.Diffuse)
|
||||
&& Specular.NearEqual(rhs.Specular)
|
||||
&& Emissive.NearEqual(rhs.Emissive)
|
||||
&& SpecularStrength.NearEqual(rhs.SpecularStrength)
|
||||
&& GlossStrength.NearEqual(rhs.GlossStrength);
|
||||
&& (float.IsNaN(SpecularStrength) ? skipEmpty || float.IsNaN(rhs.SpecularStrength) : SpecularStrength.NearEqual(rhs.SpecularStrength))
|
||||
&& (float.IsNaN(GlossStrength) ? skipEmpty || float.IsNaN(rhs.GlossStrength) : GlossStrength.NearEqual(rhs.GlossStrength))
|
||||
&& (float.IsNaN(Roughness) ? skipEmpty || float.IsNaN(rhs.Roughness) : Roughness.NearEqual(rhs.Roughness))
|
||||
&& (float.IsNaN(Metalness) ? skipEmpty || float.IsNaN(rhs.Metalness) : Metalness.NearEqual(rhs.Metalness))
|
||||
&& (float.IsNaN(Sheen) ? skipEmpty || float.IsNaN(rhs.Sheen) : Sheen.NearEqual(rhs.Sheen))
|
||||
&& (float.IsNaN(SheenAperture) ? skipEmpty || float.IsNaN(rhs.SheenAperture) : SheenAperture.NearEqual(rhs.SheenAperture))
|
||||
&& (float.IsNaN(SheenTint) ? skipEmpty || float.IsNaN(rhs.SheenTint) : SheenTint.NearEqual(rhs.SheenTint));
|
||||
|
||||
private static Vector3 Square(Vector3 value)
|
||||
=> new(Square(value.X), Square(value.Y), Square(value.Z));
|
||||
|
|
@ -76,23 +100,85 @@ public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, floa
|
|||
ret = true;
|
||||
}
|
||||
|
||||
if (mode is Mode.Legacy)
|
||||
switch (mode)
|
||||
{
|
||||
if (!((float)row.LegacySpecularStrength()).NearEqual(SpecularStrength))
|
||||
{
|
||||
row.LegacySpecularStrengthWrite() = (Half)SpecularStrength;
|
||||
ret = true;
|
||||
}
|
||||
case Mode.Legacy:
|
||||
if (!float.IsNaN(SpecularStrength) && !((float)row.LegacySpecularStrength()).NearEqual(SpecularStrength))
|
||||
{
|
||||
row.LegacySpecularStrengthWrite() = (Half)SpecularStrength;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!((float)row.LegacyGloss()).NearEqual(GlossStrength))
|
||||
{
|
||||
row.LegacyGlossWrite() = (Half)GlossStrength;
|
||||
ret = true;
|
||||
}
|
||||
if (!float.IsNaN(GlossStrength) && !((float)row.LegacyGloss()).NearEqual(GlossStrength))
|
||||
{
|
||||
row.LegacyGlossWrite() = (Half)GlossStrength;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Mode.Dawntrail:
|
||||
if (!float.IsNaN(Roughness) && !((float)row.DawntrailRoughness()).NearEqual(Roughness))
|
||||
{
|
||||
row.DawntrailRoughnessWrite() = (Half)Roughness;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!float.IsNaN(Metalness) && !((float)row.DawntrailMetalness()).NearEqual(Metalness))
|
||||
{
|
||||
row.DawntrailMetalnessWrite() = (Half)Metalness;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!float.IsNaN(Sheen) && !((float)row.DawntrailSheen()).NearEqual(Sheen))
|
||||
{
|
||||
row.DawntrailSheenWrite() = (Half)Sheen;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!float.IsNaN(SheenAperture) && !((float)row.DawntrailSheenAperture()).NearEqual(SheenAperture))
|
||||
{
|
||||
row.DawntrailSheenApertureWrite() = (Half)SheenAperture;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!float.IsNaN(SheenTint) && !((float)row.DawntrailSheenTint()).NearEqual(SheenTint))
|
||||
{
|
||||
row.DawntrailSheenTintWrite() = (Half)SheenTint;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public readonly ColorRow MergeOnto(ColorRow previous)
|
||||
=> new(Diffuse, Specular, Emissive, float.IsNaN(SpecularStrength) ? previous.SpecularStrength : SpecularStrength,
|
||||
float.IsNaN(GlossStrength) ? previous.GlossStrength : GlossStrength, float.IsNaN(Roughness) ? previous.Roughness : Roughness,
|
||||
float.IsNaN(Metalness) ? previous.Metalness : Metalness, float.IsNaN(Sheen) ? previous.Sheen : Sheen,
|
||||
float.IsNaN(SheenTint) ? previous.SheenTint : SheenTint, float.IsNaN(SheenAperture) ? previous.SheenAperture : SheenAperture);
|
||||
|
||||
public readonly bool IsPartial(Mode mode)
|
||||
=> mode switch
|
||||
{
|
||||
Mode.Legacy => float.IsNaN(SpecularStrength) || float.IsNaN(GlossStrength),
|
||||
Mode.Dawntrail => float.IsNaN(Roughness)
|
||||
|| float.IsNaN(Metalness)
|
||||
|| float.IsNaN(Sheen)
|
||||
|| float.IsNaN(SheenTint)
|
||||
|| float.IsNaN(SheenAperture),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
public readonly Mode GuessMode()
|
||||
=> float.IsNaN(Roughness) && float.IsNaN(Metalness) && float.IsNaN(Sheen) && float.IsNaN(SheenTint) && float.IsNaN(SheenAperture)
|
||||
? Mode.Legacy
|
||||
: Mode.Dawntrail;
|
||||
|
||||
public override readonly string ToString()
|
||||
=> $"[ColorRow Diffuse={Diffuse} Specular={Specular} Emissive={Emissive} SpecularStrength={SpecularStrength} GlossStrength={GlossStrength} Roughness={Roughness} Metalness={Metalness} Sheen={Sheen} SheenTint={SheenTint} SheenAperture={SheenAperture}]";
|
||||
}
|
||||
|
||||
internal static class ColorTableRowExtensions
|
||||
|
|
@ -103,19 +189,50 @@ internal static class ColorTableRowExtensions
|
|||
internal static Half LegacyGloss(this in ColorTableRow row)
|
||||
=> row[3];
|
||||
|
||||
internal static Half DawntrailSheen(this in ColorTableRow row)
|
||||
=> row[12];
|
||||
|
||||
internal static Half DawntrailSheenTint(this in ColorTableRow row)
|
||||
=> row[13];
|
||||
|
||||
internal static Half DawntrailSheenAperture(this in ColorTableRow row)
|
||||
=> row[14];
|
||||
|
||||
internal static Half DawntrailRoughness(this in ColorTableRow row)
|
||||
=> row[16];
|
||||
|
||||
internal static Half DawntrailMetalness(this in ColorTableRow row)
|
||||
=> row[18];
|
||||
|
||||
internal static ref Half LegacySpecularStrengthWrite(this ref ColorTableRow row)
|
||||
=> ref row[7];
|
||||
|
||||
internal static ref Half LegacyGlossWrite(this ref ColorTableRow row)
|
||||
=> ref row[3];
|
||||
|
||||
internal static ref Half DawntrailSheenWrite(this ref ColorTableRow row)
|
||||
=> ref row[12];
|
||||
|
||||
internal static ref Half DawntrailSheenTintWrite(this ref ColorTableRow row)
|
||||
=> ref row[13];
|
||||
|
||||
internal static ref Half DawntrailSheenApertureWrite(this ref ColorTableRow row)
|
||||
=> ref row[14];
|
||||
|
||||
internal static ref Half DawntrailRoughnessWrite(this ref ColorTableRow row)
|
||||
=> ref row[16];
|
||||
|
||||
internal static ref Half DawntrailMetalnessWrite(this ref ColorTableRow row)
|
||||
=> ref row[18];
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert)
|
||||
public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert, ColorRow.Mode mode)
|
||||
{
|
||||
public ColorRow Value = value;
|
||||
public bool Enabled = enabled;
|
||||
public bool Revert = revert;
|
||||
public ColorRow Value = value;
|
||||
public bool Enabled = enabled;
|
||||
public bool Revert = revert;
|
||||
public ColorRow.Mode Mode = mode;
|
||||
|
||||
public readonly bool Apply(ref MaterialValueState state)
|
||||
{
|
||||
|
|
@ -145,6 +262,8 @@ public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert)
|
|||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("Revert");
|
||||
writer.WriteValue(value.Revert);
|
||||
writer.WritePropertyName("Mode");
|
||||
writer.WriteValue(value.Mode.ToString());
|
||||
writer.WritePropertyName("DiffuseR");
|
||||
writer.WriteValue(value.Value.Diffuse.X);
|
||||
writer.WritePropertyName("DiffuseG");
|
||||
|
|
@ -157,16 +276,54 @@ public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert)
|
|||
writer.WriteValue(value.Value.Specular.Y);
|
||||
writer.WritePropertyName("SpecularB");
|
||||
writer.WriteValue(value.Value.Specular.Z);
|
||||
writer.WritePropertyName("SpecularA");
|
||||
writer.WriteValue(value.Value.SpecularStrength);
|
||||
if (!float.IsNaN(value.Value.SpecularStrength))
|
||||
{
|
||||
writer.WritePropertyName("SpecularA");
|
||||
writer.WriteValue(value.Value.SpecularStrength);
|
||||
}
|
||||
|
||||
writer.WritePropertyName("EmissiveR");
|
||||
writer.WriteValue(value.Value.Emissive.X);
|
||||
writer.WritePropertyName("EmissiveG");
|
||||
writer.WriteValue(value.Value.Emissive.Y);
|
||||
writer.WritePropertyName("EmissiveB");
|
||||
writer.WriteValue(value.Value.Emissive.Z);
|
||||
writer.WritePropertyName("Gloss");
|
||||
writer.WriteValue(value.Value.GlossStrength);
|
||||
if (!float.IsNaN(value.Value.GlossStrength))
|
||||
{
|
||||
writer.WritePropertyName("Gloss");
|
||||
writer.WriteValue(value.Value.GlossStrength);
|
||||
}
|
||||
|
||||
if (!float.IsNaN(value.Value.Roughness))
|
||||
{
|
||||
writer.WritePropertyName("Roughness");
|
||||
writer.WriteValue(value.Value.Roughness);
|
||||
}
|
||||
|
||||
if (!float.IsNaN(value.Value.Metalness))
|
||||
{
|
||||
writer.WritePropertyName("Metalness");
|
||||
writer.WriteValue(value.Value.Metalness);
|
||||
}
|
||||
|
||||
if (!float.IsNaN(value.Value.Sheen))
|
||||
{
|
||||
writer.WritePropertyName("Sheen");
|
||||
writer.WriteValue(value.Value.Sheen);
|
||||
}
|
||||
|
||||
if (!float.IsNaN(value.Value.SheenTint))
|
||||
{
|
||||
writer.WritePropertyName("SheenTint");
|
||||
writer.WriteValue(value.Value.SheenTint);
|
||||
}
|
||||
|
||||
if (!float.IsNaN(value.Value.SheenAperture))
|
||||
{
|
||||
writer.WritePropertyName("SheenAperture");
|
||||
writer.WriteValue(value.Value.SheenAperture);
|
||||
}
|
||||
|
||||
writer.WritePropertyName("Enabled");
|
||||
writer.WriteValue(value.Enabled);
|
||||
writer.WriteEndObject();
|
||||
|
|
@ -177,19 +334,26 @@ public struct MaterialValueDesign(ColorRow value, bool enabled, bool revert)
|
|||
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>());
|
||||
Set(ref existingValue.Value.Specular.X, obj["SpecularR"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Specular.Y, obj["SpecularG"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Specular.Z, obj["SpecularB"]?.Value<float>());
|
||||
Set(ref existingValue.Value.SpecularStrength, obj["SpecularA"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Emissive.X, obj["EmissiveR"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Emissive.Y, obj["EmissiveG"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Emissive.Z, obj["EmissiveB"]?.Value<float>());
|
||||
Set(ref existingValue.Value.GlossStrength, obj["Gloss"]?.Value<float>());
|
||||
existingValue.Enabled = obj["Enabled"]?.Value<bool>() ?? false;
|
||||
if (obj["Mode"]?.Value<string>() is { } mode)
|
||||
Enum.TryParse(mode, true, out existingValue.Mode);
|
||||
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>());
|
||||
Set(ref existingValue.Value.Specular.X, obj["SpecularR"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Specular.Y, obj["SpecularG"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Specular.Z, obj["SpecularB"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Emissive.X, obj["EmissiveR"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Emissive.Y, obj["EmissiveG"]?.Value<float>());
|
||||
Set(ref existingValue.Value.Emissive.Z, obj["EmissiveB"]?.Value<float>());
|
||||
existingValue.Value.SpecularStrength = obj["SpecularA"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Value.GlossStrength = obj["Gloss"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Value.Roughness = obj["Roughness"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Value.Metalness = obj["Metalness"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Value.Sheen = obj["Sheen"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Value.SheenTint = obj["SheenTint"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Value.SheenAperture = obj["SheenAperture"]?.Value<float>() ?? float.NaN;
|
||||
existingValue.Enabled = obj["Enabled"]?.Value<bool>() ?? false;
|
||||
return existingValue;
|
||||
|
||||
static void Set<T>(ref T target, T? value)
|
||||
|
|
@ -222,10 +386,13 @@ public struct MaterialValueState(
|
|||
&& DrawData.Weapon == rhsData.Weapon
|
||||
&& DrawData.Variant == rhsData.Variant
|
||||
&& DrawData.Stains == rhsData.Stains
|
||||
&& Game.NearEqual(rhsRow);
|
||||
&& rhsRow.NearEqual(Game, true);
|
||||
|
||||
public readonly MaterialValueDesign Convert()
|
||||
=> new(Model, true, false);
|
||||
=> new(Model, true, false, Model.GuessMode());
|
||||
|
||||
public readonly MaterialValueState MergeOnto(in ColorRow previous)
|
||||
=> new(Game, Model.MergeOnto(previous), DrawData, Source);
|
||||
}
|
||||
|
||||
public readonly struct MaterialValueManager<T>
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ public class InternalStateEditor(
|
|||
}
|
||||
|
||||
// Update if edited.
|
||||
state.Materials.UpdateValue(index, newValue, out _);
|
||||
state.Materials.UpdateValue(index, newValue.MergeOnto(old.Model), out _);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ public class StateEditor(
|
|||
Glamourer.Log.Verbose(
|
||||
$"Set material value in state {state.Identifier.Incognito(null)} from {oldValue} to {newValue.Game}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChangeType.MaterialValue, settings.Source, state, actors,
|
||||
new MaterialTransaction(index, oldValue, newValue.Game));
|
||||
new MaterialTransaction(index, oldValue, newValue.Game, null, null));
|
||||
}
|
||||
|
||||
public void ResetMaterialValue(object data, MaterialValueIndex index, ApplySettings settings)
|
||||
|
|
@ -243,7 +243,8 @@ public class StateEditor(
|
|||
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(StateChangeType.MaterialValue, settings.Source, state, actors, new MaterialTransaction(index, null, null));
|
||||
StateChanged.Invoke(StateChangeType.MaterialValue, settings.Source, state, actors,
|
||||
new MaterialTransaction(index, null, null, null, null));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue