Skip to content

Commit

Permalink
Reduced amount of body state that is saved by SaveState. (#654)
Browse files Browse the repository at this point in the history
Only the state that is modified by the simulation is saved (just like constraints).
  • Loading branch information
jrouwe authored Aug 5, 2023
1 parent 09b07c9 commit 7ff5042
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 69 deletions.
8 changes: 0 additions & 8 deletions Jolt/Physics/Body/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand Down
16 changes: 5 additions & 11 deletions Jolt/Physics/Body/BodyManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 2 additions & 14 deletions Jolt/Physics/Body/MotionProperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down
10 changes: 5 additions & 5 deletions Jolt/Physics/Constraints/ConstraintManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -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())
{
Expand All @@ -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);
Expand Down
16 changes: 8 additions & 8 deletions Jolt/Physics/PhysicsSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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;
}

Expand Down
6 changes: 4 additions & 2 deletions Jolt/Physics/StateRecorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class JPH_EXPORT StateRecorderFilter
};

/// 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:
Expand All @@ -53,7 +54,8 @@ class JPH_EXPORT StateRecorder : public StreamIn, public StreamOut
/// 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; }

Expand Down
13 changes: 10 additions & 3 deletions Samples/Tests/General/ChangeMotionQualityTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -68,4 +73,6 @@ void ChangeMotionQualityTest::SaveState(StateRecorder &inStream) const
void ChangeMotionQualityTest::RestoreState(StateRecorder &inStream)
{
inStream.Read(mTime);

UpdateMotionQuality();
}
2 changes: 2 additions & 0 deletions Samples/Tests/General/ChangeMotionQualityTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class ChangeMotionQualityTest : public Test
virtual void RestoreState(StateRecorder &inStream) override;

private:
void UpdateMotionQuality();

Body * mBody = nullptr;
float mTime = 0.0f;
};
29 changes: 18 additions & 11 deletions Samples/Tests/General/ChangeMotionTypeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -24,28 +24,33 @@ 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)];

// 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);
}

Expand All @@ -57,4 +62,6 @@ void ChangeMotionTypeTest::SaveState(StateRecorder &inStream) const
void ChangeMotionTypeTest::RestoreState(StateRecorder &inStream)
{
inStream.Read(mTime);

UpdateMotionType();
}
2 changes: 2 additions & 0 deletions Samples/Tests/General/ChangeMotionTypeTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class ChangeMotionTypeTest : public Test
virtual void RestoreState(StateRecorder &inStream) override;

private:
void UpdateMotionType();

Body * mBody = nullptr;
float mTime = 0.0f;
};
14 changes: 7 additions & 7 deletions Samples/Tests/General/ConveyorBeltTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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);
}

Expand All @@ -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);
}
}
Expand All @@ -89,15 +89,15 @@ 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();
Vec3 body2_angular_surface_velocity = body2_angular? inBody2.GetRotation() * cLocalSpaceAngularVelocity : Vec3::sZero();

// 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;
Expand Down

0 comments on commit 7ff5042

Please sign in to comment.