Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
RotationEulerConstraintPart.h
Go to the documentation of this file.
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
9
11
36{
37private:
39 JPH_INLINE bool ApplyVelocityStep(Body &ioBody1, Body &ioBody2, Vec3Arg inLambda) const
40 {
41 // Apply impulse if delta is not zero
42 if (inLambda != Vec3::sZero())
43 {
44 // Calculate velocity change due to constraint
45 //
46 // Impulse:
47 // P = J^T lambda
48 //
49 // Euler velocity integration:
50 // v' = v + M^-1 P
51 if (ioBody1.IsDynamic())
52 ioBody1.GetMotionProperties()->SubAngularVelocityStep(mInvI1.Multiply3x3(inLambda));
53 if (ioBody2.IsDynamic())
54 ioBody2.GetMotionProperties()->AddAngularVelocityStep(mInvI2.Multiply3x3(inLambda));
55 return true;
56 }
57
58 return false;
59 }
60
61public:
63 static Quat sGetInvInitialOrientation(const Body &inBody1, const Body &inBody2)
64 {
65 // q20 = q10 r0
66 // <=> r0 = q10^-1 q20
67 // <=> r0^-1 = q20^-1 q10
68 //
69 // where:
70 //
71 // q20 = initial orientation of body 2
72 // q10 = initial orientation of body 1
73 // r0 = initial rotation from body 1 to body 2
74 return inBody2.GetRotation().Conjugated() * inBody1.GetRotation();
75 }
76
82 static Quat sGetInvInitialOrientationXY(Vec3Arg inAxisX1, Vec3Arg inAxisY1, Vec3Arg inAxisX2, Vec3Arg inAxisY2)
83 {
84 // Store inverse of initial rotation from body 1 to body 2 in body 1 space:
85 //
86 // q20 = q10 r0
87 // <=> r0 = q10^-1 q20
88 // <=> r0^-1 = q20^-1 q10
89 //
90 // where:
91 //
92 // q10, q20 = world space initial orientation of body 1 and 2
93 // r0 = initial rotation from body 1 to body 2 in local space of body 1
94 //
95 // We can also write this in terms of the constraint matrices:
96 //
97 // q20 c2 = q10 c1
98 // <=> q20 = q10 c1 c2^-1
99 // => r0 = c1 c2^-1
100 // <=> r0^-1 = c2 c1^-1
101 //
102 // where:
103 //
104 // c1, c2 = matrix that takes us from body 1 and 2 COM to constraint space 1 and 2
105 if (inAxisX1 == inAxisX2 && inAxisY1 == inAxisY2)
106 {
107 // Axis are the same -> identity transform
108 return Quat::sIdentity();
109 }
110 else
111 {
112 Mat44 constraint1(Vec4(inAxisX1, 0), Vec4(inAxisY1, 0), Vec4(inAxisX1.Cross(inAxisY1), 0), Vec4(0, 0, 0, 1));
113 Mat44 constraint2(Vec4(inAxisX2, 0), Vec4(inAxisY2, 0), Vec4(inAxisX2.Cross(inAxisY2), 0), Vec4(0, 0, 0, 1));
114 return constraint2.GetQuaternion() * constraint1.GetQuaternion().Conjugated();
115 }
116 }
117
123 static Quat sGetInvInitialOrientationXZ(Vec3Arg inAxisX1, Vec3Arg inAxisZ1, Vec3Arg inAxisX2, Vec3Arg inAxisZ2)
124 {
125 // See comment at sGetInvInitialOrientationXY
126 if (inAxisX1 == inAxisX2 && inAxisZ1 == inAxisZ2)
127 {
128 return Quat::sIdentity();
129 }
130 else
131 {
132 Mat44 constraint1(Vec4(inAxisX1, 0), Vec4(inAxisZ1.Cross(inAxisX1), 0), Vec4(inAxisZ1, 0), Vec4(0, 0, 0, 1));
133 Mat44 constraint2(Vec4(inAxisX2, 0), Vec4(inAxisZ2.Cross(inAxisX2), 0), Vec4(inAxisZ2, 0), Vec4(0, 0, 0, 1));
134 return constraint2.GetQuaternion() * constraint1.GetQuaternion().Conjugated();
135 }
136 }
137
139 inline void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2)
140 {
141 // Calculate properties used during constraint solving
142 mInvI1 = inBody1.IsDynamic()? inBody1.GetMotionProperties()->GetInverseInertiaForRotation(inRotation1) : Mat44::sZero();
143 mInvI2 = inBody2.IsDynamic()? inBody2.GetMotionProperties()->GetInverseInertiaForRotation(inRotation2) : Mat44::sZero();
144
145 // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1
146 Mat44 inertia_sum = mInvI1 + mInvI2;
147 if (!mEffectiveMass.SetInversed3x3(inertia_sum))
148 {
149 // If a column is zero, the axis is locked and we set the column to identity.
150 // This does not matter because any impulse will always be multiplied with mInvI1 or mInvI2 which will result in zero for the locked coordinate.
151 Vec4 zero = Vec4::sZero();
152 if (inertia_sum.GetColumn4(0) == zero)
153 inertia_sum.SetColumn4(0, Vec4(1, 0, 0, 0));
154 if (inertia_sum.GetColumn4(1) == zero)
155 inertia_sum.SetColumn4(1, Vec4(0, 1, 0, 0));
156 if (inertia_sum.GetColumn4(2) == zero)
157 inertia_sum.SetColumn4(2, Vec4(0, 0, 1, 0));
158 if (!mEffectiveMass.SetInversed3x3(inertia_sum))
159 Deactivate();
160 }
161 }
162
164 inline void Deactivate()
165 {
166 mEffectiveMass = Mat44::sZero();
167 mTotalLambda = Vec3::sZero();
168 }
169
171 inline bool IsActive() const
172 {
173 return mEffectiveMass(3, 3) != 0.0f;
174 }
175
177 inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
178 {
179 mTotalLambda *= inWarmStartImpulseRatio;
180 ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda);
181 }
182
184 inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2)
185 {
186 // Calculate lagrange multiplier:
187 //
188 // lambda = -K^-1 (J v + b)
189 Vec3 lambda = mEffectiveMass.Multiply3x3(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity());
190 mTotalLambda += lambda;
191 return ApplyVelocityStep(ioBody1, ioBody2, lambda);
192 }
193
195 inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const
196 {
197 // Calculate difference in rotation
198 //
199 // The rotation should be:
200 //
201 // q2 = q1 r0
202 //
203 // But because of drift the actual rotation is
204 //
205 // q2 = diff q1 r0
206 // <=> diff = q2 r0^-1 q1^-1
207 //
208 // Where:
209 // q1 = current rotation of body 1
210 // q2 = current rotation of body 2
211 // diff = error that needs to be reduced to zero
212 Quat diff = ioBody2.GetRotation() * inInvInitialOrientation * ioBody1.GetRotation().Conjugated();
213
214 // A quaternion can be seen as:
215 //
216 // q = [sin(theta / 2) * v, cos(theta/2)]
217 //
218 // Where:
219 // v = rotation vector
220 // theta = rotation angle
221 //
222 // If we assume theta is small (error is small) then sin(x) = x so an approximation of the error angles is:
223 Vec3 error = 2.0f * diff.EnsureWPositive().GetXYZ();
224 if (error != Vec3::sZero())
225 {
226 // Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
227 //
228 // lambda = -K^-1 * beta / dt * C
229 //
230 // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
231 Vec3 lambda = -inBaumgarte * mEffectiveMass * error;
232
233 // Directly integrate velocity change for one time step
234 //
235 // Euler velocity integration:
236 // dv = M^-1 P
237 //
238 // Impulse:
239 // P = J^T lambda
240 //
241 // Euler position integration:
242 // x' = x + dv * dt
243 //
244 // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
245 // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
246 // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
247 // integrate + a position integrate and then discard the velocity change.
248 if (ioBody1.IsDynamic())
249 ioBody1.SubRotationStep(mInvI1.Multiply3x3(lambda));
250 if (ioBody2.IsDynamic())
251 ioBody2.AddRotationStep(mInvI2.Multiply3x3(lambda));
252 return true;
253 }
254
255 return false;
256 }
257
260 {
261 return mTotalLambda;
262 }
263
265 void SaveState(StateRecorder &inStream) const
266 {
267 inStream.Write(mTotalLambda);
268 }
269
272 {
273 inStream.Read(mTotalLambda);
274 }
275
276private:
277 Mat44 mInvI1;
278 Mat44 mInvI2;
279 Mat44 mEffectiveMass;
280 Vec3 mTotalLambda { Vec3::sZero() };
281};
282
#define JPH_NAMESPACE_END
Definition Core.h:419
#define JPH_NAMESPACE_BEGIN
Definition Core.h:413
Definition Body.h:39
const MotionProperties * GetMotionProperties() const
Access to the motion properties.
Definition Body.h:308
bool IsDynamic() const
Check if this body is dynamic, which means that it moves and forces can act on it.
Definition Body.h:67
void AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
Update rotation using an Euler step (used during position integrate & constraint solving)
Definition Body.inl:81
Quat GetRotation() const
World space rotation of the body.
Definition Body.h:271
void SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
Definition Body.inl:100
Vec3 GetAngularVelocity() const
Get world space angular velocity of the center of mass (unit: rad/s)
Definition Body.h:161
Holds a 4x4 matrix of floats, but supports also operations on the 3x3 upper left part of the matrix.
Definition Mat44.h:13
static JPH_INLINE Mat44 sZero()
Zero matrix.
Definition Mat44.inl:30
JPH_INLINE Quat GetQuaternion() const
Convert to quaternion.
Definition Mat44.inl:783
JPH_INLINE bool SetInversed3x3(Mat44Arg inM)
*this = inM.Inversed3x3(), returns false if the matrix is singular in which case *this is unchanged
Definition Mat44.inl:767
JPH_INLINE Vec4 GetColumn4(uint inCol) const
Definition Mat44.h:160
JPH_INLINE Vec3 Multiply3x3(Vec3Arg inV) const
Multiply vector by only 3x3 part of the matrix.
Definition Mat44.inl:316
JPH_INLINE void SetColumn4(uint inCol, Vec4Arg inV)
Definition Mat44.h:161
void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange)
Definition MotionProperties.h:220
Mat44 GetInverseInertiaForRotation(Mat44Arg inRotation) const
Get inverse inertia matrix ( ) for a given object rotation (translation will be ignored)....
Definition MotionProperties.inl:69
void AddAngularVelocityStep(Vec3Arg inAngularVelocityChange)
Definition MotionProperties.h:219
Definition Quat.h:33
static JPH_INLINE Quat sIdentity()
Definition Quat.h:104
JPH_INLINE Quat EnsureWPositive() const
Ensures that the W component is positive by negating the entire quaternion if it is not....
Definition Quat.h:188
JPH_INLINE Quat Conjugated() const
The conjugate [w, -x, -y, -z] is the same as the inverse for unit quaternions.
Definition Quat.h:182
JPH_INLINE Vec3 GetXYZ() const
Get the imaginary part of the quaternion.
Definition Quat.h:82
Definition RotationEulerConstraintPart.h:36
static Quat sGetInvInitialOrientation(const Body &inBody1, const Body &inBody2)
Return inverse of initial rotation from body 1 to body 2 in body 1 space.
Definition RotationEulerConstraintPart.h:63
void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses.
Definition RotationEulerConstraintPart.h:177
bool IsActive() const
Check if constraint is active.
Definition RotationEulerConstraintPart.h:171
static Quat sGetInvInitialOrientationXY(Vec3Arg inAxisX1, Vec3Arg inAxisY1, Vec3Arg inAxisX2, Vec3Arg inAxisY2)
Return inverse of initial rotation from body 1 to body 2 in body 1 space.
Definition RotationEulerConstraintPart.h:82
Vec3 GetTotalLambda() const
Return lagrange multiplier.
Definition RotationEulerConstraintPart.h:259
bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const
Iteratively update the position constraint. Makes sure C(...) = 0.
Definition RotationEulerConstraintPart.h:195
void Deactivate()
Deactivate this constraint.
Definition RotationEulerConstraintPart.h:164
void RestoreState(StateRecorder &inStream)
Restore state of this constraint part.
Definition RotationEulerConstraintPart.h:271
static Quat sGetInvInitialOrientationXZ(Vec3Arg inAxisX1, Vec3Arg inAxisZ1, Vec3Arg inAxisX2, Vec3Arg inAxisZ2)
Return inverse of initial rotation from body 1 to body 2 in body 1 space.
Definition RotationEulerConstraintPart.h:123
void CalculateConstraintProperties(const Body &inBody1, Mat44Arg inRotation1, const Body &inBody2, Mat44Arg inRotation2)
Calculate properties used during the functions below.
Definition RotationEulerConstraintPart.h:139
bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2)
Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equ...
Definition RotationEulerConstraintPart.h:184
void SaveState(StateRecorder &inStream) const
Save state of this constraint part.
Definition RotationEulerConstraintPart.h:265
Definition StateRecorder.h:110
void Read(T &outT)
Read a primitive (e.g. float, int, etc.) from the binary stream.
Definition StreamIn.h:30
void Write(const T &inT)
Write a primitive (e.g. float, int, etc.) to the binary stream.
Definition StreamOut.h:26
Definition Vec3.h:17
JPH_INLINE Vec3 Cross(Vec3Arg inV2) const
Cross product.
Definition Vec3.inl:595
static JPH_INLINE Vec3 sZero()
Vector with all zeros.
Definition Vec3.inl:103
Definition Vec4.h:14
static JPH_INLINE Vec4 sZero()
Vector with all zeros.
Definition Vec4.inl:63