Add decal color, fix some bugs and improve logic and handling somewhat.

This commit is contained in:
Ottermandias 2024-01-09 16:05:18 +01:00
parent 6158bcb2f9
commit 5ea779a34c
14 changed files with 351 additions and 174 deletions

View file

@ -371,7 +371,7 @@ public class DesignBase
};
}
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
foreach (var flag in CustomizeParameterExtensions.RgbFlags)
{
ret[flag.ToString()] = new JObject()
{
@ -382,6 +382,18 @@ public class DesignBase
};
}
foreach (var flag in CustomizeParameterExtensions.RgbaFlags)
{
ret[flag.ToString()] = new JObject()
{
["Red"] = DesignData.Parameters[flag][0],
["Green"] = DesignData.Parameters[flag][1],
["Blue"] = DesignData.Parameters[flag][2],
["Alpha"] = DesignData.Parameters[flag][3],
["Apply"] = DoApplyParameter(flag),
};
}
return ret;
}
@ -424,7 +436,7 @@ public class DesignBase
continue;
var value = token["Value"]?.ToObject<float>() ?? 0f;
design.GetDesignDataRef().Parameters[flag] = new Vector3(value);
design.GetDesignDataRef().Parameters[flag] = new CustomizeParameterValue(value);
}
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
@ -433,10 +445,10 @@ public class DesignBase
continue;
var value = Math.Clamp(token["Percentage"]?.ToObject<float>() ?? 0f, 0f, 1f);
design.GetDesignDataRef().Parameters[flag] = new Vector3(value);
design.GetDesignDataRef().Parameters[flag] = new CustomizeParameterValue(value);
}
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
foreach (var flag in CustomizeParameterExtensions.RgbFlags)
{
if (!TryGetToken(flag, out var token))
continue;
@ -444,7 +456,19 @@ public class DesignBase
var r = Math.Clamp(token["Red"]?.ToObject<float>() ?? 0f, 0, 1);
var g = Math.Clamp(token["Green"]?.ToObject<float>() ?? 0f, 0, 1);
var b = Math.Clamp(token["Blue"]?.ToObject<float>() ?? 0f, 0, 1);
design.GetDesignDataRef().Parameters[flag] = new Vector3(r, g, b);
design.GetDesignDataRef().Parameters[flag] = new CustomizeParameterValue(r, g, b);
}
foreach (var flag in CustomizeParameterExtensions.RgbaFlags)
{
if (!TryGetToken(flag, out var token))
continue;
var r = Math.Clamp(token["Red"]?.ToObject<float>() ?? 0f, 0, 1);
var g = Math.Clamp(token["Green"]?.ToObject<float>() ?? 0f, 0, 1);
var b = Math.Clamp(token["Blue"]?.ToObject<float>() ?? 0f, 0, 1);
var a = Math.Clamp(token["Alpha"]?.ToObject<float>() ?? 0f, 0, 1);
design.GetDesignDataRef().Parameters[flag] = new CustomizeParameterValue(r, g, b, a);
}
return;
@ -461,7 +485,7 @@ public class DesignBase
}
design.ApplyParameters &= ~flag;
design.GetDesignDataRef().Parameters[flag] = Vector3.Zero;
design.GetDesignDataRef().Parameters[flag] = CustomizeParameterValue.Zero;
return false;
}
}

View file

@ -418,7 +418,7 @@ public class DesignManager
}
/// <summary> Change a customize parameter. </summary>
public void ChangeCustomizeParameter(Design design, CustomizeParameterFlag flag, Vector3 value)
public void ChangeCustomizeParameter(Design design, CustomizeParameterFlag flag, CustomizeParameterValue value)
{
var old = design.DesignData.Parameters[flag];
if (!design.GetDesignDataRef().Parameters.Set(flag, value))

View file

@ -13,55 +13,58 @@ public struct CustomizeParameterData
public Vector3 LeftEye;
public Vector3 RightEye;
public Vector3 FeatureColor;
public Vector4 DecalColor;
public float FacePaintUvMultiplier;
public float FacePaintUvOffset;
public float MuscleTone;
public float LipOpacity;
public Vector3 this[CustomizeParameterFlag flag]
public CustomizeParameterValue 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,
CustomizeParameterFlag.SkinDiffuse => new CustomizeParameterValue(SkinDiffuse),
CustomizeParameterFlag.MuscleTone => new CustomizeParameterValue(MuscleTone),
CustomizeParameterFlag.SkinSpecular => new CustomizeParameterValue(SkinSpecular),
CustomizeParameterFlag.LipDiffuse => new CustomizeParameterValue(LipDiffuse),
CustomizeParameterFlag.LipOpacity => new CustomizeParameterValue(LipOpacity),
CustomizeParameterFlag.HairDiffuse => new CustomizeParameterValue(HairDiffuse),
CustomizeParameterFlag.HairSpecular => new CustomizeParameterValue(HairSpecular),
CustomizeParameterFlag.HairHighlight => new CustomizeParameterValue(HairHighlight),
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(LeftEye),
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(RightEye),
CustomizeParameterFlag.FeatureColor => new CustomizeParameterValue(FeatureColor),
CustomizeParameterFlag.DecalColor => new CustomizeParameterValue(DecalColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new CustomizeParameterValue(FacePaintUvMultiplier),
CustomizeParameterFlag.FacePaintUvOffset => new CustomizeParameterValue(FacePaintUvOffset),
_ => CustomizeParameterValue.Zero,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
set => Set(flag, value);
}
public bool Set(CustomizeParameterFlag flag, Vector3 value)
public bool Set(CustomizeParameterFlag flag, CustomizeParameterValue 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]),
CustomizeParameterFlag.SkinDiffuse => SetIfDifferent(ref SkinDiffuse, value.InternalTriple),
CustomizeParameterFlag.MuscleTone => SetIfDifferent(ref MuscleTone, value.Single),
CustomizeParameterFlag.SkinSpecular => SetIfDifferent(ref SkinSpecular, value.InternalTriple),
CustomizeParameterFlag.LipDiffuse => SetIfDifferent(ref LipDiffuse, value.InternalTriple),
CustomizeParameterFlag.LipOpacity => SetIfDifferent(ref LipOpacity, value.Single),
CustomizeParameterFlag.HairDiffuse => SetIfDifferent(ref HairDiffuse, value.InternalTriple),
CustomizeParameterFlag.HairSpecular => SetIfDifferent(ref HairSpecular, value.InternalTriple),
CustomizeParameterFlag.HairHighlight => SetIfDifferent(ref HairHighlight, value.InternalTriple),
CustomizeParameterFlag.LeftEye => SetIfDifferent(ref LeftEye, value.InternalTriple),
CustomizeParameterFlag.RightEye => SetIfDifferent(ref RightEye, value.InternalTriple),
CustomizeParameterFlag.FeatureColor => SetIfDifferent(ref FeatureColor, value.InternalTriple),
CustomizeParameterFlag.DecalColor => SetIfDifferent(ref DecalColor, value.InternalQuadruple),
CustomizeParameterFlag.FacePaintUvMultiplier => SetIfDifferent(ref FacePaintUvMultiplier, value.Single),
CustomizeParameterFlag.FacePaintUvOffset => SetIfDifferent(ref FacePaintUvOffset, value.Single),
_ => false,
};
}
@ -69,32 +72,55 @@ public struct CustomizeParameterData
[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;
parameters.SkinColor = (flags & (CustomizeParameterFlag.SkinDiffuse | CustomizeParameterFlag.MuscleTone)) switch
{
0 => parameters.SkinColor,
CustomizeParameterFlag.SkinDiffuse => new CustomizeParameterValue(SkinDiffuse, parameters.SkinColor.W).XivQuadruple,
CustomizeParameterFlag.MuscleTone => parameters.SkinColor with { W = MuscleTone },
_ => new CustomizeParameterValue(SkinDiffuse, MuscleTone).XivQuadruple,
};
parameters.LipColor = (flags & (CustomizeParameterFlag.LipDiffuse | CustomizeParameterFlag.LipOpacity)) switch
{
0 => parameters.LipColor,
CustomizeParameterFlag.LipDiffuse => new CustomizeParameterValue(LipDiffuse, parameters.LipColor.W).XivQuadruple,
CustomizeParameterFlag.LipOpacity => parameters.LipColor with { W = LipOpacity },
_ => new CustomizeParameterValue(LipDiffuse, LipOpacity).XivQuadruple,
};
parameters.LeftColor = (flags & (CustomizeParameterFlag.LeftEye | CustomizeParameterFlag.FacePaintUvMultiplier)) switch
{
0 => parameters.LeftColor,
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(LeftEye, parameters.LeftColor.W).XivQuadruple,
CustomizeParameterFlag.FacePaintUvMultiplier => parameters.LeftColor with { W = FacePaintUvMultiplier },
_ => new CustomizeParameterValue(LeftEye, FacePaintUvMultiplier).XivQuadruple,
};
parameters.RightColor = (flags & (CustomizeParameterFlag.RightEye | CustomizeParameterFlag.FacePaintUvOffset)) switch
{
0 => parameters.RightColor,
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(RightEye, parameters.RightColor.W).XivQuadruple,
CustomizeParameterFlag.FacePaintUvOffset => parameters.RightColor with { W = FacePaintUvOffset },
_ => new CustomizeParameterValue(RightEye, FacePaintUvOffset).XivQuadruple,
};
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;
parameters.SkinFresnelValue0 = new CustomizeParameterValue(SkinDiffuse).XivQuadruple;
if (flags.HasFlag(CustomizeParameterFlag.HairDiffuse))
parameters.MainColor = Convert(HairDiffuse);
parameters.MainColor = new CustomizeParameterValue(HairDiffuse).XivTriple;
if (flags.HasFlag(CustomizeParameterFlag.HairSpecular))
parameters.HairFresnelValue0 = Convert(HairSpecular);
parameters.HairFresnelValue0 = new CustomizeParameterValue(HairSpecular).XivTriple;
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);
parameters.MeshColor = new CustomizeParameterValue(HairHighlight).XivTriple;
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;
parameters.OptionColor = new CustomizeParameterValue(FeatureColor).XivTriple;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly void Apply(ref DecalParameters parameters, CustomizeParameterFlag flags = CustomizeParameterExtensions.All)
{
if (flags.HasFlag(CustomizeParameterFlag.DecalColor))
parameters.Color = new CustomizeParameterValue(DecalColor).XivQuadruple;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@ -103,37 +129,37 @@ public struct CustomizeParameterData
switch (flag)
{
case CustomizeParameterFlag.SkinDiffuse:
parameters.SkinColor = Convert(SkinDiffuse, parameters.SkinColor.W);
parameters.SkinColor = new CustomizeParameterValue(SkinDiffuse, parameters.SkinColor.W).XivQuadruple;
break;
case CustomizeParameterFlag.MuscleTone:
parameters.SkinColor.W = MuscleTone;
break;
case CustomizeParameterFlag.SkinSpecular:
parameters.SkinFresnelValue0 = Convert(SkinSpecular, 0);
parameters.SkinFresnelValue0 = new CustomizeParameterValue(SkinSpecular).XivQuadruple;
break;
case CustomizeParameterFlag.LipDiffuse:
parameters.LipColor = Convert(LipDiffuse, parameters.LipColor.W);
parameters.LipColor = new CustomizeParameterValue(LipDiffuse, parameters.LipColor.W).XivQuadruple;
break;
case CustomizeParameterFlag.LipOpacity:
parameters.LipColor.W = LipOpacity;
break;
case CustomizeParameterFlag.HairDiffuse:
parameters.MainColor = Convert(HairDiffuse);
parameters.MainColor = new CustomizeParameterValue(HairDiffuse).XivTriple;
break;
case CustomizeParameterFlag.HairSpecular:
parameters.HairFresnelValue0 = Convert(HairSpecular);
parameters.HairFresnelValue0 = new CustomizeParameterValue(HairSpecular).XivTriple;
break;
case CustomizeParameterFlag.HairHighlight:
parameters.MeshColor = Convert(HairHighlight);
parameters.MeshColor = new CustomizeParameterValue(HairHighlight).XivTriple;
break;
case CustomizeParameterFlag.LeftEye:
parameters.LeftColor = Convert(LeftEye, parameters.LeftColor.W);
parameters.LeftColor = new CustomizeParameterValue(LeftEye, parameters.LeftColor.W).XivQuadruple;
break;
case CustomizeParameterFlag.RightEye:
parameters.RightColor = Convert(RightEye, parameters.RightColor.W);
parameters.RightColor = new CustomizeParameterValue(RightEye, parameters.RightColor.W).XivQuadruple;
break;
case CustomizeParameterFlag.FeatureColor:
parameters.OptionColor = Convert(FeatureColor);
parameters.OptionColor = new CustomizeParameterValue(FeatureColor).XivTriple;
break;
case CustomizeParameterFlag.FacePaintUvMultiplier:
parameters.LeftColor.W = FacePaintUvMultiplier;
@ -144,61 +170,49 @@ public struct CustomizeParameterData
}
}
public static CustomizeParameterData FromParameters(in CustomizeParameter parameter)
public static CustomizeParameterData FromParameters(in CustomizeParameter parameter, in DecalParameters decal)
=> 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),
SkinDiffuse = new CustomizeParameterValue(parameter.SkinColor).InternalTriple,
SkinSpecular = new CustomizeParameterValue(parameter.SkinFresnelValue0).InternalTriple,
LipDiffuse = new CustomizeParameterValue(parameter.LipColor).InternalTriple,
HairDiffuse = new CustomizeParameterValue(parameter.MainColor).InternalTriple,
HairSpecular = new CustomizeParameterValue(parameter.HairFresnelValue0).InternalTriple,
HairHighlight = new CustomizeParameterValue(parameter.MeshColor).InternalTriple,
LeftEye = new CustomizeParameterValue(parameter.LeftColor).InternalTriple,
RightEye = new CustomizeParameterValue(parameter.RightColor).InternalTriple,
FeatureColor = new CustomizeParameterValue(parameter.OptionColor).InternalTriple,
DecalColor = FromParameter(decal),
};
public static Vector3 FromParameter(in CustomizeParameter parameter, CustomizeParameterFlag flag)
public static CustomizeParameterValue 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,
CustomizeParameterFlag.SkinDiffuse => new CustomizeParameterValue(parameter.SkinColor),
CustomizeParameterFlag.MuscleTone => new CustomizeParameterValue(parameter.SkinColor.W),
CustomizeParameterFlag.SkinSpecular => new CustomizeParameterValue(parameter.SkinFresnelValue0),
CustomizeParameterFlag.LipDiffuse => new CustomizeParameterValue(parameter.LipColor),
CustomizeParameterFlag.LipOpacity => new CustomizeParameterValue(parameter.LipColor.W),
CustomizeParameterFlag.HairDiffuse => new CustomizeParameterValue(parameter.MainColor),
CustomizeParameterFlag.HairSpecular => new CustomizeParameterValue(parameter.HairFresnelValue0),
CustomizeParameterFlag.HairHighlight => new CustomizeParameterValue(parameter.MeshColor),
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(parameter.LeftColor),
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(parameter.RightColor),
CustomizeParameterFlag.FeatureColor => new CustomizeParameterValue(parameter.OptionColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new CustomizeParameterValue(parameter.LeftColor.W),
CustomizeParameterFlag.FacePaintUvOffset => new CustomizeParameterValue(parameter.RightColor.W),
_ => CustomizeParameterValue.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);
public static Vector4 FromParameter(in DecalParameters parameter)
=> new CustomizeParameterValue(parameter.Color).InternalQuadruple;
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;
@ -206,7 +220,16 @@ public struct CustomizeParameterData
return true;
}
private static bool SetIfDifferent<T>(ref T val, T @new) where T : IEqualityOperators<T, T, bool>
private static bool SetIfDifferent(ref float val, float @new)
{
if (@new == val)
return false;
val = @new;
return true;
}
private static bool SetIfDifferent(ref Vector4 val, Vector4 @new)
{
if (@new == val)
return false;

View file

@ -16,28 +16,28 @@ public enum CustomizeParameterFlag : ushort
FeatureColor = 0x0400,
FacePaintUvMultiplier = 0x0800,
FacePaintUvOffset = 0x1000,
DecalColor = 0x2000,
}
public static class CustomizeParameterExtensions
{
public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x1FFF;
public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x3FFF;
public const CustomizeParameterFlag Triples = All
& ~(CustomizeParameterFlag.MuscleTone
| CustomizeParameterFlag.LipOpacity
| CustomizeParameterFlag.FacePaintUvOffset
| CustomizeParameterFlag.FacePaintUvMultiplier);
public const CustomizeParameterFlag RgbTriples = All
& ~(RgbaQuadruples | Percentages | Values);
public const CustomizeParameterFlag RgbaQuadruples = CustomizeParameterFlag.DecalColor;
public const CustomizeParameterFlag Percentages = CustomizeParameterFlag.MuscleTone | CustomizeParameterFlag.LipOpacity;
public const CustomizeParameterFlag Values = CustomizeParameterFlag.FacePaintUvOffset | CustomizeParameterFlag.FacePaintUvMultiplier;
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> RgbaFlags = AllFlags.Where(f => RgbaQuadruples.HasFlag(f)).ToArray();
public static readonly IReadOnlyList<CustomizeParameterFlag> RgbFlags = AllFlags.Where(f => RgbTriples.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;
=> RgbaQuadruples.HasFlag(flag) ? 4 : RgbTriples.HasFlag(flag) ? 3 : 1;
public static IEnumerable<CustomizeParameterFlag> Iterate(this CustomizeParameterFlag flags)
=> AllFlags.Where(f => flags.HasFlag(f));

View file

@ -0,0 +1,47 @@
namespace Glamourer.GameData;
public readonly struct CustomizeParameterValue
{
public static readonly CustomizeParameterValue Zero = default;
private readonly Vector4 _data;
public CustomizeParameterValue(Vector4 data)
=> _data = data;
public CustomizeParameterValue(Vector3 data, float w = 0)
=> _data = new Vector4(data, w);
public CustomizeParameterValue(FFXIVClientStructs.FFXIV.Common.Math.Vector4 data)
=> _data = new Vector4(Root(data.X), Root(data.Y), Root(data.Z), data.W);
public CustomizeParameterValue(FFXIVClientStructs.FFXIV.Common.Math.Vector3 data)
=> _data = new Vector4(Root(data.X), Root(data.Y), Root(data.Z), 0);
public CustomizeParameterValue(float value, float y = 0, float z = 0, float w = 0)
=> _data = new Vector4(value, y, z, w);
public Vector3 InternalTriple
=> new(_data.X, _data.Y, _data.Z);
public float Single
=> _data.X;
public Vector4 InternalQuadruple
=> _data;
public FFXIVClientStructs.FFXIV.Common.Math.Vector4 XivQuadruple
=> new(Square(_data.X), Square(_data.Y), Square(_data.Z), _data.W);
public FFXIVClientStructs.FFXIV.Common.Math.Vector3 XivTriple
=> new(Square(_data.X), Square(_data.Y), Square(_data.Z));
private static float Square(float x)
=> x < 0 ? -x * x : x * x;
private static float Root(float x)
=> x < 0 ? -(float)Math.Sqrt(-x) : x;
public float this[int idx]
=> _data[idx];
}

View file

@ -0,0 +1,8 @@
using Vector4 = FFXIVClientStructs.FFXIV.Common.Math.Vector4;
namespace Glamourer.GameData;
public struct DecalParameters
{
public Vector4 Color;
}

View file

@ -10,11 +10,13 @@ public ref struct CustomizeParameterDrawData(CustomizeParameterFlag flag, in Des
public readonly CustomizeParameterFlag Flag = flag;
public bool Locked;
public bool DisplayApplication;
public bool AllowRevert;
public Action<Vector3> ValueSetter = null!;
public Action<bool> ApplySetter = null!;
public Vector3 CurrentValue = data.Parameters[flag];
public bool CurrentApply;
public Action<CustomizeParameterValue> ValueSetter = null!;
public Action<bool> ApplySetter = null!;
public CustomizeParameterValue CurrentValue = data.Parameters[flag];
public CustomizeParameterValue GameValue;
public bool CurrentApply;
public static CustomizeParameterDrawData FromDesign(DesignManager manager, Design design, CustomizeParameterFlag flag)
=> new(flag, design.DesignData)
@ -32,5 +34,7 @@ public ref struct CustomizeParameterDrawData(CustomizeParameterFlag flag, in Des
Locked = state.IsLocked,
DisplayApplication = false,
ValueSetter = v => manager.ChangeCustomizeParameter(state, flag, v, StateChanged.Source.Manual),
GameValue = state.BaseData.Parameters[flag],
AllowRevert = true,
};
}

View file

@ -3,6 +3,7 @@ using Glamourer.GameData;
using Glamourer.State;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using OtterGui;
using OtterGui.Services;
namespace Glamourer.Gui.Customization;
@ -11,8 +12,11 @@ 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.RgbFlags)
DrawColorInput3(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
foreach (var flag in CustomizeParameterExtensions.RgbaFlags)
DrawColorInput4(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
DrawPercentageInput(CustomizeParameterDrawData.FromDesign(designManager, design, flag));
@ -23,8 +27,11 @@ public class CustomizeParameterDrawer(Configuration config) : IService
public void Draw(StateManager stateManager, ActorState state)
{
foreach (var flag in CustomizeParameterExtensions.TripleFlags)
DrawColorInput(CustomizeParameterDrawData.FromState(stateManager, state, flag));
foreach (var flag in CustomizeParameterExtensions.RgbFlags)
DrawColorInput3(CustomizeParameterDrawData.FromState(stateManager, state, flag));
foreach (var flag in CustomizeParameterExtensions.RgbaFlags)
DrawColorInput4(CustomizeParameterDrawData.FromState(stateManager, state, flag));
foreach (var flag in CustomizeParameterExtensions.PercentageFlags)
DrawPercentageInput(CustomizeParameterDrawData.FromState(stateManager, state, flag));
@ -33,15 +40,30 @@ public class CustomizeParameterDrawer(Configuration config) : IService
DrawValueInput(CustomizeParameterDrawData.FromState(stateManager, state, flag));
}
private void DrawColorInput(in CustomizeParameterDrawData data)
private void DrawColorInput3(in CustomizeParameterDrawData data)
{
using var id = ImRaii.PushId((int)data.Flag);
var value = data.CurrentValue;
var value = data.CurrentValue.InternalTriple;
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.ColorEdit3("##value", ref value, ImGuiColorEditFlags.Float))
data.ValueSetter(value);
if (ImGui.ColorEdit3("##value", ref value, ImGuiColorEditFlags.Float | ImGuiColorEditFlags.HDR | ImGuiColorEditFlags.NoOptions))
data.ValueSetter(new CustomizeParameterValue(value));
}
DrawRevert(data);
DrawApplyAndLabel(data);
}
private void DrawColorInput4(in CustomizeParameterDrawData data)
{
using var id = ImRaii.PushId((int)data.Flag);
var value = data.CurrentValue.InternalQuadruple;
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.ColorEdit4("##value", ref value, ImGuiColorEditFlags.Float | ImGuiColorEditFlags.HDR | ImGuiColorEditFlags.NoOptions))
data.ValueSetter(new CustomizeParameterValue(value));
}
DrawRevert(data);
DrawApplyAndLabel(data);
}
@ -54,8 +76,9 @@ public class CustomizeParameterDrawer(Configuration config) : IService
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.InputFloat("##value", ref value, 0.1f, 0.5f))
data.ValueSetter(new Vector3(value));
data.ValueSetter(new CustomizeParameterValue(value));
}
DrawRevert(data);
DrawApplyAndLabel(data);
}
@ -67,13 +90,26 @@ public class CustomizeParameterDrawer(Configuration config) : IService
using (_ = ImRaii.Disabled(data.Locked))
{
if (ImGui.SliderFloat("##value", ref value, 0, 100, "%.2f", ImGuiSliderFlags.AlwaysClamp))
data.ValueSetter(new Vector3(value / 100f));
if (ImGui.SliderFloat("##value", ref value, -1000f, 1000f, "%.2f"))
data.ValueSetter(new CustomizeParameterValue(value / 100f));
}
DrawRevert(data);
DrawApplyAndLabel(data);
}
private static void DrawRevert(in CustomizeParameterDrawData data)
{
if (data.Locked || !data.AllowRevert)
return;
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
data.ValueSetter(data.GameValue);
ImGuiUtil.HoverTooltip("Hold Control and Right-click to revert to game values.");
}
private static void DrawApply(in CustomizeParameterDrawData data)
{
if (UiHelpers.DrawCheckbox("##apply", "Apply this custom parameter when applying the Design.", data.CurrentApply, out var enabled,

View file

@ -85,19 +85,13 @@ public unsafe class ModelEvaluationPanel(
{
if (!model.IsHuman)
return;
if (model.AsHuman->CustomizeParameterCBuffer == null)
return;
var ptr = (CustomizeParameter*)model.AsHuman->CustomizeParameterCBuffer->UnsafeSourcePointer;
if (ptr == null)
return;
var convert = CustomizeParameterData.FromParameters(*ptr);
var convert = model.GetParameterData();
foreach (var flag in CustomizeParameterExtensions.AllFlags)
{
ImGuiUtil.DrawTableColumn(flag.ToString());
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(convert[flag].ToString());
ImGuiUtil.DrawTableColumn(convert[flag].InternalQuadruple.ToString());
ImGui.TableNextColumn();
}
}

View file

@ -1,5 +1,7 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Shader;
using Glamourer.GameData;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
@ -159,6 +161,65 @@ public readonly unsafe struct Model : IEquatable<Model>
return (main, off, mainData, offData);
}
public CustomizeParameterData GetParameterData()
{
if (!IsHuman)
return default;
var cBuffer1 = AsHuman->CustomizeParameterCBuffer;
var cBuffer2 = AsHuman->DecalColorCBuffer;
var ptr1 = (CustomizeParameter*)(cBuffer1 == null ? null : cBuffer1->UnsafeSourcePointer);
var ptr2 = (DecalParameters*)(cBuffer2 == null ? null : cBuffer2->UnsafeSourcePointer);
return CustomizeParameterData.FromParameters(ptr1 != null ? *ptr1 : default, ptr2 != null ? *ptr2 : default);
}
public void ApplyParameterData(CustomizeParameterFlag flags, in CustomizeParameterData data)
{
if (!IsHuman)
return;
if (flags.HasFlag(CustomizeParameterFlag.DecalColor))
{
var cBufferDecal = AsHuman->DecalColorCBuffer;
var ptrDecal = (DecalParameters*)(cBufferDecal == null ? null : cBufferDecal->UnsafeSourcePointer);
if (ptrDecal != null)
data.Apply(ref *ptrDecal);
}
flags &= ~CustomizeParameterFlag.DecalColor;
var cBuffer = AsHuman->CustomizeParameterCBuffer;
var ptr = (CustomizeParameter*)(cBuffer == null ? null : cBuffer->UnsafeSourcePointer);
if (ptr != null)
data.Apply(ref *ptr, flags);
}
public bool ApplySingleParameterData(CustomizeParameterFlag flag, in CustomizeParameterData data)
{
if (!IsHuman)
return false;
if (flag is CustomizeParameterFlag.DecalColor)
{
var cBuffer = AsHuman->DecalColorCBuffer;
var ptr = (DecalParameters*)(cBuffer == null ? null : cBuffer->UnsafeSourcePointer);
if (ptr == null)
return false;
data.Apply(ref *ptr);
return true;
}
else
{
var cBuffer = AsHuman->CustomizeParameterCBuffer;
var ptr = (CustomizeParameter*)(cBuffer == null ? null : cBuffer->UnsafeSourcePointer);
if (ptr == null)
return false;
data.ApplySingle(ref *ptr, flag);
return true;
}
}
private (Model, Model, int) GetChildrenWeapons()
{
Span<Model> weapons = stackalloc Model[2];

View file

@ -1,5 +1,4 @@
using FFXIVClientStructs.FFXIV.Shader;
using Glamourer.Events;
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
@ -282,23 +281,13 @@ public class StateApplier(
}
/// <summary> Change the customize parameters on models. Can change multiple at once. </summary>
public unsafe void ChangeParameters(ActorData data, CustomizeParameterFlag flags, in CustomizeParameterData values)
public void ChangeParameters(ActorData data, CustomizeParameterFlag flags, in CustomizeParameterData values)
{
if (!_config.UseAdvancedParameters)
if (!_config.UseAdvancedParameters || flags == 0)
return;
foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true }))
{
var buffer = actor.Model.AsHuman->CustomizeParameterCBuffer;
if (buffer == null)
continue;
var ptr = (CustomizeParameter*)buffer->UnsafeSourcePointer;
if (ptr == null)
continue;
values.Apply(ref *ptr, flags);
}
actor.Model.ApplyParameterData(flags, values);
}
/// <inheritdoc cref="ChangeParameters(ActorData,CustomizeParameterFlag,in CustomizeParameterData)"/>

View file

@ -210,7 +210,7 @@ public class StateEditor
}
/// <summary> Change the customize flags of a character. </summary>
public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, Vector3 value, StateChanged.Source source, out Vector3 oldValue,
public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, StateChanged.Source source, out CustomizeParameterValue oldValue,
uint key = 0)
{
oldValue = state.ModelData.Parameters[flag];

View file

@ -729,30 +729,27 @@ public class StateListener : IDisposable
if (!model.IsHuman)
return;
var cBuffer = model.AsHuman->CustomizeParameterCBuffer;
if (cBuffer == null)
return;
var ptr = (CustomizeParameter*)cBuffer->UnsafeSourcePointer;
if (ptr == null)
return;
var data = model.GetParameterData();
foreach (var flag in CustomizeParameterExtensions.AllFlags)
{
var newValue = CustomizeParameterData.FromParameter(*ptr, flag);
var newValue = data[flag];
switch (state[flag])
{
case StateChanged.Source.Game:
if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, StateChanged.Source.Game);
break;
case StateChanged.Source.Manual:
if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, StateChanged.Source.Game);
else if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;
case StateChanged.Source.Fixed:
case StateChanged.Source.Ipc:
state.BaseData.Parameters.Set(flag, newValue);
if (_config.UseAdvancedParameters)
state.ModelData.Parameters.ApplySingle(ref *ptr, flag);
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;
}
}

View file

@ -186,13 +186,7 @@ public class StateManager(
// Weapon visibility could technically be inferred from the weapon draw objects,
// but since we use hat visibility from the game object we can also use weapon visibility from it.
ret.SetWeaponVisible(!actor.AsCharacter->DrawData.IsWeaponHidden);
if (model.IsHuman && model.AsHuman->CustomizeParameterCBuffer != null)
{
var ptr = model.AsHuman->CustomizeParameterCBuffer->UnsafeSourcePointer;
if (ptr != null)
ret.Parameters = CustomizeParameterData.FromParameters(*(CustomizeParameter*)ptr);
}
ret.Parameters = model.GetParameterData();
return ret;
}
@ -315,7 +309,7 @@ public class StateManager(
}
/// <summary> Change the crest of an equipment piece. </summary>
public void ChangeCustomizeParameter(ActorState state, CustomizeParameterFlag flag, Vector3 value, StateChanged.Source source, uint key = 0)
public void ChangeCustomizeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, StateChanged.Source source, uint key = 0)
{
if (!_editor.ChangeParameter(state, flag, value, source, out var old, key))
return;