Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/Shape.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/Shape/Shape.h>
8
#include <Jolt/Physics/Collision/Shape/ScaledShape.h>
9
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
10
#include <Jolt/Physics/Collision/TransformedShape.h>
11
#include <Jolt/Physics/Collision/PhysicsMaterial.h>
12
#include <Jolt/Physics/Collision/RayCast.h>
13
#include <Jolt/Physics/Collision/CastResult.h>
14
#include <Jolt/Physics/Collision/CollidePointResult.h>
15
#include <Jolt/Core/StreamIn.h>
16
#include <Jolt/Core/StreamOut.h>
17
#include <Jolt/Core/Factory.h>
18
#include <Jolt/ObjectStream/TypeDeclarations.h>
19
20
JPH_NAMESPACE_BEGIN
21
22
JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT_BASE(ShapeSettings)
23
{
24
JPH_ADD_BASE_CLASS(ShapeSettings, SerializableObject)
25
26
JPH_ADD_ATTRIBUTE(ShapeSettings, mUserData)
27
}
28
29
#ifdef JPH_DEBUG_RENDERER
30
bool Shape::sDrawSubmergedVolumes = false;
31
#endif // JPH_DEBUG_RENDERER
32
33
ShapeFunctions ShapeFunctions::sRegistry[NumSubShapeTypes];
34
35
const Shape *Shape::GetLeafShape([[maybe_unused]] const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const
36
{
37
outRemainder = inSubShapeID;
38
return this;
39
}
40
41
TransformedShape Shape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const
42
{
43
// We have reached the leaf shape so there is no remainder
44
outRemainder = SubShapeID();
45
46
// Just return the transformed shape for this shape
47
TransformedShape ts(RVec3(inPositionCOM), inRotation, this, BodyID());
48
ts.SetShapeScale(inScale);
49
return ts;
50
}
51
52
void Shape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const
53
{
54
// Test shape filter
55
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
56
return;
57
58
TransformedShape ts(RVec3(inPositionCOM), inRotation, this, TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator);
59
ts.SetShapeScale(inScale);
60
ioCollector.AddHit(ts);
61
}
62
63
void Shape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
64
{
65
Vec3 scale;
66
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
67
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
68
ts.SetShapeScale(MakeScaleValid(scale));
69
ioCollector.AddHit(ts);
70
}
71
72
void Shape::SaveBinaryState(StreamOut &inStream) const
73
{
74
inStream.Write(mShapeSubType);
75
inStream.Write(mUserData);
76
}
77
78
void Shape::RestoreBinaryState(StreamIn &inStream)
79
{
80
// Type hash read by sRestoreFromBinaryState
81
inStream.Read(mUserData);
82
}
83
84
Shape::ShapeResult Shape::sRestoreFromBinaryState(StreamIn &inStream)
85
{
86
ShapeResult result;
87
88
// Read the type of the shape
89
EShapeSubType shape_sub_type;
90
inStream.Read(shape_sub_type);
91
if (inStream.IsEOF() || inStream.IsFailed())
92
{
93
result.SetError("Failed to read type id");
94
return result;
95
}
96
97
// Construct and read the data of the shape
98
Ref<Shape> shape = ShapeFunctions::sGet(shape_sub_type).mConstruct();
99
shape->RestoreBinaryState(inStream);
100
if (inStream.IsEOF() || inStream.IsFailed())
101
{
102
result.SetError("Failed to restore shape");
103
return result;
104
}
105
106
result.Set(shape);
107
return result;
108
}
109
110
void Shape::SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const
111
{
112
ShapeToIDMap::const_iterator shape_id_iter = ioShapeMap.find(this);
113
if (shape_id_iter == ioShapeMap.end())
114
{
115
// Write shape ID of this shape
116
uint32 shape_id = ioShapeMap.size();
117
ioShapeMap[this] = shape_id;
118
inStream.Write(shape_id);
119
120
// Write the shape itself
121
SaveBinaryState(inStream);
122
123
// Write the ID's of all sub shapes
124
ShapeList sub_shapes;
125
SaveSubShapeState(sub_shapes);
126
inStream.Write(uint32(sub_shapes.size()));
127
for (const Shape *shape : sub_shapes)
128
{
129
if (shape == nullptr)
130
inStream.Write(~uint32(0));
131
else
132
shape->SaveWithChildren(inStream, ioShapeMap, ioMaterialMap);
133
}
134
135
// Write the materials
136
PhysicsMaterialList materials;
137
SaveMaterialState(materials);
138
StreamUtils::SaveObjectArray(inStream, materials, &ioMaterialMap);
139
}
140
else
141
{
142
// Known shape, just write the ID
143
inStream.Write(shape_id_iter->second);
144
}
145
}
146
147
Shape::ShapeResult Shape::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap)
148
{
149
ShapeResult result;
150
151
// Read ID of this shape
152
uint32 shape_id;
153
inStream.Read(shape_id);
154
if (inStream.IsEOF() || inStream.IsFailed())
155
{
156
result.SetError("Failed to read shape id");
157
return result;
158
}
159
160
// Check nullptr shape
161
if (shape_id == ~uint32(0))
162
{
163
result.Set(nullptr);
164
return result;
165
}
166
167
// Check if we already read this shape
168
if (shape_id < ioShapeMap.size())
169
{
170
result.Set(ioShapeMap[shape_id]);
171
return result;
172
}
173
174
// Read the shape
175
result = sRestoreFromBinaryState(inStream);
176
if (result.HasError())
177
return result;
178
JPH_ASSERT(ioShapeMap.size() == shape_id); // Assert that this is the next ID in the map
179
ioShapeMap.push_back(result.Get());
180
181
// Read the sub shapes
182
uint32 len;
183
inStream.Read(len);
184
if (inStream.IsEOF() || inStream.IsFailed())
185
{
186
result.SetError("Failed to read stream");
187
return result;
188
}
189
ShapeList sub_shapes;
190
sub_shapes.reserve(len);
191
for (size_t i = 0; i < len; ++i)
192
{
193
ShapeResult sub_shape_result = sRestoreWithChildren(inStream, ioShapeMap, ioMaterialMap);
194
if (sub_shape_result.HasError())
195
return sub_shape_result;
196
sub_shapes.push_back(sub_shape_result.Get());
197
}
198
result.Get()->RestoreSubShapeState(sub_shapes.data(), (uint)sub_shapes.size());
199
200
// Read the materials
201
Result mlresult = StreamUtils::RestoreObjectArray<PhysicsMaterialList>(inStream, ioMaterialMap);
202
if (mlresult.HasError())
203
{
204
result.SetError(mlresult.GetError());
205
return result;
206
}
207
const PhysicsMaterialList &materials = mlresult.Get();
208
result.Get()->RestoreMaterialState(materials.data(), (uint)materials.size());
209
210
return result;
211
}
212
213
Shape::Stats Shape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const
214
{
215
Stats stats = GetStats();
216
217
// If shape is already visited, don't count its size again
218
if (!ioVisitedShapes.insert(this).second)
219
stats.mSizeBytes = 0;
220
221
return stats;
222
}
223
224
bool Shape::IsValidScale(Vec3Arg inScale) const
225
{
226
return !ScaleHelpers::IsZeroScale(inScale);
227
}
228
229
Vec3 Shape::MakeScaleValid(Vec3Arg inScale) const
230
{
231
return ScaleHelpers::MakeNonZeroScale(inScale);
232
}
233
234
Shape::ShapeResult Shape::ScaleShape(Vec3Arg inScale) const
235
{
236
const Vec3 unit_scale = Vec3::sOne();
237
238
if (inScale.IsNearZero())
239
{
240
ShapeResult result;
241
result.SetError("Can't use zero scale!");
242
return result;
243
}
244
245
// First test if we can just wrap this shape in a scaled shape
246
if (IsValidScale(inScale))
247
{
248
// Test if the scale is near unit
249
ShapeResult result;
250
if (inScale.IsClose(unit_scale))
251
result.Set(const_cast<Shape *>(this));
252
else
253
result.Set(new ScaledShape(this, inScale));
254
return result;
255
}
256
257
// Collect the leaf shapes and their transforms
258
struct Collector : TransformedShapeCollector
259
{
260
virtual void AddHit(const ResultType &inResult) override
261
{
262
mShapes.push_back(inResult);
263
}
264
265
Array<TransformedShape> mShapes;
266
};
267
Collector collector;
268
TransformShape(Mat44::sScale(inScale) * Mat44::sTranslation(GetCenterOfMass()), collector);
269
270
// Construct a compound shape
271
StaticCompoundShapeSettings compound;
272
compound.mSubShapes.reserve(collector.mShapes.size());
273
for (const TransformedShape &ts : collector.mShapes)
274
{
275
const Shape *shape = ts.mShape;
276
277
// Construct a scaled shape if scale is not unit
278
Vec3 scale = ts.GetShapeScale();
279
if (!scale.IsClose(unit_scale))
280
shape = new ScaledShape(shape, scale);
281
282
// Add the shape
283
compound.AddShape(Vec3(ts.mShapePositionCOM) - ts.mShapeRotation * shape->GetCenterOfMass(), ts.mShapeRotation, shape);
284
}
285
286
return compound.Create();
287
}
288
289
void Shape::sCollidePointUsingRayCast(const Shape &inShape, Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter)
290
{
291
// First test if we're inside our bounding box
292
AABox bounds = inShape.GetLocalBounds();
293
if (bounds.Contains(inPoint))
294
{
295
// A collector that just counts the number of hits
296
class HitCountCollector : public CastRayCollector
297
{
298
public:
299
virtual void AddHit(const RayCastResult &inResult) override
300
{
301
// Store the last sub shape ID so that we can provide something to our outer hit collector
302
mSubShapeID = inResult.mSubShapeID2;
303
304
++mHitCount;
305
}
306
307
int mHitCount = 0;
308
SubShapeID mSubShapeID;
309
};
310
HitCountCollector collector;
311
312
// Configure the raycast
313
RayCastSettings settings;
314
settings.SetBackFaceMode(EBackFaceMode::CollideWithBackFaces);
315
316
// Cast a ray that's 10% longer than the height of our bounding box
317
inShape.CastRay(RayCast { inPoint, 1.1f * bounds.GetSize().GetY() * Vec3::sAxisY() }, settings, inSubShapeIDCreator, collector, inShapeFilter);
318
319
// Odd amount of hits means inside
320
if ((collector.mHitCount & 1) == 1)
321
ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), collector.mSubShapeID });
322
}
323
}
324
325
JPH_NAMESPACE_END
326
327