Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/EstimateCollisionResponse.cpp
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Collision/EstimateCollisionResponse.h>
8
#include <Jolt/Physics/Body/Body.h>
9
10
JPH_NAMESPACE_BEGIN
11
12
void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution, uint inNumIterations)
13
{
14
// Note this code is based on AxisConstraintPart, see that class for more comments on the math
15
16
ContactPoints::size_type num_points = inManifold.mRelativeContactPointsOn1.size();
17
JPH_ASSERT(num_points == inManifold.mRelativeContactPointsOn2.size());
18
19
// Start with zero impulses
20
outResult.mImpulses.resize(num_points);
21
memset(outResult.mImpulses.data(), 0, num_points * sizeof(CollisionEstimationResult::Impulse));
22
23
// Calculate friction directions
24
outResult.mTangent1 = inManifold.mWorldSpaceNormal.GetNormalizedPerpendicular();
25
outResult.mTangent2 = inManifold.mWorldSpaceNormal.Cross(outResult.mTangent1);
26
27
// Get body velocities
28
EMotionType motion_type1 = inBody1.GetMotionType();
29
const MotionProperties *motion_properties1 = inBody1.GetMotionPropertiesUnchecked();
30
if (motion_type1 != EMotionType::Static)
31
{
32
outResult.mLinearVelocity1 = motion_properties1->GetLinearVelocity();
33
outResult.mAngularVelocity1 = motion_properties1->GetAngularVelocity();
34
}
35
else
36
outResult.mLinearVelocity1 = outResult.mAngularVelocity1 = Vec3::sZero();
37
38
EMotionType motion_type2 = inBody2.GetMotionType();
39
const MotionProperties *motion_properties2 = inBody2.GetMotionPropertiesUnchecked();
40
if (motion_type2 != EMotionType::Static)
41
{
42
outResult.mLinearVelocity2 = motion_properties2->GetLinearVelocity();
43
outResult.mAngularVelocity2 = motion_properties2->GetAngularVelocity();
44
}
45
else
46
outResult.mLinearVelocity2 = outResult.mAngularVelocity2 = Vec3::sZero();
47
48
// Get inverse mass and inertia
49
float inv_m1, inv_m2;
50
Mat44 inv_i1, inv_i2;
51
if (motion_type1 == EMotionType::Dynamic)
52
{
53
inv_m1 = motion_properties1->GetInverseMass();
54
inv_i1 = inBody1.GetInverseInertia();
55
}
56
else
57
{
58
inv_m1 = 0.0f;
59
inv_i1 = Mat44::sZero();
60
}
61
62
if (motion_type2 == EMotionType::Dynamic)
63
{
64
inv_m2 = motion_properties2->GetInverseMass();
65
inv_i2 = inBody2.GetInverseInertia();
66
}
67
else
68
{
69
inv_m2 = 0.0f;
70
inv_i2 = Mat44::sZero();
71
}
72
73
// Get center of masses relative to the base offset
74
Vec3 com1 = Vec3(inBody1.GetCenterOfMassPosition() - inManifold.mBaseOffset);
75
Vec3 com2 = Vec3(inBody2.GetCenterOfMassPosition() - inManifold.mBaseOffset);
76
77
struct AxisConstraint
78
{
79
inline void Initialize(Vec3Arg inR1, Vec3Arg inR2, Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2)
80
{
81
// Calculate effective mass: K^-1 = (J M^-1 J^T)^-1
82
mR1PlusUxAxis = inR1.Cross(inWorldSpaceNormal);
83
mR2xAxis = inR2.Cross(inWorldSpaceNormal);
84
mInvI1_R1PlusUxAxis = inInvI1.Multiply3x3(mR1PlusUxAxis);
85
mInvI2_R2xAxis = inInvI2.Multiply3x3(mR2xAxis);
86
mEffectiveMass = 1.0f / (inInvM1 + mInvI1_R1PlusUxAxis.Dot(mR1PlusUxAxis) + inInvM2 + mInvI2_R2xAxis.Dot(mR2xAxis));
87
mBias = 0.0f;
88
}
89
90
inline float SolveGetLambda(Vec3Arg inWorldSpaceNormal, const CollisionEstimationResult &inResult) const
91
{
92
// Calculate jacobian multiplied by linear/angular velocity
93
float jv = inWorldSpaceNormal.Dot(inResult.mLinearVelocity1 - inResult.mLinearVelocity2) + mR1PlusUxAxis.Dot(inResult.mAngularVelocity1) - mR2xAxis.Dot(inResult.mAngularVelocity2);
94
95
// Lagrange multiplier is:
96
//
97
// lambda = -K^-1 (J v + b)
98
return mEffectiveMass * (jv - mBias);
99
}
100
101
inline void SolveApplyLambda(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inLambda, CollisionEstimationResult &ioResult) const
102
{
103
// Apply impulse to body velocities
104
ioResult.mLinearVelocity1 -= (inLambda * inInvM1) * inWorldSpaceNormal;
105
ioResult.mAngularVelocity1 -= inLambda * mInvI1_R1PlusUxAxis;
106
ioResult.mLinearVelocity2 += (inLambda * inInvM2) * inWorldSpaceNormal;
107
ioResult.mAngularVelocity2 += inLambda * mInvI2_R2xAxis;
108
}
109
110
inline void Solve(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inMinLambda, float inMaxLambda, float &ioTotalLambda, CollisionEstimationResult &ioResult) const
111
{
112
// Calculate new total lambda
113
float total_lambda = ioTotalLambda + SolveGetLambda(inWorldSpaceNormal, ioResult);
114
115
// Clamp impulse
116
total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
117
118
SolveApplyLambda(inWorldSpaceNormal, inInvM1, inInvM2, total_lambda - ioTotalLambda, ioResult);
119
120
ioTotalLambda = total_lambda;
121
}
122
123
Vec3 mR1PlusUxAxis;
124
Vec3 mR2xAxis;
125
Vec3 mInvI1_R1PlusUxAxis;
126
Vec3 mInvI2_R2xAxis;
127
float mEffectiveMass;
128
float mBias;
129
};
130
131
struct Constraint
132
{
133
AxisConstraint mContact;
134
AxisConstraint mFriction1;
135
AxisConstraint mFriction2;
136
};
137
138
// Initialize the constraint properties
139
Constraint constraints[ContactPoints::Capacity];
140
for (uint c = 0; c < num_points; ++c)
141
{
142
Constraint &constraint = constraints[c];
143
144
// Calculate contact points relative to body 1 and 2
145
Vec3 p = 0.5f * (inManifold.mRelativeContactPointsOn1[c] + inManifold.mRelativeContactPointsOn2[c]);
146
Vec3 r1 = p - com1;
147
Vec3 r2 = p - com2;
148
149
// Initialize contact constraint
150
constraint.mContact.Initialize(r1, r2, inManifold.mWorldSpaceNormal, inv_m1, inv_m2, inv_i1, inv_i2);
151
152
// Handle elastic collisions
153
if (inCombinedRestitution > 0.0f)
154
{
155
// Calculate velocity of contact point
156
Vec3 relative_velocity = outResult.mLinearVelocity2 + outResult.mAngularVelocity2.Cross(r2) - outResult.mLinearVelocity1 - outResult.mAngularVelocity1.Cross(r1);
157
float normal_velocity = relative_velocity.Dot(inManifold.mWorldSpaceNormal);
158
159
// If it is big enough, apply restitution
160
if (normal_velocity < -inMinVelocityForRestitution)
161
constraint.mContact.mBias = inCombinedRestitution * normal_velocity;
162
}
163
164
if (inCombinedFriction > 0.0f)
165
{
166
// Initialize friction constraints
167
constraint.mFriction1.Initialize(r1, r2, outResult.mTangent1, inv_m1, inv_m2, inv_i1, inv_i2);
168
constraint.mFriction2.Initialize(r1, r2, outResult.mTangent2, inv_m1, inv_m2, inv_i1, inv_i2);
169
}
170
}
171
172
// If there's only 1 contact point, we only need 1 iteration
173
int num_iterations = inCombinedFriction <= 0.0f && num_points == 1? 1 : inNumIterations;
174
175
// Solve iteratively
176
for (int iteration = 0; iteration < num_iterations; ++iteration)
177
{
178
// Solve friction constraints first
179
if (inCombinedFriction > 0.0f && iteration > 0) // For first iteration the contact impulse is zero so there's no point in applying friction
180
for (uint c = 0; c < num_points; ++c)
181
{
182
const Constraint &constraint = constraints[c];
183
CollisionEstimationResult::Impulse &impulse = outResult.mImpulses[c];
184
185
float lambda1 = impulse.mFrictionImpulse1 + constraint.mFriction1.SolveGetLambda(outResult.mTangent1, outResult);
186
float lambda2 = impulse.mFrictionImpulse2 + constraint.mFriction2.SolveGetLambda(outResult.mTangent2, outResult);
187
188
// Calculate max impulse based on contact impulse
189
float max_impulse = inCombinedFriction * impulse.mContactImpulse;
190
191
// If the total lambda that we will apply is too large, scale it back
192
float total_lambda_sq = Square(lambda1) + Square(lambda2);
193
if (total_lambda_sq > Square(max_impulse))
194
{
195
float scale = max_impulse / sqrt(total_lambda_sq);
196
lambda1 *= scale;
197
lambda2 *= scale;
198
}
199
200
constraint.mFriction1.SolveApplyLambda(outResult.mTangent1, inv_m1, inv_m2, lambda1 - impulse.mFrictionImpulse1, outResult);
201
constraint.mFriction2.SolveApplyLambda(outResult.mTangent2, inv_m1, inv_m2, lambda2 - impulse.mFrictionImpulse2, outResult);
202
203
impulse.mFrictionImpulse1 = lambda1;
204
impulse.mFrictionImpulse2 = lambda2;
205
}
206
207
// Solve contact constraints last
208
for (uint c = 0; c < num_points; ++c)
209
constraints[c].mContact.Solve(inManifold.mWorldSpaceNormal, inv_m1, inv_m2, 0.0f, FLT_MAX, outResult.mImpulses[c].mContactImpulse, outResult);
210
}
211
}
212
213
JPH_NAMESPACE_END
214
215