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
 	// Only write properties that can change at runtime
 	inStream.Write(mPosition);
 	inStream.Write(mPosition);
 	inStream.Write(mRotation);
 	inStream.Write(mRotation);
-	inStream.Write(mFriction);
-	inStream.Write(mRestitution);
-	mCollisionGroup.SaveBinaryState(inStream);
-	inStream.Write(mMotionType);
 
 
 	if (mMotionProperties != nullptr)
 	if (mMotionProperties != nullptr)
 	{
 	{
@@ -306,10 +302,6 @@ void Body::RestoreState(StateRecorder &inStream)
 {
 {
 	inStream.Read(mPosition);
 	inStream.Read(mPosition);
 	inStream.Read(mRotation);
 	inStream.Read(mRotation);
-	inStream.Read(mFriction);
-	inStream.Read(mRestitution);
-	mCollisionGroup.RestoreBinaryState(inStream);
-	inStream.Read(mMotionType);
 
 
 	if (mMotionProperties != nullptr)
 	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);
 				bodies.push_back(b);
 
 
 		// Write state of bodies
 		// Write state of bodies
-		size_t num_bodies = bodies.size();
+		uint32 num_bodies = (uint32)bodies.size();
 		inStream.Write(num_bodies);
 		inStream.Write(num_bodies);
 		for (const Body *b : bodies)
 		for (const Body *b : bodies)
 		{
 		{
@@ -774,11 +774,11 @@ bool BodyManager::RestoreState(StateRecorder &inStream)
 		if (inStream.IsValidating())
 		if (inStream.IsValidating())
 		{
 		{
 			// Read state of bodies, note this reads it in a way to be consistent with validation
 			// 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)
 			for (const Body *b : mBodies)
 				if (sIsValidBodyPointer(b) && b->IsInBroadPhase())
 				if (sIsValidBodyPointer(b) && b->IsInBroadPhase())
 					++old_num_bodies;
 					++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);
 			inStream.Read(num_bodies);
 			if (num_bodies != old_num_bodies)
 			if (num_bodies != old_num_bodies)
 			{
 			{
@@ -813,11 +813,11 @@ bool BodyManager::RestoreState(StateRecorder &inStream)
 		else
 		else
 		{
 		{
 			// Not validating, we can be a bit more loose, read number of bodies
 			// 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);
 			inStream.Read(num_bodies);
 
 
 			// Iterate over the stored bodies and restore their state
 			// 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;
 				BodyID body_id;
 				inStream.Read(body_id);
 				inStream.Read(body_id);
@@ -858,12 +858,6 @@ bool BodyManager::RestoreState(StateRecorder &inStream)
 			Body *body = TryGetBody(body_id);
 			Body *body = TryGetBody(body_id);
 			RemoveBodyFromActiveBodies(*body);
 			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;
 	return true;

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

@@ -35,9 +35,9 @@ void MotionProperties::SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassP
 		// Set inverse inertia
 		// Set inverse inertia
 		Mat44 rotation;
 		Mat44 rotation;
 		Vec3 diagonal;
 		Vec3 diagonal;
-		if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal) 
+		if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal)
 			&& !diagonal.IsNearZero())
 			&& !diagonal.IsNearZero())
-		{	
+		{
 			mInvInertiaDiagonal = diagonal.Reciprocal();
 			mInvInertiaDiagonal = diagonal.Reciprocal();
 			mInertiaRotation = rotation.GetQuaternion();
 			mInertiaRotation = rotation.GetQuaternion();
 		}
 		}
@@ -127,17 +127,11 @@ void MotionProperties::SaveState(StateRecorder &inStream) const
 	inStream.Write(mAngularVelocity);
 	inStream.Write(mAngularVelocity);
 	inStream.Write(mForce);
 	inStream.Write(mForce);
 	inStream.Write(mTorque);
 	inStream.Write(mTorque);
-	inStream.Write(mLinearDamping);
-	inStream.Write(mAngularDamping);
-	inStream.Write(mMaxLinearVelocity);
-	inStream.Write(mMaxAngularVelocity);
-	inStream.Write(mGravityFactor);
 #ifdef JPH_DOUBLE_PRECISION
 #ifdef JPH_DOUBLE_PRECISION
 	inStream.Write(mSleepTestOffset);
 	inStream.Write(mSleepTestOffset);
 #endif // JPH_DOUBLE_PRECISION
 #endif // JPH_DOUBLE_PRECISION
 	inStream.Write(mSleepTestSpheres);
 	inStream.Write(mSleepTestSpheres);
 	inStream.Write(mSleepTestTimer);
 	inStream.Write(mSleepTestTimer);
-	inStream.Write(mMotionQuality);
 	inStream.Write(mAllowSleeping);
 	inStream.Write(mAllowSleeping);
 }
 }
 
 
@@ -147,17 +141,11 @@ void MotionProperties::RestoreState(StateRecorder &inStream)
 	inStream.Read(mAngularVelocity);
 	inStream.Read(mAngularVelocity);
 	inStream.Read(mForce);
 	inStream.Read(mForce);
 	inStream.Read(mTorque);
 	inStream.Read(mTorque);
-	inStream.Read(mLinearDamping);
-	inStream.Read(mAngularDamping);
-	inStream.Read(mMaxLinearVelocity);
-	inStream.Read(mMaxAngularVelocity);
-	inStream.Read(mGravityFactor);
 #ifdef JPH_DOUBLE_PRECISION
 #ifdef JPH_DOUBLE_PRECISION
 	inStream.Read(mSleepTestOffset);
 	inStream.Read(mSleepTestOffset);
 #endif // JPH_DOUBLE_PRECISION
 #endif // JPH_DOUBLE_PRECISION
 	inStream.Read(mSleepTestSpheres);
 	inStream.Read(mSleepTestSpheres);
 	inStream.Read(mSleepTestTimer);
 	inStream.Read(mSleepTestTimer);
-	inStream.Read(mMotionQuality);
 	inStream.Read(mAllowSleeping);
 	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);
 				constraints.push_back(c);
 
 
 		// Save them
 		// Save them
-		size_t num_constraints = constraints.size();
+		uint32 num_constraints = (uint32)constraints.size();
 		inStream.Write(num_constraints);
 		inStream.Write(num_constraints);
 		for (const Constraint *c : constraints)
 		for (const Constraint *c : constraints)
 		{
 		{
@@ -249,7 +249,7 @@ void ConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFi
 	else
 	else
 	{
 	{
 		// Save all constraints
 		// Save all constraints
-		size_t num_constraints = mConstraints.size();
+		uint32 num_constraints = (uint32)mConstraints.size();
 		inStream.Write(num_constraints);
 		inStream.Write(num_constraints);
 		for (const Ref<Constraint> &c : mConstraints)
 		for (const Ref<Constraint> &c : mConstraints)
 		{
 		{
@@ -266,7 +266,7 @@ bool ConstraintManager::RestoreState(StateRecorder &inStream)
 	if (inStream.IsValidating())
 	if (inStream.IsValidating())
 	{
 	{
 		// Read state of constraints
 		// 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);
 		inStream.Read(num_constraints);
 		if (num_constraints != mConstraints.size())
 		if (num_constraints != mConstraints.size())
 		{
 		{
@@ -288,10 +288,10 @@ bool ConstraintManager::RestoreState(StateRecorder &inStream)
 	else
 	else
 	{
 	{
 		// Not validating, use more flexible reading, read number of constraints
 		// Not validating, use more flexible reading, read number of constraints
-		size_t num_constraints = 0;
+		uint32 num_constraints = 0;
 		inStream.Read(num_constraints);
 		inStream.Read(num_constraints);
 
 
-		for (size_t idx = 0; idx < num_constraints; ++idx)
+		for (uint32 idx = 0; idx < num_constraints; ++idx)
 		{
 		{
 			uint32 constraint_index;
 			uint32 constraint_index;
 			inStream.Read(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))
 		if (!mBodyManager.RestoreState(inStream))
 			return false;
 			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))
 	if (uint8(state) & uint8(EStateRecorderState::Contacts))
@@ -2422,14 +2430,6 @@ bool PhysicsSystem::RestoreState(StateRecorder &inStream)
 			return false;
 			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;
 	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.
 /// 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
 class JPH_EXPORT StateRecorder : public StreamIn, public StreamOut
 {
 {
 public:
 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
 	/// 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
 	/// 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
 	/// 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; }
 	void				SetValidating(bool inValidating)							{ mIsValidating = inValidating; }
 	bool				IsValidating() const										{ return mIsValidating; }
 	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);
 	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)
 void ChangeMotionQualityTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 {
 {
 	// Increment time
 	// Increment time
 	mTime += inParams.mDeltaTime;
 	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
 void ChangeMotionQualityTest::SaveState(StateRecorder &inStream) const
@@ -68,4 +73,6 @@ void ChangeMotionQualityTest::SaveState(StateRecorder &inStream) const
 void ChangeMotionQualityTest::RestoreState(StateRecorder &inStream)
 void ChangeMotionQualityTest::RestoreState(StateRecorder &inStream)
 {
 {
 	inStream.Read(mTime);
 	inStream.Read(mTime);
+
+	UpdateMotionQuality();
 }
 }

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

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

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

@@ -10,9 +10,9 @@
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Layers.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()
 void ChangeMotionTypeTest::Initialize()
@@ -24,18 +24,15 @@ void ChangeMotionTypeTest::Initialize()
 	BodyCreationSettings settings;
 	BodyCreationSettings settings;
 	settings.SetShape(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)));
 	settings.SetShape(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)));
 	settings.mPosition = RVec3(0, 10, 0);
 	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.mObjectLayer = Layers::MOVING; // Put in moving layer, this will result in some overhead when the body is static
 	settings.mAllowDynamicOrKinematic = true;
 	settings.mAllowDynamicOrKinematic = true;
 	mBody = mBodyInterface->CreateBody(settings);
 	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
 	// Calculate desired motion type
 	static const EMotionType cycle[] = { EMotionType::Dynamic, EMotionType::Kinematic, EMotionType::Static, EMotionType::Kinematic, EMotionType::Dynamic, EMotionType::Static };
 	static const EMotionType cycle[] = { EMotionType::Dynamic, EMotionType::Kinematic, EMotionType::Static, EMotionType::Kinematic, EMotionType::Dynamic, EMotionType::Static };
 	EMotionType motion_type = cycle[int(mTime) % size(cycle)];
 	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
 	// Update motion type and reactivate the body
 	if (motion_type != mBody->GetMotionType())
 	if (motion_type != mBody->GetMotionType())
 		mBodyInterface->SetMotionType(mBody->GetID(), motion_type, EActivation::Activate);
 		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
 	// 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);
 		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)
 void ChangeMotionTypeTest::RestoreState(StateRecorder &inStream)
 {
 {
 	inStream.Read(mTime);
 	inStream.Read(mTime);
+
+	UpdateMotionType();
 }
 }

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

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

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

@@ -10,9 +10,9 @@
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Jolt/Physics/Body/BodyCreationSettings.h>
 #include <Layers.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()
 void ConveyorBeltTest::Initialize()
@@ -37,7 +37,7 @@ void ConveyorBeltTest::Initialize()
 	for (int i = 0; i <= 10; ++i)
 	for (int i = 0; i <= 10; ++i)
 	{
 	{
 		cargo_settings.mPosition = RVec3(-cBeltLength + i * 10.0f, 10.0f, -cBeltLength);
 		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);
 		mBodyInterface->CreateAndAddBody(cargo_settings, EActivation::Activate);
 	}
 	}
 
 
@@ -64,7 +64,7 @@ void ConveyorBeltTest::Initialize()
 	for (int i = 0; i <= 6; ++i)
 	for (int i = 0; i <= 6; ++i)
 	{
 	{
 		cargo_settings.mPosition = RVec3(10.0f, 10.0f, -15.0f + 5.0f * 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);
 		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 body1_angular = inBody1.GetID() == mAngularBelt;
 	bool body2_angular = inBody2.GetID() == mAngularBelt;
 	bool body2_angular = inBody2.GetID() == mAngularBelt;
 	if (body1_angular || body2_angular)
 	if (body1_angular || body2_angular)
-	{		
+	{
 		// Determine the world space angular surface velocity of both bodies
 		// Determine the world space angular surface velocity of both bodies
 		const Vec3 cLocalSpaceAngularVelocity(0, DegreesToRadians(10.0f), 0);
 		const Vec3 cLocalSpaceAngularVelocity(0, DegreesToRadians(10.0f), 0);
 		Vec3 body1_angular_surface_velocity = body1_angular? inBody1.GetRotation() * cLocalSpaceAngularVelocity : Vec3::sZero();
 		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
 		// 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();
 		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
 		// Calculate the relative angular surface velocity
 		ioSettings.mRelativeSurfaceVelocity = body2_linear_surface_velocity;
 		ioSettings.mRelativeSurfaceVelocity = body2_linear_surface_velocity;
 		ioSettings.mRelativeAngularSurfaceVelocity = body2_angular_surface_velocity - body1_angular_surface_velocity;
 		ioSettings.mRelativeAngularSurfaceVelocity = body2_angular_surface_velocity - body1_angular_surface_velocity;