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