diff --git a/doc/python_api/rst/bge_types/bge.types.KX_ConstraintWrapper.rst b/doc/python_api/rst/bge_types/bge.types.KX_ConstraintWrapper.rst index d9f03395c348..cb7e708501ca 100644 --- a/doc/python_api/rst/bge_types/bge.types.KX_ConstraintWrapper.rst +++ b/doc/python_api/rst/bge_types/bge.types.KX_ConstraintWrapper.rst @@ -141,3 +141,14 @@ base class --- :class:`PyObjectPlus` - :class:`~bge.constraints.VEHICLE_CONSTRAINT` - :class:`~bge.constraints.GENERIC_6DOF_CONSTRAINT` + .. attribute:: breakingThreshold + + The impulse threshold breaking the constraint, if the constraint is broken :data:`enabled` is set to `False`. + + :type: float greater or equal to 0 + + .. attribute:: enabled + + The status of the constraint. Set to `True` to restore a constraint after breaking. + + :type: boolean diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 00892d5f85b3..a48cba3b2891 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -553,6 +553,12 @@ def RIGID_BODY_JOINT(self, context, layout, con): row.prop(con, "use_linked_collision", text="Linked Collision") row.prop(con, "show_pivot", text="Display Pivot") + row = layout.row() + row.prop(con, "use_breaking") + row = row.row() + row.active = con.use_breaking + row.prop(con, "breaking_threshold") + split = layout.split() col = split.column(align=True) diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index ca774864e95b..0dcbc31027cb 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -317,8 +317,7 @@ typedef struct bRigidBodyJointConstraint { float extraFz; short flag; short pad; - short pad1; - short pad2; + float breaking; } bRigidBodyJointConstraint; /* Clamp-To Constraint */ @@ -845,6 +844,7 @@ typedef enum eObjectSolver_Flags { /* Rigid-Body Constraint */ #define CONSTRAINT_DRAW_PIVOT 0x40 #define CONSTRAINT_DISABLE_LINKED_COLLISION 0x80 +#define CONSTRAINT_USE_BREAKING 0x100 /* ObjectSolver Constraint -> flag */ typedef enum eStretchTo_Flags { diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index de1a0f24c31b..23505d63a75c 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -1590,6 +1590,15 @@ static void rna_def_constraint_rigid_body_joint(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", 32); RNA_def_property_ui_text(prop, "Angular Z Limit", "Use minimum/maximum Z angular limit"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + + prop = RNA_def_property(srna, "use_breaking", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_USE_BREAKING); + RNA_def_property_ui_text(prop, "Use Breaking", "Allow breaking on high impulse"); + + prop = RNA_def_property(srna, "breaking_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "breaking"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Breaking Impulse Threshold", "Break on impulse greater than threshold"); } static void rna_def_constraint_clamp_to(BlenderRNA *brna) diff --git a/source/gameengine/Ketsji/KX_ConstraintWrapper.cpp b/source/gameengine/Ketsji/KX_ConstraintWrapper.cpp index 8b60d130339a..2fa897499712 100644 --- a/source/gameengine/Ketsji/KX_ConstraintWrapper.cpp +++ b/source/gameengine/Ketsji/KX_ConstraintWrapper.cpp @@ -113,6 +113,8 @@ PyMethodDef KX_ConstraintWrapper::Methods[] = { PyAttributeDef KX_ConstraintWrapper::Attributes[] = { KX_PYATTRIBUTE_RO_FUNCTION("constraint_id", KX_ConstraintWrapper, pyattr_get_constraintId), KX_PYATTRIBUTE_RO_FUNCTION("constraint_type", KX_ConstraintWrapper, pyattr_get_constraintType), + KX_PYATTRIBUTE_RW_FUNCTION("breakingThreshold", KX_ConstraintWrapper, pyattr_get_breakingThreshold, pyattr_set_breakingThreshold), + KX_PYATTRIBUTE_RW_FUNCTION("enabled", KX_ConstraintWrapper, pyattr_get_enabled, pyattr_set_enabled), KX_PYATTRIBUTE_NULL //Sentinel }; @@ -128,4 +130,44 @@ PyObject *KX_ConstraintWrapper::pyattr_get_constraintType(PyObjectPlus *self_v, return PyLong_FromLong(self->m_constraint->GetType()); } +PyObject *KX_ConstraintWrapper::pyattr_get_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_ConstraintWrapper *self = static_cast(self_v); + return PyFloat_FromDouble(self->m_constraint->GetBreakingThreshold()); +} + +int KX_ConstraintWrapper::pyattr_set_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_ConstraintWrapper *self = static_cast(self_v); + float val = PyFloat_AsDouble(value); + + if (val == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "constraint.%s = float: KX_ConstraintWrapper, expected a float", attrdef->m_name.c_str()); + return PY_SET_ATTR_FAIL; + } + + self->m_constraint->SetBreakingThreshold(val); + return PY_SET_ATTR_SUCCESS; +} + +PyObject *KX_ConstraintWrapper::pyattr_get_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_ConstraintWrapper *self = static_cast(self_v); + return PyBool_FromLong(self->m_constraint->GetEnabled()); +} + +int KX_ConstraintWrapper::pyattr_set_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + KX_ConstraintWrapper *self = static_cast(self_v); + bool val = PyObject_IsTrue(value); + + if (val == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "constraint.%s = bool: KX_ConstraintWrapper, expected True or False", attrdef->m_name.c_str()); + return PY_SET_ATTR_FAIL; + } + + self->m_constraint->SetEnabled(val); + return PY_SET_ATTR_SUCCESS; +} + #endif // WITH_PYTHON diff --git a/source/gameengine/Ketsji/KX_ConstraintWrapper.h b/source/gameengine/Ketsji/KX_ConstraintWrapper.h index cd3f624caeec..efdec76e910c 100644 --- a/source/gameengine/Ketsji/KX_ConstraintWrapper.h +++ b/source/gameengine/Ketsji/KX_ConstraintWrapper.h @@ -52,6 +52,10 @@ class KX_ConstraintWrapper : public CValue static PyObject *pyattr_get_constraintId(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); static PyObject *pyattr_get_constraintType(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject *pyattr_get_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); + static PyObject *pyattr_get_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); #endif private: diff --git a/source/gameengine/Physics/Bullet/CcdConstraint.cpp b/source/gameengine/Physics/Bullet/CcdConstraint.cpp index 5e965b507d29..2420b0a448f1 100644 --- a/source/gameengine/Physics/Bullet/CcdConstraint.cpp +++ b/source/gameengine/Physics/Bullet/CcdConstraint.cpp @@ -1,12 +1,15 @@ #include "CcdConstraint.h" +#include "BLI_utildefines.h" + #include "btBulletDynamicsCommon.h" CcdConstraint::CcdConstraint(btTypedConstraint *constraint, bool disableCollision) :m_constraint(constraint), m_disableCollision(disableCollision), - m_enabled(true) + m_active(true) { + BLI_assert(m_constraint); } CcdConstraint::~CcdConstraint() @@ -18,21 +21,34 @@ bool CcdConstraint::GetDisableCollision() const return m_disableCollision; } +bool CcdConstraint::GetActive() const +{ + return m_active; +} + +void CcdConstraint::SetActive(bool active) +{ + m_active = active; +} + bool CcdConstraint::GetEnabled() const { - return m_enabled; + return m_constraint->isEnabled(); } void CcdConstraint::SetEnabled(bool enabled) { - m_enabled = enabled; + m_constraint->setEnabled(enabled); + + // Unsleep objects to enable constraint influence. + if (enabled) { + m_constraint->getRigidBodyA().activate(true); + m_constraint->getRigidBodyB().activate(true); + } } void CcdConstraint::SetParam(int param, float value0, float value1) { - if (!m_constraint) - return; - switch (m_constraint->getUserConstraintType()) { case PHY_GENERIC_6DOF_CONSTRAINT: @@ -140,9 +156,6 @@ void CcdConstraint::SetParam(int param, float value0, float value1) float CcdConstraint::GetParam(int param) { - if (!m_constraint) - return 0.0f; - switch (m_constraint->getUserConstraintType()) { case PHY_GENERIC_6DOF_CONSTRAINT: @@ -178,6 +191,16 @@ float CcdConstraint::GetParam(int param) return 0.0f; } +float CcdConstraint::GetBreakingThreshold() const +{ + return m_constraint->getBreakingImpulseThreshold(); +} + +void CcdConstraint::SetBreakingThreshold(float threshold) +{ + m_constraint->setBreakingImpulseThreshold(threshold); +} + int CcdConstraint::GetIdentifier() const { return m_constraint->getUserConstraintId(); diff --git a/source/gameengine/Physics/Bullet/CcdConstraint.h b/source/gameengine/Physics/Bullet/CcdConstraint.h index 9534c390407a..84a7e32cff47 100644 --- a/source/gameengine/Physics/Bullet/CcdConstraint.h +++ b/source/gameengine/Physics/Bullet/CcdConstraint.h @@ -13,19 +13,25 @@ class CcdConstraint : public PHY_IConstraint /// Disable collision between constrained objects? bool m_disableCollision; /// The constraint is added in dynamic world? - bool m_enabled; + bool m_active; public: CcdConstraint(btTypedConstraint *constraint, bool disableCollision); virtual ~CcdConstraint(); bool GetDisableCollision() const; - bool GetEnabled() const; - void SetEnabled(bool enabled); + bool GetActive() const; + void SetActive(bool active); + + virtual bool GetEnabled() const; + virtual void SetEnabled(bool enabled); virtual void SetParam(int param, float value0, float value1); virtual float GetParam(int param); + virtual float GetBreakingThreshold() const; + virtual void SetBreakingThreshold(float threshold); + virtual int GetIdentifier() const; virtual PHY_ConstraintType GetType() const; }; diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index e6f6f9ec5578..e428d6cb9ed8 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -499,7 +499,7 @@ void CcdPhysicsEnvironment::AddCcdPhysicsController(CcdPhysicsController *ctrl) void CcdPhysicsEnvironment::RemoveConstraint(btTypedConstraint *con, bool free) { CcdConstraint *userData = (CcdConstraint *)con->getUserConstraintPtr(); - if (!userData->GetEnabled()) { + if (!userData->GetActive()) { return; } @@ -508,7 +508,7 @@ void CcdPhysicsEnvironment::RemoveConstraint(btTypedConstraint *con, bool free) rbA.activate(); rbB.activate(); - userData->SetEnabled(false); + userData->SetActive(false); m_dynamicsWorld->removeConstraint(con); if (free) { @@ -539,7 +539,7 @@ void CcdPhysicsEnvironment::RemoveVehicle(WrapperVehicle *vehicle, bool free) void CcdPhysicsEnvironment::RestoreConstraint(CcdPhysicsController *ctrl, btTypedConstraint *con) { CcdConstraint *userData = (CcdConstraint *)con->getUserConstraintPtr(); - if (userData->GetEnabled()) { + if (userData->GetActive()) { return; } @@ -558,7 +558,7 @@ void CcdPhysicsEnvironment::RestoreConstraint(CcdPhysicsController *ctrl, btType // Avoid add constraint if one of the objects are not available. if (IsActiveCcdPhysicsController(other)) { - userData->SetEnabled(true); + userData->SetActive(true); m_dynamicsWorld->addConstraint(con, userData->GetDisableCollision()); } } @@ -3178,6 +3178,10 @@ void CcdPhysicsEnvironment::SetupObjectConstraints(KX_GameObject *obj_src, KX_Ga } dofbit <<= 1; } + + if (dat->flag & CONSTRAINT_USE_BREAKING) { + constraint->SetBreakingThreshold(dat->breaking); + } } CcdCollData::CcdCollData(const btPersistentManifold *manifoldPoint) diff --git a/source/gameengine/Physics/Common/PHY_IConstraint.h b/source/gameengine/Physics/Common/PHY_IConstraint.h index 3b279df4deda..573eae662032 100644 --- a/source/gameengine/Physics/Common/PHY_IConstraint.h +++ b/source/gameengine/Physics/Common/PHY_IConstraint.h @@ -9,9 +9,15 @@ class PHY_IConstraint PHY_IConstraint() = default; virtual ~PHY_IConstraint() = default; + virtual bool GetEnabled() const = 0; + virtual void SetEnabled(bool enabled) = 0; + virtual void SetParam(int param, float value, float value1) = 0; virtual float GetParam(int param) = 0; + virtual float GetBreakingThreshold() const = 0; + virtual void SetBreakingThreshold(float threshold) = 0; + virtual int GetIdentifier() const = 0; virtual PHY_ConstraintType GetType() const = 0; };