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};
46
47template <>
49{
50protected:
51 // Note: Constructor will not be called
53};
54
55template <>
56class ContactConstraintPart1<EMotionType::Dynamic> : public ContactConstraintPart1<EMotionType::Kinematic>
57{
58protected:
59 // Note: Constructor will not be called
61};
62
63template <EMotionType Type2>
67
68template <>
70{
71protected:
72 // Note: Constructor will not be called
74};
75
76template <>
77class ContactConstraintPart2<EMotionType::Dynamic> : public ContactConstraintPart2<EMotionType::Kinematic>
78{
79protected:
80 // Note: Constructor will not be called
82};
83
85template <EMotionType Type1, EMotionType Type2>
87{
88private:
90 JPH_INLINE bool ApplyVelocityStep(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inLambda) const
91 {
92 if (inLambda != 0.0f)
93 {
94 if constexpr (Type1 == EMotionType::Dynamic)
95 {
96 ioLinearVelocity1 -= (inLambda * inInvMass1) * inWorldSpaceAxis;
97 ioAngularVelocity1 -= inLambda * Vec3::sLoadFloat3Unsafe(this->mInvI1_R1PlusUxAxis);
98 }
99 if constexpr (Type2 == EMotionType::Dynamic)
100 {
101 ioLinearVelocity2 += (inLambda * inInvMass2) * inWorldSpaceAxis;
102 ioAngularVelocity2 += inLambda * Vec3::sLoadFloat3Unsafe(this->mInvI2_R2xAxis);
103 }
104 return true;
105 }
106
107 return false;
108 }
109
110public:
112 JPH_INLINE void CalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
113 {
114 JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-5f));
115
116 // Store bias
117 mBias = inBias;
118
119 // Calculate inverse effective mass: K = J M^-1 J^T
120 float inv_effective_mass;
121
122 if constexpr (Type1 != EMotionType::Static)
123 {
124 Vec3 r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis);
125 r1_plus_u_x_axis.StoreFloat3(&this->mR1PlusUxAxis);
126
127 if constexpr (Type1 == EMotionType::Dynamic)
128 {
129 Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis);
130 invi1_r1_plus_u_x_axis.StoreFloat3(&this->mInvI1_R1PlusUxAxis);
131
132 inv_effective_mass = inInvMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis);
133 }
134 else
135 inv_effective_mass = 0.0f;
136 }
137 else
138 inv_effective_mass = 0.0f;
139
140 if constexpr (Type2 != EMotionType::Static)
141 {
142 Vec3 r2_x_axis = inR2.Cross(inWorldSpaceAxis);
143 r2_x_axis.StoreFloat3(&this->mR2xAxis);
144
145 if constexpr (Type2 == EMotionType::Dynamic)
146 {
147 Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis);
148 invi2_r2_x_axis.StoreFloat3(&this->mInvI2_R2xAxis);
149
150 inv_effective_mass += inInvMass2 + invi2_r2_x_axis.Dot(r2_x_axis);
151 }
152 }
153
154 if (inv_effective_mass == 0.0f)
155 this->Deactivate();
156 else
157 this->mEffectiveMass = 1.0f / inv_effective_mass;
158 }
159
161 JPH_INLINE bool WarmStart(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
162 {
163 this->mTotalLambda *= inWarmStartImpulseRatio;
164
165 return ApplyVelocityStep(ioLinearVelocity1, ioAngularVelocity1, ioLinearVelocity2, ioAngularVelocity2, inInvMass1, inInvMass2, inWorldSpaceAxis, this->mTotalLambda);
166 }
167
169 JPH_INLINE float SolveVelocityConstraintGetTotalLambda(Vec3Arg inLinearVelocity1, Vec3Arg inAngularVelocity1, Vec3Arg inLinearVelocity2, Vec3Arg inAngularVelocity2, Vec3Arg inWorldSpaceAxis) const
170 {
171 // Calculate jacobian multiplied by linear velocity
172 float jv;
173 if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
174 jv = inWorldSpaceAxis.Dot(inLinearVelocity1 - inLinearVelocity2);
175 else if constexpr (Type1 != EMotionType::Static)
176 jv = inWorldSpaceAxis.Dot(inLinearVelocity1);
177 else if constexpr (Type2 != EMotionType::Static)
178 jv = inWorldSpaceAxis.Dot(-inLinearVelocity2);
179 else
180 JPH_ASSERT(false); // Static vs static is nonsensical!
181
182 // Calculate jacobian multiplied by angular velocity
183 if constexpr (Type1 != EMotionType::Static)
184 jv += Vec3::sLoadFloat3Unsafe(this->mR1PlusUxAxis).Dot(inAngularVelocity1);
185 if constexpr (Type2 != EMotionType::Static)
186 jv -= Vec3::sLoadFloat3Unsafe(this->mR2xAxis).Dot(inAngularVelocity2);
187
188 // Lagrange multiplier is:
189 //
190 // lambda = -K^-1 (J v + b)
191 float lambda = this->mEffectiveMass * (jv - mBias);
192
193 // Return the total accumulated lambda
194 return this->mTotalLambda + lambda;
195 }
196
198 JPH_INLINE bool SolveVelocityConstraintApplyLambda(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
199 {
200 float delta_lambda = inTotalLambda - this->mTotalLambda; // Calculate change in lambda
201 this->mTotalLambda = inTotalLambda; // Store accumulated impulse
202
203 return ApplyVelocityStep(ioLinearVelocity1, ioAngularVelocity1, ioLinearVelocity2, ioAngularVelocity2, inInvMass1, inInvMass2, inWorldSpaceAxis, delta_lambda);
204 }
205
207 JPH_INLINE bool SolveVelocityConstraint(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
208 {
209 float total_lambda = SolveVelocityConstraintGetTotalLambda(ioLinearVelocity1, ioAngularVelocity1, ioLinearVelocity2, ioAngularVelocity2, inWorldSpaceAxis);
210
211 // Clamp impulse to specified range
212 total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
213
214 return SolveVelocityConstraintApplyLambda(ioLinearVelocity1, ioAngularVelocity1, ioLinearVelocity2, ioAngularVelocity2, inInvMass1, inInvMass2, inWorldSpaceAxis, total_lambda);
215 }
216
218 JPH_INLINE bool SolvePositionConstraint(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
219 {
220 if (inC != 0.0f)
221 {
222 float lambda = -this->mEffectiveMass * inBaumgarte * inC;
223 if constexpr (Type1 == EMotionType::Dynamic)
224 {
225 ioBody1.SubPositionStep((lambda * inInvMass1) * inWorldSpaceAxis);
226 ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(this->mInvI1_R1PlusUxAxis));
227 }
228 if constexpr (Type2 == EMotionType::Dynamic)
229 {
230 ioBody2.AddPositionStep((lambda * inInvMass2) * inWorldSpaceAxis);
231 ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(this->mInvI2_R2xAxis));
232 }
233 return true;
234 }
235
236 return false;
237 }
238
239private:
240 // Note: Constructor will not be called. This serves as 1 extra float so we can read the previous member using Vec3::sLoadFloat3Unsafe
241 float mBias;
242};
243
244static_assert(sizeof(ContactConstraintPart<EMotionType::Dynamic, EMotionType::Dynamic>) == 3 * sizeof(float) + 4 * sizeof(Float3));
245static_assert(sizeof(ContactConstraintPart<EMotionType::Dynamic, EMotionType::Kinematic>) == 3 * sizeof(float) + 3 * sizeof(Float3));
246static_assert(sizeof(ContactConstraintPart<EMotionType::Dynamic, EMotionType::Static>) == 3 * sizeof(float) + 2 * sizeof(Float3));
247static_assert(sizeof(ContactConstraintPart<EMotionType::Kinematic, EMotionType::Dynamic>) == 3 * sizeof(float) + 3 * sizeof(Float3));
248static_assert(sizeof(ContactConstraintPart<EMotionType::Kinematic, EMotionType::Kinematic>) == 3 * sizeof(float) + 2 * sizeof(Float3));
249static_assert(sizeof(ContactConstraintPart<EMotionType::Kinematic, EMotionType::Static>) == 3 * sizeof(float) + sizeof(Float3));
250static_assert(sizeof(ContactConstraintPart<EMotionType::Static, EMotionType::Dynamic>) == 3 * sizeof(float) + 2 * sizeof(Float3));
251static_assert(sizeof(ContactConstraintPart<EMotionType::Static, EMotionType::Kinematic>) == 3 * sizeof(float) + sizeof(Float3));
252static_assert(sizeof(ContactConstraintPart<EMotionType::Static, EMotionType::Static>) == 3 * sizeof(float));
253
#define JPH_NAMESPACE_END
Definition Core.h:434
#define JPH_NAMESPACE_BEGIN
Definition Core.h:428
#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:63
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
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:349
void SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
Definition Body.inl:100
void AddPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime)
Update position using an Euler step (used during position integrate & constraint solving)
Definition Body.h:348
Float3 mInvI1_R1PlusUxAxis
Definition ContactConstraintPart.h:60
Float3 mR1PlusUxAxis
Definition ContactConstraintPart.h:52
Decide which members this constraint part needs based on motion type.
Definition ContactConstraintPart.h:14
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:81
Float3 mR2xAxis
Definition ContactConstraintPart.h:73
Definition ContactConstraintPart.h:65
This is a copy of AxisConstraintPart, specialized to handle contact constraints. See the documentatio...
Definition ContactConstraintPart.h:87
JPH_INLINE bool SolveVelocityConstraint(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
See: AxisConstraintPart::SolveVelocityConstraint.
Definition ContactConstraintPart.h:207
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:112
JPH_INLINE bool SolveVelocityConstraintApplyLambda(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
Part 2 of AxisConstraint::SolveVelocityConstraint: apply new lambda.
Definition ContactConstraintPart.h:198
JPH_INLINE bool SolvePositionConstraint(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
See: AxisConstraintPart::SolvePositionConstraint.
Definition ContactConstraintPart.h:218
JPH_INLINE float SolveVelocityConstraintGetTotalLambda(Vec3Arg inLinearVelocity1, Vec3Arg inAngularVelocity1, Vec3Arg inLinearVelocity2, Vec3Arg inAngularVelocity2, Vec3Arg inWorldSpaceAxis) const
Part 1 of AxisConstraint::SolveVelocityConstraint: get the total lambda.
Definition ContactConstraintPart.h:169
JPH_INLINE bool WarmStart(Vec3 &ioLinearVelocity1, Vec3 &ioAngularVelocity1, Vec3 &ioLinearVelocity2, Vec3 &ioAngularVelocity2, float inInvMass1, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
See AxisConstraintPart::WarmStart.
Definition ContactConstraintPart.h:161
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
Definition Vec3.h:17
JPH_INLINE float Dot(Vec3Arg inV2) const
Dot product.
Definition Vec3.inl:931
JPH_INLINE Vec3 Cross(Vec3Arg inV2) const
Cross product.
Definition Vec3.inl:855
JPH_INLINE bool IsNormalized(float inTolerance=1.0e-6f) const
Test if vector is normalized.
Definition Vec3.inl:1030
JPH_INLINE void StoreFloat3(Float3 *outV) const
Store 3 floats to memory.
Definition Vec3.inl:1055
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