Browse Source

Motorcycle: Added option to disable the lean spring controller (#528)

Jorrit Rouwe 2 years ago
parent
commit
adc8050e63

+ 72 - 59
Jolt/Physics/Vehicle/MotorcycleController.cpp

@@ -85,25 +85,31 @@ void MotorcycleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysic
 	const Body *body = mConstraint.GetVehicleBody();
 	const Body *body = mConstraint.GetVehicleBody();
 	Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward();
 	Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward();
 	float wheel_base = GetWheelBase();
 	float wheel_base = GetWheelBase();
-	float velocity = body->GetLinearVelocity().Dot(forward);
-	float velocity_sq = Square(velocity);
+	Vec3 world_up = mConstraint.GetWorldUp();
 
 
-	// Calculate the target lean vector, this is in the direction of the total applied impulse by the ground on the wheels
-	Vec3 target_lean = Vec3::sZero();
-	for (const Wheel *w : mConstraint.GetWheels())
-		if (w->HasContact())
-			target_lean += w->GetContactNormal() * w->GetSuspensionLambda() + w->GetContactLateral() * w->GetLateralLambda();
+	if (mEnableLeanController)
+	{
+		// Calculate the target lean vector, this is in the direction of the total applied impulse by the ground on the wheels
+		Vec3 target_lean = Vec3::sZero();
+		for (const Wheel *w : mConstraint.GetWheels())
+			if (w->HasContact())
+				target_lean += w->GetContactNormal() * w->GetSuspensionLambda() + w->GetContactLateral() * w->GetLateralLambda();
 
 
-	// Normalize the impulse
-	Vec3 world_up = mConstraint.GetWorldUp();
-	target_lean = target_lean.NormalizedOr(world_up);
+		// Normalize the impulse
+		target_lean = target_lean.NormalizedOr(world_up);
 
 
-	// Smooth the impulse to avoid jittery behavior
-	mTargetLean = mLeanSmoothingFactor * mTargetLean + (1.0f - mLeanSmoothingFactor) * target_lean;
+		// Smooth the impulse to avoid jittery behavior
+		mTargetLean = mLeanSmoothingFactor * mTargetLean + (1.0f - mLeanSmoothingFactor) * target_lean;
 
 
-	// Remove forward component, we can only lean sideways
-	mTargetLean -= mTargetLean * mTargetLean.Dot(forward);
-	mTargetLean = mTargetLean.NormalizedOr(world_up);
+		// Remove forward component, we can only lean sideways
+		mTargetLean -= mTargetLean * mTargetLean.Dot(forward);
+		mTargetLean = mTargetLean.NormalizedOr(world_up);
+	}
+	else
+	{
+		// Controller not enabled, reset target lean
+		mTargetLean = world_up;
+	}
 
 
 	JPH_DET_LOG("WheeledVehicleController::PreCollide: target_lean: " << target_lean << " mTargetLean: " << mTargetLean);
 	JPH_DET_LOG("WheeledVehicleController::PreCollide: target_lean: " << target_lean << " mTargetLean: " << mTargetLean);
 
 
@@ -117,6 +123,10 @@ void MotorcycleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysic
 	// The caster angle is different for each wheel so we can only calculate part of the equation here
 	// The caster angle is different for each wheel so we can only calculate part of the equation here
 	float max_steer_angle_factor = wheel_base * Tan(mMaxLeanAngle) * inPhysicsSystem.GetGravity().Length();
 	float max_steer_angle_factor = wheel_base * Tan(mMaxLeanAngle) * inPhysicsSystem.GetGravity().Length();
 
 
+	// Calculate forward velocity
+	float velocity = body->GetLinearVelocity().Dot(forward);
+	float velocity_sq = Square(velocity);
+
 	// Decompose steering into sign and direction
 	// Decompose steering into sign and direction
 	float steer_strength = abs(mRightInput);
 	float steer_strength = abs(mRightInput);
 	float steer_sign = -Sign(mRightInput);
 	float steer_sign = -Sign(mRightInput);
@@ -155,61 +165,64 @@ bool MotorcycleController::SolveLongitudinalAndLateralConstraints(float inDeltaT
 {
 {
 	bool impulse = WheeledVehicleController::SolveLongitudinalAndLateralConstraints(inDeltaTime);
 	bool impulse = WheeledVehicleController::SolveLongitudinalAndLateralConstraints(inDeltaTime);
 
 
-	// Only apply a lean impulse if all wheels are in contact, otherwise we can easily spin out
-	bool all_in_contact = true;
-	for (const Wheel *w : mConstraint.GetWheels())
-		if (!w->HasContact() || w->GetSuspensionLambda() <= 0.0f)
-		{
-			all_in_contact = false;
-			break;
-		}
-
-	if (all_in_contact)
+	if (mEnableLeanController)
 	{
 	{
-		Body *body = mConstraint.GetVehicleBody();
-		const MotionProperties *mp = body->GetMotionProperties();
+		// Only apply a lean impulse if all wheels are in contact, otherwise we can easily spin out
+		bool all_in_contact = true;
+		for (const Wheel *w : mConstraint.GetWheels())
+			if (!w->HasContact() || w->GetSuspensionLambda() <= 0.0f)
+			{
+				all_in_contact = false;
+				break;
+			}
+
+		if (all_in_contact)
+		{
+			Body *body = mConstraint.GetVehicleBody();
+			const MotionProperties *mp = body->GetMotionProperties();
 
 
-		Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward();
-		Vec3 up = body->GetRotation() * mConstraint.GetLocalUp();
+			Vec3 forward = body->GetRotation() * mConstraint.GetLocalForward();
+			Vec3 up = body->GetRotation() * mConstraint.GetLocalUp();
 
 
-		// Calculate delta to target angle and derivative
-		float d_angle = -Sign(mTargetLean.Cross(up).Dot(forward)) * ACos(mTargetLean.Dot(up));
-		float ddt_angle = body->GetAngularVelocity().Dot(forward);
+			// Calculate delta to target angle and derivative
+			float d_angle = -Sign(mTargetLean.Cross(up).Dot(forward)) * ACos(mTargetLean.Dot(up));
+			float ddt_angle = body->GetAngularVelocity().Dot(forward);
 
 
-		// Calculate impulse to apply to get to target lean angle
-		float total_impulse = (mLeanSpringConstant * d_angle - mLeanSpringDamping * ddt_angle) * inDeltaTime;
+			// Calculate impulse to apply to get to target lean angle
+			float total_impulse = (mLeanSpringConstant * d_angle - mLeanSpringDamping * ddt_angle) * inDeltaTime;
 
 
-		// Remember angular velocity pre angular impulse
-		Vec3 old_w = mp->GetAngularVelocity();
+			// Remember angular velocity pre angular impulse
+			Vec3 old_w = mp->GetAngularVelocity();
 
 
-		// Apply impulse taking into account the impulse we've applied earlier
-		float delta_impulse = total_impulse - mAppliedImpulse;
-		body->AddAngularImpulse(delta_impulse * forward);
-		mAppliedImpulse = total_impulse;
+			// Apply impulse taking into account the impulse we've applied earlier
+			float delta_impulse = total_impulse - mAppliedImpulse;
+			body->AddAngularImpulse(delta_impulse * forward);
+			mAppliedImpulse = total_impulse;
 
 
-		// Calculate delta angular velocity due to angular impulse
-		Vec3 dw = mp->GetAngularVelocity() - old_w;
-		Vec3 linear_acceleration = Vec3::sZero();
-		float total_lambda = 0.0f;
-		for (Wheel *w_base : mConstraint.GetWheels())
-		{
-			const WheelWV *w = static_cast<WheelWV *>(w_base);
+			// Calculate delta angular velocity due to angular impulse
+			Vec3 dw = mp->GetAngularVelocity() - old_w;
+			Vec3 linear_acceleration = Vec3::sZero();
+			float total_lambda = 0.0f;
+			for (Wheel *w_base : mConstraint.GetWheels())
+			{
+				const WheelWV *w = static_cast<WheelWV *>(w_base);
 
 
-			// We weigh the importance of each contact point according to the contact force
-			float lambda = w->GetSuspensionLambda();
-			total_lambda += lambda;
+				// We weigh the importance of each contact point according to the contact force
+				float lambda = w->GetSuspensionLambda();
+				total_lambda += lambda;
 
 
-			// Linear acceleration of contact point is dw x com_to_contact
-			Vec3 r = Vec3(w->GetContactPosition() - body->GetCenterOfMassPosition());
-			linear_acceleration += lambda * dw.Cross(r);
-		}
+				// Linear acceleration of contact point is dw x com_to_contact
+				Vec3 r = Vec3(w->GetContactPosition() - body->GetCenterOfMassPosition());
+				linear_acceleration += lambda * dw.Cross(r);
+			}
 
 
-		// Apply linear impulse to COM to cancel the average velocity change on the wheels due to the angular impulse
-		Vec3 linear_impulse = -linear_acceleration / (total_lambda * mp->GetInverseMass());
-		body->AddImpulse(linear_impulse);
+			// Apply linear impulse to COM to cancel the average velocity change on the wheels due to the angular impulse
+			Vec3 linear_impulse = -linear_acceleration / (total_lambda * mp->GetInverseMass());
+			body->AddImpulse(linear_impulse);
 
 
-		// Return true if we applied an impulse
-		impulse |= delta_impulse != 0.0f;
+			// Return true if we applied an impulse
+			impulse |= delta_impulse != 0.0f;
+		}
 	}
 	}
 
 
 	return impulse;
 	return impulse;

+ 7 - 0
Jolt/Physics/Vehicle/MotorcycleController.h

@@ -46,6 +46,12 @@ public:
 	/// Get the distance between the front and back wheels
 	/// Get the distance between the front and back wheels
 	float						GetWheelBase() const;
 	float						GetWheelBase() const;
 
 
+	/// Enable or disable the lean spring. This allows you to temporarily disable the lean spring to allow the motorcycle to fall over.
+	void						EnableLeanController(bool inEnable)					{ mEnableLeanController = inEnable; }
+
+	/// Check if the lean spring is enabled.
+	bool						IsLeanControllerEnabled() const						{ return mEnableLeanController; }
+
 protected:
 protected:
 	// See: VehicleController
 	// See: VehicleController
 	virtual void				PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
 	virtual void				PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
@@ -57,6 +63,7 @@ protected:
 #endif // JPH_DEBUG_RENDERER
 #endif // JPH_DEBUG_RENDERER
 
 
 	// Configuration properties
 	// Configuration properties
+	bool						mEnableLeanController = true;
 	float						mMaxLeanAngle;
 	float						mMaxLeanAngle;
 	float						mLeanSpringConstant;
 	float						mLeanSpringConstant;
 	float						mLeanSpringDamping;
 	float						mLeanSpringDamping;

+ 9 - 0
Samples/Tests/Vehicle/MotorcycleTest.cpp

@@ -9,6 +9,7 @@
 #include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
 #include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
 #include <Jolt/Physics/Vehicle/MotorcycleController.h>
 #include <Jolt/Physics/Vehicle/MotorcycleController.h>
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
+#include <Application/DebugUI.h>
 #include <Layers.h>
 #include <Layers.h>
 #include <Renderer/DebugRendererImp.h>
 #include <Renderer/DebugRendererImp.h>
 
 
@@ -221,3 +222,11 @@ RMat44 MotorcycleTest::GetCameraPivot(float inCameraHeading, float inCameraPitch
 	Vec3 right = up.Cross(fwd);
 	Vec3 right = up.Cross(fwd);
 	return RMat44(Vec4(right, 0), Vec4(up, 0), Vec4(fwd, 0), mMotorcycleBody->GetPosition());
 	return RMat44(Vec4(right, 0), Vec4(up, 0), Vec4(fwd, 0), mMotorcycleBody->GetPosition());
 }
 }
+
+void MotorcycleTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
+{
+	VehicleTest::CreateSettingsMenu(inUI, inSubMenu);
+
+	MotorcycleController *controller = static_cast<MotorcycleController *>(mVehicleConstraint->GetController());
+	inUI->CreateCheckBox(inSubMenu, "Enable Lean Controller", controller->IsLeanControllerEnabled(), [controller](UICheckBox::EState inState) { controller->EnableLeanController(inState == UICheckBox::STATE_CHECKED); });
+}

+ 2 - 0
Samples/Tests/Vehicle/MotorcycleTest.h

@@ -26,6 +26,8 @@ public:
 	virtual void				GetInitialCamera(CameraState &ioState) const override;
 	virtual void				GetInitialCamera(CameraState &ioState) const override;
 	virtual RMat44				GetCameraPivot(float inCameraHeading, float inCameraPitch) const override;
 	virtual RMat44				GetCameraPivot(float inCameraHeading, float inCameraPitch) const override;
 
 
+	virtual void				CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
+
 private:
 private:
 	Body *						mMotorcycleBody;							///< The vehicle
 	Body *						mMotorcycleBody;							///< The vehicle
 	Ref<VehicleConstraint>		mVehicleConstraint;							///< The vehicle constraint
 	Ref<VehicleConstraint>		mVehicleConstraint;							///< The vehicle constraint