feat: quaternion operations, and transforms that mean the same in 4x4 and 4x3
- A 4-vector doubles as a unit quaternion in (x, y, z, w) order: QuanternionMultiply, QuanternionConjugate, QuanternionFromAxisAngle, QuanternionFromUnitVectors (shortest arc), QuanternionNlerp and QuanternionSlerp, next to the existing QuanternionFromBasis. - MatrixRowMajor gains Rotation (from a quaternion), TRS, InverseAffine, Transpose and Decompose. The 4x4 transforms row vectors and the 4x3 column vectors (the VkTransformMatrixKHR layout); each of these means the same transform in both. The 4x3 Rotation copied the 4x4's rows, which rotated by the inverse. - The static Normalize no longer assumes three components. - Tests: Quaternion, and Matrix extended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0c55e76873
commit
087c8b32c3
6 changed files with 533 additions and 8 deletions
|
|
@ -334,7 +334,7 @@ namespace Crafter {
|
|||
|
||||
template <typename AT, std::uint32_t Alen, std::uint32_t AAlignment>
|
||||
constexpr static Vector<T, Len, Aligment> Normalize(Vector<AT, Alen, AAlignment> a) requires(Len == Alen) {
|
||||
Vector<T, 3, 0> returned;
|
||||
Vector<T, Len, Aligment> returned;
|
||||
T fLength = a.Length();
|
||||
|
||||
if (fLength > 0) {
|
||||
|
|
@ -438,6 +438,102 @@ namespace Crafter {
|
|||
cr * cp * cy + sr * sp * sy
|
||||
);
|
||||
}
|
||||
|
||||
// The quaternions here are Vector<T, 4> in (x, y, z, w) order, w the
|
||||
// scalar part, applied as Rotate does (q v q*). They take and return
|
||||
// unit quaternions.
|
||||
|
||||
// The Hamilton product: rotation b followed by rotation a, so
|
||||
// Rotate(v, QuanternionMultiply(a, b)) == Rotate(Rotate(v, b), a).
|
||||
template <typename AT, std::uint32_t AAlignment, typename BT, std::uint32_t BAlignment>
|
||||
constexpr static Vector<T, 4, Aligment> QuanternionMultiply(Vector<AT, 4, AAlignment> a, Vector<BT, 4, BAlignment> b) requires(Len == 4) {
|
||||
return Vector<T, 4, Aligment>(
|
||||
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
||||
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
||||
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
||||
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z
|
||||
);
|
||||
}
|
||||
|
||||
// The inverse rotation.
|
||||
template <typename AT, std::uint32_t AAlignment>
|
||||
constexpr static Vector<T, 4, Aligment> QuanternionConjugate(Vector<AT, 4, AAlignment> q) requires(Len == 4) {
|
||||
return Vector<T, 4, Aligment>(-q.x, -q.y, -q.z, q.w);
|
||||
}
|
||||
|
||||
// `angle` radians about the unit vector `axis`, right-handed: a
|
||||
// positive angle about +Z turns +X towards +Y.
|
||||
template <typename AT, std::uint32_t AAlignment>
|
||||
constexpr static Vector<T, 4, Aligment> QuanternionFromAxisAngle(Vector<AT, 3, AAlignment> axis, T angle) requires(Len == 4) {
|
||||
const T s = std::sin(angle * T(0.5));
|
||||
return Vector<T, 4, Aligment>(axis.x * s, axis.y * s, axis.z * s, std::cos(angle * T(0.5)));
|
||||
}
|
||||
|
||||
// The shortest rotation taking unit direction `from` onto unit
|
||||
// direction `to`. For opposite directions every axis perpendicular
|
||||
// to `from` is a shortest arc, and the cross product that picks the
|
||||
// axis otherwise is zero; the axis is then taken against whichever of
|
||||
// X and Y is further from parallel to `from`.
|
||||
template <typename AT, std::uint32_t AAlignment, typename BT, std::uint32_t BAlignment>
|
||||
constexpr static Vector<T, 4, Aligment> QuanternionFromUnitVectors(Vector<AT, 3, AAlignment> from, Vector<BT, 3, BAlignment> to) requires(Len == 4) {
|
||||
const Vector<T, 3, 0> f(from.x, from.y, from.z);
|
||||
const T d = f.x * to.x + f.y * to.y + f.z * to.z;
|
||||
if (d < T(-1) + T(1e-6)) {
|
||||
Vector<T, 3, 0> axis = std::abs(f.x) < T(0.9)
|
||||
? Vector<T, 3, 0>::Cross(Vector<T, 3, 0>(1, 0, 0), f)
|
||||
: Vector<T, 3, 0>::Cross(Vector<T, 3, 0>(0, 1, 0), f);
|
||||
axis.Normalize();
|
||||
return Vector<T, 4, Aligment>(axis.x, axis.y, axis.z, T(0));
|
||||
}
|
||||
const Vector<T, 3, 0> c = Vector<T, 3, 0>::Cross(f, Vector<T, 3, 0>(to.x, to.y, to.z));
|
||||
Vector<T, 4, Aligment> q(c.x, c.y, c.z, T(1) + d);
|
||||
q.Normalize();
|
||||
return q;
|
||||
}
|
||||
|
||||
// Normalized linear interpolation from a to b, along the shorter arc
|
||||
// (q and -q are the same rotation, and blending towards the wrong one
|
||||
// swings the long way round). Cheaper than Slerp; its angular speed
|
||||
// varies a little along the arc, which damping and blending between
|
||||
// nearby poses don't notice.
|
||||
template <typename AT, std::uint32_t AAlignment, typename BT, std::uint32_t BAlignment>
|
||||
constexpr static Vector<T, 4, Aligment> QuanternionNlerp(Vector<AT, 4, AAlignment> a, Vector<BT, 4, BAlignment> b, T t) requires(Len == 4) {
|
||||
const T sign = (a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w) < T(0) ? T(-1) : T(1);
|
||||
Vector<T, 4, Aligment> q(
|
||||
a.x + (b.x * sign - a.x) * t,
|
||||
a.y + (b.y * sign - a.y) * t,
|
||||
a.z + (b.z * sign - a.z) * t,
|
||||
a.w + (b.w * sign - a.w) * t
|
||||
);
|
||||
q.Normalize();
|
||||
return q;
|
||||
}
|
||||
|
||||
// Spherical linear interpolation from a to b: constant angular speed,
|
||||
// along the shorter arc. Nearly identical inputs go through Nlerp,
|
||||
// which is exact enough there, instead of dividing by sin(~0).
|
||||
template <typename AT, std::uint32_t AAlignment, typename BT, std::uint32_t BAlignment>
|
||||
constexpr static Vector<T, 4, Aligment> QuanternionSlerp(Vector<AT, 4, AAlignment> a, Vector<BT, 4, BAlignment> b, T t) requires(Len == 4) {
|
||||
T cosAngle = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
T sign = T(1);
|
||||
if (cosAngle < T(0)) {
|
||||
cosAngle = -cosAngle;
|
||||
sign = T(-1);
|
||||
}
|
||||
if (cosAngle > T(0.9995)) {
|
||||
return QuanternionNlerp(a, b, t);
|
||||
}
|
||||
const T angle = std::acos(cosAngle);
|
||||
const T sinAngle = std::sin(angle);
|
||||
const T wa = std::sin((T(1) - t) * angle) / sinAngle;
|
||||
const T wb = std::sin(t * angle) / sinAngle * sign;
|
||||
return Vector<T, 4, Aligment>(
|
||||
a.x * wa + b.x * wb,
|
||||
a.y * wa + b.y * wb,
|
||||
a.z * wa + b.z * wb,
|
||||
a.w * wa + b.w * wb
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue