diff --git a/Penumbra/Import/Models/Import/VertexAttribute.cs b/Penumbra/Import/Models/Import/VertexAttribute.cs index b7f5dcf1..a4651776 100644 --- a/Penumbra/Import/Models/Import/VertexAttribute.cs +++ b/Penumbra/Import/Models/Import/VertexAttribute.cs @@ -138,7 +138,27 @@ public class VertexAttribute return new VertexAttribute( element, - index => BuildNByte4(values[index]) + index => { + // Blend weights are _very_ sensitive to float imprecision - a vertex sum being off + // by one, such as 256, is enough to cause a visible defect. To avoid this, we tweak + // the converted values to have the expected sum, preferencing values with minimal differences. + var originalValues = values[index]; + var byteValues = BuildNByte4(originalValues); + + var adjustment = 255 - byteValues.Select(value => (int)value).Sum(); + while (adjustment != 0) + { + var convertedValues = byteValues.Select(value => value * (1f / 255f)).ToArray(); + var closestIndex = Enumerable.Range(0, 4) + .Select(index => (index, delta: Math.Abs(originalValues[index] - convertedValues[index]))) + .MinBy(x => x.delta) + .index; + byteValues[closestIndex] = (byte)(byteValues[closestIndex] + Math.CopySign(1, adjustment)); + adjustment = 255 - byteValues.Select(value => (int)value).Sum(); + } + + return byteValues; + } ); } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index 0be95c99..a7d39c6e 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -401,13 +401,13 @@ public partial class ModEditWindow private void DrawInvalidMaterialMarker() { - using var colorHandle = ImRaii.PushColor(ImGuiCol.TextDisabled, 0xFF0000FF, true); + using (var font = ImRaii.PushFont(UiBuilder.IconFont)) + ImGuiUtil.TextColored(0xFF0000FF, FontAwesomeIcon.TimesCircle.ToIconString()); - ImGuiComponents.HelpMarker( + ImGuiUtil.HoverTooltip( "Materials must be either relative (e.g. \"/filename.mtrl\")\n" + "or absolute (e.g. \"bg/full/path/to/filename.mtrl\"),\n" - + "and must end in \".mtrl\".", - FontAwesomeIcon.TimesCircle); + + "and must end in \".mtrl\"."); } private bool DrawModelLodDetails(MdlTab tab, int lodIndex, bool disabled)