Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Character/Character.cpp
9912 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/Character/Character.h>
8
#include <Jolt/Physics/Body/BodyCreationSettings.h>
9
#include <Jolt/Physics/Body/BodyLock.h>
10
#include <Jolt/Physics/Collision/CollideShape.h>
11
#include <Jolt/Physics/PhysicsSystem.h>
12
#include <Jolt/ObjectStream/TypeDeclarations.h>
13
14
JPH_NAMESPACE_BEGIN
15
16
static inline const BodyLockInterface &sCharacterGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)
17
{
18
return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());
19
}
20
21
static inline BodyInterface &sCharacterGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)
22
{
23
return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();
24
}
25
26
static inline const NarrowPhaseQuery &sCharacterGetNarrowPhaseQuery(const PhysicsSystem *inSystem, bool inLockBodies)
27
{
28
return inLockBodies? inSystem->GetNarrowPhaseQuery() : inSystem->GetNarrowPhaseQueryNoLock();
29
}
30
31
Character::Character(const CharacterSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) :
32
CharacterBase(inSettings, inSystem),
33
mLayer(inSettings->mLayer)
34
{
35
// Construct rigid body
36
BodyCreationSettings settings(mShape, inPosition, inRotation, EMotionType::Dynamic, mLayer);
37
settings.mAllowedDOFs = inSettings->mAllowedDOFs;
38
settings.mEnhancedInternalEdgeRemoval = inSettings->mEnhancedInternalEdgeRemoval;
39
settings.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
40
settings.mMassPropertiesOverride.mMass = inSettings->mMass;
41
settings.mFriction = inSettings->mFriction;
42
settings.mGravityFactor = inSettings->mGravityFactor;
43
settings.mUserData = inUserData;
44
const Body *body = mSystem->GetBodyInterface().CreateBody(settings);
45
if (body != nullptr)
46
mBodyID = body->GetID();
47
}
48
49
Character::~Character()
50
{
51
// Destroy the body
52
mSystem->GetBodyInterface().DestroyBody(mBodyID);
53
}
54
55
void Character::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)
56
{
57
sCharacterGetBodyInterface(mSystem, inLockBodies).AddBody(mBodyID, inActivationMode);
58
}
59
60
void Character::RemoveFromPhysicsSystem(bool inLockBodies)
61
{
62
sCharacterGetBodyInterface(mSystem, inLockBodies).RemoveBody(mBodyID);
63
}
64
65
void Character::Activate(bool inLockBodies)
66
{
67
sCharacterGetBodyInterface(mSystem, inLockBodies).ActivateBody(mBodyID);
68
}
69
70
void Character::CheckCollision(RMat44Arg inCenterOfMassTransform, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const
71
{
72
// Create query broadphase layer filter
73
DefaultBroadPhaseLayerFilter broadphase_layer_filter = mSystem->GetDefaultBroadPhaseLayerFilter(mLayer);
74
75
// Create query object layer filter
76
DefaultObjectLayerFilter object_layer_filter = mSystem->GetDefaultLayerFilter(mLayer);
77
78
// Ignore sensors and my own body
79
class CharacterBodyFilter : public IgnoreSingleBodyFilter
80
{
81
public:
82
using IgnoreSingleBodyFilter::IgnoreSingleBodyFilter;
83
84
virtual bool ShouldCollideLocked(const Body &inBody) const override
85
{
86
return !inBody.IsSensor();
87
}
88
};
89
CharacterBodyFilter body_filter(mBodyID);
90
91
// Settings for collide shape
92
CollideShapeSettings settings;
93
settings.mMaxSeparationDistance = inMaxSeparationDistance;
94
settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;
95
settings.mActiveEdgeMovementDirection = inMovementDirection;
96
settings.mBackFaceMode = EBackFaceMode::IgnoreBackFaces;
97
98
sCharacterGetNarrowPhaseQuery(mSystem, inLockBodies).CollideShape(inShape, Vec3::sOne(), inCenterOfMassTransform, settings, inBaseOffset, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter);
99
}
100
101
void Character::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const
102
{
103
// Calculate center of mass transform
104
RMat44 center_of_mass = RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(inShape->GetCenterOfMass());
105
106
CheckCollision(center_of_mass, inMovementDirection, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies);
107
}
108
109
void Character::CheckCollision(const Shape *inShape, float inMaxSeparationDistance, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const
110
{
111
// Determine position and velocity of body
112
RMat44 query_transform;
113
Vec3 velocity;
114
{
115
BodyLockRead lock(sCharacterGetBodyLockInterface(mSystem, inLockBodies), mBodyID);
116
if (!lock.Succeeded())
117
return;
118
119
const Body &body = lock.GetBody();
120
121
// Correct the center of mass transform for the difference between the old and new center of mass shape
122
query_transform = body.GetCenterOfMassTransform().PreTranslated(inShape->GetCenterOfMass() - mShape->GetCenterOfMass());
123
velocity = body.GetLinearVelocity();
124
}
125
126
CheckCollision(query_transform, velocity, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies);
127
}
128
129
void Character::PostSimulation(float inMaxSeparationDistance, bool inLockBodies)
130
{
131
// Get character position, rotation and velocity
132
RVec3 char_pos;
133
Quat char_rot;
134
Vec3 char_vel;
135
{
136
BodyLockRead lock(sCharacterGetBodyLockInterface(mSystem, inLockBodies), mBodyID);
137
if (!lock.Succeeded())
138
return;
139
const Body &body = lock.GetBody();
140
char_pos = body.GetPosition();
141
char_rot = body.GetRotation();
142
char_vel = body.GetLinearVelocity();
143
}
144
145
// Collector that finds the hit with the normal that is the most 'up'
146
class MyCollector : public CollideShapeCollector
147
{
148
public:
149
// Constructor
150
explicit MyCollector(Vec3Arg inUp, RVec3 inBaseOffset) : mBaseOffset(inBaseOffset), mUp(inUp) { }
151
152
// See: CollectorType::AddHit
153
virtual void AddHit(const CollideShapeResult &inResult) override
154
{
155
Vec3 normal = -inResult.mPenetrationAxis.Normalized();
156
float dot = normal.Dot(mUp);
157
if (dot > mBestDot) // Find the hit that is most aligned with the up vector
158
{
159
mGroundBodyID = inResult.mBodyID2;
160
mGroundBodySubShapeID = inResult.mSubShapeID2;
161
mGroundPosition = mBaseOffset + inResult.mContactPointOn2;
162
mGroundNormal = normal;
163
mBestDot = dot;
164
}
165
}
166
167
BodyID mGroundBodyID;
168
SubShapeID mGroundBodySubShapeID;
169
RVec3 mGroundPosition = RVec3::sZero();
170
Vec3 mGroundNormal = Vec3::sZero();
171
172
private:
173
RVec3 mBaseOffset;
174
Vec3 mUp;
175
float mBestDot = -FLT_MAX;
176
};
177
178
// Collide shape
179
MyCollector collector(mUp, char_pos);
180
CheckCollision(char_pos, char_rot, char_vel, inMaxSeparationDistance, mShape, char_pos, collector, inLockBodies);
181
182
// Copy results
183
mGroundBodyID = collector.mGroundBodyID;
184
mGroundBodySubShapeID = collector.mGroundBodySubShapeID;
185
mGroundPosition = collector.mGroundPosition;
186
mGroundNormal = collector.mGroundNormal;
187
188
// Get additional data from body
189
BodyLockRead lock(sCharacterGetBodyLockInterface(mSystem, inLockBodies), mGroundBodyID);
190
if (lock.Succeeded())
191
{
192
const Body &body = lock.GetBody();
193
194
// Update ground state
195
RMat44 inv_transform = RMat44::sInverseRotationTranslation(char_rot, char_pos);
196
if (mSupportingVolume.SignedDistance(Vec3(inv_transform * mGroundPosition)) > 0.0f)
197
mGroundState = EGroundState::NotSupported;
198
else if (IsSlopeTooSteep(mGroundNormal))
199
mGroundState = EGroundState::OnSteepGround;
200
else
201
mGroundState = EGroundState::OnGround;
202
203
// Copy other body properties
204
mGroundMaterial = body.GetShape()->GetMaterial(mGroundBodySubShapeID);
205
mGroundVelocity = body.GetPointVelocity(mGroundPosition);
206
mGroundUserData = body.GetUserData();
207
}
208
else
209
{
210
mGroundState = EGroundState::InAir;
211
mGroundMaterial = PhysicsMaterial::sDefault;
212
mGroundVelocity = Vec3::sZero();
213
mGroundUserData = 0;
214
}
215
}
216
217
void Character::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)
218
{
219
sCharacterGetBodyInterface(mSystem, inLockBodies).SetLinearAndAngularVelocity(mBodyID, inLinearVelocity, inAngularVelocity);
220
}
221
222
Vec3 Character::GetLinearVelocity(bool inLockBodies) const
223
{
224
return sCharacterGetBodyInterface(mSystem, inLockBodies).GetLinearVelocity(mBodyID);
225
}
226
227
void Character::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
228
{
229
sCharacterGetBodyInterface(mSystem, inLockBodies).SetLinearVelocity(mBodyID, inLinearVelocity);
230
}
231
232
void Character::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
233
{
234
sCharacterGetBodyInterface(mSystem, inLockBodies).AddLinearVelocity(mBodyID, inLinearVelocity);
235
}
236
237
void Character::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)
238
{
239
sCharacterGetBodyInterface(mSystem, inLockBodies).AddImpulse(mBodyID, inImpulse);
240
}
241
242
void Character::GetPositionAndRotation(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const
243
{
244
sCharacterGetBodyInterface(mSystem, inLockBodies).GetPositionAndRotation(mBodyID, outPosition, outRotation);
245
}
246
247
void Character::SetPositionAndRotation(RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) const
248
{
249
sCharacterGetBodyInterface(mSystem, inLockBodies).SetPositionAndRotation(mBodyID, inPosition, inRotation, inActivationMode);
250
}
251
252
RVec3 Character::GetPosition(bool inLockBodies) const
253
{
254
return sCharacterGetBodyInterface(mSystem, inLockBodies).GetPosition(mBodyID);
255
}
256
257
void Character::SetPosition(RVec3Arg inPosition, EActivation inActivationMode, bool inLockBodies)
258
{
259
sCharacterGetBodyInterface(mSystem, inLockBodies).SetPosition(mBodyID, inPosition, inActivationMode);
260
}
261
262
Quat Character::GetRotation(bool inLockBodies) const
263
{
264
return sCharacterGetBodyInterface(mSystem, inLockBodies).GetRotation(mBodyID);
265
}
266
267
void Character::SetRotation(QuatArg inRotation, EActivation inActivationMode, bool inLockBodies)
268
{
269
sCharacterGetBodyInterface(mSystem, inLockBodies).SetRotation(mBodyID, inRotation, inActivationMode);
270
}
271
272
RVec3 Character::GetCenterOfMassPosition(bool inLockBodies) const
273
{
274
return sCharacterGetBodyInterface(mSystem, inLockBodies).GetCenterOfMassPosition(mBodyID);
275
}
276
277
RMat44 Character::GetWorldTransform(bool inLockBodies) const
278
{
279
return sCharacterGetBodyInterface(mSystem, inLockBodies).GetWorldTransform(mBodyID);
280
}
281
282
void Character::SetLayer(ObjectLayer inLayer, bool inLockBodies)
283
{
284
mLayer = inLayer;
285
286
sCharacterGetBodyInterface(mSystem, inLockBodies).SetObjectLayer(mBodyID, inLayer);
287
}
288
289
bool Character::SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies)
290
{
291
if (inMaxPenetrationDepth < FLT_MAX)
292
{
293
// Collector that checks if there is anything in the way while switching to inShape
294
class MyCollector : public CollideShapeCollector
295
{
296
public:
297
// Constructor
298
explicit MyCollector(float inMaxPenetrationDepth) : mMaxPenetrationDepth(inMaxPenetrationDepth) { }
299
300
// See: CollectorType::AddHit
301
virtual void AddHit(const CollideShapeResult &inResult) override
302
{
303
if (inResult.mPenetrationDepth > mMaxPenetrationDepth)
304
{
305
mHadCollision = true;
306
ForceEarlyOut();
307
}
308
}
309
310
float mMaxPenetrationDepth;
311
bool mHadCollision = false;
312
};
313
314
// Test if anything is in the way of switching
315
RVec3 char_pos = GetPosition(inLockBodies);
316
MyCollector collector(inMaxPenetrationDepth);
317
CheckCollision(inShape, 0.0f, char_pos, collector, inLockBodies);
318
if (collector.mHadCollision)
319
return false;
320
}
321
322
// Switch the shape
323
mShape = inShape;
324
sCharacterGetBodyInterface(mSystem, inLockBodies).SetShape(mBodyID, mShape, false, EActivation::Activate);
325
return true;
326
}
327
328
TransformedShape Character::GetTransformedShape(bool inLockBodies) const
329
{
330
return sCharacterGetBodyInterface(mSystem, inLockBodies).GetTransformedShape(mBodyID);
331
}
332
333
JPH_NAMESPACE_END
334
335