Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Math/Quat.h
21732 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
#include <Jolt/Math/Vec3.h>
8
#include <Jolt/Math/Vec4.h>
9
10
JPH_NAMESPACE_BEGIN
11
12
/// Quaternion class, quaternions are 4 dimensional vectors which can describe rotations in 3 dimensional
13
/// space if their length is 1.
14
///
15
/// They are written as:
16
///
17
/// \f$q = w + x \: i + y \: j + z \: k\f$
18
///
19
/// or in vector notation:
20
///
21
/// \f$q = [w, v] = [w, x, y, z]\f$
22
///
23
/// Where:
24
///
25
/// w = the real part
26
/// v = the imaginary part, (x, y, z)
27
///
28
/// Note that we store the quaternion in a Vec4 as [x, y, z, w] because that makes
29
/// it easy to extract the rotation axis of the quaternion:
30
///
31
/// q = [cos(angle / 2), sin(angle / 2) * rotation_axis]
32
class [[nodiscard]] alignas(JPH_VECTOR_ALIGNMENT) Quat
33
{
34
public:
35
JPH_OVERRIDE_NEW_DELETE
36
37
///@name Constructors
38
///@{
39
inline Quat() = default; ///< Intentionally not initialized for performance reasons
40
Quat(const Quat &inRHS) = default;
41
Quat & operator = (const Quat &inRHS) = default;
42
inline Quat(float inX, float inY, float inZ, float inW) : mValue(inX, inY, inZ, inW) { }
43
inline explicit Quat(const Float4 &inV) : mValue(Vec4::sLoadFloat4(&inV)) { }
44
inline explicit Quat(Vec4Arg inV) : mValue(inV) { }
45
///@}
46
47
///@name Tests
48
///@{
49
50
/// Check if two quaternions are exactly equal
51
inline bool operator == (QuatArg inRHS) const { return mValue == inRHS.mValue; }
52
53
/// Check if two quaternions are different
54
inline bool operator != (QuatArg inRHS) const { return mValue != inRHS.mValue; }
55
56
/// If this quaternion is close to inRHS. Note that q and -q represent the same rotation, this is not checked here.
57
inline bool IsClose(QuatArg inRHS, float inMaxDistSq = 1.0e-12f) const { return mValue.IsClose(inRHS.mValue, inMaxDistSq); }
58
59
/// If the length of this quaternion is 1 +/- inTolerance
60
inline bool IsNormalized(float inTolerance = 1.0e-5f) const { return mValue.IsNormalized(inTolerance); }
61
62
/// If any component of this quaternion is a NaN (not a number)
63
inline bool IsNaN() const { return mValue.IsNaN(); }
64
65
///@}
66
///@name Get components
67
///@{
68
69
/// Get X component (imaginary part i)
70
JPH_INLINE float GetX() const { return mValue.GetX(); }
71
72
/// Get Y component (imaginary part j)
73
JPH_INLINE float GetY() const { return mValue.GetY(); }
74
75
/// Get Z component (imaginary part k)
76
JPH_INLINE float GetZ() const { return mValue.GetZ(); }
77
78
/// Get W component (real part)
79
JPH_INLINE float GetW() const { return mValue.GetW(); }
80
81
/// Get the imaginary part of the quaternion
82
JPH_INLINE Vec3 GetXYZ() const { return Vec3(mValue); }
83
84
/// Get the quaternion as a Vec4
85
JPH_INLINE Vec4 GetXYZW() const { return mValue; }
86
87
/// Set individual components
88
JPH_INLINE void SetX(float inX) { mValue.SetX(inX); }
89
JPH_INLINE void SetY(float inY) { mValue.SetY(inY); }
90
JPH_INLINE void SetZ(float inZ) { mValue.SetZ(inZ); }
91
JPH_INLINE void SetW(float inW) { mValue.SetW(inW); }
92
93
/// Set all components
94
JPH_INLINE void Set(float inX, float inY, float inZ, float inW) { mValue.Set(inX, inY, inZ, inW); }
95
96
///@}
97
///@name Default quaternions
98
///@{
99
100
/// @return [0, 0, 0, 0]
101
JPH_INLINE static Quat sZero() { return Quat(Vec4::sZero()); }
102
103
/// @return [1, 0, 0, 0] (or in storage format Quat(0, 0, 0, 1))
104
JPH_INLINE static Quat sIdentity() { return Quat(0, 0, 0, 1); }
105
106
///@}
107
108
/// Rotation from axis and angle
109
JPH_INLINE static Quat sRotation(Vec3Arg inAxis, float inAngle);
110
111
/// Get axis and angle that represents this quaternion, outAngle will always be in the range \f$[0, \pi]\f$
112
JPH_INLINE void GetAxisAngle(Vec3 &outAxis, float &outAngle) const;
113
114
/// Calculate angular velocity given that this quaternion represents the rotation that is reached after inDeltaTime when starting from identity rotation
115
JPH_INLINE Vec3 GetAngularVelocity(float inDeltaTime) const;
116
117
/// Create quaternion that rotates a vector from the direction of inFrom to the direction of inTo along the shortest path
118
/// @see https://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
119
JPH_INLINE static Quat sFromTo(Vec3Arg inFrom, Vec3Arg inTo);
120
121
/// Random unit quaternion
122
template <class Random>
123
inline static Quat sRandom(Random &inRandom);
124
125
/// Conversion from Euler angles. Rotation order is X then Y then Z (RotZ * RotY * RotX). Angles in radians.
126
inline static Quat sEulerAngles(Vec3Arg inAngles);
127
128
/// Conversion to Euler angles. Rotation order is X then Y then Z (RotZ * RotY * RotX). Angles in radians.
129
inline Vec3 GetEulerAngles() const;
130
131
///@name Length / normalization operations
132
///@{
133
134
/// Squared length of quaternion.
135
/// @return Squared length of quaternion (\f$|v|^2\f$)
136
JPH_INLINE float LengthSq() const { return mValue.LengthSq(); }
137
138
/// Length of quaternion.
139
/// @return Length of quaternion (\f$|v|\f$)
140
JPH_INLINE float Length() const { return mValue.Length(); }
141
142
/// Normalize the quaternion (make it length 1)
143
JPH_INLINE Quat Normalized() const { return Quat(mValue.Normalized()); }
144
145
///@}
146
///@name Additions / multiplications
147
///@{
148
149
JPH_INLINE void operator += (QuatArg inRHS) { mValue += inRHS.mValue; }
150
JPH_INLINE void operator -= (QuatArg inRHS) { mValue -= inRHS.mValue; }
151
JPH_INLINE void operator *= (float inValue) { mValue *= inValue; }
152
JPH_INLINE void operator /= (float inValue) { mValue /= inValue; }
153
JPH_INLINE Quat operator - () const { return Quat(-mValue); }
154
JPH_INLINE Quat operator + (QuatArg inRHS) const { return Quat(mValue + inRHS.mValue); }
155
JPH_INLINE Quat operator - (QuatArg inRHS) const { return Quat(mValue - inRHS.mValue); }
156
JPH_INLINE Quat operator * (QuatArg inRHS) const;
157
JPH_INLINE Quat operator * (float inValue) const { return Quat(mValue * inValue); }
158
inline friend Quat operator * (float inValue, QuatArg inRHS) { return Quat(inRHS.mValue * inValue); }
159
JPH_INLINE Quat operator / (float inValue) const { return Quat(mValue / inValue); }
160
161
///@}
162
163
/// Rotate a vector by this quaternion
164
JPH_INLINE Vec3 operator * (Vec3Arg inValue) const;
165
166
/// Multiply a quaternion with imaginary components and no real component (x, y, z, 0) with a quaternion
167
static JPH_INLINE Quat sMultiplyImaginary(Vec3Arg inLHS, QuatArg inRHS);
168
169
/// Rotate a vector by the inverse of this quaternion
170
JPH_INLINE Vec3 InverseRotate(Vec3Arg inValue) const;
171
172
/// Rotate a the vector (1, 0, 0) with this quaternion
173
JPH_INLINE Vec3 RotateAxisX() const;
174
175
/// Rotate a the vector (0, 1, 0) with this quaternion
176
JPH_INLINE Vec3 RotateAxisY() const;
177
178
/// Rotate a the vector (0, 0, 1) with this quaternion
179
JPH_INLINE Vec3 RotateAxisZ() const;
180
181
/// Dot product
182
JPH_INLINE float Dot(QuatArg inRHS) const { return mValue.Dot(inRHS.mValue); }
183
184
/// The conjugate [w, -x, -y, -z] is the same as the inverse for unit quaternions
185
JPH_INLINE Quat Conjugated() const { return Quat(mValue.FlipSign<-1, -1, -1, 1>()); }
186
187
/// Get inverse quaternion
188
JPH_INLINE Quat Inversed() const { return Conjugated() / Length(); }
189
190
/// Ensures that the W component is positive by negating the entire quaternion if it is not. This is useful when you want to store a quaternion as a 3 vector by discarding W and reconstructing it as sqrt(1 - x^2 - y^2 - z^2).
191
JPH_INLINE Quat EnsureWPositive() const { return Quat(Vec4::sXor(mValue, Vec4::sAnd(mValue.SplatW(), UVec4::sReplicate(0x80000000).ReinterpretAsFloat()))); }
192
193
/// Get a quaternion that is perpendicular to this quaternion
194
JPH_INLINE Quat GetPerpendicular() const { return Quat(mValue.Swizzle<SWIZZLE_Y, SWIZZLE_X, SWIZZLE_W, SWIZZLE_Z>().FlipSign<1, -1, 1, -1>()); }
195
196
/// Get rotation angle around inAxis (uses Swing Twist Decomposition to get the twist quaternion and uses q(axis, angle) = [cos(angle / 2), axis * sin(angle / 2)])
197
JPH_INLINE float GetRotationAngle(Vec3Arg inAxis) const { return GetW() == 0.0f? JPH_PI : 2.0f * ATan(GetXYZ().Dot(inAxis) / GetW()); }
198
199
/// Swing Twist Decomposition: any quaternion can be split up as:
200
///
201
/// \f[q = q_{swing} \: q_{twist}\f]
202
///
203
/// where \f$q_{twist}\f$ rotates only around axis v.
204
///
205
/// \f$q_{twist}\f$ is:
206
///
207
/// \f[q_{twist} = \frac{[q_w, q_{ijk} \cdot v \: v]}{\left|[q_w, q_{ijk} \cdot v \: v]\right|}\f]
208
///
209
/// where q_w is the real part of the quaternion and q_i the imaginary part (a 3 vector).
210
///
211
/// The swing can then be calculated as:
212
///
213
/// \f[q_{swing} = q \: q_{twist}^* \f]
214
///
215
/// Where \f$q_{twist}^*\f$ = complex conjugate of \f$q_{twist}\f$
216
JPH_INLINE Quat GetTwist(Vec3Arg inAxis) const;
217
218
/// Decomposes quaternion into swing and twist component:
219
///
220
/// \f$q = q_{swing} \: q_{twist}\f$
221
///
222
/// where \f$q_{swing} \: \hat{x} = q_{twist} \: \hat{y} = q_{twist} \: \hat{z} = 0\f$
223
///
224
/// In other words:
225
///
226
/// - \f$q_{twist}\f$ only rotates around the X-axis.
227
/// - \f$q_{swing}\f$ only rotates around the Y and Z-axis.
228
///
229
/// @see Gino van den Bergen - Rotational Joint Limits in Quaternion Space - GDC 2016
230
JPH_INLINE void GetSwingTwist(Quat &outSwing, Quat &outTwist) const;
231
232
/// Linear interpolation between two quaternions (for small steps).
233
/// @param inFraction is in the range [0, 1]
234
/// @param inDestination The destination quaternion
235
/// @return (1 - inFraction) * this + fraction * inDestination
236
JPH_INLINE Quat LERP(QuatArg inDestination, float inFraction) const;
237
238
/// Spherical linear interpolation between two quaternions.
239
/// @param inFraction is in the range [0, 1]
240
/// @param inDestination The destination quaternion
241
/// @return When fraction is zero this quaternion is returned, when fraction is 1 inDestination is returned.
242
/// When fraction is between 0 and 1 an interpolation along the shortest path is returned.
243
JPH_INLINE Quat SLERP(QuatArg inDestination, float inFraction) const;
244
245
/// Load 3 floats from memory (X, Y and Z component and then calculates W) reads 32 bits extra which it doesn't use
246
static JPH_INLINE Quat sLoadFloat3Unsafe(const Float3 &inV);
247
248
/// Store as 3 floats to memory (X, Y and Z component). Ensures that W is positive before storing.
249
JPH_INLINE void StoreFloat3(Float3 *outV) const;
250
251
/// Store as 4 floats
252
JPH_INLINE void StoreFloat4(Float4 *outV) const;
253
254
/// Compress a unit quaternion to a 32 bit value, precision is around 0.5 degree
255
JPH_INLINE uint32 CompressUnitQuat() const { return mValue.CompressUnitVector(); }
256
257
/// Decompress a unit quaternion from a 32 bit value
258
JPH_INLINE static Quat sDecompressUnitQuat(uint32 inValue) { return Quat(Vec4::sDecompressUnitVector(inValue)); }
259
260
/// To String
261
friend ostream & operator << (ostream &inStream, QuatArg inQ) { inStream << inQ.mValue; return inStream; }
262
263
/// 4 vector that stores [x, y, z, w] parts of the quaternion
264
Vec4 mValue;
265
};
266
267
static_assert(std::is_trivial<Quat>(), "Is supposed to be a trivial type!");
268
269
JPH_NAMESPACE_END
270
271
#include "Quat.inl"
272
273