Browse Source

Reduced amount of body state that is saved by SaveState. (#654)

Only the state that is modified by the simulation is saved (just like constraints).
Jorrit Rouwe 2 years ago
parent
commit
7ff50429ab

+ 0 - 8
Jolt/Physics/Body/Body.cpp

@@ -288,10 +288,6 @@ void Body::SaveState(StateRecorder &inStream) const
 	// Only write properties that can change at runtime
 	inStream.Write(mPosition);
 	inStream.Write(mRotation);
-	inStream.Write(mFriction);
-	inStream.Write(mRestitution);
-	mCollisionGroup.SaveBinaryState(inStream);
-	inStream.Write(mMotionType);
 
 	if (mMotionProperties != nullptr)
 	{
@@ -306,10 +302,6 @@ void Body::RestoreState(StateRecorder &inStream)
 {
 	inStream.Read(mPosition);
 	inStream.Read(mRotation);
-	inStream.Read(mFriction);
-	inStream.Read(mRestitution);
-	mCollisionGroup.RestoreBinaryState(inStream);
-	inStream.Read(mMotionType);
 
 	if (mMotionProperties != nullptr)
 	{

+ 5 - 11
Jolt/Physics/Body/BodyManager.cpp

@@ -751,7 +751,7 @@ void BodyManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *
 				bodies.push_back(b);
 
 		// Write state of bodies
-		size_t num_bodies = bodies.size();
+		uint32 num_bodies = (uint32)bodies.size();
 		inStream.Write(num_bodies);
 		for (const Body *b : bodies)
 		{
@@ -774,11 +774,11 @@ bool BodyManager::RestoreState(StateRecorder &inStream)
 		if (inStream.IsValidating())
 		{
 			// Read state of bodies, note this reads it in a way to be consistent with validation
-			size_t old_num_bodies = 0;
+			uint32 old_num_bodies = 0;
 			for (const Body *b : mBodies)
 				if (sIsValidBodyPointer(b) && b->IsInBroadPhase())
 					++old_num_bodies;
-			size_t num_bodies = old_num_bodies; // Initialize to current value for validation
+			uint32 num_bodies = old_num_bodies; // Initialize to current value for validation
 			inStream.Read(num_bodies);
 			if (num_bodies != old_num_bodies)
 			{
@@ -813,11 +813,11 @@ bool BodyManager::RestoreState(StateRecorder &inStream)
 		else
 		{
 			// Not validating, we can be a bit more loose, read number of bodies
-			size_t num_bodies = 0;
+			uint32 num_bodies = 0;
 			inStream.Read(num_bodies);
 
 			// Iterate over the stored bodies and restore their state
-			for (size_t idx = 0; idx < num_bodies; ++idx)
+			for (uint32 idx = 0; idx < num_bodies; ++idx)
 			{
 				BodyID body_id;
 				inStream.Read(body_id);
@@ -858,12 +858,6 @@ bool BodyManager::RestoreState(StateRecorder &inStream)
 			Body *body = TryGetBody(body_id);
 			RemoveBodyFromActiveBodies(*body);
 		}
-
-		// Count CCD bodies (needs to be done because Body::RestoreState can change the motion quality without notifying the system)
-		mNumActiveCCDBodies = 0;
-		for (const BodyID *id = mActiveBodies[(int)EBodyType::RigidBody], *end = id + mNumActiveBodies[(int)EBodyType::RigidBody]; id < end; ++id)
-			if (mBodies[id->GetIndex()]->GetMotionProperties()->GetMotionQuality() == EMotionQuality::LinearCast)
-				mNumActiveCCDBodies++;
 	}
 
 	return true;

+ 2 - 14
Jolt/Physics/Body/MotionProperties.cpp

@@ -35,9 +35,9 @@ void MotionProperties::SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassP
 		// Set inverse inertia
 		Mat44 rotation;
 		Vec3 diagonal;
-		if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal) 
+		if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal)
 			&& !diagonal.IsNearZero())
-		{	
+		{
 			mInvInertiaDiagonal = diagonal.Reciprocal();
 			mInertiaRotation = rotation.GetQuaternion();
 		}
@@ -127,17 +127,11 @@ void MotionProperties::SaveState(StateRecorder &inStream) const
 	inStream.Write(mAngularVelocity);
 	inStream.Write(mForce);
 	inStream.Write(mTorque);
-	inStream.Write(mLinearDamping);
-	inStream.Write(mAngularDamping);
-	inStream.Write(mMaxLinearVelocity);
-	inStream.Write(mMaxAngularVelocity);
-	inStream.Write(mGravityFactor);
 #ifdef JPH_DOUBLE_PRECISION
 	inStream.Write(mSleepTestOffset);
 #endif // JPH_DOUBLE_PRECISION
 	inStream.Write(mSleepTestSpheres);
 	inStream.Write(mSleepTestTimer);
-	inStream.Write(mMotionQuality);
 	inStream.Write(mAllowSleeping);
 }
 
@@ -147,17 +141,11 @@ void MotionProperties::RestoreState(StateRecorder &inStream)
 	inStream.Read(mAngularVelocity);
 	inStream.Read(mForce);
 	inStream.Read(mTorque);
-	inStream.Read(mLinearDamping);
-	inStream.Read(mAngularDamping);
-	inStream.Read(mMaxLinearVelocity);
-	inStream.Read(mMaxAngularVelocity);
-	inStream.Read(mGravityFactor);
 #ifdef JPH_DOUBLE_PRECISION
 	inStream.Read(mSleepTestOffset);
 #endif // JPH_DOUBLE_PRECISION
 	inStream.Read(mSleepTestSpheres);
 	inStream.Read(mSleepTestTimer);
-	inStream.Read(mMotionQuality);
 	inStream.Read(mAllowSleeping);
 }
 

+ 5 - 5
Jolt/Physics/Constraints/ConstraintManager.cpp

@@ -238,7 +238,7 @@ void ConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFi
 				constraints.push_back(c);
 
 		// Save them
-		size_t num_constraints = constraints.size();
+		uint32 num_constraints = (uint32)constraints.size();
 		inStream.Write(num_constraints);
 		for (const Constraint *c : constraints)
 		{
@@ -249,7 +249,7 @@ void ConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFi
 	else
 	{
 		// Save all constraints
-		size_t num_constraints = mConstraints.size();
+		uint32 num_constraints = (uint32)mConstraints.size();
 		inStream.Write(num_constraints);
 		for (const Ref<Constraint> &c : mConstraints)
 		{
@@ -266,7 +266,7 @@ bool ConstraintManager::RestoreState(StateRecorder &inStream)
 	if (inStream.IsValidating())
 	{
 		// Read state of constraints
-		size_t num_constraints = mConstraints.size(); // Initialize to current value for validation
+		uint32 num_constraints = (uint32)mConstraints.size(); // Initialize to current value for validation
 		inStream.Read(num_constraints);
 		if (num_constraints != mConstraints.size())
 		{
@@ -288,10 +288,10 @@ bool ConstraintManager::RestoreState(StateRecorder &inStream)
 	else
 	{
 		// Not validating, use more flexible reading, read number of constraints
-		size_t num_constraints = 0;
+		uint32 num_constraints = 0;
 		inStream.Read(num_constraints);
 
-		for (size_t idx = 0; idx < num_constraints; ++idx)
+		for (uint32 idx = 0; idx < num_constraints; ++idx)
 		{
 			uint32 constraint_index;
 			inStream.Read(constraint_index);

+ 8 - 8
Jolt/Physics/PhysicsSystem.cpp

@@ -2408,6 +2408,14 @@ bool PhysicsSystem::RestoreState(StateRecorder &inStream)
 	{
 		if (!mBodyManager.RestoreState(inStream))
 			return false;
+
+		// Update bounding boxes for all bodies in the broadphase
+		Array<BodyID> bodies;
+		for (const Body *b : mBodyManager.GetBodies())
+			if (BodyManager::sIsValidBodyPointer(b) && b->IsInBroadPhase())
+				bodies.push_back(b->GetID());
+		if (!bodies.empty())
+			mBroadPhase->NotifyBodiesAABBChanged(&bodies[0], (int)bodies.size());
 	}
 
 	if (uint8(state) & uint8(EStateRecorderState::Contacts))
@@ -2422,14 +2430,6 @@ bool PhysicsSystem::RestoreState(StateRecorder &inStream)
 			return false;
 	}
 
-	// Update bounding boxes for all bodies in the broadphase
-	Array<BodyID> bodies;
-	for (const Body *b : mBodyManager.GetBodies())
-		if (BodyManager::sIsValidBodyPointer(b) && b->IsInBroadPhase())
-			bodies.push_back(b->GetID());
-	if (!bodies.empty())
-		mBroadPhase->NotifyBodiesAABBChanged(&bodies[0], (int)bodies.size());
-
 	return true;
 }
 

+ 4 - 2
Jolt/Physics/StateRecorder.h

@@ -42,7 +42,8 @@ public:
 };
 
 /// Class that records the state of a physics system. Can be used to check if the simulation is deterministic by putting the recorder in validation mode.
-/// Can be used to restore the state to an earlier point in time.
+/// Can be used to restore the state to an earlier point in time. Note that only the state that is modified by the simulation is saved, configuration settings
+/// like body friction or restitution, motion quality etc. are not saved and need to be saved by the user if desired.
 class JPH_EXPORT StateRecorder : public StreamIn, public StreamOut
 {
 public:
@@ -53,7 +54,8 @@ public:
 	/// Sets the stream in validation mode. In this case the physics system ensures that before it calls ReadBytes that it will
 	/// ensure that those bytes contain the current state. This makes it possible to step and save the state, restore to the previous
 	/// step and step again and when the recorded state is not the same it can restore the expected state and any byte that changes
-	/// due to a ReadBytes function can be caught to find out which part of the simulation is not deterministic
+	/// due to a ReadBytes function can be caught to find out which part of the simulation is not deterministic.
+	/// Note that validation only works when saving the full state of the simulation (EStateRecorderState::All, StateRecorderFilter == nullptr).
 	void				SetValidating(bool inValidating)							{ mIsValidating = inValidating; }
 	bool				IsValidating() const										{ return mIsValidating; }
 

+ 10 - 3
Samples/Tests/General/ChangeMotionQualityTest.cpp

@@ -50,14 +50,19 @@ void ChangeMotionQualityTest::Initialize()
 	mBodyInterface->AddBody(mBody->GetID(), EActivation::Activate);
 }
 
+void ChangeMotionQualityTest::UpdateMotionQuality()
+{
+	// Calculate desired motion quality
+	EMotionQuality motion_quality = (int(mTime) & 1) == 0? EMotionQuality::LinearCast : EMotionQuality::Discrete;
+	mBodyInterface->SetMotionQuality(mBody->GetID(), motion_quality);
+}
+
 void ChangeMotionQualityTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 {
 	// Increment time
 	mTime += inParams.mDeltaTime;
 
-	// Calculate desired motion quality
-	EMotionQuality motion_quality = (int(mTime) & 1) == 0? EMotionQuality::LinearCast : EMotionQuality::Discrete;
-	mBodyInterface->SetMotionQuality(mBody->GetID(), motion_quality);
+	UpdateMotionQuality();
 }
 
 void ChangeMotionQualityTest::SaveState(StateRecorder &inStream) const
@@ -68,4 +73,6 @@ void ChangeMotionQualityTest::SaveState(StateRecorder &inStream) const
 void ChangeMotionQualityTest::RestoreState(StateRecorder &inStream)
 {
 	inStream.Read(mTime);
+
+	UpdateMotionQuality();
 }

+ 2 - 0
Samples/Tests/General/ChangeMotionQualityTest.h

@@ -20,6 +20,8 @@ public:
 	virtual void	RestoreState(StateRecorder &inStream) override;
 
 private:
+	void			UpdateMotionQuality();
+
 	Body *			mBody = nullptr;
 	float			mTime = 0.0f;
 };

+ 18 - 11
Samples/Tests/General/ChangeMotionTypeTest.cpp

@@ -10,9 +10,9 @@
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Layers.h>
 
-JPH_IMPLEMENT_RTTI_VIRTUAL(ChangeMotionTypeTest) 
-{ 
-	JPH_ADD_BASE_CLASS(ChangeMotionTypeTest, Test) 
+JPH_IMPLEMENT_RTTI_VIRTUAL(ChangeMotionTypeTest)
+{
+	JPH_ADD_BASE_CLASS(ChangeMotionTypeTest, Test)
 }
 
 void ChangeMotionTypeTest::Initialize()
@@ -24,18 +24,15 @@ void ChangeMotionTypeTest::Initialize()
 	BodyCreationSettings settings;
 	settings.SetShape(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)));
 	settings.mPosition = RVec3(0, 10, 0);
-	settings.mMotionType = EMotionType::Static; 
+	settings.mMotionType = EMotionType::Dynamic;
 	settings.mObjectLayer = Layers::MOVING; // Put in moving layer, this will result in some overhead when the body is static
 	settings.mAllowDynamicOrKinematic = true;
 	mBody = mBodyInterface->CreateBody(settings);
-	mBodyInterface->AddBody(mBody->GetID(), EActivation::DontActivate);
+	mBodyInterface->AddBody(mBody->GetID(), EActivation::Activate);
 }
 
-void ChangeMotionTypeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
-{ 
-	// Increment time
-	mTime += inParams.mDeltaTime;
-
+void ChangeMotionTypeTest::UpdateMotionType()
+{
 	// Calculate desired motion type
 	static const EMotionType cycle[] = { EMotionType::Dynamic, EMotionType::Kinematic, EMotionType::Static, EMotionType::Kinematic, EMotionType::Dynamic, EMotionType::Static };
 	EMotionType motion_type = cycle[int(mTime) % size(cycle)];
@@ -43,9 +40,17 @@ void ChangeMotionTypeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 	// Update motion type and reactivate the body
 	if (motion_type != mBody->GetMotionType())
 		mBodyInterface->SetMotionType(mBody->GetID(), motion_type, EActivation::Activate);
+}
+
+void ChangeMotionTypeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
+{
+	// Increment time
+	mTime += inParams.mDeltaTime;
+
+	UpdateMotionType();
 
 	// Provide kinematic body a target
-	if (motion_type == EMotionType::Kinematic)
+	if (mBody->IsKinematic())
 		mBody->MoveKinematic(RVec3(Sin(mTime), 10, 0), Quat::sRotation(Vec3::sAxisX(), Cos(mTime)), inParams.mDeltaTime);
 }
 
@@ -57,4 +62,6 @@ void ChangeMotionTypeTest::SaveState(StateRecorder &inStream) const
 void ChangeMotionTypeTest::RestoreState(StateRecorder &inStream)
 {
 	inStream.Read(mTime);
+
+	UpdateMotionType();
 }

+ 2 - 0
Samples/Tests/General/ChangeMotionTypeTest.h

@@ -20,6 +20,8 @@ public:
 	virtual void	RestoreState(StateRecorder &inStream) override;
 
 private:
+	void			UpdateMotionType();
+
 	Body *			mBody = nullptr;
 	float			mTime = 0.0f;
 };

+ 7 - 7
Samples/Tests/General/ConveyorBeltTest.cpp

@@ -10,9 +10,9 @@
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Layers.h>
 
-JPH_IMPLEMENT_RTTI_VIRTUAL(ConveyorBeltTest) 
-{ 
-	JPH_ADD_BASE_CLASS(ConveyorBeltTest, Test) 
+JPH_IMPLEMENT_RTTI_VIRTUAL(ConveyorBeltTest)
+{
+	JPH_ADD_BASE_CLASS(ConveyorBeltTest, Test)
 }
 
 void ConveyorBeltTest::Initialize()
@@ -37,7 +37,7 @@ void ConveyorBeltTest::Initialize()
 	for (int i = 0; i <= 10; ++i)
 	{
 		cargo_settings.mPosition = RVec3(-cBeltLength + i * 10.0f, 10.0f, -cBeltLength);
-		cargo_settings.mFriction = 1.0f - 0.1f * i;
+		cargo_settings.mFriction = max(0.0f, 1.0f - 0.1f * i);
 		mBodyInterface->CreateAndAddBody(cargo_settings, EActivation::Activate);
 	}
 
@@ -64,7 +64,7 @@ void ConveyorBeltTest::Initialize()
 	for (int i = 0; i <= 6; ++i)
 	{
 		cargo_settings.mPosition = RVec3(10.0f, 10.0f, -15.0f + 5.0f * i);
-		cargo_settings.mFriction = 1.0f - 0.1f * i;
+		cargo_settings.mFriction = max(0.0f, 1.0f - 0.1f * i);
 		mBodyInterface->CreateAndAddBody(cargo_settings, EActivation::Activate);
 	}
 }
@@ -89,7 +89,7 @@ void ConveyorBeltTest::OnContactAdded(const Body &inBody1, const Body &inBody2,
 	bool body1_angular = inBody1.GetID() == mAngularBelt;
 	bool body2_angular = inBody2.GetID() == mAngularBelt;
 	if (body1_angular || body2_angular)
-	{		
+	{
 		// Determine the world space angular surface velocity of both bodies
 		const Vec3 cLocalSpaceAngularVelocity(0, DegreesToRadians(10.0f), 0);
 		Vec3 body1_angular_surface_velocity = body1_angular? inBody1.GetRotation() * cLocalSpaceAngularVelocity : Vec3::sZero();
@@ -97,7 +97,7 @@ void ConveyorBeltTest::OnContactAdded(const Body &inBody1, const Body &inBody2,
 
 		// Note that the angular velocity is the angular velocity around body 1's center of mass, so we need to add the linear velocity of body 2's center of mass
 		Vec3 body2_linear_surface_velocity = body2_angular? body2_angular_surface_velocity.Cross(Vec3(inBody1.GetCenterOfMassPosition() - inBody2.GetCenterOfMassPosition())) : Vec3::sZero();
-		
+
 		// Calculate the relative angular surface velocity
 		ioSettings.mRelativeSurfaceVelocity = body2_linear_surface_velocity;
 		ioSettings.mRelativeAngularSurfaceVelocity = body2_angular_surface_velocity - body1_angular_surface_velocity;