Add CustomizeParameter data.

This commit is contained in:
Ottermandias 2024-01-08 22:58:44 +01:00
parent bbf460f5e0
commit 9361560350
4 changed files with 406 additions and 0 deletions

View file

@ -0,0 +1,220 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using FFXIVClientStructs.FFXIV.Shader;
namespace Glamourer.GameData;
public struct CustomizeParameterData
{
public Vector3 SkinDiffuse;
public Vector3 SkinSpecular;
public Vector3 LipDiffuse;
public Vector3 HairDiffuse;
public Vector3 HairSpecular;
public Vector3 HairHighlight;
public Vector3 LeftEye;
public Vector3 RightEye;
public Vector3 FeatureColor;
public float FacePaintUvMultiplier;
public float FacePaintUvOffset;
public float MuscleTone;
public float LipOpacity;
public Vector3 this[CustomizeParameterFlag flag]
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
readonly get
{
return flag switch
{
CustomizeParameterFlag.SkinDiffuse => SkinDiffuse,
CustomizeParameterFlag.MuscleTone => new Vector3(MuscleTone, 0, 0),
CustomizeParameterFlag.SkinSpecular => SkinSpecular,
CustomizeParameterFlag.LipDiffuse => LipDiffuse,
CustomizeParameterFlag.LipOpacity => new Vector3(LipOpacity, 0, 0),
CustomizeParameterFlag.HairDiffuse => HairDiffuse,
CustomizeParameterFlag.HairSpecular => HairSpecular,
CustomizeParameterFlag.HairHighlight => HairHighlight,
CustomizeParameterFlag.LeftEye => LeftEye,
CustomizeParameterFlag.RightEye => RightEye,
CustomizeParameterFlag.FeatureColor => FeatureColor,
CustomizeParameterFlag.FacePaintUvMultiplier => new Vector3(FacePaintUvMultiplier, 0, 0),
CustomizeParameterFlag.FacePaintUvOffset => new Vector3(FacePaintUvOffset, 0, 0),
_ => Vector3.Zero,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
set => Set(flag, value);
}
public bool Set(CustomizeParameterFlag flag, Vector3 value)
{
return flag switch
{
CustomizeParameterFlag.SkinDiffuse => SetIfDifferent(ref SkinDiffuse, value),
CustomizeParameterFlag.MuscleTone => SetIfDifferent(ref MuscleTone, Math.Clamp(value[0], -100, 100)),
CustomizeParameterFlag.SkinSpecular => SetIfDifferent(ref SkinSpecular, value),
CustomizeParameterFlag.LipDiffuse => SetIfDifferent(ref LipDiffuse, value),
CustomizeParameterFlag.LipOpacity => SetIfDifferent(ref LipOpacity, Math.Clamp(value[0], -100, 100)),
CustomizeParameterFlag.HairDiffuse => SetIfDifferent(ref HairDiffuse, value),
CustomizeParameterFlag.HairSpecular => SetIfDifferent(ref HairSpecular, value),
CustomizeParameterFlag.HairHighlight => SetIfDifferent(ref HairHighlight, value),
CustomizeParameterFlag.LeftEye => SetIfDifferent(ref LeftEye, value),
CustomizeParameterFlag.RightEye => SetIfDifferent(ref RightEye, value),
CustomizeParameterFlag.FeatureColor => SetIfDifferent(ref FeatureColor, value),
CustomizeParameterFlag.FacePaintUvMultiplier => SetIfDifferent(ref FacePaintUvMultiplier, value[0]),
CustomizeParameterFlag.FacePaintUvOffset => SetIfDifferent(ref FacePaintUvOffset, value[0]),
_ => false,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly void Apply(ref CustomizeParameter parameters, CustomizeParameterFlag flags = CustomizeParameterExtensions.All)
{
if (flags.HasFlag(CustomizeParameterFlag.SkinDiffuse))
parameters.SkinColor = Convert(SkinDiffuse, parameters.SkinColor.W);
if (flags.HasFlag(CustomizeParameterFlag.MuscleTone))
parameters.SkinColor.W = MuscleTone;
if (flags.HasFlag(CustomizeParameterFlag.SkinSpecular))
parameters.SkinFresnelValue0 = Convert(SkinSpecular, 0);
if (flags.HasFlag(CustomizeParameterFlag.LipDiffuse))
parameters.LipColor = Convert(LipDiffuse, parameters.LipColor.W);
if (flags.HasFlag(CustomizeParameterFlag.LipOpacity))
parameters.LipColor.W = LipOpacity;
if (flags.HasFlag(CustomizeParameterFlag.HairDiffuse))
parameters.MainColor = Convert(HairDiffuse);
if (flags.HasFlag(CustomizeParameterFlag.HairSpecular))
parameters.HairFresnelValue0 = Convert(HairSpecular);
if (flags.HasFlag(CustomizeParameterFlag.HairHighlight))
parameters.MeshColor = Convert(HairHighlight);
if (flags.HasFlag(CustomizeParameterFlag.LeftEye))
parameters.LeftColor = Convert(LeftEye, parameters.LeftColor.W);
if (flags.HasFlag(CustomizeParameterFlag.RightEye))
parameters.RightColor = Convert(RightEye, parameters.RightColor.W);
if (flags.HasFlag(CustomizeParameterFlag.FeatureColor))
parameters.OptionColor = Convert(FeatureColor);
if (flags.HasFlag(CustomizeParameterFlag.FacePaintUvMultiplier))
parameters.LeftColor.W = FacePaintUvMultiplier;
if (flags.HasFlag(CustomizeParameterFlag.FacePaintUvOffset))
parameters.RightColor.W = FacePaintUvOffset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly void ApplySingle(ref CustomizeParameter parameters, CustomizeParameterFlag flag)
{
switch (flag)
{
case CustomizeParameterFlag.SkinDiffuse:
parameters.SkinColor = Convert(SkinDiffuse, parameters.SkinColor.W);
break;
case CustomizeParameterFlag.MuscleTone:
parameters.SkinColor.W = MuscleTone;
break;
case CustomizeParameterFlag.SkinSpecular:
parameters.SkinFresnelValue0 = Convert(SkinSpecular, 0);
break;
case CustomizeParameterFlag.LipDiffuse:
parameters.LipColor = Convert(LipDiffuse, parameters.LipColor.W);
break;
case CustomizeParameterFlag.LipOpacity:
parameters.LipColor.W = LipOpacity;
break;
case CustomizeParameterFlag.HairDiffuse:
parameters.MainColor = Convert(HairDiffuse);
break;
case CustomizeParameterFlag.HairSpecular:
parameters.HairFresnelValue0 = Convert(HairSpecular);
break;
case CustomizeParameterFlag.HairHighlight:
parameters.MeshColor = Convert(HairHighlight);
break;
case CustomizeParameterFlag.LeftEye:
parameters.LeftColor = Convert(LeftEye, parameters.LeftColor.W);
break;
case CustomizeParameterFlag.RightEye:
parameters.RightColor = Convert(RightEye, parameters.RightColor.W);
break;
case CustomizeParameterFlag.FeatureColor:
parameters.OptionColor = Convert(FeatureColor);
break;
case CustomizeParameterFlag.FacePaintUvMultiplier:
parameters.LeftColor.W = FacePaintUvMultiplier;
break;
case CustomizeParameterFlag.FacePaintUvOffset:
parameters.RightColor.W = FacePaintUvOffset;
break;
}
}
public static CustomizeParameterData FromParameters(in CustomizeParameter parameter)
=> new()
{
FacePaintUvOffset = parameter.RightColor.W,
FacePaintUvMultiplier = parameter.LeftColor.W,
MuscleTone = parameter.SkinColor.W,
LipOpacity = parameter.LipColor.W,
SkinDiffuse = Convert(parameter.SkinColor),
SkinSpecular = Convert(parameter.SkinFresnelValue0),
LipDiffuse = Convert(parameter.LipColor),
HairDiffuse = Convert(parameter.MainColor),
HairSpecular = Convert(parameter.HairFresnelValue0),
HairHighlight = Convert(parameter.MeshColor),
LeftEye = Convert(parameter.LeftColor),
RightEye = Convert(parameter.RightColor),
FeatureColor = Convert(parameter.OptionColor),
};
public static Vector3 FromParameter(in CustomizeParameter parameter, CustomizeParameterFlag flag)
=> flag switch
{
CustomizeParameterFlag.SkinDiffuse => Convert(parameter.SkinColor),
CustomizeParameterFlag.MuscleTone => new Vector3(parameter.SkinColor.W),
CustomizeParameterFlag.SkinSpecular => Convert(parameter.SkinFresnelValue0),
CustomizeParameterFlag.LipDiffuse => Convert(parameter.LipColor),
CustomizeParameterFlag.LipOpacity => new Vector3(parameter.LipColor.W),
CustomizeParameterFlag.HairDiffuse => Convert(parameter.MainColor),
CustomizeParameterFlag.HairSpecular => Convert(parameter.HairFresnelValue0),
CustomizeParameterFlag.HairHighlight => Convert(parameter.MeshColor),
CustomizeParameterFlag.LeftEye => Convert(parameter.LeftColor),
CustomizeParameterFlag.RightEye => Convert(parameter.RightColor),
CustomizeParameterFlag.FeatureColor => Convert(parameter.OptionColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new Vector3(parameter.LeftColor.W),
CustomizeParameterFlag.FacePaintUvOffset => new Vector3(parameter.RightColor.W),
_ => Vector3.Zero,
};
private static FFXIVClientStructs.FFXIV.Common.Math.Vector4 Convert(Vector3 value, float w)
=> new(value.X * value.X, value.Y * value.Y, value.Z * value.Z, w);
private static Vector3 Convert(FFXIVClientStructs.FFXIV.Common.Math.Vector3 value)
=> new((float)Math.Sqrt(value.X), (float)Math.Sqrt(value.Y), (float)Math.Sqrt(value.Z));
private static Vector3 Convert(FFXIVClientStructs.FFXIV.Common.Math.Vector4 value)
=> new((float)Math.Sqrt(value.X), (float)Math.Sqrt(value.Y), (float)Math.Sqrt(value.Z));
private static FFXIVClientStructs.FFXIV.Common.Math.Vector3 Convert(Vector3 value)
=> new(value.X * value.X, value.Y * value.Y, value.Z * value.Z);
private static bool SetIfDifferent(ref Vector3 val, Vector3 @new)
{
@new.X = Math.Clamp(@new.X, 0, 1);
@new.Y = Math.Clamp(@new.Y, 0, 1);
@new.Z = Math.Clamp(@new.Z, 0, 1);
if (@new == val)
return false;
val = @new;
return true;
}
private static bool SetIfDifferent<T>(ref T val, T @new) where T : IEqualityOperators<T, T, bool>
{
if (@new == val)
return false;
val = @new;
return true;
}
}

View file

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Glamourer.GameData;
[Flags]
public enum CustomizeParameterFlag : ushort
{
SkinDiffuse = 0x0001,
MuscleTone = 0x0002,
SkinSpecular = 0x0004,
LipDiffuse = 0x0008,
LipOpacity = 0x0010,
HairDiffuse = 0x0020,
HairSpecular = 0x0040,
HairHighlight = 0x0080,
LeftEye = 0x0100,
RightEye = 0x0200,
FeatureColor = 0x0400,
FacePaintUvMultiplier = 0x0800,
FacePaintUvOffset = 0x1000,
}
public static class CustomizeParameterExtensions
{
public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x1FFF;
public const CustomizeParameterFlag Triples = All
& ~(CustomizeParameterFlag.MuscleTone
| CustomizeParameterFlag.LipOpacity
| CustomizeParameterFlag.FacePaintUvOffset
| CustomizeParameterFlag.FacePaintUvMultiplier);
public const CustomizeParameterFlag Percentages = CustomizeParameterFlag.MuscleTone | CustomizeParameterFlag.LipOpacity;
public const CustomizeParameterFlag Values = CustomizeParameterFlag.FacePaintUvOffset | CustomizeParameterFlag.FacePaintUvMultiplier;
public static readonly IReadOnlyList<CustomizeParameterFlag> AllFlags = [.. Enum.GetValues<CustomizeParameterFlag>()];
public static readonly IReadOnlyList<CustomizeParameterFlag> TripleFlags = AllFlags.Where(f => Triples.HasFlag(f)).ToArray();
public static readonly IReadOnlyList<CustomizeParameterFlag> PercentageFlags = AllFlags.Where(f => Percentages.HasFlag(f)).ToArray();
public static readonly IReadOnlyList<CustomizeParameterFlag> ValueFlags = AllFlags.Where(f => Values.HasFlag(f)).ToArray();
public static int Count(this CustomizeParameterFlag flag)
=> Triples.HasFlag(flag) ? 3 : 1;
public static IEnumerable<CustomizeParameterFlag> Iterate(this CustomizeParameterFlag flags)
=> AllFlags.Where(f => flags.HasFlag(f));
public static int ToInternalIndex(this CustomizeParameterFlag flag)
=> BitOperations.TrailingZeroCount((uint)flag);
}

View file

@ -0,0 +1,38 @@
using System;
using System.Numerics;
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.State;
namespace Glamourer.Gui.Customization;
public ref struct CustomizeParameterDrawData(CustomizeParameterFlag flag, in DesignData data)
{
public readonly CustomizeParameterFlag Flag = flag;
public bool Locked;
public bool DisplayApplication;
public Action<Vector3> ValueSetter = null!;
public Action<bool> ApplySetter = null!;
public Vector3 CurrentValue = data.Parameters[flag];
public bool CurrentApply;
public static CustomizeParameterDrawData FromDesign(DesignManager manager, Design design, CustomizeParameterFlag flag)
=> new(flag, design.DesignData)
{
Locked = design.WriteProtected(),
DisplayApplication = true,
CurrentApply = design.DoApplyParameter(flag),
ValueSetter = v => manager.ChangeCustomizeParameter(design, flag, v),
ApplySetter = v => manager.ChangeApplyParameter(design, flag, v),
};
public static CustomizeParameterDrawData FromState(StateManager manager, ActorState state, CustomizeParameterFlag flag)
=> new(flag, state.ModelData)
{
Locked = state.IsLocked,
DisplayApplication = false,
ValueSetter = v => manager.ChangeCustomizeParameter(state, flag, v, StateChanged.Source.Manual),
};
}

View file

@ -0,0 +1,96 @@
using Glamourer.Designs;
using Glamourer.GameData;
using Glamourer.State;
using System.Numerics;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using OtterGui.Services;
namespace Glamourer.Gui.Customization;
public class CustomizeParameterDrawer(Configuration config) : IService
{
public void Draw(DesignManager designManager, Design design)
{
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
DrawColorInput(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
DrawPercentageInput(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
foreach (var flag in CustomizeParameterExtensions.ValueFlags)
DrawValueInput(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
}
public void Draw(StateManager stateManager, ActorState state)
{
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
DrawColorInput(CustomizeParameterDrawData.FromState(stateManager, state, flag));
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
DrawPercentageInput(CustomizeParameterDrawData.FromState(stateManager, state, flag));
foreach (var flag in CustomizeParameterExtensions.ValueFlags)
DrawValueInput(CustomizeParameterDrawData.FromState(stateManager, state, flag));
}
private void DrawColorInput(in CustomizeParameterDrawData data)
{
using var id = ImRaii.PushId((int)data.Flag);
var value = data.CurrentValue;
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.ColorEdit3("##value", ref value, ImGuiColorEditFlags.Float))
data.ValueSetter(value);
}
DrawApplyAndLabel(data);
}
private void DrawValueInput(in CustomizeParameterDrawData data)
{
using var id = ImRaii.PushId((int)data.Flag);
var value = data.CurrentValue[0];
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.InputFloat("##value", ref value, 0.1f, 0.5f))
data.ValueSetter(new Vector3(value));
}
DrawApplyAndLabel(data);
}
private void DrawPercentageInput(in CustomizeParameterDrawData data)
{
using var id = ImRaii.PushId((int)data.Flag);
var value = data.CurrentValue[0] * 100f;
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.SliderFloat("##value", ref value, 0, 100, "%.2f", ImGuiSliderFlags.AlwaysClamp))
data.ValueSetter(new Vector3(value / 100f));
}
DrawApplyAndLabel(data);
}
private static void DrawApply(in CustomizeParameterDrawData data)
{
if (UiHelpers.DrawCheckbox("##apply", "Apply this custom parameter when applying the Design.", data.CurrentApply, out var enabled,
data.Locked))
data.ApplySetter(enabled);
}
private void DrawApplyAndLabel(in CustomizeParameterDrawData data)
{
if (data.DisplayApplication && !config.HideApplyCheckmarks)
{
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
DrawApply(data);
}
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.TextUnformatted(data.Flag.ToString());
}
}