Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
InternalEdgeRemovingCollector.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
9
10//#define JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
11
12#ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
14#endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
15
17
21{
22 static constexpr uint cMaxDelayedResults = 16;
23 static constexpr uint cMaxVoidedFeatures = 128;
24
26 inline bool IsVoided(const SubShapeID &inSubShapeID, Vec3 inV) const
27 {
28 for (const Voided &vf : mVoidedFeatures)
29 if (vf.mSubShapeID == inSubShapeID
30 && inV.IsClose(Vec3::sLoadFloat3Unsafe(vf.mFeature), 1.0e-8f))
31 return true;
32 return false;
33 }
34
36 inline void VoidFeatures(const CollideShapeResult &inResult)
37 {
38 if (mVoidedFeatures.size() < cMaxVoidedFeatures)
39 for (const Vec3 &v : inResult.mShape2Face)
40 if (!IsVoided(inResult.mSubShapeID1, v))
41 {
42 Voided vf;
43 v.StoreFloat3(&vf.mFeature);
44 vf.mSubShapeID = inResult.mSubShapeID1;
45 mVoidedFeatures.push_back(vf);
46 if (mVoidedFeatures.size() == cMaxVoidedFeatures)
47 break;
48 }
49 }
50
52 inline void Chain(const CollideShapeResult &inResult)
53 {
54 // Make sure the chained collector has the same context as we do
55 mChainedCollector.SetContext(GetContext());
56
57 // Forward the hit
58 mChainedCollector.AddHit(inResult);
59
60 // If our chained collector updated its early out fraction, we need to follow
62 }
63
65 inline void ChainAndVoid(const CollideShapeResult &inResult)
66 {
67 Chain(inResult);
68 VoidFeatures(inResult);
69
70 #ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
73 #endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
74 }
75
76public:
79 mChainedCollector(inChainedCollector)
80 {
81 }
82
83 // See: CollideShapeCollector::Reset
84 virtual void Reset() override
85 {
87
88 mChainedCollector.Reset();
89
90 mVoidedFeatures.clear();
91 mDelayedResults.clear();
92 }
93
94 // See: CollideShapeCollector::OnBody
95 virtual void OnBody(const Body &inBody) override
96 {
97 // Just forward the call to our chained collector
98 mChainedCollector.OnBody(inBody);
99 }
100
101 // See: CollideShapeCollector::AddHit
102 virtual void AddHit(const CollideShapeResult &inResult) override
103 {
104 // We only support welding when the shape is a triangle or has more vertices so that we can calculate a normal
105 if (inResult.mShape2Face.size() < 3)
106 return ChainAndVoid(inResult);
107
108 // Get the triangle normal of shape 2 face
109 Vec3 triangle_normal = (inResult.mShape2Face[1] - inResult.mShape2Face[0]).Cross(inResult.mShape2Face[2] - inResult.mShape2Face[0]);
110 float triangle_normal_len = triangle_normal.Length();
111 if (triangle_normal_len < 1e-6f)
112 return ChainAndVoid(inResult);
113
114 // If the triangle normal matches the contact normal within 1 degree, we can process the contact immediately
115 // We make the assumption here that if the contact normal and the triangle normal align that the we're dealing with a 'face contact'
116 Vec3 contact_normal = -inResult.mPenetrationAxis;
117 float contact_normal_len = inResult.mPenetrationAxis.Length();
118 if (triangle_normal.Dot(contact_normal) > 0.999848f * contact_normal_len * triangle_normal_len) // cos(1 degree)
119 return ChainAndVoid(inResult);
120
121 // Delayed processing
122 if (mDelayedResults.size() == cMaxDelayedResults)
123 return ChainAndVoid(inResult);
124 mDelayedResults.push_back(inResult);
125 }
126
128 void Flush()
129 {
130 // Sort on biggest penetration depth first
131 uint sorted_indices[cMaxDelayedResults];
132 for (uint i = 0; i < uint(mDelayedResults.size()); ++i)
133 sorted_indices[i] = i;
134 QuickSort(sorted_indices, sorted_indices + mDelayedResults.size(), [this](uint inLHS, uint inRHS) { return mDelayedResults[inLHS].mPenetrationDepth > mDelayedResults[inRHS].mPenetrationDepth; });
135
136 // Loop over all results
137 for (uint i = 0; i < uint(mDelayedResults.size()); ++i)
138 {
139 const CollideShapeResult &r = mDelayedResults[sorted_indices[i]];
140
141 // Determine which vertex or which edge is the closest to the contact point
142 float best_dist_sq = FLT_MAX;
143 uint best_v1_idx = 0;
144 uint best_v2_idx = 0;
145 uint num_v = uint(r.mShape2Face.size());
146 uint v1_idx = num_v - 1;
147 Vec3 v1 = r.mShape2Face[v1_idx] - r.mContactPointOn2;
148 for (uint v2_idx = 0; v2_idx < num_v; ++v2_idx)
149 {
150 Vec3 v2 = r.mShape2Face[v2_idx] - r.mContactPointOn2;
151 Vec3 v1_v2 = v2 - v1;
152 float denominator = v1_v2.LengthSq();
153 if (denominator < Square(FLT_EPSILON))
154 {
155 // Degenerate, assume v1 is closest, v2 will be tested in a later iteration
156 float v1_len_sq = v1.LengthSq();
157 if (v1_len_sq < best_dist_sq)
158 {
159 best_dist_sq = v1_len_sq;
160 best_v1_idx = v1_idx;
161 best_v2_idx = v1_idx;
162 }
163 }
164 else
165 {
166 // Taken from ClosestPoint::GetBaryCentricCoordinates
167 float fraction = -v1.Dot(v1_v2) / denominator;
168 if (fraction < 1.0e-6f)
169 {
170 // Closest lies on v1
171 float v1_len_sq = v1.LengthSq();
172 if (v1_len_sq < best_dist_sq)
173 {
174 best_dist_sq = v1_len_sq;
175 best_v1_idx = v1_idx;
176 best_v2_idx = v1_idx;
177 }
178 }
179 else if (fraction < 1.0f - 1.0e-6f)
180 {
181 // Closest lies on the line segment v1, v2
182 Vec3 closest = v1 + fraction * v1_v2;
183 float closest_len_sq = closest.LengthSq();
184 if (closest_len_sq < best_dist_sq)
185 {
186 best_dist_sq = closest_len_sq;
187 best_v1_idx = v1_idx;
188 best_v2_idx = v2_idx;
189 }
190 }
191 // else closest is v2, but v2 will be tested in a later iteration
192 }
193
194 v1_idx = v2_idx;
195 v1 = v2;
196 }
197
198 // Check if this vertex/edge is voided
199 bool voided = IsVoided(r.mSubShapeID1, r.mShape2Face[best_v1_idx])
200 && (best_v1_idx == best_v2_idx || IsVoided(r.mSubShapeID1, r.mShape2Face[best_v2_idx]));
201
202 #ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
203 Color color = voided? Color::sRed : Color::sYellow;
207 DebugRenderer::sInstance->DrawMarker(RVec3(r.mShape2Face[best_v1_idx]), IsVoided(r.mSubShapeID1, r.mShape2Face[best_v1_idx])? Color::sRed : Color::sYellow, 0.1f);
208 DebugRenderer::sInstance->DrawMarker(RVec3(r.mShape2Face[best_v2_idx]), IsVoided(r.mSubShapeID1, r.mShape2Face[best_v2_idx])? Color::sRed : Color::sYellow, 0.1f);
209 #endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
210
211 // No voided features, accept the contact
212 if (!voided)
213 Chain(r);
214
215 // Void the features of this face
216 VoidFeatures(r);
217 }
218
219 // All delayed results have been processed
220 mVoidedFeatures.clear();
221 mDelayedResults.clear();
222 }
223
225 static void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter = { })
226 {
227 JPH_ASSERT(inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces); // Won't work without collecting faces
228
229 InternalEdgeRemovingCollector wrapper(ioCollector);
230 CollisionDispatch::sCollideShapeVsShape(inShape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, wrapper, inShapeFilter);
231 wrapper.Flush();
232 }
233
234private:
235 // This algorithm tests a convex shape (shape 1) against a set of polygons (shape 2).
236 // This assumption doesn't hold if the shape we're testing is a compound shape, so we must also
237 // store the sub shape ID and ignore voided features that belong to another sub shape ID.
238 struct Voided
239 {
240 Float3 mFeature; // Feature that is voided (of shape 2). Read with Vec3::sLoadFloat3Unsafe so must not be the last member.
241 SubShapeID mSubShapeID; // Sub shape ID of the shape that is colliding against the feature (of shape 1).
242 };
243
244 CollideShapeCollector & mChainedCollector;
247};
248
unsigned int uint
Definition: Core.h:452
#define JPH_NAMESPACE_END
Definition: Core.h:378
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:372
#define JPH_ASSERT(...)
Definition: IssueReporting.h:33
JPH_INLINE constexpr T Square(T inV)
Square a value.
Definition: Math.h:52
void QuickSort(Iterator inBegin, Iterator inEnd, Compare inCompare)
Implementation of the quick sort algorithm. The STL version implementation is not consistent across p...
Definition: QuickSort.h:53
Vec3 RVec3
Definition: Real.h:29
JPH_SUPPRESS_WARNINGS_STD_BEGIN JPH_SUPPRESS_WARNINGS_STD_END JPH_NAMESPACE_BEGIN String StringFormat(const char *inFMT,...)
Definition: StringTools.cpp:15
Definition: Body.h:35
ECollectFacesMode mCollectFacesMode
If colliding faces should be collected or only the collision point.
Definition: CollideShape.h:80
Class that contains all information of two colliding shapes.
Definition: CollideShape.h:19
SubShapeID mSubShapeID1
Sub shape ID that identifies the face on shape 1.
Definition: CollideShape.h:63
Vec3 mContactPointOn2
Contact point on the surface of shape 2 (in world space or relative to base offset)....
Definition: CollideShape.h:60
Vec3 mPenetrationAxis
Direction to move shape 2 out of collision along the shortest path (magnitude is meaningless,...
Definition: CollideShape.h:61
float mPenetrationDepth
Penetration depth (move shape 2 by this distance to resolve the collision)
Definition: CollideShape.h:62
Face mShape2Face
Colliding face on shape 2 (optional result, in world space or relative to base offset)
Definition: CollideShape.h:67
Settings to be passed with a collision query.
Definition: CollideShape.h:94
Virtual interface that allows collecting multiple collision results.
Definition: CollisionCollector.h:45
virtual void AddHit(const ResultType &inResult)=0
This function will be called for every hit found, it's up to the application to decide how to store t...
virtual void Reset()
If you want to reuse this collector, call Reset()
Definition: CollisionCollector.h:62
float GetEarlyOutFraction() const
Get the current early out value.
Definition: CollisionCollector.h:92
const TransformedShape * GetContext() const
Definition: CollisionCollector.h:71
virtual void OnBody(const Body &inBody)
Definition: CollisionCollector.h:67
void UpdateEarlyOutFraction(float inFraction)
Update the early out fraction (should be lower than before)
Definition: CollisionCollector.h:80
void SetContext(const TransformedShape *inContext)
Set by the collision detection functions to the current TransformedShape that we're colliding against...
Definition: CollisionCollector.h:70
static void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter={ })
Definition: CollisionDispatch.h:33
Class that holds an RGBA color with 8-bits per component.
Definition: Color.h:16
static const Color sGreen
Definition: Color.h:57
static const Color sRed
Definition: Color.h:55
static const Color sYellow
Definition: Color.h:60
virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor=Color::sWhite, float inHeight=0.5f)=0
Draw text.
void DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize)
Draw a marker on a position.
Definition: DebugRenderer.cpp:172
static DebugRenderer * sInstance
Singleton instance.
Definition: DebugRenderer.h:179
void DrawWirePolygon(RMat44Arg inTransform, const VERTEX_ARRAY &inVertices, ColorArg inColor, float inArrowSize=0.0f)
Draw a wireframe polygon.
Definition: DebugRenderer.h:83
void DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize)
Draw an arrow.
Definition: DebugRenderer.cpp:184
Class that holds 3 floats. Used as a storage class. Convert to Vec3 for calculations.
Definition: Float3.h:13
Definition: InternalEdgeRemovingCollector.h:21
virtual void OnBody(const Body &inBody) override
Definition: InternalEdgeRemovingCollector.h:95
void Flush()
After all hits have been added, call this function to process the delayed results.
Definition: InternalEdgeRemovingCollector.h:128
InternalEdgeRemovingCollector(CollideShapeCollector &inChainedCollector)
Constructor, configures a collector to be called with all the results that do not hit internal edges.
Definition: InternalEdgeRemovingCollector.h:78
static void sCollideShapeVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter={ })
Version of CollisionDispatch::sCollideShapeVsShape that removes internal edges.
Definition: InternalEdgeRemovingCollector.h:225
virtual void Reset() override
If you want to reuse this collector, call Reset()
Definition: InternalEdgeRemovingCollector.h:84
virtual void AddHit(const CollideShapeResult &inResult) override
Definition: InternalEdgeRemovingCollector.h:102
Holds a 4x4 matrix of floats, but supports also operations on the 3x3 upper left part of the matrix.
Definition: Mat44.h:13
static JPH_INLINE Mat44 sIdentity()
Identity matrix.
Definition: Mat44.inl:35
Filter class.
Definition: ShapeFilter.h:17
Base class for all shapes (collision volume of a body). Defines a virtual interface for collision det...
Definition: Shape.h:186
Simple variable length array backed by a fixed size buffer.
Definition: StaticArray.h:14
void push_back(const T &inElement)
Add element to the back of the array.
Definition: StaticArray.h:61
void clear()
Destruct all elements and set length to zero.
Definition: StaticArray.h:52
size_type size() const
Returns amount of elements in the array.
Definition: StaticArray.h:89
Definition: SubShapeID.h:108
A sub shape id contains a path to an element (usually a triangle or other primitive type) of a compou...
Definition: SubShapeID.h:23
Definition: Vec3.h:17
JPH_INLINE bool IsClose(Vec3Arg inV2, float inMaxDistSq=1.0e-12f) const
Test if two vectors are close.
Definition: Vec3.inl:346
JPH_INLINE float Dot(Vec3Arg inV2) const
Dot product.
Definition: Vec3.inl:649
JPH_INLINE float Length() const
Length of vector.
Definition: Vec3.inl:681
JPH_INLINE Vec3 NormalizedOr(Vec3Arg inZeroValue) const
Normalize vector or return inZeroValue if the length of the vector is zero.
Definition: Vec3.inl:720
JPH_INLINE void StoreFloat3(Float3 *outV) const
Store 3 floats to memory.
Definition: Vec3.inl:769
JPH_INLINE float LengthSq() const
Squared length of vector.
Definition: Vec3.inl:665
static JPH_INLINE Vec3 sZero()
Vector with all zeros.
Definition: Vec3.inl:107
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:134