Browse Source

Allow negative friction on a body as long as the combined friction is positive (#550)

See discussion at godot-jolt/godot-jolt#396
Jorrit Rouwe 2 years ago
parent
commit
f084fd7537

+ 3 - 3
Jolt/Physics/Body/Body.h

@@ -97,9 +97,9 @@ public:
 	bool					GetAllowSleeping() const										{ return mMotionProperties->mAllowSleeping; }
 	bool					GetAllowSleeping() const										{ return mMotionProperties->mAllowSleeping; }
 	void					SetAllowSleeping(bool inAllow);
 	void					SetAllowSleeping(bool inAllow);
 
 
-	/// Friction (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together)
+	/// Friction (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero.
 	inline float			GetFriction() const												{ return mFriction; }
 	inline float			GetFriction() const												{ return mFriction; }
-	void					SetFriction(float inFriction)									{ JPH_ASSERT(inFriction >= 0.0f); mFriction = inFriction; }
+	void					SetFriction(float inFriction)									{ mFriction = inFriction; }
 
 
 	/// Restitution (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero.
 	/// Restitution (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero.
 	inline float			GetRestitution() const											{ return mRestitution; }
 	inline float			GetRestitution() const											{ return mRestitution; }
@@ -321,7 +321,7 @@ private:
 	CollisionGroup			mCollisionGroup;												///< The collision group this body belongs to (determines if two objects can collide)
 	CollisionGroup			mCollisionGroup;												///< The collision group this body belongs to (determines if two objects can collide)
 
 
 	// 4 byte aligned
 	// 4 byte aligned
-	float					mFriction;														///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together)
+	float					mFriction;														///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero.
 	float					mRestitution;													///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero.
 	float					mRestitution;													///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero.
 	BodyID					mID;															///< ID of the body (index in the bodies array)
 	BodyID					mID;															///< ID of the body (index in the bodies array)
 
 

+ 1 - 1
Jolt/Physics/Body/BodyCreationSettings.h

@@ -94,7 +94,7 @@ public:
 	bool					mUseManifoldReduction = true;									///< If this body should use manifold reduction (see description at Body::SetUseManifoldReduction)
 	bool					mUseManifoldReduction = true;									///< If this body should use manifold reduction (see description at Body::SetUseManifoldReduction)
 	EMotionQuality			mMotionQuality = EMotionQuality::Discrete;						///< Motion quality, or how well it detects collisions when it has a high velocity
 	EMotionQuality			mMotionQuality = EMotionQuality::Discrete;						///< Motion quality, or how well it detects collisions when it has a high velocity
 	bool					mAllowSleeping = true;											///< If this body can go to sleep or not
 	bool					mAllowSleeping = true;											///< If this body can go to sleep or not
-	float					mFriction = 0.2f;												///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together)
+	float					mFriction = 0.2f;												///< Friction of the body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together). Note that bodies can have negative friction but the combined friction (see PhysicsSystem::SetCombineFriction) should never go below zero.
 	float					mRestitution = 0.0f;											///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero.
 	float					mRestitution = 0.0f;											///< Restitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response). Note that bodies can have negative restitution but the combined restitution (see PhysicsSystem::SetCombineRestitution) should never go below zero.
 	float					mLinearDamping = 0.05f;											///< Linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0.
 	float					mLinearDamping = 0.05f;											///< Linear damping: dv/dt = -c * v. c must be between 0 and 1 but is usually close to 0.
 	float					mAngularDamping = 0.05f;										///< Angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0.
 	float					mAngularDamping = 0.05f;										///< Angular damping: dw/dt = -c * w. c must be between 0 and 1 but is usually close to 0.

+ 1 - 1
Jolt/Physics/Collision/EstimateCollisionResponse.cpp

@@ -157,7 +157,7 @@ void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const C
 	}
 	}
 
 
 	// If there's only 1 contact point, we only need 1 iteration
 	// If there's only 1 contact point, we only need 1 iteration
-	int num_iterations = inCombinedFriction == 0.0f && num_points == 1? 1 : inNumIterations;
+	int num_iterations = inCombinedFriction <= 0.0f && num_points == 1? 1 : inNumIterations;
 
 
 	// Solve iteratively
 	// Solve iteratively
 	for (int iteration = 0; iteration < num_iterations; ++iteration)
 	for (int iteration = 0; iteration < num_iterations; ++iteration)

+ 3 - 2
Jolt/Physics/Vehicle/TrackedVehicleController.cpp

@@ -71,8 +71,9 @@ void WheelTV::Update(float inDeltaTime, const VehicleConstraint &inConstraint)
 	{
 	{
 		// Friction at the point of this wheel between track and floor
 		// Friction at the point of this wheel between track and floor
 		const WheelSettingsTV *settings = GetSettings();
 		const WheelSettingsTV *settings = GetSettings();
-		mCombinedLongitudinalFriction = sqrt(settings->mLongitudinalFriction * mContactBody->GetFriction());
-		mCombinedLateralFriction = sqrt(settings->mLateralFriction * mContactBody->GetFriction());
+		VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction();
+		mCombinedLongitudinalFriction = combine_friction(settings->mLongitudinalFriction, *mContactBody, mContactSubShapeID);
+		mCombinedLateralFriction = combine_friction(settings->mLateralFriction, *mContactBody, mContactSubShapeID);
 	}
 	}
 	else
 	else
 	{
 	{

+ 9 - 0
Jolt/Physics/Vehicle/VehicleConstraint.h

@@ -78,6 +78,14 @@ public:
 	/// Set the interface that tests collision between wheel and ground
 	/// Set the interface that tests collision between wheel and ground
 	void						SetVehicleCollisionTester(const VehicleCollisionTester *inTester) { mVehicleCollisionTester = inTester; }
 	void						SetVehicleCollisionTester(const VehicleCollisionTester *inTester) { mVehicleCollisionTester = inTester; }
 
 
+	/// Callback function to combine the friction of a tire with the friction of the body it is colliding with.
+	using CombineFunction = float (*)(float inTireFriction, const Body &inBody2, const SubShapeID &inSubShapeID2);
+
+	/// Set the function that combines the friction of two bodies and returns it
+	/// Default method is the geometric mean: sqrt(friction1 * friction2).
+	void						SetCombineFriction(CombineFunction inCombineFriction) { mCombineFriction = inCombineFriction; }
+	CombineFunction				GetCombineFriction() const					{ return mCombineFriction; }
+
 	/// Get the local space forward vector of the vehicle
 	/// Get the local space forward vector of the vehicle
 	Vec3						GetLocalForward() const						{ return mForward; }
 	Vec3						GetLocalForward() const						{ return mForward; }
 
 
@@ -170,6 +178,7 @@ private:
 
 
 	// Interfaces
 	// Interfaces
 	RefConst<VehicleCollisionTester> mVehicleCollisionTester;				///< Class that performs testing of collision for the wheels
 	RefConst<VehicleCollisionTester> mVehicleCollisionTester;				///< Class that performs testing of collision for the wheels
+	CombineFunction				mCombineFriction = [](float inTireFriction, const Body &inBody2, const SubShapeID &) { return sqrt(inTireFriction * inBody2.GetFriction()); };
 };
 };
 
 
 JPH_NAMESPACE_END
 JPH_NAMESPACE_END

+ 3 - 2
Jolt/Physics/Vehicle/WheeledVehicleController.cpp

@@ -118,8 +118,9 @@ void WheelWV::Update(float inDeltaTime, const VehicleConstraint &inConstraint)
 		float lateral_slip_friction = settings->mLateralFriction.GetValue(lateral_slip_angle);
 		float lateral_slip_friction = settings->mLateralFriction.GetValue(lateral_slip_angle);
 
 
 		// Tire friction
 		// Tire friction
-		mCombinedLongitudinalFriction = sqrt(longitudinal_slip_friction * mContactBody->GetFriction());
-		mCombinedLateralFriction = sqrt(lateral_slip_friction * mContactBody->GetFriction());
+		VehicleConstraint::CombineFunction combine_friction = inConstraint.GetCombineFriction();
+		mCombinedLongitudinalFriction = combine_friction(longitudinal_slip_friction, *mContactBody, mContactSubShapeID);
+		mCombinedLateralFriction = combine_friction(lateral_slip_friction, *mContactBody, mContactSubShapeID);
 	}
 	}
 	else
 	else
 	{
 	{