Improve Palette+ Import.

This commit is contained in:
Ottermandias 2024-01-16 21:33:18 +01:00
parent 13bf83b2b7
commit ed0ee6439a
4 changed files with 256 additions and 156 deletions

View file

@ -14,8 +14,8 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
private readonly Dictionary<Design, CustomizeParameterData> _lastData = [];
private string _paletteName = string.Empty;
private CustomizeParameterData _data;
private CustomizeParameterFlag _flags;
private float _width;
private bool _foundPalette;
public void Draw(DesignManager designManager, Design design)
@ -35,6 +35,24 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
DrawValueInput(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
}
private void DrawPaletteCombo()
{
using var id = ImRaii.PushId("Palettes");
using var combo = ImRaii.Combo("##import", _paletteName.Length > 0 ? _paletteName : "Select Palette...");
if (!combo)
return;
foreach (var (name, (palette, flags)) in import.Data)
{
if (!ImGui.Selectable(name, _paletteName == name))
continue;
_paletteName = name;
_data = palette;
_flags = flags;
}
}
private void DrawPaletteImport(DesignManager manager, Design design)
{
if (!config.ShowPalettePlusImport)
@ -42,11 +60,7 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
if (ImGui.InputTextWithHint("##import", "Palette Name...", ref _paletteName, 256))
{
_data = design.DesignData.Parameters;
_foundPalette = import.TryRead(_paletteName, ref _data);
}
DrawPaletteCombo();
ImGui.SameLine(0, spacing);
var value = true;
@ -59,16 +73,13 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
ImGuiUtil.HoverTooltip("Hide the Palette+ Import bar from all designs. You can re-enable it in Glamourers interface settings.");
var buttonWidth = new Vector2((_width - spacing) / 2, 0);
var tt = _foundPalette
var tt = _paletteName.Length > 0
? $"Apply the imported data from the Palette+ palette [{_paletteName}] to this design."
: $"The palette [{_paletteName}] could not be imported from your Palette+ configuration.";
if (ImGuiUtil.DrawDisabledButton("Apply Import", buttonWidth, tt, !_foundPalette || design.WriteProtected()))
: "Please select a palette first.";
if (ImGuiUtil.DrawDisabledButton("Apply Import", buttonWidth, tt, _paletteName.Length == 0 || design.WriteProtected()))
{
// Reload Data in case anything changed since entering text.
_data = design.DesignData.Parameters;
_foundPalette = import.TryRead(_paletteName, ref _data);
_lastData[design] = design.DesignData.Parameters;
foreach (var parameter in CustomizeParameterExtensions.AllFlags)
foreach (var parameter in _flags.Iterate())
manager.ChangeCustomizeParameter(design, parameter, _data[parameter]);
}

View file

@ -6,6 +6,7 @@ using Dalamud.Plugin.Services;
using Glamourer.Designs;
using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop;
using Glamourer.Interop.PalettePlus;
using Glamourer.Interop.Penumbra;
using Glamourer.Services;
using Glamourer.State;
@ -16,34 +17,21 @@ using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs;
public class SettingsTab : ITab
public class SettingsTab(
Configuration config,
DesignFileSystemSelector selector,
CodeService codeService,
PenumbraAutoRedraw autoRedraw,
ContextMenuService contextMenuService,
UiBuilder uiBuilder,
GlamourerChangelog changelog,
FunModule funModule,
IKeyState keys,
DesignColorUi designColorUi,
PaletteImport paletteImport)
: ITab
{
private readonly VirtualKey[] _validKeys;
private readonly Configuration _config;
private readonly DesignFileSystemSelector _selector;
private readonly CodeService _codeService;
private readonly PenumbraAutoRedraw _autoRedraw;
private readonly ContextMenuService _contextMenuService;
private readonly UiBuilder _uiBuilder;
private readonly GlamourerChangelog _changelog;
private readonly FunModule _funModule;
private readonly DesignColorUi _designColorUi;
public SettingsTab(Configuration config, DesignFileSystemSelector selector, CodeService codeService, PenumbraAutoRedraw autoRedraw,
ContextMenuService contextMenuService, UiBuilder uiBuilder, GlamourerChangelog changelog, FunModule funModule, IKeyState keys,
DesignColorUi designColorUi)
{
_config = config;
_selector = selector;
_codeService = codeService;
_autoRedraw = autoRedraw;
_contextMenuService = contextMenuService;
_uiBuilder = uiBuilder;
_changelog = changelog;
_funModule = funModule;
_designColorUi = designColorUi;
_validKeys = keys.GetValidVirtualKeys().Prepend(VirtualKey.NO_KEY).ToArray();
}
private readonly VirtualKey[] _validKeys = keys.GetValidVirtualKeys().Prepend(VirtualKey.NO_KEY).ToArray();
public ReadOnlySpan<byte> Label
=> "Settings"u8;
@ -58,7 +46,7 @@ public class SettingsTab : ITab
Checkbox("Enable Auto Designs",
"Enable the application of designs associated to characters in the Automation tab to be applied automatically.",
_config.EnableAutoDesigns, v => _config.EnableAutoDesigns = v);
config.EnableAutoDesigns, v => config.EnableAutoDesigns = v);
ImGui.NewLine();
ImGui.NewLine();
ImGui.NewLine();
@ -71,7 +59,7 @@ public class SettingsTab : ITab
DrawCodes();
}
MainWindow.DrawSupportButtons(_changelog.Changelog);
MainWindow.DrawSupportButtons(changelog.Changelog);
}
private void DrawBehaviorSettings()
@ -81,22 +69,23 @@ public class SettingsTab : ITab
Checkbox("Use Replacement Gear for Gear Unavailable to Your Race or Gender",
"Use different gender- and race-appropriate models as a substitute when detecting certain items not available for a characters current gender and race.",
_config.UseRestrictedGearProtection, v => _config.UseRestrictedGearProtection = v);
config.UseRestrictedGearProtection, v => config.UseRestrictedGearProtection = v);
Checkbox("Do Not Apply Unobtained Items in Automation",
"Enable this if you want automatically applied designs to only consider items and customizations you have actually unlocked once, and skip those you have not.",
_config.UnlockedItemMode, v => _config.UnlockedItemMode = v);
config.UnlockedItemMode, v => config.UnlockedItemMode = v);
Checkbox("Enable Festival Easter-Eggs",
"Glamourer may do some fun things on specific dates. Disable this if you do not want your experience disrupted by this.",
_config.DisableFestivals == 0, v => _config.DisableFestivals = v ? (byte)0 : (byte)2);
config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2);
Checkbox("Auto-Reload Gear",
"Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection.",
_config.AutoRedrawEquipOnChanges, _autoRedraw.SetState);
config.AutoRedrawEquipOnChanges, autoRedraw.SetState);
Checkbox("Revert Manual Changes on Zone Change",
"Restores the old behaviour of reverting your character to its game or automation base whenever you change the zone.",
_config.RevertManualChangesOnZoneChange, v => _config.RevertManualChangesOnZoneChange = v);
config.RevertManualChangesOnZoneChange, v => config.RevertManualChangesOnZoneChange = v);
Checkbox("Enable Advanced Customization Options",
"Enable the display and editing of advanced customization options like arbitrary colors.",
_config.UseAdvancedParameters, v => _config.UseAdvancedParameters = v);
config.UseAdvancedParameters, v => config.UseAdvancedParameters = v);
PaletteImportButton();
ImGui.NewLine();
}
@ -107,59 +96,59 @@ public class SettingsTab : ITab
EphemeralCheckbox("Show Quick Design Bar",
"Show a bar separate from the main window that allows you to quickly apply designs or revert your character and target.",
_config.Ephemeral.ShowDesignQuickBar, v => _config.Ephemeral.ShowDesignQuickBar = v);
config.Ephemeral.ShowDesignQuickBar, v => config.Ephemeral.ShowDesignQuickBar = v);
EphemeralCheckbox("Lock Quick Design Bar", "Prevent the quick design bar from being moved and lock it in place.",
_config.Ephemeral.LockDesignQuickBar,
v => _config.Ephemeral.LockDesignQuickBar = v);
config.Ephemeral.LockDesignQuickBar,
v => config.Ephemeral.LockDesignQuickBar = v);
if (Widget.ModifiableKeySelector("Hotkey to Toggle Quick Design Bar", "Set a hotkey that opens or closes the quick design bar.",
100 * ImGuiHelpers.GlobalScale,
_config.ToggleQuickDesignBar, v => _config.ToggleQuickDesignBar = v, _validKeys))
_config.Save();
config.ToggleQuickDesignBar, v => config.ToggleQuickDesignBar = v, _validKeys))
config.Save();
Checkbox("Show Quick Design Bar in Main Window",
"Show the quick design bar in the tab selection part of the main window, too.",
_config.ShowQuickBarInTabs, v => _config.ShowQuickBarInTabs = v);
config.ShowQuickBarInTabs, v => config.ShowQuickBarInTabs = v);
ImGui.Dummy(Vector2.Zero);
ImGui.Separator();
ImGui.Dummy(Vector2.Zero);
Checkbox("Enable Game Context Menus", "Whether to show a Try On via Glamourer button on context menus for equippable items.",
_config.EnableGameContextMenu, v =>
config.EnableGameContextMenu, v =>
{
_config.EnableGameContextMenu = v;
config.EnableGameContextMenu = v;
if (v)
_contextMenuService.Enable();
contextMenuService.Enable();
else
_contextMenuService.Disable();
contextMenuService.Disable();
});
Checkbox("Hide Window in Cutscenes", "Whether the main Glamourer window should automatically be hidden when entering cutscenes or not.",
_config.HideWindowInCutscene,
config.HideWindowInCutscene,
v =>
{
_config.HideWindowInCutscene = v;
_uiBuilder.DisableCutsceneUiHide = !v;
config.HideWindowInCutscene = v;
uiBuilder.DisableCutsceneUiHide = !v;
});
EphemeralCheckbox("Lock Main Window", "Prevent the main window from being moved and lock it in place.",
_config.Ephemeral.LockMainWindow,
v => _config.Ephemeral.LockMainWindow = v);
config.Ephemeral.LockMainWindow,
v => config.Ephemeral.LockMainWindow = v);
Checkbox("Open Main Window at Game Start", "Whether the main Glamourer window should be open or closed after launching the game.",
_config.OpenWindowAtStart, v => _config.OpenWindowAtStart = v);
config.OpenWindowAtStart, v => config.OpenWindowAtStart = v);
ImGui.Dummy(Vector2.Zero);
ImGui.Separator();
ImGui.Dummy(Vector2.Zero);
Checkbox("Smaller Equip Display", "Use single-line display without icons and small dye buttons instead of double-line display.",
_config.SmallEquip, v => _config.SmallEquip = v);
config.SmallEquip, v => config.SmallEquip = v);
Checkbox("Show Application Checkboxes",
"Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules.",
!_config.HideApplyCheckmarks, v => _config.HideApplyCheckmarks = !v);
!config.HideApplyCheckmarks, v => config.HideApplyCheckmarks = !v);
if (Widget.DoubleModifierSelector("Design Deletion Modifier",
"A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale,
_config.DeleteDesignModifier, v => _config.DeleteDesignModifier = v))
_config.Save();
config.DeleteDesignModifier, v => config.DeleteDesignModifier = v))
config.Save();
Checkbox("Auto-Open Design Folders",
"Have design folders open or closed as their default state after launching.", _config.OpenFoldersByDefault,
v => _config.OpenFoldersByDefault = v);
"Have design folders open or closed as their default state after launching.", config.OpenFoldersByDefault,
v => config.OpenFoldersByDefault = v);
DrawFolderSortType();
ImGui.Dummy(Vector2.Zero);
@ -168,19 +157,36 @@ public class SettingsTab : ITab
Checkbox("Show all Application Rule Checkboxes for Automation",
"Show multiple separate application rule checkboxes for automated designs, instead of a single box for enabling or disabling.",
_config.ShowAllAutomatedApplicationRules, v => _config.ShowAllAutomatedApplicationRules = v);
config.ShowAllAutomatedApplicationRules, v => config.ShowAllAutomatedApplicationRules = v);
Checkbox("Show Unobtained Item Warnings",
"Show information whether you have unlocked all items and customizations in your automated design or not.",
_config.ShowUnlockedItemWarnings, v => _config.ShowUnlockedItemWarnings = v);
if (_config.UseAdvancedParameters)
config.ShowUnlockedItemWarnings, v => config.ShowUnlockedItemWarnings = v);
if (config.UseAdvancedParameters)
{
Checkbox("Show Palette+ Import Button",
"Show the import button that allows you to import Palette+ palettes onto a design in the Advanced Customization options section for designs.",
_config.ShowPalettePlusImport, v => _config.ShowPalettePlusImport = v);
Checkbox("Debug Mode", "Show the debug tab. Only useful for debugging or advanced use. Not recommended in general.", _config.DebugMode,
v => _config.DebugMode = v);
config.ShowPalettePlusImport, v => config.ShowPalettePlusImport = v);
using var id = ImRaii.PushId(1);
PaletteImportButton();
}
Checkbox("Debug Mode", "Show the debug tab. Only useful for debugging or advanced use. Not recommended in general.", config.DebugMode,
v => config.DebugMode = v);
ImGui.NewLine();
}
private void PaletteImportButton()
{
if (!config.UseAdvancedParameters || !config.ShowPalettePlusImport)
return;
ImGui.SameLine();
if (ImGui.Button("Import Palette+ to Designs"))
paletteImport.ImportDesigns();
ImGuiUtil.HoverTooltip(
$"Import all existing Palettes from your Palette+ Config into Designs at PalettePlus/[Name] if these do not exist. Existing Palettes are:\n\n\t - {string.Join("\n\t - ", paletteImport.Data.Keys)}");
}
/// <summary> Draw the entire Color subsection. </summary>
private void DrawColorSettings()
{
@ -190,7 +196,7 @@ public class SettingsTab : ITab
using (var tree = ImRaii.TreeNode("Custom Design Colors"))
{
if (tree)
_designColorUi.Draw();
designColorUi.Draw();
}
using (var tree = ImRaii.TreeNode("Color Settings"))
@ -199,9 +205,9 @@ public class SettingsTab : ITab
foreach (var color in Enum.GetValues<ColorId>())
{
var (defaultColor, name, description) = color.Data();
var currentColor = _config.Colors.TryGetValue(color, out var current) ? current : defaultColor;
if (Widget.ColorPicker(name, description, currentColor, c => _config.Colors[color] = c, defaultColor))
_config.Save();
var currentColor = config.Colors.TryGetValue(color, out var current) ? current : defaultColor;
if (Widget.ColorPicker(name, description, currentColor, c => config.Colors[color] = c, defaultColor))
config.Save();
}
}
@ -228,10 +234,10 @@ public class SettingsTab : ITab
using (var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, _currentCode.Length > 0))
{
var color = _codeService.CheckCode(_currentCode) != null ? ColorId.ActorAvailable : ColorId.ActorUnavailable;
var color = codeService.CheckCode(_currentCode) != null ? ColorId.ActorAvailable : ColorId.ActorUnavailable;
using var c = ImRaii.PushColor(ImGuiCol.Border, color.Value(), _currentCode.Length > 0);
if (ImGui.InputTextWithHint("##Code", "Enter Cheat Code...", ref _currentCode, 512, ImGuiInputTextFlags.EnterReturnsTrue))
if (_codeService.AddCode(_currentCode))
if (codeService.AddCode(_currentCode))
_currentCode = string.Empty;
}
@ -240,30 +246,30 @@ public class SettingsTab : ITab
DrawCodeHints();
if (_config.Codes.Count <= 0)
if (config.Codes.Count <= 0)
return;
for (var i = 0; i < _config.Codes.Count; ++i)
for (var i = 0; i < config.Codes.Count; ++i)
{
var (code, state) = _config.Codes[i];
var action = _codeService.CheckCode(code);
var (code, state) = config.Codes[i];
var action = codeService.CheckCode(code);
if (action == null)
continue;
if (ImGui.Checkbox(code, ref state))
{
action(state);
_codeService.SaveState();
codeService.SaveState();
}
}
if (ImGui.Button("Who am I?!?"))
_funModule.WhoAmI();
funModule.WhoAmI();
ImGui.SameLine();
if (ImGui.Button("Who is that!?!"))
_funModule.WhoIsThat();
funModule.WhoIsThat();
}
private void DrawCodeHints()
@ -279,7 +285,7 @@ public class SettingsTab : ITab
if (ImGui.Checkbox(string.Empty, ref tmp) && tmp != current)
{
setter(tmp);
_config.Save();
config.Save();
}
ImGui.SameLine();
@ -294,7 +300,7 @@ public class SettingsTab : ITab
if (ImGui.Checkbox(string.Empty, ref tmp) && tmp != current)
{
setter(tmp);
_config.Ephemeral.Save();
config.Ephemeral.Save();
}
ImGui.SameLine();
@ -304,7 +310,7 @@ public class SettingsTab : ITab
/// <summary> Different supported sort modes as a combo. </summary>
private void DrawFolderSortType()
{
var sortMode = _config.SortMode;
var sortMode = config.SortMode;
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
using (var combo = ImRaii.Combo("##sortMode", sortMode.Name))
{
@ -313,9 +319,9 @@ public class SettingsTab : ITab
{
if (ImGui.Selectable(val.Name, val.GetType() == sortMode.GetType()) && val.GetType() != sortMode.GetType())
{
_config.SortMode = val;
_selector.SetFilterDirty();
_config.Save();
config.SortMode = val;
selector.SetFilterDirty();
config.Save();
}
ImGuiUtil.HoverTooltip(val.Description);

View file

@ -1,94 +1,177 @@
using Dalamud.Plugin;
using Glamourer.Designs;
using Glamourer.GameData;
using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Services;
namespace Glamourer.Interop.PalettePlus;
public class PaletteImport(DalamudPluginInterface pluginInterface) : IService
public class PaletteImport(DalamudPluginInterface pluginInterface, DesignManager designManager, DesignFileSystem designFileSystem) : IService
{
private string ConfigFile
=> Path.Combine(Path.GetDirectoryName(pluginInterface.GetPluginConfigDirectory())!, "PalettePlus.json");
public bool TryRead(string name, ref CustomizeParameterData data)
private readonly Dictionary<string, (CustomizeParameterData, CustomizeParameterFlag)> _data = [];
public IReadOnlyDictionary<string, (CustomizeParameterData, CustomizeParameterFlag)> Data
{
if (name.Length == 0)
return false;
get
{
if (_data.Count > 0)
return _data;
PopulateDict();
return _data;
}
}
public void ImportDesigns()
{
foreach (var (name, (palette, flags)) in Data)
{
var fullPath = $"PalettePlus/{name}";
if (designFileSystem.Find(fullPath, out _))
{
Glamourer.Log.Information($"Skipped adding palette {name} because {fullPath} already exists.");
continue;
}
var design = designManager.CreateEmpty(fullPath, true);
design.ApplyCustomize = 0;
design.ApplyEquip = 0;
design.ApplyCrest = 0;
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.VisorState, false);
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.HatState, false);
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.WeaponState, false);
foreach (var flag in flags.Iterate())
{
designManager.ChangeApplyParameter(design, flag, true);
designManager.ChangeCustomizeParameter(design, flag, palette[flag]);
}
Glamourer.Log.Information($"Added design for palette {name} at {fullPath}.");
}
}
private void PopulateDict()
{
var path = ConfigFile;
if (!File.Exists(path))
return false;
return;
try
{
var text = File.ReadAllText(path);
var obj = JObject.Parse(text);
var palettes = obj["SavedPalettes"];
if (palettes == null)
return;
var target = palettes?.Children().FirstOrDefault(c => c["Name"]?.ToObject<string>() == name);
if (target == null)
return false;
var conditions = target["Conditions"]?.ToObject<int>() ?? 0;
var parameters = target["ShaderParams"];
if (parameters == null)
return false;
var discard = 0f;
Parse("SkinTone", ref data.SkinDiffuse.X, ref data.SkinDiffuse.Y, ref data.SkinDiffuse.Z, ref discard);
Parse("SkinGloss", ref data.SkinSpecular.X, ref data.SkinSpecular.Y, ref data.SkinSpecular.Z, ref discard);
Parse("LipColor", ref data.LipDiffuse.X, ref data.LipDiffuse.Y, ref data.LipDiffuse.Z, ref data.LipDiffuse.W);
Parse("HairColor", ref data.HairDiffuse.X, ref data.HairDiffuse.Y, ref data.HairDiffuse.Z, ref discard);
Parse("HairShine", ref data.HairSpecular.X, ref data.HairSpecular.Y, ref data.HairSpecular.Z, ref discard);
Parse("LeftEyeColor", ref data.LeftEye.X, ref data.LeftEye.Y, ref data.LeftEye.Z, ref discard);
Parse("RaceFeatureColor", ref data.FeatureColor.X, ref data.FeatureColor.Y, ref data.FeatureColor.Z, ref discard);
Parse("FacePaintColor", ref data.DecalColor.X, ref data.DecalColor.Y, ref data.DecalColor.Z, ref data.DecalColor.W);
// Highlights is flag 2.
if ((conditions & 2) == 2)
Parse("HighlightsColor", ref data.HairHighlight.X, ref data.HairHighlight.Y, ref data.HairHighlight.Z, ref discard);
// Heterochromia is flag 1
if ((conditions & 1) == 1)
Parse("RightEyeColor", ref data.RightEye.X, ref data.RightEye.Y, ref data.RightEye.Z, ref discard);
ParseSingle("FacePaintOffset", ref data.FacePaintUvOffset);
ParseSingle("FacePaintWidth", ref data.FacePaintUvMultiplier);
ParseSingle("MuscleTone", ref data.MuscleTone);
return true;
void Parse(string attribute, ref float x, ref float y, ref float z, ref float w)
foreach (var child in palettes.Children())
{
var node = parameters![attribute];
if (node == null)
return;
var name = child["Name"]?.ToObject<string>() ?? string.Empty;
if (name.Length == 0)
continue;
var xVal = node["X"]?.ToObject<float>();
var yVal = node["Y"]?.ToObject<float>();
var zVal = node["Z"]?.ToObject<float>();
var wVal = node["W"]?.ToObject<float>();
if (xVal.HasValue)
x = xVal.Value > 0 ? MathF.Sqrt(xVal.Value) : -MathF.Sqrt(-xVal.Value);
if (yVal.HasValue)
y = yVal.Value > 0 ? MathF.Sqrt(yVal.Value) : -MathF.Sqrt(-yVal.Value);
if (zVal.HasValue)
z = zVal.Value > 0 ? MathF.Sqrt(zVal.Value) : -MathF.Sqrt(-zVal.Value);
if (wVal.HasValue)
w = wVal.Value;
}
var conditions = child["Conditions"]?.ToObject<int>() ?? 0;
var parameters = child["ShaderParams"];
if (parameters == null)
continue;
void ParseSingle(string attribute, ref float value)
{
var node = parameters![attribute]?.ToObject<float>();
if (node.HasValue)
value = node.Value;
var orig = name;
var counter = 1;
while (_data.ContainsKey(name))
name = $"{orig} #{++counter}";
var data = new CustomizeParameterData();
CustomizeParameterFlag flags = 0;
var discard = 0f;
Parse("SkinTone", CustomizeParameterFlag.SkinDiffuse,
ref data.SkinDiffuse.X, ref data.SkinDiffuse.Y, ref data.SkinDiffuse.Z, ref discard);
Parse("SkinGloss", CustomizeParameterFlag.SkinSpecular,
ref data.SkinSpecular.X, ref data.SkinSpecular.Y, ref data.SkinSpecular.Z, ref discard);
Parse("LipColor", CustomizeParameterFlag.LipDiffuse,
ref data.LipDiffuse.X, ref data.LipDiffuse.Y, ref data.LipDiffuse.Z, ref data.LipDiffuse.W);
Parse("HairColor", CustomizeParameterFlag.HairDiffuse,
ref data.HairDiffuse.X, ref data.HairDiffuse.Y, ref data.HairDiffuse.Z, ref discard);
Parse("HairShine", CustomizeParameterFlag.HairSpecular,
ref data.HairSpecular.X, ref data.HairSpecular.Y, ref data.HairSpecular.Z, ref discard);
Parse("LeftEyeColor", CustomizeParameterFlag.LeftEye,
ref data.LeftEye.X, ref data.LeftEye.Y, ref data.LeftEye.Z, ref discard);
Parse("RaceFeatureColor", CustomizeParameterFlag.FeatureColor,
ref data.FeatureColor.X, ref data.FeatureColor.Y, ref data.FeatureColor.Z, ref discard);
Parse("FacePaintColor", CustomizeParameterFlag.DecalColor,
ref data.DecalColor.X, ref data.DecalColor.Y, ref data.DecalColor.Z, ref data.DecalColor.W);
// Highlights is flag 2.
if ((conditions & 2) == 2)
Parse("HighlightsColor", CustomizeParameterFlag.HairHighlight,
ref data.HairHighlight.X, ref data.HairHighlight.Y, ref data.HairHighlight.Z, ref discard);
// Heterochromia is flag 1
if ((conditions & 1) == 1)
{
Parse("RightEyeColor", CustomizeParameterFlag.RightEye,
ref data.RightEye.X, ref data.RightEye.Y, ref data.RightEye.Z, ref discard);
}
else if (flags.HasFlag(CustomizeParameterFlag.LeftEye))
{
data.RightEye = data.LeftEye;
flags |= CustomizeParameterFlag.RightEye;
}
ParseSingle("FacePaintOffset", CustomizeParameterFlag.FacePaintUvOffset, ref data.FacePaintUvOffset);
ParseSingle("FacePaintWidth", CustomizeParameterFlag.FacePaintUvMultiplier, ref data.FacePaintUvMultiplier);
ParseSingle("MuscleTone", CustomizeParameterFlag.MuscleTone, ref data.MuscleTone);
while (!_data.TryAdd(name, (data, flags)))
name = $"{orig} ({++counter})";
continue;
void Parse(string attribute, CustomizeParameterFlag flag, ref float x, ref float y, ref float z, ref float w)
{
var node = parameters![attribute];
if (node == null)
return;
flags |= flag;
var xVal = node["X"]?.ToObject<float>();
var yVal = node["Y"]?.ToObject<float>();
var zVal = node["Z"]?.ToObject<float>();
var wVal = node["W"]?.ToObject<float>();
if (xVal.HasValue)
x = xVal.Value > 0 ? MathF.Sqrt(xVal.Value) : -MathF.Sqrt(-xVal.Value);
if (yVal.HasValue)
y = yVal.Value > 0 ? MathF.Sqrt(yVal.Value) : -MathF.Sqrt(-yVal.Value);
if (zVal.HasValue)
z = zVal.Value > 0 ? MathF.Sqrt(zVal.Value) : -MathF.Sqrt(-zVal.Value);
if (wVal.HasValue)
w = wVal.Value;
}
void ParseSingle(string attribute, CustomizeParameterFlag flag, ref float value)
{
var node = parameters![attribute]?.ToObject<float>();
if (!node.HasValue)
return;
value = node.Value;
flags |= flag;
}
}
}
catch (Exception ex)
{
Glamourer.Log.Error($"Could not read Palette+ configuration:\n{ex}");
return false;
}
}
public bool TryRead(string name, ref CustomizeParameterData data)
{
if (!Data.TryGetValue(name, out var t))
return false;
foreach (var flag in t.Item2.Iterate())
data[flag] = t.Item1[flag];
return true;
}
}

@ -1 +1 @@
Subproject commit 6d6e7b37c31bc82b8b2811c85a09f67fb0434f8a
Subproject commit 9259090121b26f097948e7bbd83b32708ea0410d