|
@@ -6,45 +6,51 @@ using System.Text;
|
|
|
namespace SharpGLTF.Transforms
|
|
namespace SharpGLTF.Transforms
|
|
|
{
|
|
{
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// Represents an affine transform in 3D space,
|
|
|
|
|
- /// defined by a <see cref="Quaternion"/> rotation,
|
|
|
|
|
- /// a <see cref="Vector3"/> scale
|
|
|
|
|
- /// and a <see cref="Vector3"/> translation.
|
|
|
|
|
|
|
+ /// Represents an affine transform in 3D space, defined by:
|
|
|
|
|
+ /// - A <see cref="Vector3"/> scale.
|
|
|
|
|
+ /// - A <see cref="Quaternion"/> rotation.
|
|
|
|
|
+ /// - A <see cref="Vector3"/> translation.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- /// <see href="https://github.com/KhronosGroup/glTF-Validator/issues/33"/>
|
|
|
|
|
|
|
+ /// <remarks>
|
|
|
|
|
+ /// <see cref="AffineTransform"/> cannot represent skewed matrices. This means
|
|
|
|
|
+ /// that it can be used to represent <see cref="Schema2.Node"/> local transforms,
|
|
|
|
|
+ /// but since chained transforms can become skewed, a world transform cannot be
|
|
|
|
|
+ /// represented by a <see cref="AffineTransform"/>.
|
|
|
|
|
+ /// </remarks>
|
|
|
|
|
+ /// <see href="https://github.com/vpenades/SharpGLTF/issues/41"/>
|
|
|
public struct AffineTransform
|
|
public struct AffineTransform
|
|
|
{
|
|
{
|
|
|
#region lifecycle
|
|
#region lifecycle
|
|
|
|
|
|
|
|
- public static AffineTransform Create(Matrix4x4 matrix)
|
|
|
|
|
|
|
+ public static implicit operator AffineTransform(Matrix4x4 matrix) { return new AffineTransform(matrix); }
|
|
|
|
|
+
|
|
|
|
|
+ public AffineTransform(Matrix4x4 matrix)
|
|
|
{
|
|
{
|
|
|
- return new AffineTransform(matrix, null, null, null);
|
|
|
|
|
|
|
+ if (!Matrix4x4.Decompose(matrix, out this.Scale, out this.Rotation, out this.Translation))
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new ArgumentException("matrix is invalid or skewed.", nameof(matrix));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public static AffineTransform Create(Vector3? scale, Quaternion? rotation, Vector3? translation)
|
|
|
|
|
|
|
+ public AffineTransform(Vector3? scale, Quaternion? rotation, Vector3? translation)
|
|
|
{
|
|
{
|
|
|
- return new AffineTransform(null, scale, rotation, translation);
|
|
|
|
|
|
|
+ this.Scale = scale ?? Vector3.One;
|
|
|
|
|
+ this.Rotation = rotation ?? Quaternion.Identity;
|
|
|
|
|
+ this.Translation = translation ?? Vector3.Zero;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- internal AffineTransform(Matrix4x4? matrix, Vector3? scale, Quaternion? rotation, Vector3? translation)
|
|
|
|
|
|
|
+ public static AffineTransform CreateFromAny(Matrix4x4? matrix, Vector3? scale, Quaternion? rotation, Vector3? translation)
|
|
|
{
|
|
{
|
|
|
if (matrix.HasValue)
|
|
if (matrix.HasValue)
|
|
|
{
|
|
{
|
|
|
- Matrix4x4.Decompose(matrix.Value, out this.Scale, out this.Rotation, out this.Translation);
|
|
|
|
|
|
|
+ return new AffineTransform(matrix.Value);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- this.Scale = scale ?? Vector3.One;
|
|
|
|
|
- this.Rotation = rotation ?? Quaternion.Identity;
|
|
|
|
|
- this.Translation = translation ?? Vector3.Zero;
|
|
|
|
|
|
|
+ return new AffineTransform(scale, rotation, translation);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public static implicit operator AffineTransform(Matrix4x4 matrix)
|
|
|
|
|
- {
|
|
|
|
|
- return new AffineTransform(matrix, null, null, null);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
|
|
#region data
|
|
#region data
|
|
@@ -62,7 +68,7 @@ namespace SharpGLTF.Transforms
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Translation
|
|
/// Translation
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public Vector3 Translation;
|
|
|
|
|
|
|
+ public Vector3 Translation;
|
|
|
|
|
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
|
@@ -77,12 +83,9 @@ namespace SharpGLTF.Transforms
|
|
|
{
|
|
{
|
|
|
get
|
|
get
|
|
|
{
|
|
{
|
|
|
- return
|
|
|
|
|
- Matrix4x4.CreateScale(Scale)
|
|
|
|
|
- *
|
|
|
|
|
- Matrix4x4.CreateFromQuaternion(Rotation.Sanitized())
|
|
|
|
|
- *
|
|
|
|
|
- Matrix4x4.CreateTranslation(Translation);
|
|
|
|
|
|
|
+ var m = Matrix4x4.CreateScale(this.Scale) * Matrix4x4.CreateFromQuaternion(this.Rotation.Sanitized());
|
|
|
|
|
+ m.Translation = this.Translation;
|
|
|
|
|
+ return m;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -130,7 +133,47 @@ namespace SharpGLTF.Transforms
|
|
|
|
|
|
|
|
r = Quaternion.Normalize(r);
|
|
r = Quaternion.Normalize(r);
|
|
|
|
|
|
|
|
- return AffineTransform.Create(s, r, t);
|
|
|
|
|
|
|
+ return new AffineTransform(s, r, t);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static AffineTransform operator *(in AffineTransform a, in AffineTransform b)
|
|
|
|
|
+ {
|
|
|
|
|
+ return Multiply(a, b);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static AffineTransform Multiply(in AffineTransform a, in AffineTransform b)
|
|
|
|
|
+ {
|
|
|
|
|
+ AffineTransform r;
|
|
|
|
|
+
|
|
|
|
|
+ r.Scale = Vector3Transform(b.Scale * Vector3Transform(a.Scale, a.Rotation), Quaternion.Inverse(a.Rotation));
|
|
|
|
|
+
|
|
|
|
|
+ r.Rotation = Quaternion.Multiply(b.Rotation, a.Rotation);
|
|
|
|
|
+
|
|
|
|
|
+ r.Translation
|
|
|
|
|
+ = b.Translation
|
|
|
|
|
+ + Vector3Transform(a.Translation * b.Scale, b.Rotation);
|
|
|
|
|
+
|
|
|
|
|
+ return r;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// This method is equivalent to System.Numerics.Vector3.Transform(Vector3 v, Quaternion q)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="v">The vector to transform</param>
|
|
|
|
|
+ /// <param name="q">The transform rotation</param>
|
|
|
|
|
+ /// <returns>The rotated vector</returns>
|
|
|
|
|
+ private static Vector3 Vector3Transform(Vector3 v, Quaternion q)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Extract the vector part of the quaternion
|
|
|
|
|
+ var u = new Vector3(q.X, q.Y, q.Z);
|
|
|
|
|
+
|
|
|
|
|
+ // Extract the scalar part of the quaternion
|
|
|
|
|
+ var s = q.W;
|
|
|
|
|
+
|
|
|
|
|
+ // Do the math
|
|
|
|
|
+ return (2.0f * Vector3.Dot(u, v) * u)
|
|
|
|
|
+ + (((s * s) - Vector3.Dot(u, u)) * v)
|
|
|
|
|
+ + (2.0f * s * Vector3.Cross(u, v));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
#endregion
|