Browse Source

Bugfix: When not calling SkinVertices every frame (#1056)

- When SkinVertices is not called every frame, the previous position of the skin was still used causing a replay of the motion of the previous frame.
- Fixed issue in updating mSkinStateTransform when position of soft body changes
- Added some extra settings to the SoftBodySkinnedConstraintTest
Jorrit Rouwe 1 year ago
parent
commit
4c8875e7cf

+ 10 - 4
Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp

@@ -401,7 +401,7 @@ void SoftBodyMotionProperties::ApplySkinConstraints(const SoftBodyUpdateContext
 
 
 	// We're going to iterate multiple times over the skin constraints, update the skinned position accordingly.
 	// We're going to iterate multiple times over the skin constraints, update the skinned position accordingly.
 	// If we don't do this, the simulation will see a big jump and the first iteration will cause a big velocity change in the system.
 	// If we don't do this, the simulation will see a big jump and the first iteration will cause a big velocity change in the system.
-	float factor = inContext.mNextIteration.load(std::memory_order_relaxed) / float(mNumIterations);
+	float factor = mSkinStatePreviousPositionValid? inContext.mNextIteration.load(std::memory_order_relaxed) / float(mNumIterations) : 1.0f;
 	float prev_factor = 1.0f - factor;
 	float prev_factor = 1.0f - factor;
 
 
 	// Apply the constraints
 	// Apply the constraints
@@ -684,7 +684,7 @@ void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioCont
 		// Update the skin state too since we will use this position as the previous position in the next update
 		// Update the skin state too since we will use this position as the previous position in the next update
 		for (SkinState &s : mSkinState)
 		for (SkinState &s : mSkinState)
 			s.mPosition -= delta;
 			s.mPosition -= delta;
-		mSkinStateTransform.SetTranslation(mSkinStateTransform.GetTranslation() - delta);
+		JPH_IF_DEBUG_RENDERER(mSkinStateTransform.SetTranslation(mSkinStateTransform.GetTranslation() + ioContext.mDeltaPosition);)
 
 
 		// Offset bounds to match new position
 		// Offset bounds to match new position
 		mLocalBounds.Translate(-delta);
 		mLocalBounds.Translate(-delta);
@@ -706,6 +706,9 @@ void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioCont
 	}
 	}
 	else
 	else
 		ioContext.mCanSleep = ECanSleep::CannotSleep;
 		ioContext.mCanSleep = ECanSleep::CannotSleep;
+
+	// If SkinVertices is not called after this then don't use the previous position as the skin is static
+	mSkinStatePreviousPositionValid = false;
 }
 }
 
 
 void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface)
 void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface)
@@ -882,7 +885,7 @@ SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftB
 	}
 	}
 }
 }
 
 
-void SoftBodyMotionProperties::SkinVertices(RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator)
+void SoftBodyMotionProperties::SkinVertices([[maybe_unused]] RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator)
 {
 {
 	// Calculate the skin matrices
 	// Calculate the skin matrices
 	uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size());
 	uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size());
@@ -894,7 +897,7 @@ void SoftBodyMotionProperties::SkinVertices(RMat44Arg inCenterOfMassTransform, c
 		*s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind;
 		*s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind;
 
 
 	// Skin the vertices
 	// Skin the vertices
-	mSkinStateTransform = inCenterOfMassTransform;
+	JPH_IF_DEBUG_RENDERER(mSkinStateTransform = inCenterOfMassTransform;)
 	JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());)
 	JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());)
 	JPH_ASSERT(mSkinState.size() == num_vertices);
 	JPH_ASSERT(mSkinState.size() == num_vertices);
 	const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data();
 	const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data();
@@ -968,6 +971,9 @@ void SoftBodyMotionProperties::SkinVertices(RMat44Arg inCenterOfMassTransform, c
 				vertex.mPosition = mSkinState[s.mVertex].mPosition;
 				vertex.mPosition = mSkinState[s.mVertex].mPosition;
 			}
 			}
 	}
 	}
+
+	// Indicate that the previous positions are valid for the coming update
+	mSkinStatePreviousPositionValid = true;
 }
 }
 
 
 void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem)
 void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem)

+ 6 - 4
Jolt/Physics/SoftBody/SoftBodyMotionProperties.h

@@ -191,9 +191,9 @@ private:
 	// Information about the state of all skinned vertices
 	// Information about the state of all skinned vertices
 	struct SkinState
 	struct SkinState
 	{
 	{
-		Vec3							mPreviousPosition = Vec3::sNaN();
-		Vec3							mPosition = Vec3::sNaN();
-		Vec3							mNormal = Vec3::sNaN();
+		Vec3							mPreviousPosition = Vec3::sZero();			///< Previous position of the skinned vertex, used to interpolate between the previous and current position
+		Vec3							mPosition = Vec3::sNaN();					///< Current position of the skinned vertex
+		Vec3							mNormal = Vec3::sNaN();						///< Normal of the skinned vertex
 	};
 	};
 
 
 	/// Do a narrow phase check and determine the closest feature that we can collide with
 	/// Do a narrow phase check and determine the closest feature that we can collide with
@@ -245,9 +245,10 @@ private:
 	/// Helper function to draw constraints
 	/// Helper function to draw constraints
 	template <typename GetEndIndex, typename DrawConstraint>
 	template <typename GetEndIndex, typename DrawConstraint>
 		inline void						DrawConstraints(ESoftBodyConstraintColor inConstraintColor, const GetEndIndex &inGetEndIndex, const DrawConstraint &inDrawConstraint, ColorArg inBaseColor) const;
 		inline void						DrawConstraints(ESoftBodyConstraintColor inConstraintColor, const GetEndIndex &inGetEndIndex, const DrawConstraint &inDrawConstraint, ColorArg inBaseColor) const;
-#endif // JPH_DEBUG_RENDERER
 
 
 	RMat44								mSkinStateTransform = RMat44::sIdentity();	///< The matrix that transforms mSkinState to world space
 	RMat44								mSkinStateTransform = RMat44::sIdentity();	///< The matrix that transforms mSkinState to world space
+#endif // JPH_DEBUG_RENDERER
+
 	RefConst<SoftBodySharedSettings>	mSettings;									///< Configuration of the particles and constraints
 	RefConst<SoftBodySharedSettings>	mSettings;									///< Configuration of the particles and constraints
 	Array<Vertex>						mVertices;									///< Current state of all vertices in the simulation
 	Array<Vertex>						mVertices;									///< Current state of all vertices in the simulation
 	Array<CollidingShape>				mCollidingShapes;							///< List of colliding shapes retrieved during the last update
 	Array<CollidingShape>				mCollidingShapes;							///< List of colliding shapes retrieved during the last update
@@ -260,6 +261,7 @@ private:
 	bool								mUpdatePosition;							///< Update the position of the body while simulating (set to false for something that is attached to the static world)
 	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
 	bool								mHasContact = false;						///< True if the soft body has collided with anything in the last update
 	bool								mEnableSkinConstraints = true;				///< If skin constraints are enabled
 	bool								mEnableSkinConstraints = true;				///< If skin constraints are enabled
+	bool								mSkinStatePreviousPositionValid = false;	///< True if the skinning was updated in the last update so that the previous position of the skin state is valid
 };
 };
 
 
 JPH_NAMESPACE_END
 JPH_NAMESPACE_END

+ 5 - 2
Samples/Tests/SoftBody/SoftBodySkinnedConstraintTest.cpp

@@ -52,7 +52,8 @@ void SoftBodySkinnedConstraintTest::SkinVertices(bool inHardSkinAll)
 	SoftBodyMotionProperties *mp = static_cast<SoftBodyMotionProperties *>(mBody->GetMotionProperties());
 	SoftBodyMotionProperties *mp = static_cast<SoftBodyMotionProperties *>(mBody->GetMotionProperties());
 	mp->SetEnableSkinConstraints(sEnableSkinConstraints);
 	mp->SetEnableSkinConstraints(sEnableSkinConstraints);
 	mp->SetSkinnedMaxDistanceMultiplier(sMaxDistanceMultiplier);
 	mp->SetSkinnedMaxDistanceMultiplier(sMaxDistanceMultiplier);
-	mp->SkinVertices(com, pose.data(), cNumJoints, inHardSkinAll, *mTempAllocator);
+	if (sUpdateSkinning || inHardSkinAll)
+		mp->SkinVertices(com, pose.data(), cNumJoints, inHardSkinAll, *mTempAllocator);
 }
 }
 
 
 void SoftBodySkinnedConstraintTest::Initialize()
 void SoftBodySkinnedConstraintTest::Initialize()
@@ -148,7 +149,7 @@ void SoftBodySkinnedConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inPa
 	}
 	}
 
 
 	// Update time
 	// Update time
-	mTime += inParams.mDeltaTime;
+	mTime += sTimeScale * inParams.mDeltaTime;
 
 
 	// Calculate skinned vertices but do not hard skin them
 	// Calculate skinned vertices but do not hard skin them
 	SkinVertices(false);
 	SkinVertices(false);
@@ -166,6 +167,8 @@ void SoftBodySkinnedConstraintTest::RestoreState(StateRecorder &inStream)
 
 
 void SoftBodySkinnedConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
 void SoftBodySkinnedConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
 {
 {
+	inUI->CreateSlider(inSubMenu, "Time Scale", sTimeScale, 0.0f, 10.0f, 0.1f, [](float inValue) { sTimeScale = inValue; });
+	inUI->CreateCheckBox(inSubMenu, "Update Skinning", sUpdateSkinning, [](UICheckBox::EState inState) { sUpdateSkinning = inState == UICheckBox::STATE_CHECKED; });
 	inUI->CreateCheckBox(inSubMenu, "Enable Skin Constraints", sEnableSkinConstraints, [](UICheckBox::EState inState) { sEnableSkinConstraints = inState == UICheckBox::STATE_CHECKED; });
 	inUI->CreateCheckBox(inSubMenu, "Enable Skin Constraints", sEnableSkinConstraints, [](UICheckBox::EState inState) { sEnableSkinConstraints = inState == UICheckBox::STATE_CHECKED; });
 	inUI->CreateSlider(inSubMenu, "Max Distance Multiplier", sMaxDistanceMultiplier, 0.0f, 10.0f, 0.1f, [](float inValue) { sMaxDistanceMultiplier = inValue; });
 	inUI->CreateSlider(inSubMenu, "Max Distance Multiplier", sMaxDistanceMultiplier, 0.0f, 10.0f, 0.1f, [](float inValue) { sMaxDistanceMultiplier = inValue; });
 }
 }

+ 2 - 0
Samples/Tests/SoftBody/SoftBodySkinnedConstraintTest.h

@@ -47,6 +47,8 @@ private:
 	float					mTime = 0.0f;
 	float					mTime = 0.0f;
 
 
 	// Settings
 	// Settings
+	static inline float		sTimeScale = 1.0f;
+	static inline bool		sUpdateSkinning = true;
 	static inline bool		sEnableSkinConstraints = true;
 	static inline bool		sEnableSkinConstraints = true;
 	static inline float		sMaxDistanceMultiplier = 1.0f;
 	static inline float		sMaxDistanceMultiplier = 1.0f;
 };
 };