239 lines
11 KiB
C++
239 lines
11 KiB
C++
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <Jolt/Core/Reference.h>
|
|
#include <Jolt/Core/NonCopyable.h>
|
|
#include <Jolt/Core/Result.h>
|
|
#include <Jolt/ObjectStream/SerializableObject.h>
|
|
|
|
JPH_NAMESPACE_BEGIN
|
|
|
|
class BodyID;
|
|
class IslandBuilder;
|
|
class LargeIslandSplitter;
|
|
class BodyManager;
|
|
class StateRecorder;
|
|
class StreamIn;
|
|
class StreamOut;
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
class DebugRenderer;
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
|
/// Enum to identify constraint type
|
|
enum class EConstraintType
|
|
{
|
|
Constraint,
|
|
TwoBodyConstraint,
|
|
};
|
|
|
|
/// Enum to identify constraint sub type
|
|
enum class EConstraintSubType
|
|
{
|
|
Fixed,
|
|
Point,
|
|
Hinge,
|
|
Slider,
|
|
Distance,
|
|
Cone,
|
|
SwingTwist,
|
|
SixDOF,
|
|
Path,
|
|
Vehicle,
|
|
RackAndPinion,
|
|
Gear,
|
|
Pulley,
|
|
|
|
/// User defined constraint types start here
|
|
User1,
|
|
User2,
|
|
User3,
|
|
User4
|
|
};
|
|
|
|
/// Certain constraints support setting them up in local or world space. This governs what is used.
|
|
enum class EConstraintSpace
|
|
{
|
|
LocalToBodyCOM, ///< All constraint properties are specified in local space to center of mass of the bodies that are being constrained (so e.g. 'constraint position 1' will be local to body 1 COM, 'constraint position 2' will be local to body 2 COM). Note that this means you need to subtract Shape::GetCenterOfMass() from positions!
|
|
WorldSpace, ///< All constraint properties are specified in world space
|
|
};
|
|
|
|
/// Class used to store the configuration of a constraint. Allows run-time creation of constraints.
|
|
class JPH_EXPORT ConstraintSettings : public SerializableObject, public RefTarget<ConstraintSettings>
|
|
{
|
|
JPH_DECLARE_SERIALIZABLE_VIRTUAL(JPH_EXPORT, ConstraintSettings)
|
|
|
|
public:
|
|
using ConstraintResult = Result<Ref<ConstraintSettings>>;
|
|
|
|
/// Saves the contents of the constraint settings in binary form to inStream.
|
|
virtual void SaveBinaryState(StreamOut &inStream) const;
|
|
|
|
/// Creates a constraint of the correct type and restores its contents from the binary stream inStream.
|
|
static ConstraintResult sRestoreFromBinaryState(StreamIn &inStream);
|
|
|
|
/// If this constraint is enabled initially. Use Constraint::SetEnabled to toggle after creation.
|
|
bool mEnabled = true;
|
|
|
|
/// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly.
|
|
/// Note that if you want a deterministic simulation and you cannot guarantee the order in which constraints are added/removed, you can make the priority for all constraints unique to get a deterministic ordering.
|
|
uint32 mConstraintPriority = 0;
|
|
|
|
/// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island.
|
|
uint mNumVelocityStepsOverride = 0;
|
|
|
|
/// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island.
|
|
uint mNumPositionStepsOverride = 0;
|
|
|
|
/// Size of constraint when drawing it through the debug renderer
|
|
float mDrawConstraintSize = 1.0f;
|
|
|
|
/// User data value (can be used by application)
|
|
uint64 mUserData = 0;
|
|
|
|
protected:
|
|
/// This function should not be called directly, it is used by sRestoreFromBinaryState.
|
|
virtual void RestoreBinaryState(StreamIn &inStream);
|
|
};
|
|
|
|
/// Base class for all physics constraints. A constraint removes one or more degrees of freedom for a rigid body.
|
|
class JPH_EXPORT Constraint : public RefTarget<Constraint>, public NonCopyable
|
|
{
|
|
public:
|
|
JPH_OVERRIDE_NEW_DELETE
|
|
|
|
/// Constructor
|
|
explicit Constraint(const ConstraintSettings &inSettings) :
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
mDrawConstraintSize(inSettings.mDrawConstraintSize),
|
|
#endif // JPH_DEBUG_RENDERER
|
|
mConstraintPriority(inSettings.mConstraintPriority),
|
|
mNumVelocityStepsOverride(uint8(inSettings.mNumVelocityStepsOverride)),
|
|
mNumPositionStepsOverride(uint8(inSettings.mNumPositionStepsOverride)),
|
|
mEnabled(inSettings.mEnabled),
|
|
mUserData(inSettings.mUserData)
|
|
{
|
|
JPH_ASSERT(inSettings.mNumVelocityStepsOverride < 256);
|
|
JPH_ASSERT(inSettings.mNumPositionStepsOverride < 256);
|
|
}
|
|
|
|
/// Virtual destructor
|
|
virtual ~Constraint() = default;
|
|
|
|
/// Get the type of a constraint
|
|
virtual EConstraintType GetType() const { return EConstraintType::Constraint; }
|
|
|
|
/// Get the sub type of a constraint
|
|
virtual EConstraintSubType GetSubType() const = 0;
|
|
|
|
/// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly.
|
|
/// Note that if you want a deterministic simulation and you cannot guarantee the order in which constraints are added/removed, you can make the priority for all constraints unique to get a deterministic ordering.
|
|
uint32 GetConstraintPriority() const { return mConstraintPriority; }
|
|
void SetConstraintPriority(uint32 inPriority) { mConstraintPriority = inPriority; }
|
|
|
|
/// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island.
|
|
void SetNumVelocityStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumVelocityStepsOverride = uint8(inN); }
|
|
uint GetNumVelocityStepsOverride() const { return mNumVelocityStepsOverride; }
|
|
|
|
/// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island.
|
|
void SetNumPositionStepsOverride(uint inN) { JPH_ASSERT(inN < 256); mNumPositionStepsOverride = uint8(inN); }
|
|
uint GetNumPositionStepsOverride() const { return mNumPositionStepsOverride; }
|
|
|
|
/// Enable / disable this constraint. This can e.g. be used to implement a breakable constraint by detecting that the constraint impulse
|
|
/// (see e.g. PointConstraint::GetTotalLambdaPosition) went over a certain limit and then disabling the constraint.
|
|
/// Note that although a disabled constraint will not affect the simulation in any way anymore, it does incur some processing overhead.
|
|
/// Alternatively you can remove a constraint from the constraint manager (which may be more costly if you want to disable the constraint for a short while).
|
|
void SetEnabled(bool inEnabled) { mEnabled = inEnabled; }
|
|
|
|
/// Test if a constraint is enabled.
|
|
bool GetEnabled() const { return mEnabled; }
|
|
|
|
/// Access to the user data, can be used for anything by the application
|
|
uint64 GetUserData() const { return mUserData; }
|
|
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;
|
|
|
|
/// Notify the system that the configuration of the bodies and/or constraint has changed enough so that the warm start impulses should not be applied the next frame.
|
|
/// You can use this function for example when repositioning a ragdoll through Ragdoll::SetPose in such a way that the orientation of the bodies completely changes so that
|
|
/// the previous frame impulses are no longer a good approximation of what the impulses will be in the next frame. Calling this function when there are no big changes
|
|
/// will result in the constraints being much 'softer' than usual so they are more easily violated (e.g. a long chain of bodies might sag a bit if you call this every frame).
|
|
virtual void ResetWarmStart() = 0;
|
|
|
|
///@name Solver interface
|
|
///@{
|
|
virtual bool IsActive() const { return mEnabled; }
|
|
virtual void SetupVelocityConstraint(float inDeltaTime) = 0;
|
|
virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) = 0;
|
|
virtual bool SolveVelocityConstraint(float inDeltaTime) = 0;
|
|
virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) = 0;
|
|
///@}
|
|
|
|
/// Link bodies that are connected by this constraint in the island builder
|
|
virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) = 0;
|
|
|
|
/// Link bodies that are connected by this constraint in the same split. Returns the split index.
|
|
virtual uint BuildIslandSplits(LargeIslandSplitter &ioSplitter) const = 0;
|
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
// Drawing interface
|
|
virtual void DrawConstraint(DebugRenderer *inRenderer) const = 0;
|
|
virtual void DrawConstraintLimits([[maybe_unused]] DebugRenderer *inRenderer) const { }
|
|
virtual void DrawConstraintReferenceFrame([[maybe_unused]] DebugRenderer *inRenderer) const { }
|
|
|
|
/// Size of constraint when drawing it through the debug renderer
|
|
float GetDrawConstraintSize() const { return mDrawConstraintSize; }
|
|
void SetDrawConstraintSize(float inSize) { mDrawConstraintSize = inSize; }
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
|
/// Saving state for replay
|
|
virtual void SaveState(StateRecorder &inStream) const;
|
|
|
|
/// Restoring state for replay
|
|
virtual void RestoreState(StateRecorder &inStream);
|
|
|
|
/// Debug function to convert a constraint to its settings, note that this will not save to which bodies the constraint is connected to
|
|
virtual Ref<ConstraintSettings> GetConstraintSettings() const = 0;
|
|
|
|
protected:
|
|
/// Helper function to copy settings back to constraint settings for this base class
|
|
void ToConstraintSettings(ConstraintSettings &outSettings) const;
|
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
/// Size of constraint when drawing it through the debug renderer
|
|
float mDrawConstraintSize;
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
|
private:
|
|
friend class ConstraintManager;
|
|
|
|
/// Index that indicates this constraint is not in the constraint manager
|
|
static constexpr uint32 cInvalidConstraintIndex = 0xffffffff;
|
|
|
|
/// Index in the mConstraints list of the ConstraintManager for easy finding
|
|
uint32 mConstraintIndex = cInvalidConstraintIndex;
|
|
|
|
/// Priority of the constraint when solving. Higher numbers have are more likely to be solved correctly.
|
|
uint32 mConstraintPriority = 0;
|
|
|
|
/// Used only when the constraint is active. Override for the number of solver velocity iterations to run, 0 means use the default in PhysicsSettings::mNumVelocitySteps. The number of iterations to use is the max of all contacts and constraints in the island.
|
|
uint8 mNumVelocityStepsOverride = 0;
|
|
|
|
/// Used only when the constraint is active. Override for the number of solver position iterations to run, 0 means use the default in PhysicsSettings::mNumPositionSteps. The number of iterations to use is the max of all contacts and constraints in the island.
|
|
uint8 mNumPositionStepsOverride = 0;
|
|
|
|
/// If this constraint is currently enabled
|
|
bool mEnabled = true;
|
|
|
|
/// User data value (can be used by application)
|
|
uint64 mUserData;
|
|
};
|
|
|
|
JPH_NAMESPACE_END
|