浏览代码

Implemented NotifyShapeChange to notify constraint of COM changes (#506)

Added example test to show how it is used.
Jorrit Rouwe 2 年之前
父节点
当前提交
a37d60e13c
共有 29 个文件被更改,包括 267 次插入0 次删除
  1. 1 0
      Jolt/Physics/Collision/Shape/MutableCompoundShape.h
  2. 8 0
      Jolt/Physics/Constraints/ConeConstraint.cpp
  3. 1 0
      Jolt/Physics/Constraints/ConeConstraint.h
  4. 7 0
      Jolt/Physics/Constraints/Constraint.h
  5. 8 0
      Jolt/Physics/Constraints/DistanceConstraint.cpp
  6. 1 0
      Jolt/Physics/Constraints/DistanceConstraint.h
  7. 8 0
      Jolt/Physics/Constraints/FixedConstraint.cpp
  8. 1 0
      Jolt/Physics/Constraints/FixedConstraint.h
  9. 1 0
      Jolt/Physics/Constraints/GearConstraint.h
  10. 8 0
      Jolt/Physics/Constraints/HingeConstraint.cpp
  11. 1 0
      Jolt/Physics/Constraints/HingeConstraint.h
  12. 8 0
      Jolt/Physics/Constraints/PathConstraint.cpp
  13. 1 0
      Jolt/Physics/Constraints/PathConstraint.h
  14. 8 0
      Jolt/Physics/Constraints/PointConstraint.cpp
  15. 1 0
      Jolt/Physics/Constraints/PointConstraint.h
  16. 8 0
      Jolt/Physics/Constraints/PulleyConstraint.cpp
  17. 1 0
      Jolt/Physics/Constraints/PulleyConstraint.h
  18. 1 0
      Jolt/Physics/Constraints/RackAndPinionConstraint.h
  19. 8 0
      Jolt/Physics/Constraints/SixDOFConstraint.cpp
  20. 1 0
      Jolt/Physics/Constraints/SixDOFConstraint.h
  21. 8 0
      Jolt/Physics/Constraints/SliderConstraint.cpp
  22. 1 0
      Jolt/Physics/Constraints/SliderConstraint.h
  23. 8 0
      Jolt/Physics/Constraints/SwingTwistConstraint.cpp
  24. 1 0
      Jolt/Physics/Constraints/SwingTwistConstraint.h
  25. 1 0
      Jolt/Physics/Vehicle/VehicleConstraint.h
  26. 2 0
      Samples/Samples.cmake
  27. 2 0
      Samples/SamplesApp.cpp
  28. 130 0
      Samples/Tests/Constraints/ConstraintVsCOMChangeTest.cpp
  29. 32 0
      Samples/Tests/Constraints/ConstraintVsCOMChangeTest.h

+ 1 - 0
Jolt/Physics/Collision/Shape/MutableCompoundShape.h

@@ -85,6 +85,7 @@ public:
 
 
 	/// Recalculate the center of mass and shift all objects so they're centered around it 
 	/// Recalculate the center of mass and shift all objects so they're centered around it 
 	/// (this needs to be done of dynamic bodies and if the center of mass changes significantly due to adding / removing / repositioning sub shapes or else the simulation will look unnatural)
 	/// (this needs to be done of dynamic bodies and if the center of mass changes significantly due to adding / removing / repositioning sub shapes or else the simulation will look unnatural)
+	/// Note that after adjusting the center of mass of an object you need to call BodyInterface::NotifyShapeChanged and Constraint::NotifyShapeChanged on the relevant bodies / constraints.
 	void							AdjustCenterOfMass();
 	void							AdjustCenterOfMass();
 
 
 	///@}
 	///@}

+ 8 - 0
Jolt/Physics/Constraints/ConeConstraint.cpp

@@ -89,6 +89,14 @@ ConeConstraint::ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstrain
 	}
 	}
 }
 }
 
 
+void ConeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 void ConeConstraint::CalculateRotationConstraintProperties(float inDeltaTime, Mat44Arg inRotation1, Mat44Arg inRotation2)
 void ConeConstraint::CalculateRotationConstraintProperties(float inDeltaTime, Mat44Arg inRotation1, Mat44Arg inRotation2)
 {
 {
 	// Rotation is along the cross product of both twist axis
 	// Rotation is along the cross product of both twist axis

+ 1 - 0
Jolt/Physics/Constraints/ConeConstraint.h

@@ -76,6 +76,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override					{ return EConstraintSubType::Cone; }
 	virtual EConstraintSubType	GetSubType() const override					{ return EConstraintSubType::Cone; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 7 - 0
Jolt/Physics/Constraints/Constraint.h

@@ -11,6 +11,7 @@
 
 
 JPH_NAMESPACE_BEGIN
 JPH_NAMESPACE_BEGIN
 
 
+class BodyID;
 class IslandBuilder;
 class IslandBuilder;
 class LargeIslandSplitter;
 class LargeIslandSplitter;
 class BodyManager;
 class BodyManager;
@@ -141,6 +142,12 @@ public:
 	uint64						GetUserData() const							{ return mUserData; }
 	uint64						GetUserData() const							{ return mUserData; }
 	void						SetUserData(uint64 inUserData)				{ mUserData = inUserData; }
 	void						SetUserData(uint64 inUserData)				{ mUserData = inUserData; }
 
 
+	/// Notify the constraint that the shape of a body has changed and that its center of mass has moved by inDeltaCOM.
+	/// Bodies don't know which constraints are connected to them so the user is responsible for notifying the relevant constraints when a body changes.
+	/// @param inBodyID ID of the body that has changed
+	/// @param inDeltaCOM The delta of the center of mass of the body (shape->GetCenterOfMass() - shape_before_change->GetCenterOfMass())
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) = 0;
+
 	///@name Solver interface
 	///@name Solver interface
 	///@{
 	///@{
 	virtual bool				IsActive() const							{ return mEnabled; }
 	virtual bool				IsActive() const							{ return mEnabled; }

+ 8 - 0
Jolt/Physics/Constraints/DistanceConstraint.cpp

@@ -95,6 +95,14 @@ DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const Dista
 	SetDamping(inSettings.mDamping);
 	SetDamping(inSettings.mDamping);
 }
 }
 
 
+void DistanceConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime)
 void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime)
 {
 {
 	// Update world space positions (the bodies may have moved)
 	// Update world space positions (the bodies may have moved)

+ 1 - 0
Jolt/Physics/Constraints/DistanceConstraint.h

@@ -59,6 +59,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Distance; }
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Distance; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/FixedConstraint.cpp

@@ -107,6 +107,14 @@ FixedConstraint::FixedConstraint(Body &inBody1, Body &inBody2, const FixedConstr
 	}
 	}
 }
 }
 
 
+void FixedConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 void FixedConstraint::SetupVelocityConstraint(float inDeltaTime)
 void FixedConstraint::SetupVelocityConstraint(float inDeltaTime)
 {
 {
 	// Calculate constraint values that don't change when the bodies don't change position
 	// Calculate constraint values that don't change when the bodies don't change position

+ 1 - 0
Jolt/Physics/Constraints/FixedConstraint.h

@@ -55,6 +55,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Fixed; }
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Fixed; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 1 - 0
Jolt/Physics/Constraints/GearConstraint.h

@@ -59,6 +59,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override								{ return EConstraintSubType::Gear; }
 	virtual EConstraintSubType	GetSubType() const override								{ return EConstraintSubType::Gear; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Do nothing */ }
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/HingeConstraint.cpp

@@ -112,6 +112,14 @@ HingeConstraint::HingeConstraint(Body &inBody1, Body &inBody2, const HingeConstr
 	}
 	}
 }
 }
 
 
+void HingeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 float HingeConstraint::GetCurrentAngle() const
 float HingeConstraint::GetCurrentAngle() const
 {
 {
 	// See: CalculateA1AndTheta
 	// See: CalculateA1AndTheta

+ 1 - 0
Jolt/Physics/Constraints/HingeConstraint.h

@@ -65,6 +65,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override								{ return EConstraintSubType::Hinge; }
 	virtual EConstraintSubType	GetSubType() const override								{ return EConstraintSubType::Hinge; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/PathConstraint.cpp

@@ -74,6 +74,14 @@ PathConstraint::PathConstraint(Body &inBody1, Body &inBody2, const PathConstrain
 	SetPath(inSettings.mPath, inSettings.mPathFraction);
 	SetPath(inSettings.mPath, inSettings.mPathFraction);
 }
 }
 
 
+void PathConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mPathToBody1.SetTranslation(mPathToBody1.GetTranslation() - inDeltaCOM);
+	else if (mBody2->GetID() == inBodyID)
+		mPathToBody2.SetTranslation(mPathToBody2.GetTranslation() - inDeltaCOM);
+}
+
 void PathConstraint::SetPath(const PathConstraintPath *inPath, float inPathFraction)
 void PathConstraint::SetPath(const PathConstraintPath *inPath, float inPathFraction)
 {
 {
 	mPath = inPath;
 	mPath = inPath;

+ 1 - 0
Jolt/Physics/Constraints/PathConstraint.h

@@ -74,6 +74,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType		GetSubType() const override								{ return EConstraintSubType::Path; }
 	virtual EConstraintSubType		GetSubType() const override								{ return EConstraintSubType::Path; }
+	virtual void					NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void					SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void					SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void					WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void					WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool					SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool					SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/PointConstraint.cpp

@@ -63,6 +63,14 @@ PointConstraint::PointConstraint(Body &inBody1, Body &inBody2, const PointConstr
 	}
 	}
 }
 }
 
 
+void PointConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 void PointConstraint::SetPoint1(EConstraintSpace inSpace, RVec3Arg inPoint1)
 void PointConstraint::SetPoint1(EConstraintSpace inSpace, RVec3Arg inPoint1)
 {
 {
 	if (inSpace == EConstraintSpace::WorldSpace)
 	if (inSpace == EConstraintSpace::WorldSpace)

+ 1 - 0
Jolt/Physics/Constraints/PointConstraint.h

@@ -47,6 +47,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Point; }
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Point; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/PulleyConstraint.cpp

@@ -100,6 +100,14 @@ PulleyConstraint::PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyCon
 	mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY(); 
 	mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY(); 
 }
 }
 
 
+void PulleyConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 float PulleyConstraint::CalculatePositionsNormalsAndLength()
 float PulleyConstraint::CalculatePositionsNormalsAndLength()
 {
 {
 	// Update world space positions (the bodies may have moved)
 	// Update world space positions (the bodies may have moved)

+ 1 - 0
Jolt/Physics/Constraints/PulleyConstraint.h

@@ -66,6 +66,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Pulley; }
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::Pulley; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 1 - 0
Jolt/Physics/Constraints/RackAndPinionConstraint.h

@@ -61,6 +61,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override												{ return EConstraintSubType::RackAndPinion; }
 	virtual EConstraintSubType	GetSubType() const override												{ return EConstraintSubType::RackAndPinion; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Nothing */ }
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/SixDOFConstraint.cpp

@@ -155,6 +155,14 @@ SixDOFConstraint::SixDOFConstraint(Body &inBody1, Body &inBody2, const SixDOFCon
 	CacheRotationMotorActive();
 	CacheRotationMotorActive();
 }
 }
 
 
+void SixDOFConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 void SixDOFConstraint::SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax)
 void SixDOFConstraint::SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax)
 {
 {
 	mLimitMin[EAxis::TranslationX] = inLimitMin.GetX();
 	mLimitMin[EAxis::TranslationX] = inLimitMin.GetX();

+ 1 - 0
Jolt/Physics/Constraints/SixDOFConstraint.h

@@ -101,6 +101,7 @@ public:
 
 
 	/// Generic interface of a constraint
 	/// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::SixDOF; }
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::SixDOF; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/SliderConstraint.cpp

@@ -159,6 +159,14 @@ SliderConstraint::SliderConstraint(Body &inBody1, Body &inBody2, const SliderCon
 	SetDamping(inSettings.mDamping);
 	SetDamping(inSettings.mDamping);
 }
 }
 
 
+void SliderConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 float SliderConstraint::GetCurrentPosition() const
 float SliderConstraint::GetCurrentPosition() const
 {
 {
 	// See: CalculateR1R2U and CalculateSlidingAxisAndPosition
 	// See: CalculateR1R2U and CalculateSlidingAxisAndPosition

+ 1 - 0
Jolt/Physics/Constraints/SliderConstraint.h

@@ -76,6 +76,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override								{ return EConstraintSubType::Slider; }
 	virtual EConstraintSubType	GetSubType() const override								{ return EConstraintSubType::Slider; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 8 - 0
Jolt/Physics/Constraints/SwingTwistConstraint.cpp

@@ -124,6 +124,14 @@ SwingTwistConstraint::SwingTwistConstraint(Body &inBody1, Body &inBody2, const S
 	UpdateLimits();
 	UpdateLimits();
 }
 }
 
 
+void SwingTwistConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
+{
+	if (mBody1->GetID() == inBodyID)
+		mLocalSpacePosition1 -= inDeltaCOM;
+	else if (mBody2->GetID() == inBodyID)
+		mLocalSpacePosition2 -= inDeltaCOM;
+}
+
 Quat SwingTwistConstraint::GetRotationInConstraintSpace() const
 Quat SwingTwistConstraint::GetRotationInConstraintSpace() const
 {
 {
 	// Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform())
 	// Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform())

+ 1 - 0
Jolt/Physics/Constraints/SwingTwistConstraint.h

@@ -74,6 +74,7 @@ public:
 
 
 	///@name Generic interface of a constraint
 	///@name Generic interface of a constraint
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::SwingTwist; }
 	virtual EConstraintSubType	GetSubType() const override									{ return EConstraintSubType::SwingTwist; }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 1 - 0
Jolt/Physics/Vehicle/VehicleConstraint.h

@@ -125,6 +125,7 @@ public:
 
 
 	// Generic interface of a constraint
 	// Generic interface of a constraint
 	virtual bool				IsActive() const override					{ return mIsActive && Constraint::IsActive(); }
 	virtual bool				IsActive() const override					{ return mIsActive && Constraint::IsActive(); }
+	virtual void				NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM) override { /* Do nothing */ }
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				SetupVelocityConstraint(float inDeltaTime) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual void				WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;
 	virtual bool				SolveVelocityConstraint(float inDeltaTime) override;

+ 2 - 0
Samples/Samples.cmake

@@ -25,6 +25,8 @@ set(SAMPLES_SRC_FILES
 	${SAMPLES_ROOT}/Tests/Constraints/ConeConstraintTest.h
 	${SAMPLES_ROOT}/Tests/Constraints/ConeConstraintTest.h
 	${SAMPLES_ROOT}/Tests/Constraints/ConstraintSingularityTest.cpp
 	${SAMPLES_ROOT}/Tests/Constraints/ConstraintSingularityTest.cpp
 	${SAMPLES_ROOT}/Tests/Constraints/ConstraintSingularityTest.h
 	${SAMPLES_ROOT}/Tests/Constraints/ConstraintSingularityTest.h
+	${SAMPLES_ROOT}/Tests/Constraints/ConstraintVsCOMChangeTest.cpp
+	${SAMPLES_ROOT}/Tests/Constraints/ConstraintVsCOMChangeTest.h
 	${SAMPLES_ROOT}/Tests/Constraints/DistanceConstraintTest.cpp
 	${SAMPLES_ROOT}/Tests/Constraints/DistanceConstraintTest.cpp
 	${SAMPLES_ROOT}/Tests/Constraints/DistanceConstraintTest.h
 	${SAMPLES_ROOT}/Tests/Constraints/DistanceConstraintTest.h
 	${SAMPLES_ROOT}/Tests/Constraints/FixedConstraintTest.cpp
 	${SAMPLES_ROOT}/Tests/Constraints/FixedConstraintTest.cpp

+ 2 - 0
Samples/SamplesApp.cpp

@@ -149,6 +149,7 @@ JPH_DECLARE_RTTI_FOR_FACTORY(PathConstraintTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(RackAndPinionConstraintTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(RackAndPinionConstraintTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(GearConstraintTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(GearConstraintTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(PulleyConstraintTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(PulleyConstraintTest)
+JPH_DECLARE_RTTI_FOR_FACTORY(ConstraintVsCOMChangeTest)
 
 
 static TestNameAndRTTI sConstraintTests[] =
 static TestNameAndRTTI sConstraintTests[] =
 {
 {
@@ -170,6 +171,7 @@ static TestNameAndRTTI sConstraintTests[] =
 	{ "Pulley Constraint",					JPH_RTTI(PulleyConstraintTest) },
 	{ "Pulley Constraint",					JPH_RTTI(PulleyConstraintTest) },
 	{ "Spring",								JPH_RTTI(SpringTest) },
 	{ "Spring",								JPH_RTTI(SpringTest) },
 	{ "Constraint Singularity",				JPH_RTTI(ConstraintSingularityTest) },
 	{ "Constraint Singularity",				JPH_RTTI(ConstraintSingularityTest) },
+	{ "Constraint vs Center Of Mass Change",JPH_RTTI(ConstraintVsCOMChangeTest) },
 };
 };
 
 
 JPH_DECLARE_RTTI_FOR_FACTORY(BoxShapeTest)
 JPH_DECLARE_RTTI_FOR_FACTORY(BoxShapeTest)

+ 130 - 0
Samples/Tests/Constraints/ConstraintVsCOMChangeTest.cpp

@@ -0,0 +1,130 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <TestFramework.h>
+
+#include <Tests/Constraints/ConstraintVsCOMChangeTest.h>
+#include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
+#include <Jolt/Physics/Collision/Shape/BoxShape.h>
+#include <Jolt/Physics/Collision/GroupFilterTable.h>
+#include <Jolt/Physics/Constraints/HingeConstraint.h>
+#include <Jolt/Physics/Body/BodyCreationSettings.h>
+#include <Layers.h>
+
+JPH_IMPLEMENT_RTTI_VIRTUAL(ConstraintVsCOMChangeTest) 
+{ 
+	JPH_ADD_BASE_CLASS(ConstraintVsCOMChangeTest, Test) 
+}
+
+void ConstraintVsCOMChangeTest::Initialize()
+{
+	constexpr int cChainLength = 15;
+	constexpr float cMinAngle = DegreesToRadians(-10.0f);
+	constexpr float cMaxAngle = DegreesToRadians(20.0f);
+
+	// Floor
+	CreateFloor();
+
+	// Create box shape
+	mBox = new BoxShape(Vec3::sReplicate(0.5f * cBoxSize));
+
+	// Build a collision group filter that disables collision between adjacent bodies
+	Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
+	for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
+		group_filter->DisableCollision(i, i + 1);
+
+	// Create chain of bodies
+	RVec3 position(0, 25, 0);
+	for (int i = 0; i < cChainLength; ++i)
+	{
+		position += Vec3(cBoxSize, 0, 0);
+		Quat rotation = Quat::sIdentity();
+
+		// Create compound shape specific for this body
+		MutableCompoundShapeSettings compound_shape;
+		compound_shape.SetEmbedded();
+		compound_shape.AddShape(Vec3::sZero(), Quat::sIdentity(), mBox);
+
+		// Create body
+		Body& segment = *mBodyInterface->CreateBody(BodyCreationSettings(&compound_shape, position, rotation, i == 0 ? EMotionType::Static : EMotionType::Dynamic, i == 0 ? Layers::NON_MOVING : Layers::MOVING));
+		segment.SetCollisionGroup(CollisionGroup(group_filter, 0, CollisionGroup::SubGroupID(i)));
+		mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
+
+		if (i > 0)
+		{
+			// Create hinge
+			HingeConstraintSettings settings;
+			settings.mPoint1 = settings.mPoint2 = position + Vec3(-0.5f * cBoxSize, -0.5f * cBoxSize, 0);
+			settings.mHingeAxis1 = settings.mHingeAxis2 = Vec3::sAxisZ();
+			settings.mNormalAxis1 = settings.mNormalAxis2 = Vec3::sAxisX();
+			settings.mLimitsMin = cMinAngle;
+			settings.mLimitsMax = cMaxAngle;
+			Constraint* constraint = settings.Create(*mBodies.back(), segment);
+			mPhysicsSystem->AddConstraint(constraint);
+
+			mConstraints.push_back(constraint);
+		}
+
+		mBodies.push_back(&segment);
+	}
+}
+
+void ConstraintVsCOMChangeTest::PrePhysicsUpdate(const PreUpdateParams& inParams)
+{
+	// Increment time
+	mTime += inParams.mDeltaTime;
+
+	UpdateShapes();
+}
+
+void ConstraintVsCOMChangeTest::SaveState(StateRecorder& inStream) const
+{
+	inStream.Write(mTime);
+}
+
+void ConstraintVsCOMChangeTest::RestoreState(StateRecorder& inStream)
+{
+	inStream.Read(mTime);
+
+	UpdateShapes();
+}
+
+void ConstraintVsCOMChangeTest::UpdateShapes()
+{
+	// Check if we need to change the configuration
+	int num_shapes = int(mTime) & 1? 2 : 1;
+	if (mNumShapes != num_shapes)
+	{
+		mNumShapes = num_shapes;
+
+		// Change the COM of the bodies
+		for (int i = 1; i < (int)mBodies.size(); i += 2)
+		{
+			Body *b = mBodies[i];
+			MutableCompoundShape *s = static_cast<MutableCompoundShape *>(const_cast<Shape *>(b->GetShape()));
+
+			// Remember the center of mass before the change
+			Vec3 prev_com = s->GetCenterOfMass();
+
+			// First remove all existing shapes
+			for (int j = s->GetNumSubShapes() - 1; j >= 0; --j)
+				s->RemoveShape(j);
+
+			// Then create the desired number of shapes
+			for (int j = 0; j < num_shapes; ++j)
+				s->AddShape(Vec3(0, 0, (1.0f + cBoxSize) * j), Quat::sIdentity(), mBox);
+
+			// Update the center of mass to account for the new box configuration
+			s->AdjustCenterOfMass();
+
+			// Notify the physics system that the shape has changed
+			mBodyInterface->NotifyShapeChanged(b->GetID(), prev_com, true, EActivation::Activate);
+
+			// Notify the constraints that the shape has changed (this could be done more efficient as we know which constraints are affected)
+			Vec3 delta_com = s->GetCenterOfMass() - prev_com;
+			for (Constraint *c : mConstraints)
+				c->NotifyShapeChanged(b->GetID(), delta_com);
+		}
+	}
+}

+ 32 - 0
Samples/Tests/Constraints/ConstraintVsCOMChangeTest.h

@@ -0,0 +1,32 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Tests/Test.h>
+
+// This test demonstrates how to notify a constraint that the center of mass of a body changed (constraints store their attachment points in center of mass space).
+class ConstraintVsCOMChangeTest : public Test
+{
+public:
+	JPH_DECLARE_RTTI_VIRTUAL(ConstraintVsCOMChangeTest)
+
+	// See: Test
+	virtual void				Initialize() override;
+	virtual void				PrePhysicsUpdate(const PreUpdateParams& inParams) override;
+	virtual void				SaveState(StateRecorder& inStream) const override;
+	virtual void				RestoreState(StateRecorder& inStream) override;
+
+private:
+	void						UpdateShapes();
+
+	RefConst<Shape>				mBox;
+	Array<Body *>				mBodies;
+	Array<Ref<Constraint>>		mConstraints;
+
+	static constexpr float		cBoxSize = 2.0f;
+
+	float						mTime = 0.0f;
+	int							mNumShapes = -1;
+};