Browse Source

Improved RPM meter and preventing sleep when engine is not idle (#590)

* The RPM meter now goes from 0 to MaxRPM instead of from MinRPM to MaxRPM and it also shows the speed of the wheels at the clutch to compare engine turn rate with wheel turn rates.
* Vehicles could go to sleep when the engine was not fully idle yet.
Jorrit Rouwe 2 years ago
parent
commit
d668abebf8

+ 6 - 0
Jolt/Physics/Vehicle/TrackedVehicleController.cpp

@@ -153,6 +153,12 @@ TrackedVehicleController::TrackedVehicleController(const TrackedVehicleControlle
 	}
 }
 
+bool TrackedVehicleController::AllowSleep() const
+{
+	return mForwardInput == 0.0f								// No user input
+		&& mEngine.GetCurrentRPM() <= 1.01f * mEngine.mMinRPM;	// Engine is idling
+}
+
 void TrackedVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem)
 {
 	Wheels &wheels = mConstraint.GetWheels();

+ 1 - 1
Jolt/Physics/Vehicle/TrackedVehicleController.h

@@ -119,7 +119,7 @@ protected:
 
 	// See: VehicleController
 	virtual Wheel *				ConstructWheel(const WheelSettings &inWheel) const override { JPH_ASSERT(IsKindOf(&inWheel, JPH_RTTI(WheelSettingsTV))); return new WheelTV(static_cast<const WheelSettingsTV &>(inWheel)); }
-	virtual bool				AllowSleep() const override					{ return mForwardInput == 0.0f; }
+	virtual bool				AllowSleep() const override;
 	virtual void				PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
 	virtual void				PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
 	virtual bool				SolveLongitudinalAndLateralConstraints(float inDeltaTime) override;

+ 5 - 5
Jolt/Physics/Vehicle/VehicleEngine.cpp

@@ -65,14 +65,14 @@ void VehicleEngine::ApplyDamping(float inDeltaTime)
 
 void VehicleEngine::DrawRPM(DebugRenderer *inRenderer, RVec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inSize, float inShiftDownRPM, float inShiftUpRPM) const
 {
-	// Function that converts RPM to an angle in radians
-	auto rpm_to_angle = [this](float inRPM) { return (-0.75f + 1.5f * (inRPM - mMinRPM) / (mMaxRPM - mMinRPM)) * JPH_PI; };
-
 	// Function to draw part of a pie
-	auto draw_pie = [rpm_to_angle, inRenderer, inSize, inPosition, inForward, inUp](float inMinRPM, float inMaxRPM, Color inColor) { 
-		inRenderer->DrawPie(inPosition, inSize, inForward, inUp, rpm_to_angle(inMinRPM), rpm_to_angle(inMaxRPM), inColor, DebugRenderer::ECastShadow::Off);
+	auto draw_pie = [this, inRenderer, inSize, inPosition, inForward, inUp](float inMinRPM, float inMaxRPM, Color inColor) { 
+		inRenderer->DrawPie(inPosition, inSize, inForward, inUp, ConvertRPMToAngle(inMinRPM), ConvertRPMToAngle(inMaxRPM), inColor, DebugRenderer::ECastShadow::Off);
 	};
 
+	// Draw segment under min RPM
+	draw_pie(0, mMinRPM, Color::sGrey);
+
 	// Draw segment until inShiftDownRPM
 	if (mCurrentRPM < inShiftDownRPM)
 	{

+ 3 - 0
Jolt/Physics/Vehicle/VehicleEngine.h

@@ -72,6 +72,9 @@ public:
 	void					ApplyDamping(float inDeltaTime);
 
 #ifdef JPH_DEBUG_RENDERER
+	// Function that converts RPM to an angle in radians for debugging purposes
+	float					ConvertRPMToAngle(float inRPM) const		{ return (-0.75f + 1.5f * inRPM / mMaxRPM) * JPH_PI; }
+
 	/// Debug draw a RPM meter
 	void					DrawRPM(DebugRenderer *inRenderer, RVec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inSize, float inShiftDownRPM, float inShiftUpRPM) const;
 #endif // JPH_DEBUG_RENDERER

+ 1 - 1
Jolt/Physics/Vehicle/VehicleTransmission.h

@@ -38,7 +38,7 @@ public:
 	float					mSwitchLatency = 0.5f;						///< How long to wait after releasing the clutch before another switch is attempted (s), only used in auto mode
 	float					mShiftUpRPM = 4000.0f;						///< If RPM of engine is bigger then this we will shift a gear up, only used in auto mode
 	float					mShiftDownRPM = 2000.0f;					///< If RPM of engine is smaller then this we will shift a gear down, only used in auto mode
-	float					mClutchStrength = 10.0f;					///< Strength of the clutch when fully engaged. Total torque a clutch applies is Torque = ClutchStrength * (Velocity Engine - Avg Velocity Wheels) (units: k m^2 s^-1)
+	float					mClutchStrength = 10.0f;					///< Strength of the clutch when fully engaged. Total torque a clutch applies is Torque = ClutchStrength * (Velocity Engine - Avg Velocity Wheels At Clutch) (units: k m^2 s^-1)
 };
 
 /// Runtime data for transmission

+ 24 - 0
Jolt/Physics/Vehicle/WheeledVehicleController.cpp

@@ -202,6 +202,12 @@ WheeledVehicleController::WheeledVehicleController(const WheeledVehicleControlle
 	JPH_ASSERT(mDifferentialLimitedSlipRatio > 1.0f);
 }
 
+bool WheeledVehicleController::AllowSleep() const
+{
+	return mForwardInput == 0.0f								// No user input
+		&& mEngine.GetCurrentRPM() <= 1.01f * mEngine.mMinRPM;	// Engine is idling
+}
+
 void WheeledVehicleController::PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem)
 {
 	JPH_PROFILE_FUNCTION();
@@ -648,6 +654,24 @@ void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const
 	Vec3 rpm_meter_fwd = body->GetRotation() * mConstraint.GetLocalForward();
 	mEngine.DrawRPM(inRenderer, rpm_meter_pos, rpm_meter_fwd, rpm_meter_up, mRPMMeterSize, mTransmission.mShiftDownRPM, mTransmission.mShiftUpRPM);
 
+	// Calculate average wheel speed at clutch
+	float wheel_speed_at_clutch = 0.0f;
+	int num_driven_wheels = 0;
+	for (const VehicleDifferentialSettings &d : mDifferentials)
+	{
+		int wheels[] = { d.mLeftWheel, d.mRightWheel };
+		for (int w : wheels)
+			if (w >= 0)
+			{
+				wheel_speed_at_clutch += mConstraint.GetWheel(w)->GetAngularVelocity() * d.mDifferentialRatio;
+				num_driven_wheels++;
+			}
+	}
+	wheel_speed_at_clutch = abs(wheel_speed_at_clutch / float(num_driven_wheels) * VehicleEngine::cAngularVelocityToRPM * mTransmission.GetCurrentRatio());
+		
+	// Draw the average wheel speed measured at clutch to compare engine RPM with wheel RPM
+	inRenderer->DrawLine(rpm_meter_pos, rpm_meter_pos + Quat::sRotation(rpm_meter_fwd, mEngine.ConvertRPMToAngle(wheel_speed_at_clutch)) * (rpm_meter_up * 1.1f * mRPMMeterSize), Color::sYellow);
+
 	// Draw current vehicle state
 	String status = StringFormat("Forward: %.1f, Right: %.1f\nBrake: %.1f, HandBrake: %.1f\n"
 								 "Gear: %d, Clutch: %.1f\nEngineRPM: %.0f, V: %.1f km/h", 

+ 1 - 1
Jolt/Physics/Vehicle/WheeledVehicleController.h

@@ -133,7 +133,7 @@ public:
 protected:
 	// See: VehicleController
 	virtual Wheel *				ConstructWheel(const WheelSettings &inWheel) const override { JPH_ASSERT(IsKindOf(&inWheel, JPH_RTTI(WheelSettingsWV))); return new WheelWV(static_cast<const WheelSettingsWV &>(inWheel)); }
-	virtual bool				AllowSleep() const override					{ return mForwardInput == 0.0f; }
+	virtual bool				AllowSleep() const override;
 	virtual void				PreCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
 	virtual void				PostCollide(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
 	virtual bool				SolveLongitudinalAndLateralConstraints(float inDeltaTime) override;

+ 4 - 4
UnitTests/Physics/WheeledVehicleTests.cpp

@@ -193,7 +193,7 @@ TEST_SUITE("WheeledVehicleTests")
 		c.GetBodyInterface().ActivateBody(body->GetID());
 		c.Simulate(2.0f);
 		CheckOnGround(constraint, settings, floor_id);
-		CHECK(!body->IsActive()); // Car should have gone sleeping
+		CHECK(!body->IsActive()); // Car should have gone to sleep
 		RVec3 pos3 = body->GetPosition();
 		CHECK_APPROX_EQUAL(pos3.GetX(), 0, 2.0e-3_r); // Not moving left/right
 		CHECK(pos3.GetZ() > pos2.GetZ() + 1.0f); // Moving in Z direction while braking
@@ -216,9 +216,9 @@ TEST_SUITE("WheeledVehicleTests")
 		// Brake
 		controller->SetDriverInput(0.0f, 0.0f, 1.0f, 0.0f);
 		c.GetBodyInterface().ActivateBody(body->GetID());
-		c.Simulate(3.0f);
+		c.Simulate(5.0f);
 		CheckOnGround(constraint, settings, floor_id);
-		CHECK(!body->IsActive()); // Car should have gone sleeping
+		CHECK(!body->IsActive()); // Car should have gone to sleep
 		RVec3 pos5 = body->GetPosition();
 		CHECK_APPROX_EQUAL(pos5.GetX(), 0, 1.0e-2_r); // Not moving left/right
 		CHECK(pos5.GetZ() < pos4.GetZ() - 1.0f); // Moving in -Z direction while braking
@@ -239,7 +239,7 @@ TEST_SUITE("WheeledVehicleTests")
 		c.GetBodyInterface().ActivateBody(body->GetID());
 		c.Simulate(4.0f);
 		CheckOnGround(constraint, settings, floor_id);
-		CHECK(!body->IsActive()); // Car should have gone sleeping
+		CHECK(!body->IsActive()); // Car should have gone to sleep
 		vel = body->GetLinearVelocity();
 		CHECK_APPROX_EQUAL(vel, Vec3::sZero(), 1.0e-3f); // Not moving