Bladeren bron

Added ability to update a soft body outside of the physics simulation step (#942)

Jorrit Rouwe 1 jaar geleden
bovenliggende
commit
3178fd65c2

+ 2 - 2
Jolt/Physics/PhysicsSystem.cpp

@@ -2522,7 +2522,7 @@ void PhysicsSystem::JobSoftBodyCollide(PhysicsUpdateContext *ioContext) const
 
 		// Do a broadphase check
 		SoftBodyUpdateContext &sb_ctx = ioContext->mSoftBodyUpdateContexts[sb_idx];
-		sb_ctx.mMotionProperties->DetermineCollidingShapes(sb_ctx, *this);
+		sb_ctx.mMotionProperties->DetermineCollidingShapes(sb_ctx, *this, GetBodyLockInterfaceNoLock());
 	}
 }
 
@@ -2585,7 +2585,7 @@ void PhysicsSystem::JobSoftBodyFinalize(PhysicsUpdateContext *ioContext)
 	for (SoftBodyUpdateContext *sb_ctx = ioContext->mSoftBodyUpdateContexts, *sb_ctx_end = ioContext->mSoftBodyUpdateContexts + ioContext->mNumSoftBodies; sb_ctx < sb_ctx_end; ++sb_ctx)
 	{
 		// Apply the rigid body velocity deltas
-		sb_ctx->mMotionProperties->UpdateRigidBodyVelocities(*sb_ctx, *this);
+		sb_ctx->mMotionProperties->UpdateRigidBodyVelocities(*sb_ctx, GetBodyInterfaceNoLock());
 
 		// Update the position
 		sb_ctx->mBody->SetPositionAndRotationInternal(sb_ctx->mBody->GetPosition() + sb_ctx->mDeltaPosition, sb_ctx->mBody->GetRotation(), false);

+ 32 - 7
Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp

@@ -93,16 +93,16 @@ float SoftBodyMotionProperties::GetVolumeTimesSix() const
 	return six_volume;
 }
 
-void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem)
+void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface)
 {
 	JPH_PROFILE_FUNCTION();
 
 	struct Collector : public CollideShapeBodyCollector
 	{
-									Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, Array<CollidingShape> &ioHits) :
+									Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface, Array<CollidingShape> &ioHits) :
 										mContext(inContext),
 										mInverseTransform(inContext.mCenterOfMassTransform.InversedRotationTranslation()),
-										mBodyLockInterface(inSystem.GetBodyLockInterfaceNoLock()),
+										mBodyLockInterface(inBodyLockInterface),
 										mCombineFriction(inSystem.GetCombineFriction()),
 										mCombineRestitution(inSystem.GetCombineRestitution()),
 										mHits(ioHits)
@@ -158,7 +158,7 @@ void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateCont
 		Array<CollidingShape> &		mHits;
 	};
 
-	Collector collector(inContext, inSystem, mCollidingShapes);
+	Collector collector(inContext, inSystem, inBodyLockInterface, mCollidingShapes);
 	AABox bounds = mLocalBounds;
 	bounds.Encapsulate(mLocalPredictedBounds);
 	bounds = bounds.Transformed(inContext.mCenterOfMassTransform);
@@ -512,15 +512,14 @@ void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioCont
 		ioContext.mCanSleep = ECanSleep::CannotSleep;
 }
 
-void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, PhysicsSystem &inSystem)
+void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface)
 {
 	JPH_PROFILE_FUNCTION();
 
 	// Write back velocity deltas
-	BodyInterface &body_interface = inSystem.GetBodyInterfaceNoLock();
 	for (const CollidingShape &cs : mCollidingShapes)
 		if (cs.mUpdateVelocities)
-			body_interface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity));
+			inBodyInterface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity));
 
 	// Clear colliding shapes to avoid hanging on to references to shapes
 	mCollidingShapes.clear();
@@ -677,6 +676,32 @@ SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftB
 	}
 }
 
+void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem)
+{
+	JPH_PROFILE_FUNCTION();
+
+	// Create update context
+	SoftBodyUpdateContext context;
+	InitializeUpdateContext(inDeltaTime, ioSoftBody, inSystem, context);
+
+	// Determine bodies we're colliding with
+	DetermineCollidingShapes(context, inSystem, inSystem.GetBodyLockInterface());
+
+	// Call the internal update until it finishes
+	EStatus status;
+	const PhysicsSettings &settings = inSystem.GetPhysicsSettings();
+	while ((status = ParallelUpdate(context, settings)) == EStatus::DidWork)
+		continue;
+	JPH_ASSERT(status == EStatus::Done);
+
+	// Update the state of the bodies we've collided with
+	UpdateRigidBodyVelocities(context, inSystem.GetBodyInterface());
+
+	// Update position of the soft body
+	if (mUpdatePosition)
+		inSystem.GetBodyInterface().SetPosition(ioSoftBody.GetID(), ioSoftBody.GetPosition() + context.mDeltaPosition, EActivation::DontActivate);
+}
+
 #ifdef JPH_DEBUG_RENDERER
 
 void SoftBodyMotionProperties::DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const

+ 27 - 8
Jolt/Physics/SoftBody/SoftBodyMotionProperties.h

@@ -14,6 +14,8 @@
 JPH_NAMESPACE_BEGIN
 
 class PhysicsSystem;
+class BodyInterface;
+class BodyLockInterface;
 struct PhysicsSettings;
 class Body;
 class Shape;
@@ -92,11 +94,28 @@ public:
 	/// Restoring state for replay
 	void								RestoreState(StateRecorder &inStream);
 
-	/// Initialize the update context (used internally by the PhysicsSystem)
+	/// This function allows you to update the soft body immediately without going through the PhysicsSystem.
+	/// This is useful if the soft body is teleported and needs to 'settle' or it can be used if a the soft body
+	/// is not added to the PhysicsSystem and needs to be updated manually. One reason for not adding it to the
+	/// PhyicsSystem is that you might want to update a soft body immediately after updating an animated object
+	/// that has the soft body attached to it. If the soft body is added to the PhysicsSystem it will be updated
+	/// by it, so calling this function will effectively update it twice. Note that when you use this function,
+	/// only the current thread will be used, whereas if you update through the PhysicsSystem, multiple threads may
+	/// be used.
+	/// Note that this will bypass any sleep checks. Since the dynamic objects that the soft body touches
+	/// will not move during this call, there can be simulation artifacts if you call this function multiple times
+	/// without running the physics simulation step.
+	void								CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem);
+
+	////////////////////////////////////////////////////////////
+	// FUNCTIONS BELOW THIS LINE ARE FOR INTERNAL USE ONLY
+	////////////////////////////////////////////////////////////
+
+	/// Initialize the update context. Not part of the public API.
 	void								InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext);
 
-	/// Do a broad phase check and collect all bodies that can possibly collide with this soft body
-	void								DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem);
+	/// Do a broad phase check and collect all bodies that can possibly collide with this soft body. Not part of the public API.
+	void								DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface);
 
 	/// Return code for ParallelUpdate
 	enum class EStatus
@@ -106,11 +125,11 @@ public:
 		Done	= 1 << 2,				///< All work is done
 	};
 
-	/// Update the soft body, will process a batch of work. Used internally.
+	/// Update the soft body, will process a batch of work. Not part of the public API.
 	EStatus								ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings);
 
-	/// Update the velocities of all rigid bodies that we collided with. Used internally.
-	void								UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, PhysicsSystem &inSystem);
+	/// Update the velocities of all rigid bodies that we collided with. Not part of the public API.
+	void								UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface);
 
 private:
 	// SoftBodyManifold needs to have access to CollidingShape
@@ -157,7 +176,7 @@ private:
 	/// Enforce all edge constraints
 	void								ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex);
 
-	/// Enforce all collision constraints & update all velocities according the the XPBD algorithm
+	/// Enforce all collision constraints & update all velocities according the XPBD algorithm
 	void								ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext);
 
 	/// Update the state of the soft body (position, velocity, bounds)
@@ -181,7 +200,7 @@ private:
 	AABox								mLocalBounds;								///< Bounding box of all vertices
 	AABox								mLocalPredictedBounds;						///< Predicted bounding box for all vertices using extrapolation of velocity by last step delta time
 	uint32								mNumIterations;								///< Number of solver iterations
-	float								mPressure;									///< n * R * T, amount of substance * ideal gass constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure
+	float								mPressure;									///< n * R * T, amount of substance * ideal gas constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure
 	bool								mUpdatePosition;							///< Update the position of the body while simulating (set to false for something that is attached to the static world)
 	bool								mHasContact = false;						///< True if the soft body has collided with anything in the last update
 };

+ 2 - 0
Samples/Samples.cmake

@@ -169,6 +169,8 @@ set(SAMPLES_SRC_FILES
 	${SAMPLES_ROOT}/Tests/Rig/SkeletonMapperTest.h
 	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyContactListenerTest.cpp
 	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyContactListenerTest.h
+	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyCustomUpdateTest.cpp
+	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyCustomUpdateTest.h
 	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyFrictionTest.cpp
 	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyFrictionTest.h
 	${SAMPLES_ROOT}/Tests/SoftBody/SoftBodyGravityFactorTest.cpp

+ 3 - 1
Samples/SamplesApp.cpp

@@ -321,6 +321,7 @@ JPH_DECLARE_RTTI_FOR_FACTORY(JPH_NO_EXPORT, SoftBodyStressTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(JPH_NO_EXPORT, SoftBodyVsFastMovingTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(JPH_NO_EXPORT, SoftBodyVertexRadiusTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(JPH_NO_EXPORT, SoftBodyContactListenerTest)
+JPH_DECLARE_RTTI_FOR_FACTORY(JPH_NO_EXPORT, SoftBodyCustomUpdateTest)
 
 static TestNameAndRTTI sSoftBodyTests[] =
 {
@@ -334,7 +335,8 @@ static TestNameAndRTTI sSoftBodyTests[] =
 	{ "Soft Body Update Position",		JPH_RTTI(SoftBodyUpdatePositionTest) },
 	{ "Soft Body Stress Test",			JPH_RTTI(SoftBodyStressTest) },
 	{ "Soft Body Vertex Radius Test",	JPH_RTTI(SoftBodyVertexRadiusTest) },
-	{ "Soft Body Contact Listener",		JPH_RTTI(SoftBodyContactListenerTest) }
+	{ "Soft Body Contact Listener",		JPH_RTTI(SoftBodyContactListenerTest) },
+	{ "Soft Body Custom Update",		JPH_RTTI(SoftBodyCustomUpdateTest) }
 };
 
 JPH_DECLARE_RTTI_FOR_FACTORY(JPH_NO_EXPORT, BroadPhaseCastRayTest)

+ 49 - 0
Samples/Tests/SoftBody/SoftBodyCustomUpdateTest.cpp

@@ -0,0 +1,49 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <TestFramework.h>
+
+#include <Tests/SoftBody/SoftBodyCustomUpdateTest.h>
+#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
+#include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
+#include <Utils/SoftBodyCreator.h>
+#include <Layers.h>
+#include <Renderer/DebugRendererImp.h>
+
+JPH_IMPLEMENT_RTTI_VIRTUAL(SoftBodyCustomUpdateTest)
+{
+	JPH_ADD_BASE_CLASS(SoftBodyCustomUpdateTest, Test)
+}
+
+void SoftBodyCustomUpdateTest::Initialize()
+{
+	// Floor
+	CreateFloor();
+
+	// Create a body but do not add it to the physics system (we're updating it ourselves)
+	SoftBodyCreationSettings sphere(SoftBodyCreator::CreateSphere(), RVec3(0, 5, 0), Quat::sIdentity(), Layers::MOVING);
+	sphere.mPressure = 2000.0f;
+	mBody = mBodyInterface->CreateSoftBody(sphere);
+}
+
+void SoftBodyCustomUpdateTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
+{
+	// Note that passing a variable delta time results in differences in behavior, usually you want to have a fixed time step.
+	// For this demo we'll just clamp the delta time to 1/60th of a second and allow behavioral changes due to frame rate fluctuations.
+	float dt = min(inParams.mDeltaTime, 1.0f / 60.0f);
+
+	// Call the update now
+	SoftBodyMotionProperties *mp = static_cast<SoftBodyMotionProperties *>(mBody->GetMotionProperties());
+	mp->CustomUpdate(dt, *mBody, *mPhysicsSystem);
+
+#ifdef JPH_DEBUG_RENDERER
+	// Draw it as well since it's not added to the world
+	mBody->GetShape()->Draw(mDebugRenderer, mBody->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f), Color::sWhite, false, false);
+#else
+	// Draw the vertices
+	RMat44 com = mBody->GetCenterOfMassTransform();
+	for (const SoftBodyVertex &v : mp->GetVertices())
+		mDebugRenderer->DrawMarker(com * v.mPosition, Color::sRed, 0.1f);
+#endif // JPH_DEBUG_RENDERER
+}

+ 21 - 0
Samples/Tests/SoftBody/SoftBodyCustomUpdateTest.h

@@ -0,0 +1,21 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Tests/Test.h>
+
+// This test shows how you can update a soft body outside of the main physics simulation step
+class SoftBodyCustomUpdateTest : public Test
+{
+public:
+	JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SoftBodyCustomUpdateTest)
+
+	// See: Test
+	virtual void		Initialize() override;
+	virtual void		PrePhysicsUpdate(const PreUpdateParams &inParams) override;
+
+private:
+	Body *				mBody;
+};