Explorar o código

Switching vehicle wheels to new SpringSettings class so you can specify stiffness/damping (#563)

Jorrit Rouwe %!s(int64=2) %!d(string=hai) anos
pai
achega
0da97d8f33

+ 57 - 44
Jolt/Physics/Vehicle/VehicleConstraint.cpp

@@ -371,56 +371,69 @@ void VehicleConstraint::SetupVelocityConstraint(float inDeltaTime)
 			// Suspension spring
 			// Suspension spring
 			if (settings->mSuspensionMaxLength > settings->mSuspensionMinLength)
 			if (settings->mSuspensionMaxLength > settings->mSuspensionMinLength)
 			{
 			{
-				// Calculate the damping and frequency of the suspension spring given the angle between the suspension direction and the contact normal
-				// If the angle between the suspension direction and the inverse of the contact normal is alpha then the force on the spring relates to the force along the contact normal as:
-				// 
-				// Fspring = Fnormal * cos(alpha)
-				//
-				// The spring force is:
-				//
-				// Fspring = -k * x
-				//
-				// where k is the spring constant and x is the displacement of the spring. So we have:
-				//
-				// Fnormal * cos(alpha) = -k * x <=> Fnormal = -k / cos(alpha) * x
-				//
-				// So we can see this as a spring with spring constant:
-				//
-				// k' = k / cos(alpha)
-				// 
-				// In the same way the velocity relates like:
-				// 
-				// Vspring = Vnormal * cos(alpha)
-				//
-				// Which results in the modified damping constant c:
-				//
-				// c' = c / cos(alpha)
-				//
-				// Since we're not supplying k and c directly but rather the frequency and damping we can calculate the spring constant and damping constant as:
-				//
-				// w = 2 * pi * f
-				// k = m * w^2
-				// c = 2 * m * w * d
-				//
-				// where m is the mass of the spring, f is the frequency and d is the damping factor (see SpringPart::CalculateSpringProperties). So we have:
-				//
-				// w' = w * pi * f'
-				// k' = m * w'^2
-				// c' = 2 * m * w' * d'
-				//
-				// where f' = f / sqrt(cos(alpha)) and d' = d / sqrt(cos(alpha))
-				//
+				// Calculate cos(alpha) where alpha is the angle between suspension direction and contact normal
 				// Note that we clamp 1 / cos(alpha) to the range [0.1, 1] in order not to increase the stiffness / damping by too much.
 				// Note that we clamp 1 / cos(alpha) to the range [0.1, 1] in order not to increase the stiffness / damping by too much.
-				// We also ensure that the frequency doesn't go over half the simulation frequency to prevent the spring from getting unstable.
 				Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection);
 				Vec3 ws_direction = body_transform.Multiply3x3(settings->mSuspensionDirection);
-				float sqrt_cos_angle = sqrt(max(0.1f, ws_direction.Dot(neg_contact_normal)));
-				float damping = settings->mSuspensionDamping / sqrt_cos_angle;
-				float frequency = min(0.5f / inDeltaTime, settings->mSuspensionFrequency / sqrt_cos_angle);
+				float cos_angle = max(0.1f, ws_direction.Dot(neg_contact_normal));
+
+				SpringSettings spring_settings = settings->mSuspensionSpring;
+				if (spring_settings.mMode == ESpringMode::FrequencyAndDamping)
+				{
+					// Calculate the damping and frequency of the suspension spring given the angle between the suspension direction and the contact normal
+					// If the angle between the suspension direction and the inverse of the contact normal is alpha then the force on the spring relates to the force along the contact normal as:
+					// 
+					// Fspring = Fnormal * cos(alpha)
+					//
+					// The spring force is:
+					//
+					// Fspring = -k * x
+					//
+					// where k is the spring constant and x is the displacement of the spring. So we have:
+					//
+					// Fnormal * cos(alpha) = -k * x <=> Fnormal = -k / cos(alpha) * x
+					//
+					// So we can see this as a spring with spring constant:
+					//
+					// k' = k / cos(alpha)
+					// 
+					// In the same way the velocity relates like:
+					// 
+					// Vspring = Vnormal * cos(alpha)
+					//
+					// Which results in the modified damping constant c:
+					//
+					// c' = c / cos(alpha)
+					//
+					// Since we're not supplying k and c directly but rather the frequency and damping we can calculate the spring constant and damping constant as:
+					//
+					// w = 2 * pi * f
+					// k = m * w^2
+					// c = 2 * m * w * d
+					//
+					// where m is the mass of the spring, f is the frequency and d is the damping factor (see SpringPart::CalculateSpringProperties). So we have:
+					//
+					// w' = w * pi * f'
+					// k' = m * w'^2
+					// c' = 2 * m * w' * d'
+					//
+					// where f' = f / sqrt(cos(alpha)) and d' = d / sqrt(cos(alpha))
+					//
+					// We ensure that the frequency doesn't go over half the simulation frequency to prevent the spring from getting unstable.
+					float sqrt_cos_angle = sqrt(cos_angle);
+					spring_settings.mDamping /= sqrt_cos_angle;
+					spring_settings.mFrequency = min(0.5f / inDeltaTime, spring_settings.mFrequency / sqrt_cos_angle);
+				}
+				else
+				{
+					// This case is similar to the one above but we're not supplying frequency and damping but rather the spring constant and damping constant directly.
+					spring_settings.mStiffness /= cos_angle;
+					spring_settings.mDamping /= cos_angle;
+				}
 
 
 				// Get the value of the constraint equation
 				// Get the value of the constraint equation
 				float c = w->mSuspensionLength - settings->mSuspensionMaxLength - settings->mSuspensionPreloadLength;
 				float c = w->mSuspensionLength - settings->mSuspensionMaxLength - settings->mSuspensionPreloadLength;
 
 
-				w->mSuspensionPart.CalculateConstraintPropertiesWithFrequencyAndDamping(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal, w->mAntiRollBarImpulse, c, frequency, damping);
+				w->mSuspensionPart.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal, w->mAntiRollBarImpulse, c, spring_settings);
 			}
 			}
 			else
 			else
 				w->mSuspensionPart.Deactivate();
 				w->mSuspensionPart.Deactivate();

+ 7 - 8
Jolt/Physics/Vehicle/Wheel.cpp

@@ -21,8 +21,9 @@ JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(WheelSettings)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMinLength)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMinLength)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMaxLength)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionMaxLength)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionPreloadLength)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionPreloadLength)
-	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionFrequency)
-	JPH_ADD_ATTRIBUTE(WheelSettings, mSuspensionDamping)
+	JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mMode, "mSuspensionSpringMode")
+	JPH_ADD_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mFrequency, "mSuspensionFrequency") // Renaming attributes to stay compatible with old versions of the library
+	JPH_ADD_ATTRIBUTE_WITH_ALIAS(WheelSettings, mSuspensionSpring.mDamping, "mSuspensionDamping")
 	JPH_ADD_ATTRIBUTE(WheelSettings, mRadius)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mRadius)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mWidth)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mWidth)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mEnableSuspensionForcePoint)
 	JPH_ADD_ATTRIBUTE(WheelSettings, mEnableSuspensionForcePoint)
@@ -39,8 +40,7 @@ void WheelSettings::SaveBinaryState(StreamOut &inStream) const
 	inStream.Write(mSuspensionMinLength);
 	inStream.Write(mSuspensionMinLength);
 	inStream.Write(mSuspensionMaxLength);
 	inStream.Write(mSuspensionMaxLength);
 	inStream.Write(mSuspensionPreloadLength);
 	inStream.Write(mSuspensionPreloadLength);
-	inStream.Write(mSuspensionFrequency);
-	inStream.Write(mSuspensionDamping);
+	mSuspensionSpring.SaveBinaryState(inStream);
 	inStream.Write(mRadius);
 	inStream.Write(mRadius);
 	inStream.Write(mWidth);
 	inStream.Write(mWidth);
 	inStream.Write(mEnableSuspensionForcePoint);
 	inStream.Write(mEnableSuspensionForcePoint);
@@ -57,8 +57,7 @@ void WheelSettings::RestoreBinaryState(StreamIn &inStream)
 	inStream.Read(mSuspensionMinLength);
 	inStream.Read(mSuspensionMinLength);
 	inStream.Read(mSuspensionMaxLength);
 	inStream.Read(mSuspensionMaxLength);
 	inStream.Read(mSuspensionPreloadLength);
 	inStream.Read(mSuspensionPreloadLength);
-	inStream.Read(mSuspensionFrequency);
-	inStream.Read(mSuspensionDamping);
+	mSuspensionSpring.RestoreBinaryState(inStream);
 	inStream.Read(mRadius);
 	inStream.Read(mRadius);
 	inStream.Read(mWidth);
 	inStream.Read(mWidth);
 	inStream.Read(mEnableSuspensionForcePoint);
 	inStream.Read(mEnableSuspensionForcePoint);
@@ -75,8 +74,8 @@ Wheel::Wheel(const WheelSettings &inSettings) :
 	JPH_ASSERT(inSettings.mSuspensionMinLength >= 0.0f);
 	JPH_ASSERT(inSettings.mSuspensionMinLength >= 0.0f);
 	JPH_ASSERT(inSettings.mSuspensionMaxLength >= inSettings.mSuspensionMinLength);
 	JPH_ASSERT(inSettings.mSuspensionMaxLength >= inSettings.mSuspensionMinLength);
 	JPH_ASSERT(inSettings.mSuspensionPreloadLength >= 0.0f);
 	JPH_ASSERT(inSettings.mSuspensionPreloadLength >= 0.0f);
-	JPH_ASSERT(inSettings.mSuspensionFrequency > 0.0f);
-	JPH_ASSERT(inSettings.mSuspensionDamping >= 0.0f);
+	JPH_ASSERT(inSettings.mSuspensionSpring.mFrequency > 0.0f);
+	JPH_ASSERT(inSettings.mSuspensionSpring.mDamping >= 0.0f);
 	JPH_ASSERT(inSettings.mRadius > 0.0f);
 	JPH_ASSERT(inSettings.mRadius > 0.0f);
 	JPH_ASSERT(inSettings.mWidth >= 0.0f);
 	JPH_ASSERT(inSettings.mWidth >= 0.0f);
 }
 }

+ 1 - 2
Jolt/Physics/Vehicle/Wheel.h

@@ -35,8 +35,7 @@ public:
 	float					mSuspensionMinLength = 0.3f;				///< How long the suspension is in max raised position relative to the attachment point (m)
 	float					mSuspensionMinLength = 0.3f;				///< How long the suspension is in max raised position relative to the attachment point (m)
 	float					mSuspensionMaxLength = 0.5f;				///< How long the suspension is in max droop position relative to the attachment point (m)
 	float					mSuspensionMaxLength = 0.5f;				///< How long the suspension is in max droop position relative to the attachment point (m)
 	float					mSuspensionPreloadLength = 0.0f;			///< The natural length (m) of the suspension spring is defined as mSuspensionMaxLength + mSuspensionPreloadLength. Can be used to preload the suspension as the spring is compressed by mSuspensionPreloadLength when the suspension is in max droop position. Note that this means when the vehicle touches the ground there is a discontinuity so it will also make the vehicle more bouncy as we're updating with discrete time steps.
 	float					mSuspensionPreloadLength = 0.0f;			///< The natural length (m) of the suspension spring is defined as mSuspensionMaxLength + mSuspensionPreloadLength. Can be used to preload the suspension as the spring is compressed by mSuspensionPreloadLength when the suspension is in max droop position. Note that this means when the vehicle touches the ground there is a discontinuity so it will also make the vehicle more bouncy as we're updating with discrete time steps.
-	float					mSuspensionFrequency = 1.5f;				///< Natural frequency of the suspension spring (Hz)
-	float					mSuspensionDamping = 0.5f;					///< Damping factor of the suspension spring (0 = no damping, 1 = critical damping)
+	SpringSettings			mSuspensionSpring { ESpringMode::FrequencyAndDamping, 1.5f, 0.5f }; ///< Settings for the suspension spring
 	float					mRadius = 0.3f;								///< Radius of the wheel (m)
 	float					mRadius = 0.3f;								///< Radius of the wheel (m)
 	float					mWidth = 0.1f;								///< Width of the wheel (m)
 	float					mWidth = 0.1f;								///< Width of the wheel (m)
 	bool					mEnableSuspensionForcePoint = false;		///< Enables mSuspensionForcePoint, if disabled, the forces are applied at the collision contect point. This leads to a more accurate simulation when interacting with dynamic objects but makes the vehicle less stable. When settings this to true, all forces will be applied to a fixed point on the vehicle body.
 	bool					mEnableSuspensionForcePoint = false;		///< Enables mSuspensionForcePoint, if disabled, the forces are applied at the collision contect point. This leads to a more accurate simulation when interacting with dynamic objects but makes the vehicle less stable. When settings this to true, all forces will be applied to a fixed point on the vehicle body.

+ 2 - 2
Samples/Tests/Vehicle/MotorcycleTest.cpp

@@ -77,7 +77,7 @@ void MotorcycleTest::Initialize()
 	front->mWidth = front_wheel_width;
 	front->mWidth = front_wheel_width;
 	front->mSuspensionMinLength = front_suspension_min_length;
 	front->mSuspensionMinLength = front_suspension_min_length;
 	front->mSuspensionMaxLength = front_suspension_max_length;
 	front->mSuspensionMaxLength = front_suspension_max_length;
-	front->mSuspensionFrequency = front_suspension_freq;
+	front->mSuspensionSpring.mFrequency = front_suspension_freq;
 	front->mMaxBrakeTorque = front_brake_torque;
 	front->mMaxBrakeTorque = front_brake_torque;
 
 
 	WheelSettingsWV *back = new WheelSettingsWV;
 	WheelSettingsWV *back = new WheelSettingsWV;
@@ -87,7 +87,7 @@ void MotorcycleTest::Initialize()
 	back->mWidth = back_wheel_width;
 	back->mWidth = back_wheel_width;
 	back->mSuspensionMinLength = back_suspension_min_length;
 	back->mSuspensionMinLength = back_suspension_min_length;
 	back->mSuspensionMaxLength = back_suspension_max_length;
 	back->mSuspensionMaxLength = back_suspension_max_length;
-	back->mSuspensionFrequency = back_suspension_freq;
+	back->mSuspensionSpring.mFrequency = back_suspension_freq;
 	back->mMaxBrakeTorque = back_brake_torque;
 	back->mMaxBrakeTorque = back_brake_torque;
 
 
 	if (sOverrideFrontSuspensionForcePoint)
 	if (sOverrideFrontSuspensionForcePoint)

+ 1 - 1
Samples/Tests/Vehicle/TankTest.cpp

@@ -101,7 +101,7 @@ void TankTest::Initialize()
 			w->mWidth = wheel_width;
 			w->mWidth = wheel_width;
 			w->mSuspensionMinLength = suspension_min_length;
 			w->mSuspensionMinLength = suspension_min_length;
 			w->mSuspensionMaxLength = wheel == 0 || wheel == size(wheel_pos) - 1? suspension_min_length : suspension_max_length;
 			w->mSuspensionMaxLength = wheel == 0 || wheel == size(wheel_pos) - 1? suspension_min_length : suspension_max_length;
-			w->mSuspensionFrequency = suspension_frequency;
+			w->mSuspensionSpring.mFrequency = suspension_frequency;
 
 
 			// Add the wheel to the vehicle
 			// Add the wheel to the vehicle
 			track.mWheels.push_back((uint)vehicle.mWheels.size());
 			track.mWheels.push_back((uint)vehicle.mWheels.size());

+ 8 - 8
Samples/Tests/Vehicle/VehicleConstraintTest.cpp

@@ -72,8 +72,8 @@ void VehicleConstraintTest::Initialize()
 	w1->mWheelForward = front_wheel_forward;
 	w1->mWheelForward = front_wheel_forward;
 	w1->mSuspensionMinLength = sFrontSuspensionMinLength;
 	w1->mSuspensionMinLength = sFrontSuspensionMinLength;
 	w1->mSuspensionMaxLength = sFrontSuspensionMaxLength;
 	w1->mSuspensionMaxLength = sFrontSuspensionMaxLength;
-	w1->mSuspensionFrequency = sFrontSuspensionFrequency;
-	w1->mSuspensionDamping = sFrontSuspensionDamping;
+	w1->mSuspensionSpring.mFrequency = sFrontSuspensionFrequency;
+	w1->mSuspensionSpring.mDamping = sFrontSuspensionDamping;
 	w1->mMaxSteerAngle = sMaxSteeringAngle;
 	w1->mMaxSteerAngle = sMaxSteeringAngle;
 	w1->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 	w1->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 
 
@@ -86,8 +86,8 @@ void VehicleConstraintTest::Initialize()
 	w2->mWheelForward = flip_x * front_wheel_forward;
 	w2->mWheelForward = flip_x * front_wheel_forward;
 	w2->mSuspensionMinLength = sFrontSuspensionMinLength;
 	w2->mSuspensionMinLength = sFrontSuspensionMinLength;
 	w2->mSuspensionMaxLength = sFrontSuspensionMaxLength;
 	w2->mSuspensionMaxLength = sFrontSuspensionMaxLength;
-	w2->mSuspensionFrequency = sFrontSuspensionFrequency;
-	w2->mSuspensionDamping = sFrontSuspensionDamping;
+	w2->mSuspensionSpring.mFrequency = sFrontSuspensionFrequency;
+	w2->mSuspensionSpring.mDamping = sFrontSuspensionDamping;
 	w2->mMaxSteerAngle = sMaxSteeringAngle;
 	w2->mMaxSteerAngle = sMaxSteeringAngle;
 	w2->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 	w2->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 
 
@@ -100,8 +100,8 @@ void VehicleConstraintTest::Initialize()
 	w3->mWheelForward = rear_wheel_forward;
 	w3->mWheelForward = rear_wheel_forward;
 	w3->mSuspensionMinLength = sRearSuspensionMinLength;
 	w3->mSuspensionMinLength = sRearSuspensionMinLength;
 	w3->mSuspensionMaxLength = sRearSuspensionMaxLength;
 	w3->mSuspensionMaxLength = sRearSuspensionMaxLength;
-	w3->mSuspensionFrequency = sRearSuspensionFrequency;
-	w3->mSuspensionDamping = sRearSuspensionDamping;
+	w3->mSuspensionSpring.mFrequency = sRearSuspensionFrequency;
+	w3->mSuspensionSpring.mDamping = sRearSuspensionDamping;
 	w3->mMaxSteerAngle = 0.0f;
 	w3->mMaxSteerAngle = 0.0f;
 
 
 	// Right rear
 	// Right rear
@@ -113,8 +113,8 @@ void VehicleConstraintTest::Initialize()
 	w4->mWheelForward = flip_x * rear_wheel_forward;
 	w4->mWheelForward = flip_x * rear_wheel_forward;
 	w4->mSuspensionMinLength = sRearSuspensionMinLength;
 	w4->mSuspensionMinLength = sRearSuspensionMinLength;
 	w4->mSuspensionMaxLength = sRearSuspensionMaxLength;
 	w4->mSuspensionMaxLength = sRearSuspensionMaxLength;
-	w4->mSuspensionFrequency = sRearSuspensionFrequency;
-	w4->mSuspensionDamping = sRearSuspensionDamping;
+	w4->mSuspensionSpring.mFrequency = sRearSuspensionFrequency;
+	w4->mSuspensionSpring.mDamping = sRearSuspensionDamping;
 	w4->mMaxSteerAngle = 0.0f;
 	w4->mMaxSteerAngle = 0.0f;
 
 
 	vehicle.mWheels = { w1, w2, w3, w4 };
 	vehicle.mWheels = { w1, w2, w3, w4 };