Selaa lähdekoodia

No longer limiting max voided features/delayed results (#1500)

Fixed InternalEdgeRemovingCollector not working when colliding with a very dense triangle grid because it ran out of internal space. Now falling back to memory allocations when this happens to avoid ghost collisions.

See: godotengine/godot#102544
Jorrit Rouwe 6 kuukautta sitten
vanhempi
commit
b62e994da4

+ 1 - 0
Docs/ReleaseNotes.md

@@ -43,6 +43,7 @@ For breaking API changes see [this document](https://github.com/jrouwe/JoltPhysi
 * Bugfix in `Semaphore::Acquire` for non-windows platform. The count was updated before waiting, meaning that the counter would become -(number of waiting threads) and the semaphore would not wake up until at least the same amount of releases was done. In practice this meant that the main thread had to do the last (number of threads) jobs, slowing down the simulation a bit.
 * An empty `MutableCompoundShape` now returns the same local bounding box as `EmptyShape` (a point at (0, 0, 0)). This prevents floating point overflow exceptions.
 * Fixed a bug in ManifoldBetweenTwoFaces that led to incorrect `ContactManifold::mRelativeContactPointsOn2` when the contact normal and the face normal were not roughly parallel. Also it led to possibly jitter in the simulation in that case.
+* Fixed InternalEdgeRemovingCollector not working when colliding with a very dense triangle grid because it ran out of internal space. Now falling back to memory allocations when this happens to avoid ghost collisions.
 
 ## v5.2.0
 

+ 1 - 1
Jolt/Jolt.cmake

@@ -644,7 +644,7 @@ else()
 		if (JPH_USE_WASM64)
 			target_compile_options(Jolt PUBLIC -sMEMORY64)
 			target_link_options(Jolt PUBLIC -sMEMORY64)
-		endif()		
+		endif()
 	elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i386")
 		# x86 and x86_64
 		# On 32-bit builds we need to default to using SSE instructions, the x87 FPU instructions have higher intermediate precision

+ 16 - 19
Jolt/Physics/Collision/InternalEdgeRemovingCollector.h

@@ -5,6 +5,7 @@
 #pragma once
 
 #include <Jolt/Core/QuickSort.h>
+#include <Jolt/Core/STLLocalAllocator.h>
 #include <Jolt/Physics/Collision/CollisionDispatch.h>
 
 //#define JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG
@@ -22,8 +23,8 @@ JPH_NAMESPACE_BEGIN
 /// and CollideSettingsBase::mCollectFacesMode == ECollectFacesMode::CollectFaces.
 class InternalEdgeRemovingCollector : public CollideShapeCollector
 {
-	static constexpr uint cMaxDelayedResults = 16;
-	static constexpr uint cMaxVoidedFeatures = 128;
+	static constexpr uint cMaxLocalDelayedResults = 32;
+	static constexpr uint cMaxLocalVoidedFeatures = 128;
 
 	/// Check if a vertex is voided
 	inline bool				IsVoided(const SubShapeID &inSubShapeID, Vec3 inV) const
@@ -38,17 +39,14 @@ class InternalEdgeRemovingCollector : public CollideShapeCollector
 	/// Add all vertices of a face to the voided features
 	inline void				VoidFeatures(const CollideShapeResult &inResult)
 	{
-		if (mVoidedFeatures.size() < cMaxVoidedFeatures)
-			for (const Vec3 &v : inResult.mShape2Face)
-				if (!IsVoided(inResult.mSubShapeID1, v))
-				{
-					Voided vf;
-					v.StoreFloat3(&vf.mFeature);
-					vf.mSubShapeID = inResult.mSubShapeID1;
-					mVoidedFeatures.push_back(vf);
-					if (mVoidedFeatures.size() == cMaxVoidedFeatures)
-						break;
-				}
+		for (const Vec3 &v : inResult.mShape2Face)
+			if (!IsVoided(inResult.mSubShapeID1, v))
+			{
+				Voided vf;
+				v.StoreFloat3(&vf.mFeature);
+				vf.mSubShapeID = inResult.mSubShapeID1;
+				mVoidedFeatures.push_back(vf);
+			}
 	}
 
 	/// Call the chained collector
@@ -122,8 +120,6 @@ public:
 			return ChainAndVoid(inResult);
 
 		// Delayed processing
-		if (mDelayedResults.size() == cMaxDelayedResults)
-			return ChainAndVoid(inResult);
 		mDelayedResults.push_back(inResult);
 	}
 
@@ -131,10 +127,11 @@ public:
 	void					Flush()
 	{
 		// Sort on biggest penetration depth first
-		uint sorted_indices[cMaxDelayedResults];
+		Array<uint, STLLocalAllocator<uint, cMaxLocalDelayedResults>> sorted_indices;
+		sorted_indices.resize(mDelayedResults.size());
 		for (uint i = 0; i < uint(mDelayedResults.size()); ++i)
 			sorted_indices[i] = i;
-		QuickSort(sorted_indices, sorted_indices + mDelayedResults.size(), [this](uint inLHS, uint inRHS) { return mDelayedResults[inLHS].mPenetrationDepth > mDelayedResults[inRHS].mPenetrationDepth; });
+		QuickSort(sorted_indices.begin(), sorted_indices.end(), [this](uint inLHS, uint inRHS) { return mDelayedResults[inLHS].mPenetrationDepth > mDelayedResults[inRHS].mPenetrationDepth; });
 
 		// Loop over all results
 		for (uint i = 0; i < uint(mDelayedResults.size()); ++i)
@@ -253,8 +250,8 @@ private:
 	};
 
 	CollideShapeCollector &	mChainedCollector;
-	StaticArray<Voided, cMaxVoidedFeatures> mVoidedFeatures;
-	StaticArray<CollideShapeResult, cMaxDelayedResults> mDelayedResults;
+	Array<Voided, STLLocalAllocator<Voided, cMaxLocalVoidedFeatures>> mVoidedFeatures;
+	Array<CollideShapeResult, STLLocalAllocator<CollideShapeResult, cMaxLocalDelayedResults>> mDelayedResults;
 };
 
 JPH_NAMESPACE_END

+ 33 - 1
Samples/Tests/General/EnhancedInternalEdgeRemovalTest.cpp

@@ -93,7 +93,7 @@ void EnhancedInternalEdgeRemovalTest::Initialize()
 			}
 
 		MeshShapeSettings mesh_settings(triangles);
-		mesh_settings.mActiveEdgeCosThresholdAngle = FLT_MAX; // Turn off regular active edge determination so that we only rely on the mEnhancedInternalEdgeRemoval flag
+		mesh_settings.mActiveEdgeCosThresholdAngle = -1.0f; // Turn off regular active edge determination so that we only rely on the mEnhancedInternalEdgeRemoval flag
 		mesh_settings.SetEmbedded();
 		mBodyInterface->CreateAndAddBody(BodyCreationSettings(&mesh_settings, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
 
@@ -186,6 +186,38 @@ void EnhancedInternalEdgeRemovalTest::Initialize()
 		compound_bcs.mEnhancedInternalEdgeRemoval = true;
 		mBodyInterface->CreateAndAddBody(compound_bcs, EActivation::Activate);
 	}
+
+	// Create a super dense grid of triangles
+	{
+		constexpr float size = 0.25f;
+		TriangleList triangles;
+		for (int x = -100; x < 100; ++x)
+			for (int z = -5; z < 5; ++z)
+			{
+				float x1 = size * x;
+				float z1 = size * z;
+				float x2 = x1 + size;
+				float z2 = z1 + size;
+
+				Float3 v1 = Float3(x1, 0, z1);
+				Float3 v2 = Float3(x2, 0, z1);
+				Float3 v3 = Float3(x1, 0, z2);
+				Float3 v4 = Float3(x2, 0, z2);
+
+				triangles.push_back(Triangle(v1, v3, v4));
+				triangles.push_back(Triangle(v1, v4, v2));
+			}
+
+		MeshShapeSettings mesh_settings(triangles);
+		mesh_settings.mActiveEdgeCosThresholdAngle = -1.0f; // Turn off regular active edge determination so that we only rely on the mEnhancedInternalEdgeRemoval flag
+		mesh_settings.SetEmbedded();
+		mBodyInterface->CreateAndAddBody(BodyCreationSettings(&mesh_settings, RVec3(0, 0, 80), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
+
+		BodyCreationSettings box_bcs(new BoxShape(Vec3::sReplicate(1.0f)), RVec3(-24, 0.9_r, 80), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
+		box_bcs.mLinearVelocity = Vec3(20, 0, 0);
+		box_bcs.mEnhancedInternalEdgeRemoval = true;
+		mBodyInterface->CreateAndAddBody(box_bcs, EActivation::Activate);
+	}
 }
 
 void EnhancedInternalEdgeRemovalTest::PrePhysicsUpdate(const PreUpdateParams &inParams)