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