Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
ContactConstraintPart.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2026 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
8
10
12template <EMotionType Type1>
14{
15public:
17 inline void Deactivate()
18 {
19 mEffectiveMass = 0.0f;
20 mTotalLambda = 0.0f;
21 }
22
24 inline bool IsActive() const
25 {
26 return mEffectiveMass != 0.0f;
27 }
28
30 inline void SetTotalLambda(float inLambda)
31 {
32 mTotalLambda = inLambda;
33 }
34
36 inline float GetTotalLambda() const
37 {
38 return mTotalLambda;
39 }
40
41protected:
42 // Note: Constructor will not be called
45 float mBias;
46};
47
48template <>
50{
51protected:
52 // Note: Constructor will not be called
54};
55
56template <>
57class ContactConstraintPart1<EMotionType::Dynamic> : public ContactConstraintPart1<EMotionType::Kinematic>
58{
59protected:
60 // Note: Constructor will not be called
62};
63
64template <EMotionType Type2>
68
69template <>
71{
72protected:
73 // Note: Constructor will not be called
75};
76
77template <>
78class ContactConstraintPart2<EMotionType::Dynamic> : public ContactConstraintPart2<EMotionType::Kinematic>
79{
80protected:
81 // Note: Constructor will not be called
83};
84
85static_assert(sizeof(ContactConstraintPart1<EMotionType::Kinematic>) == 3 * sizeof(float) + sizeof(Float3));
86static_assert(sizeof(ContactConstraintPart1<EMotionType::Dynamic>) == 3 * sizeof(float) + 2 * sizeof(Float3));
87static_assert(sizeof(ContactConstraintPart2<EMotionType::Kinematic>) == sizeof(Float3));
88static_assert(sizeof(ContactConstraintPart2<EMotionType::Dynamic>) == 2 * sizeof(Float3));
89
92template <EMotionType Type1, EMotionType Type2>
94{
95private:
97 JPH_INLINE bool ApplyVelocityStep(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inLambda) const
98 {
99 if (inLambda != 0.0f)
100 {
101 if constexpr (Type1 == EMotionType::Dynamic)
102 {
103 ioMotionProperties1->SubLinearVelocityStep((inLambda * inInvMass1) * inWorldSpaceAxis);
104 ioMotionProperties1->SubAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(this->mInvI1_R1PlusUxAxis));
105 }
106 if constexpr (Type2 == EMotionType::Dynamic)
107 {
108 ioMotionProperties2->AddLinearVelocityStep((inLambda * inInvMass2) * inWorldSpaceAxis);
109 ioMotionProperties2->AddAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(this->mInvI2_R2xAxis));
110 }
111 return true;
112 }
113
114 return false;
115 }
116
117public:
119 JPH_INLINE void CalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
120 {
121 JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-5f));
122
123 // Calculate inverse effective mass: K = J M^-1 J^T
124 float inv_effective_mass;
125
126 if constexpr (Type1 != EMotionType::Static)
127 {
128 Vec3 r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis);
129 r1_plus_u_x_axis.StoreFloat3(&this->mR1PlusUxAxis);
130
131 if constexpr (Type1 == EMotionType::Dynamic)
132 {
133 Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis);
134 invi1_r1_plus_u_x_axis.StoreFloat3(&this->mInvI1_R1PlusUxAxis);
135
136 inv_effective_mass = inInvMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis);
137 }
138 else
139 inv_effective_mass = 0.0f;
140 }
141 else
142 inv_effective_mass = 0.0f;
143
144 if constexpr (Type2 != EMotionType::Static)
145 {
146 Vec3 r2_x_axis = inR2.Cross(inWorldSpaceAxis);
147 r2_x_axis.StoreFloat3(&this->mR2xAxis);
148
149 if constexpr (Type2 == EMotionType::Dynamic)
150 {
151 Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis);
152 invi2_r2_x_axis.StoreFloat3(&this->mInvI2_R2xAxis);
153
154 inv_effective_mass += inInvMass2 + invi2_r2_x_axis.Dot(r2_x_axis);
155 }
156 }
157
158 if (inv_effective_mass == 0.0f)
159 this->Deactivate();
160 else
161 {
162 this->mEffectiveMass = 1.0f / inv_effective_mass;
163 this->mBias = inBias;
164 }
165 }
166
168 inline void WarmStart(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
169 {
170 this->mTotalLambda *= inWarmStartImpulseRatio;
171
172 ApplyVelocityStep(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, this->mTotalLambda);
173 }
174
176 JPH_INLINE float SolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const
177 {
178 // Calculate jacobian multiplied by linear velocity
179 float jv;
180 if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
181 jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity());
182 else if constexpr (Type1 != EMotionType::Static)
183 jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity());
184 else if constexpr (Type2 != EMotionType::Static)
185 jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity());
186 else
187 JPH_ASSERT(false); // Static vs static is nonsensical!
188
189 // Calculate jacobian multiplied by angular velocity
190 if constexpr (Type1 != EMotionType::Static)
191 jv += Vec3::sLoadFloat3Unsafe(this->mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity());
192 if constexpr (Type2 != EMotionType::Static)
193 jv -= Vec3::sLoadFloat3Unsafe(this->mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity());
194
195 // Lagrange multiplier is:
196 //
197 // lambda = -K^-1 (J v + b)
198 float lambda = this->mEffectiveMass * (jv - this->mBias);
199
200 // Return the total accumulated lambda
201 return this->mTotalLambda + lambda;
202 }
203
205 JPH_INLINE bool SolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
206 {
207 float delta_lambda = inTotalLambda - this->mTotalLambda; // Calculate change in lambda
208 this->mTotalLambda = inTotalLambda; // Store accumulated impulse
209
210 return ApplyVelocityStep(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, delta_lambda);
211 }
212
214 JPH_INLINE bool SolveVelocityConstraint(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
215 {
216 float total_lambda = SolveVelocityConstraintGetTotalLambda(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis);
217
218 // Clamp impulse to specified range
219 total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
220
221 return SolveVelocityConstraintApplyLambda(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, total_lambda);
222 }
223
225 inline bool SolvePositionConstraint(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
226 {
227 if (inC != 0.0f)
228 {
229 float lambda = -this->mEffectiveMass * inBaumgarte * inC;
230 if constexpr (Type1 == EMotionType::Dynamic)
231 {
232 ioBody1.SubPositionStep((lambda * inInvMass1) * inWorldSpaceAxis);
233 ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(this->mInvI1_R1PlusUxAxis));
234 }
235 if constexpr (Type2 == EMotionType::Dynamic)
236 {
237 ioBody2.AddPositionStep((lambda * inInvMass2) * inWorldSpaceAxis);
238 ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(this->mInvI2_R2xAxis));
239 }
240 return true;
241 }
242
243 return false;
244 }
245};
246
247static_assert(sizeof(ContactConstraintPart<EMotionType::Dynamic, EMotionType::Dynamic>) == 3 * sizeof(float) + 4 * sizeof(Float3));
248static_assert(sizeof(ContactConstraintPart<EMotionType::Dynamic, EMotionType::Kinematic>) == 3 * sizeof(float) + 3 * sizeof(Float3));
249static_assert(sizeof(ContactConstraintPart<EMotionType::Dynamic, EMotionType::Static>) == 3 * sizeof(float) + 2 * sizeof(Float3));
250static_assert(sizeof(ContactConstraintPart<EMotionType::Kinematic, EMotionType::Kinematic>) == 3 * sizeof(float) + 2 * sizeof(Float3));
251static_assert(sizeof(ContactConstraintPart<EMotionType::Kinematic, EMotionType::Static>) == 3 * sizeof(float) + sizeof(Float3));
252static_assert(sizeof(ContactConstraintPart<EMotionType::Static, EMotionType::Dynamic>) == 3 * sizeof(float) + 2 * sizeof(Float3));
253static_assert(sizeof(ContactConstraintPart<EMotionType::Static, EMotionType::Kinematic>) == 3 * sizeof(float) + sizeof(Float3));
254static_assert(sizeof(ContactConstraintPart<EMotionType::Static, EMotionType::Static>) == 3 * sizeof(float));
255
258{
259public:
263
264 inline bool SolveVelocityConstraint(Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inMinLambda, float inMaxLambda)
265 {
266 // Dispatch to the correct templated form
267 switch (inBody1.GetMotionType())
268 {
270 {
271 Mat44 inv_i1 = inInvInertiaScale1 * inBody1.GetInverseInertia();
272 switch (inBody2.GetMotionType())
273 {
275 mDD.CalculateConstraintProperties(inInvMass1, inv_i1, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis, inBias);
276 return mDD.SolveVelocityConstraint(inBody1.GetMotionPropertiesUnchecked(), inInvMass1, inBody2.GetMotionPropertiesUnchecked(), inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
277
279 mDK.CalculateConstraintProperties(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis, inBias);
280 return mDK.SolveVelocityConstraint(inBody1.GetMotionPropertiesUnchecked(), inInvMass1, inBody2.GetMotionPropertiesUnchecked(), inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
281
283 mDS.CalculateConstraintProperties(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis, inBias);
284 return mDS.SolveVelocityConstraint(inBody1.GetMotionPropertiesUnchecked(), inInvMass1, nullptr /* Unused */, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
285
286 default:
287 JPH_ASSERT(false);
288 break;
289 }
290 break;
291 }
292
294 JPH_ASSERT(inBody2.IsDynamic());
295 mKD.CalculateConstraintProperties(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis, inBias);
296 return mKD.SolveVelocityConstraint(inBody1.GetMotionPropertiesUnchecked(), inInvMass1, inBody2.GetMotionPropertiesUnchecked(), inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
297
299 JPH_ASSERT(inBody2.IsDynamic());
300 mSD.CalculateConstraintProperties(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis, inBias);
301 return mSD.SolveVelocityConstraint(nullptr /* Unused */, 0.0f /* Unused */, inBody2.GetMotionPropertiesUnchecked(), inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
302
303 default:
304 JPH_ASSERT(false);
305 break;
306 }
307
308 return false;
309 }
310
311 inline float GetTotalLambda() const
312 {
313 return mDD.GetTotalLambda();
314 }
315
316private:
317 union
318 {
324 };
325 [[maybe_unused]] float mPadding; // Pad an extra float so that we can use Vec3::sLoadFloat3Unsafe on the last Float3 member without worrying about reading past the end of the struct
326};
327
#define JPH_NAMESPACE_END
Definition Core.h:428
#define JPH_NAMESPACE_BEGIN
Definition Core.h:422
#define JPH_ASSERT(...)
Definition IssueReporting.h:33
JPH_INLINE constexpr T Clamp(T inV, T inMin, T inMax)
Clamp a value between two values.
Definition Math.h:48
EMotionType
Motion type of a physics body.
Definition MotionType.h:11
@ Kinematic
Movable using velocities only, does not respond to forces.
@ Static
Non movable.
@ Dynamic
Responds to forces as a normal physics object.
Definition Body.h:39
EMotionType GetMotionType() const
Get the bodies motion type.
Definition Body.h:118
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
void SubPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime)
Definition Body.h:343
Mat44 GetInverseInertia() const
Get inverse inertia tensor in world space.
Definition Body.inl:120
void SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
Definition Body.inl:100
const MotionProperties * GetMotionPropertiesUnchecked() const
Access to the motion properties (version that does not check if the object is kinematic or dynamic)
Definition Body.h:312
void AddPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime)
Update position using an Euler step (used during position integrate & constraint solving)
Definition Body.h:342
Concrete contact constraint part that dispatches to the correct templated form based on the motion ty...
Definition ContactConstraintPart.h:258
ContactConstraintPart< EMotionType::Dynamic, EMotionType::Static > mDS
Definition ContactConstraintPart.h:321
float GetTotalLambda() const
Definition ContactConstraintPart.h:311
ContactConstraintPart< EMotionType::Dynamic, EMotionType::Kinematic > mDK
Definition ContactConstraintPart.h:320
bool SolveVelocityConstraint(Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inMinLambda, float inMaxLambda)
Definition ContactConstraintPart.h:264
ConcreteContactConstraintPart()
Constructor / destructor.
Definition ContactConstraintPart.h:261
ContactConstraintPart< EMotionType::Dynamic, EMotionType::Dynamic > mDD
Definition ContactConstraintPart.h:319
ContactConstraintPart< EMotionType::Static, EMotionType::Dynamic > mSD
Definition ContactConstraintPart.h:323
ContactConstraintPart< EMotionType::Kinematic, EMotionType::Dynamic > mKD
Definition ContactConstraintPart.h:322
Float3 mInvI1_R1PlusUxAxis
Definition ContactConstraintPart.h:61
Float3 mR1PlusUxAxis
Definition ContactConstraintPart.h:53
Decide which members this constraint part needs based on motion type.
Definition ContactConstraintPart.h:14
float mBias
Definition ContactConstraintPart.h:45
float mEffectiveMass
Definition ContactConstraintPart.h:43
void Deactivate()
Deactivate this constraint.
Definition ContactConstraintPart.h:17
bool IsActive() const
Check if constraint is active.
Definition ContactConstraintPart.h:24
void SetTotalLambda(float inLambda)
Override total lagrange multiplier, can be used to set the initial value for warm starting.
Definition ContactConstraintPart.h:30
float mTotalLambda
Definition ContactConstraintPart.h:44
float GetTotalLambda() const
Return lagrange multiplier.
Definition ContactConstraintPart.h:36
Float3 mInvI2_R2xAxis
Definition ContactConstraintPart.h:82
Float3 mR2xAxis
Definition ContactConstraintPart.h:74
Definition ContactConstraintPart.h:66
Definition ContactConstraintPart.h:94
JPH_INLINE float SolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const
Part 1 of AxisConstraint::SolveVelocityConstraint: get the total lambda.
Definition ContactConstraintPart.h:176
bool SolvePositionConstraint(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
See: AxisConstraintPart::SolvePositionConstraint.
Definition ContactConstraintPart.h:225
JPH_INLINE void CalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias=0.0f)
See AxisConstraintPart::CalculateConstraintProperties.
Definition ContactConstraintPart.h:119
void WarmStart(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
See AxisConstraintPart::WarmStart.
Definition ContactConstraintPart.h:168
JPH_INLINE bool SolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
Part 2 of AxisConstraint::SolveVelocityConstraint: apply new lambda.
Definition ContactConstraintPart.h:205
JPH_INLINE bool SolveVelocityConstraint(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
See: AxisConstraintPart::SolveVelocityConstraint.
Definition ContactConstraintPart.h:214
Class that holds 3 floats. Used as a storage class. Convert to Vec3 for calculations.
Definition Float3.h:13
Holds a 4x4 matrix of floats, but supports also operations on the 3x3 upper left part of the matrix.
Definition Mat44.h:13
JPH_INLINE Vec3 Multiply3x3(Vec3Arg inV) const
Multiply vector by only 3x3 part of the matrix.
Definition Mat44.inl:384
The Body class only keeps track of state for static bodies, the MotionProperties class keeps the addi...
Definition MotionProperties.h:29
void AddLinearVelocityStep(Vec3Arg inLinearVelocityChange)
Definition MotionProperties.h:217
Vec3 GetLinearVelocity() const
Get world space linear velocity of the center of mass.
Definition MotionProperties.h:43
Vec3 GetAngularVelocity() const
Get world space angular velocity of the center of mass.
Definition MotionProperties.h:52
void SubLinearVelocityStep(Vec3Arg inLinearVelocityChange)
Definition MotionProperties.h:218
void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange)
Definition MotionProperties.h:220
void AddAngularVelocityStep(Vec3Arg inAngularVelocityChange)
Definition MotionProperties.h:219
Definition Vec3.h:17
JPH_INLINE float Dot(Vec3Arg inV2) const
Dot product.
Definition Vec3.inl:943
JPH_INLINE Vec3 Cross(Vec3Arg inV2) const
Cross product.
Definition Vec3.inl:841
JPH_INLINE bool IsNormalized(float inTolerance=1.0e-6f) const
Test if vector is normalized.
Definition Vec3.inl:1128
JPH_INLINE void StoreFloat3(Float3 *outV) const
Store 3 floats to memory.
Definition Vec3.inl:1153
static JPH_INLINE Vec3 sLoadFloat3Unsafe(const Float3 &inV)
Load 3 floats from memory (reads 32 bits extra which it doesn't use)
Definition Vec3.inl:167