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 if (!mEffectiveMass.SetInversed3x3(mInvI1 + mInvI2))
147 Deactivate();
148 }
149
151 inline void Deactivate()
152 {
153 mEffectiveMass = Mat44::sZero();
154 mTotalLambda = Vec3::sZero();
155 }
156
158 inline bool IsActive() const
159 {
160 return mEffectiveMass(3, 3) != 0.0f;
161 }
162
164 inline void WarmStart(Body &ioBody1, Body &ioBody2, float inWarmStartImpulseRatio)
165 {
166 mTotalLambda *= inWarmStartImpulseRatio;
167 ApplyVelocityStep(ioBody1, ioBody2, mTotalLambda);
168 }
169
171 inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2)
172 {
173 // Calculate lagrange multiplier:
174 //
175 // lambda = -K^-1 (J v + b)
176 Vec3 lambda = mEffectiveMass.Multiply3x3(ioBody1.GetAngularVelocity() - ioBody2.GetAngularVelocity());
177 mTotalLambda += lambda;
178 return ApplyVelocityStep(ioBody1, ioBody2, lambda);
179 }
180
182 inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const
183 {
184 // Calculate difference in rotation
185 //
186 // The rotation should be:
187 //
188 // q2 = q1 r0
189 //
190 // But because of drift the actual rotation is
191 //
192 // q2 = diff q1 r0
193 // <=> diff = q2 r0^-1 q1^-1
194 //
195 // Where:
196 // q1 = current rotation of body 1
197 // q2 = current rotation of body 2
198 // diff = error that needs to be reduced to zero
199 Quat diff = ioBody2.GetRotation() * inInvInitialOrientation * ioBody1.GetRotation().Conjugated();
200
201 // A quaternion can be seen as:
202 //
203 // q = [sin(theta / 2) * v, cos(theta/2)]
204 //
205 // Where:
206 // v = rotation vector
207 // theta = rotation angle
208 //
209 // If we assume theta is small (error is small) then sin(x) = x so an approximation of the error angles is:
210 Vec3 error = 2.0f * diff.EnsureWPositive().GetXYZ();
211 if (error != Vec3::sZero())
212 {
213 // Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
214 //
215 // lambda = -K^-1 * beta / dt * C
216 //
217 // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
218 Vec3 lambda = -inBaumgarte * mEffectiveMass * error;
219
220 // Directly integrate velocity change for one time step
221 //
222 // Euler velocity integration:
223 // dv = M^-1 P
224 //
225 // Impulse:
226 // P = J^T lambda
227 //
228 // Euler position integration:
229 // x' = x + dv * dt
230 //
231 // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
232 // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
233 // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
234 // integrate + a position integrate and then discard the velocity change.
235 if (ioBody1.IsDynamic())
236 ioBody1.SubRotationStep(mInvI1.Multiply3x3(lambda));
237 if (ioBody2.IsDynamic())
238 ioBody2.AddRotationStep(mInvI2.Multiply3x3(lambda));
239 return true;
240 }
241
242 return false;
243 }
244
247 {
248 return mTotalLambda;
249 }
250
252 void SaveState(StateRecorder &inStream) const
253 {
254 inStream.Write(mTotalLambda);
255 }
256
259 {
260 inStream.Read(mTotalLambda);
261 }
262
263private:
264 Mat44 mInvI1;
265 Mat44 mInvI2;
266 Mat44 mEffectiveMass;
267 Vec3 mTotalLambda { Vec3::sZero() };
268};
269
#define JPH_NAMESPACE_END
Definition: Core.h:378
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:372
Definition: Body.h:35
const MotionProperties * GetMotionProperties() const
Access to the motion properties.
Definition: Body.h:249
bool IsDynamic() const
Check if this body is dynamic, which means that it moves and forces can act on it.
Definition: Body.h:61
void AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
Update rotation using an Euler step (using during position integrate & constraint solving)
Definition: Body.inl:81
Quat GetRotation() const
World space rotation of the body.
Definition: Body.h:231
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:153
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 Vec3 Multiply3x3(Vec3Arg inV) const
Multiply vector by only 3x3 part of the matrix.
Definition: Mat44.inl:316
void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange)
Definition: MotionProperties.h:194
Mat44 GetInverseInertiaForRotation(Mat44Arg inRotation) const
Get inverse inertia matrix ( ) for a given object rotation (translation will be ignored)....
Definition: MotionProperties.inl:59
void AddAngularVelocityStep(Vec3Arg inAngularVelocityChange)
Definition: MotionProperties.h:193
Definition: Quat.h:33
static JPH_INLINE Quat sIdentity()
Definition: Quat.h:103
JPH_INLINE Quat EnsureWPositive() const
Ensures that the W component is positive by negating the entire quaternion if it is not....
Definition: Quat.h:184
JPH_INLINE Quat Conjugated() const
The conjugate [w, -x, -y, -z] is the same as the inverse for unit quaternions.
Definition: Quat.h:178
JPH_INLINE Vec3 GetXYZ() const
Get the imaginary part of the quaternion.
Definition: Quat.h:81
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:164
bool IsActive() const
Check if constraint is active.
Definition: RotationEulerConstraintPart.h:158
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:246
bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, QuatArg inInvInitialOrientation, float inBaumgarte) const
Iteratively update the position constraint. Makes sure C(...) = 0.
Definition: RotationEulerConstraintPart.h:182
void Deactivate()
Deactivate this constraint.
Definition: RotationEulerConstraintPart.h:151
void RestoreState(StateRecorder &inStream)
Restore state of this constraint part.
Definition: RotationEulerConstraintPart.h:258
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:171
void SaveState(StateRecorder &inStream) const
Save state of this constraint part.
Definition: RotationEulerConstraintPart.h:252
Definition: StateRecorder.h:48
void Read(T &outT)
Read a primitive (e.g. float, int, etc.) from the binary stream.
Definition: StreamIn.h:29
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:590
static JPH_INLINE Vec3 sZero()
Vector with all zeros.
Definition: Vec3.inl:107
Definition: Vec4.h:14