Skip to content

Commit

Permalink
UPBGE: Implement physics constraint breaking.
Browse files Browse the repository at this point in the history
Bullet support an attribute for breaking impulse threshold in its
constraints (btTypedConstraint). This attribute allow bullet to
disable the constraint when the impulse is greater than the threshold
specified, when this happen the constraint function isEnabled return
false and no object are influenced by the constraint, but this constraint
still remains in dynamic world.
To renable it, the function setEnabled have to be called with true
as argument. Note that the constrained objects must be unslept.

The threshold and the enabled status are both exposed to user in the
python API under KX_ConstraintWrapper.breakingThreshold and
KX_ConstraintWrapper.enabled.

In UI the threshold is exposed in the rigid body constraint layout,
to modify this value the option "Use Breaking" must be enabled because
internally the threshold is near to infinite and it will request to show
infinite value in UI if there's no option and the user will not be able
to disable temporary breaking without lose his previous value.

Fix a part of issue: #511.
  • Loading branch information
panzergame committed Aug 5, 2017
1 parent 148a5e5 commit e02e974
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 18 deletions.
11 changes: 11 additions & 0 deletions doc/python_api/rst/bge_types/bge.types.KX_ConstraintWrapper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions release/scripts/startup/bl_ui/properties_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions source/blender/makesdna/DNA_constraint_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,7 @@ typedef struct bRigidBodyJointConstraint {
float extraFz;
short flag;
short pad;
short pad1;
short pad2;
float breaking;
} bRigidBodyJointConstraint;

/* Clamp-To Constraint */
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions source/blender/makesrna/intern/rna_constraint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
42 changes: 42 additions & 0 deletions source/gameengine/Ketsji/KX_ConstraintWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand All @@ -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<KX_ConstraintWrapper *>(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<KX_ConstraintWrapper *>(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<KX_ConstraintWrapper *>(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<KX_ConstraintWrapper *>(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
4 changes: 4 additions & 0 deletions source/gameengine/Ketsji/KX_ConstraintWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
41 changes: 32 additions & 9 deletions source/gameengine/Physics/Bullet/CcdConstraint.cpp
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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();
Expand Down
12 changes: 9 additions & 3 deletions source/gameengine/Physics/Bullet/CcdConstraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
12 changes: 8 additions & 4 deletions source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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

Expand All @@ -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());
}
}
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions source/gameengine/Physics/Common/PHY_IConstraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down

0 comments on commit e02e974

Please sign in to comment.