From bdcab22a5528758d5f2d54505f3ecb5b866efb7d Mon Sep 17 00:00:00 2001
From: Passive <20432486+PassiveModding@users.noreply.github.com>
Date: Fri, 1 Aug 2025 22:03:27 +1000
Subject: [PATCH] Cleanup methods to extension class
---
Penumbra/Import/Models/Export/MeshExporter.cs | 43 ++----------
Penumbra/Import/Models/ModelExtensions.cs | 69 +++++++++++++++++++
2 files changed, 73 insertions(+), 39 deletions(-)
create mode 100644 Penumbra/Import/Models/ModelExtensions.cs
diff --git a/Penumbra/Import/Models/Export/MeshExporter.cs b/Penumbra/Import/Models/Export/MeshExporter.cs
index 11c84677..2e41f65a 100644
--- a/Penumbra/Import/Models/Export/MeshExporter.cs
+++ b/Penumbra/Import/Models/Export/MeshExporter.cs
@@ -340,39 +340,6 @@ public class MeshExporter
return typeof(VertexPositionNormalTangent);
}
-
- private const float UnitLengthThresholdVec3 = 0.00674f;
- internal static bool _IsFinite(float value)
- {
- return float.IsFinite(value);
- }
-
- internal static bool _IsFinite(Vector2 v)
- {
- return _IsFinite(v.X) && _IsFinite(v.Y);
- }
-
- internal static bool _IsFinite(Vector3 v)
- {
- return _IsFinite(v.X) && _IsFinite(v.Y) && _IsFinite(v.Z);
- }
- internal static Boolean IsNormalized(Vector3 normal)
- {
- if (!_IsFinite(normal)) return false;
-
- return Math.Abs(normal.Length() - 1) <= UnitLengthThresholdVec3;
- }
- internal static Vector3 SanitizeNormal(Vector3 normal)
- {
- if (normal == Vector3.Zero) return Vector3.UnitX;
- return IsNormalized(normal) ? normal : Vector3.Normalize(normal);
- }
- internal static Vector4 SanitizeTangent(Vector4 tangent)
- {
- var n = SanitizeNormal(new Vector3(tangent.X, tangent.Y, tangent.Z));
- var s = float.IsNaN(tangent.W) ? 1 : tangent.W;
- return new Vector4(n, s > 0 ? 1 : -1);
- }
/// Build a geometry vertex from a vertex's attributes.
private IVertexGeometry BuildVertexGeometry(IReadOnlyDictionary> attributes)
@@ -385,21 +352,19 @@ public class MeshExporter
if (_geometryType == typeof(VertexPositionNormal))
return new VertexPositionNormal(
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position)),
- SanitizeNormal(ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal)))
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal))
);
if (_geometryType == typeof(VertexPositionNormalTangent))
{
// (Bi)tangents are universally stored as ByteFloat4, which uses 0..1 to represent the full -1..1 range.
// TODO: While this assumption is safe, it would be sensible to actually check.
- // var bitangent = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Tangent1)) * 2 - Vector4.One;
- var vec4 = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Tangent1));
- var bitangent = vec4 with { W = vec4.W == 1 ? 1 : -1 };
+ var bitangent = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Tangent1)) * 2 - Vector4.One;
return new VertexPositionNormalTangent(
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position)),
- SanitizeNormal(ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal))),
- SanitizeTangent(bitangent)
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal)),
+ bitangent.SanitizeTangent()
);
}
diff --git a/Penumbra/Import/Models/ModelExtensions.cs b/Penumbra/Import/Models/ModelExtensions.cs
new file mode 100644
index 00000000..2edb3ca4
--- /dev/null
+++ b/Penumbra/Import/Models/ModelExtensions.cs
@@ -0,0 +1,69 @@
+namespace Penumbra.Import.Models;
+
+public static class ModelExtensions
+{
+ // https://github.com/vpenades/SharpGLTF/blob/2073cf3cd671f8ecca9667f9a8c7f04ed865d3ac/src/Shared/_Extensions.cs#L158
+ private const float UnitLengthThresholdVec3 = 0.00674f;
+ private const float UnitLengthThresholdVec4 = 0.00769f;
+
+ internal static bool _IsFinite(this float value)
+ {
+ return float.IsFinite(value);
+ }
+
+ internal static bool _IsFinite(this Vector2 v)
+ {
+ return v.X._IsFinite() && v.Y._IsFinite();
+ }
+
+ internal static bool _IsFinite(this Vector3 v)
+ {
+ return v.X._IsFinite() && v.Y._IsFinite() && v.Z._IsFinite();
+ }
+
+ internal static bool _IsFinite(this in Vector4 v)
+ {
+ return v.X._IsFinite() && v.Y._IsFinite() && v.Z._IsFinite() && v.W._IsFinite();
+ }
+
+ internal static Boolean IsNormalized(this Vector3 normal)
+ {
+ if (!normal._IsFinite()) return false;
+
+ return Math.Abs(normal.Length() - 1) <= UnitLengthThresholdVec3;
+ }
+
+ internal static void ValidateNormal(this Vector3 normal, string msg)
+ {
+ if (!normal._IsFinite()) throw new NotFiniteNumberException($"{msg} is invalid.");
+
+ if (!normal.IsNormalized()) throw new ArithmeticException($"{msg} is not unit length.");
+ }
+
+ internal static void ValidateTangent(this Vector4 tangent, string msg)
+ {
+ if (tangent.W != 1 && tangent.W != -1) throw new ArithmeticException(msg);
+
+ new Vector3(tangent.X, tangent.Y, tangent.Z).ValidateNormal(msg);
+ }
+
+ internal static Vector3 SanitizeNormal(this Vector3 normal)
+ {
+ if (normal == Vector3.Zero) return Vector3.UnitX;
+ return normal.IsNormalized() ? normal : Vector3.Normalize(normal);
+ }
+
+ internal static bool IsValidTangent(this Vector4 tangent)
+ {
+ if (tangent.W != 1 && tangent.W != -1) return false;
+
+ return new Vector3(tangent.X, tangent.Y, tangent.Z).IsNormalized();
+ }
+
+ internal static Vector4 SanitizeTangent(this Vector4 tangent)
+ {
+ var n = new Vector3(tangent.X, tangent.Y, tangent.Z).SanitizeNormal();
+ var s = float.IsNaN(tangent.W) ? 1 : tangent.W;
+ return new Vector4(n, s > 0 ? 1 : -1);
+ }
+}