Skip to content

Commit e02e974

Browse files
committed
UPBGE: Implement physics constraint breaking.
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.
1 parent 148a5e5 commit e02e974

File tree

10 files changed

+129
-18
lines changed

10 files changed

+129
-18
lines changed

doc/python_api/rst/bge_types/bge.types.KX_ConstraintWrapper.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,14 @@ base class --- :class:`PyObjectPlus`
141141
- :class:`~bge.constraints.VEHICLE_CONSTRAINT`
142142
- :class:`~bge.constraints.GENERIC_6DOF_CONSTRAINT`
143143

144+
.. attribute:: breakingThreshold
145+
146+
The impulse threshold breaking the constraint, if the constraint is broken :data:`enabled` is set to `False`.
147+
148+
:type: float greater or equal to 0
149+
150+
.. attribute:: enabled
151+
152+
The status of the constraint. Set to `True` to restore a constraint after breaking.
153+
154+
:type: boolean

release/scripts/startup/bl_ui/properties_constraint.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,12 @@ def RIGID_BODY_JOINT(self, context, layout, con):
553553
row.prop(con, "use_linked_collision", text="Linked Collision")
554554
row.prop(con, "show_pivot", text="Display Pivot")
555555

556+
row = layout.row()
557+
row.prop(con, "use_breaking")
558+
row = row.row()
559+
row.active = con.use_breaking
560+
row.prop(con, "breaking_threshold")
561+
556562
split = layout.split()
557563

558564
col = split.column(align=True)

source/blender/makesdna/DNA_constraint_types.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,7 @@ typedef struct bRigidBodyJointConstraint {
317317
float extraFz;
318318
short flag;
319319
short pad;
320-
short pad1;
321-
short pad2;
320+
float breaking;
322321
} bRigidBodyJointConstraint;
323322

324323
/* Clamp-To Constraint */
@@ -845,6 +844,7 @@ typedef enum eObjectSolver_Flags {
845844
/* Rigid-Body Constraint */
846845
#define CONSTRAINT_DRAW_PIVOT 0x40
847846
#define CONSTRAINT_DISABLE_LINKED_COLLISION 0x80
847+
#define CONSTRAINT_USE_BREAKING 0x100
848848

849849
/* ObjectSolver Constraint -> flag */
850850
typedef enum eStretchTo_Flags {

source/blender/makesrna/intern/rna_constraint.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,15 @@ static void rna_def_constraint_rigid_body_joint(BlenderRNA *brna)
15901590
RNA_def_property_boolean_sdna(prop, NULL, "flag", 32);
15911591
RNA_def_property_ui_text(prop, "Angular Z Limit", "Use minimum/maximum Z angular limit");
15921592
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
1593+
1594+
prop = RNA_def_property(srna, "use_breaking", PROP_BOOLEAN, PROP_NONE);
1595+
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_USE_BREAKING);
1596+
RNA_def_property_ui_text(prop, "Use Breaking", "Allow breaking on high impulse");
1597+
1598+
prop = RNA_def_property(srna, "breaking_threshold", PROP_FLOAT, PROP_NONE);
1599+
RNA_def_property_float_sdna(prop, NULL, "breaking");
1600+
RNA_def_property_range(prop, 0.0f, FLT_MAX);
1601+
RNA_def_property_ui_text(prop, "Breaking Impulse Threshold", "Break on impulse greater than threshold");
15931602
}
15941603

15951604
static void rna_def_constraint_clamp_to(BlenderRNA *brna)

source/gameengine/Ketsji/KX_ConstraintWrapper.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ PyMethodDef KX_ConstraintWrapper::Methods[] = {
113113
PyAttributeDef KX_ConstraintWrapper::Attributes[] = {
114114
KX_PYATTRIBUTE_RO_FUNCTION("constraint_id", KX_ConstraintWrapper, pyattr_get_constraintId),
115115
KX_PYATTRIBUTE_RO_FUNCTION("constraint_type", KX_ConstraintWrapper, pyattr_get_constraintType),
116+
KX_PYATTRIBUTE_RW_FUNCTION("breakingThreshold", KX_ConstraintWrapper, pyattr_get_breakingThreshold, pyattr_set_breakingThreshold),
117+
KX_PYATTRIBUTE_RW_FUNCTION("enabled", KX_ConstraintWrapper, pyattr_get_enabled, pyattr_set_enabled),
116118
KX_PYATTRIBUTE_NULL //Sentinel
117119
};
118120

@@ -128,4 +130,44 @@ PyObject *KX_ConstraintWrapper::pyattr_get_constraintType(PyObjectPlus *self_v,
128130
return PyLong_FromLong(self->m_constraint->GetType());
129131
}
130132

133+
PyObject *KX_ConstraintWrapper::pyattr_get_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
134+
{
135+
KX_ConstraintWrapper *self = static_cast<KX_ConstraintWrapper *>(self_v);
136+
return PyFloat_FromDouble(self->m_constraint->GetBreakingThreshold());
137+
}
138+
139+
int KX_ConstraintWrapper::pyattr_set_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
140+
{
141+
KX_ConstraintWrapper *self = static_cast<KX_ConstraintWrapper *>(self_v);
142+
float val = PyFloat_AsDouble(value);
143+
144+
if (val == -1 && PyErr_Occurred()) {
145+
PyErr_Format(PyExc_AttributeError, "constraint.%s = float: KX_ConstraintWrapper, expected a float", attrdef->m_name.c_str());
146+
return PY_SET_ATTR_FAIL;
147+
}
148+
149+
self->m_constraint->SetBreakingThreshold(val);
150+
return PY_SET_ATTR_SUCCESS;
151+
}
152+
153+
PyObject *KX_ConstraintWrapper::pyattr_get_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
154+
{
155+
KX_ConstraintWrapper *self = static_cast<KX_ConstraintWrapper *>(self_v);
156+
return PyBool_FromLong(self->m_constraint->GetEnabled());
157+
}
158+
159+
int KX_ConstraintWrapper::pyattr_set_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
160+
{
161+
KX_ConstraintWrapper *self = static_cast<KX_ConstraintWrapper *>(self_v);
162+
bool val = PyObject_IsTrue(value);
163+
164+
if (val == -1 && PyErr_Occurred()) {
165+
PyErr_Format(PyExc_AttributeError, "constraint.%s = bool: KX_ConstraintWrapper, expected True or False", attrdef->m_name.c_str());
166+
return PY_SET_ATTR_FAIL;
167+
}
168+
169+
self->m_constraint->SetEnabled(val);
170+
return PY_SET_ATTR_SUCCESS;
171+
}
172+
131173
#endif // WITH_PYTHON

source/gameengine/Ketsji/KX_ConstraintWrapper.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class KX_ConstraintWrapper : public CValue
5252

5353
static PyObject *pyattr_get_constraintId(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
5454
static PyObject *pyattr_get_constraintType(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
55+
static PyObject *pyattr_get_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
56+
static int pyattr_set_breakingThreshold(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
57+
static PyObject *pyattr_get_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
58+
static int pyattr_set_enabled(PyObjectPlus *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
5559
#endif
5660

5761
private:

source/gameengine/Physics/Bullet/CcdConstraint.cpp

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#include "CcdConstraint.h"
22

3+
#include "BLI_utildefines.h"
4+
35
#include "btBulletDynamicsCommon.h"
46

57
CcdConstraint::CcdConstraint(btTypedConstraint *constraint, bool disableCollision)
68
:m_constraint(constraint),
79
m_disableCollision(disableCollision),
8-
m_enabled(true)
10+
m_active(true)
911
{
12+
BLI_assert(m_constraint);
1013
}
1114

1215
CcdConstraint::~CcdConstraint()
@@ -18,21 +21,34 @@ bool CcdConstraint::GetDisableCollision() const
1821
return m_disableCollision;
1922
}
2023

24+
bool CcdConstraint::GetActive() const
25+
{
26+
return m_active;
27+
}
28+
29+
void CcdConstraint::SetActive(bool active)
30+
{
31+
m_active = active;
32+
}
33+
2134
bool CcdConstraint::GetEnabled() const
2235
{
23-
return m_enabled;
36+
return m_constraint->isEnabled();
2437
}
2538

2639
void CcdConstraint::SetEnabled(bool enabled)
2740
{
28-
m_enabled = enabled;
41+
m_constraint->setEnabled(enabled);
42+
43+
// Unsleep objects to enable constraint influence.
44+
if (enabled) {
45+
m_constraint->getRigidBodyA().activate(true);
46+
m_constraint->getRigidBodyB().activate(true);
47+
}
2948
}
3049

3150
void CcdConstraint::SetParam(int param, float value0, float value1)
3251
{
33-
if (!m_constraint)
34-
return;
35-
3652
switch (m_constraint->getUserConstraintType())
3753
{
3854
case PHY_GENERIC_6DOF_CONSTRAINT:
@@ -140,9 +156,6 @@ void CcdConstraint::SetParam(int param, float value0, float value1)
140156

141157
float CcdConstraint::GetParam(int param)
142158
{
143-
if (!m_constraint)
144-
return 0.0f;
145-
146159
switch (m_constraint->getUserConstraintType())
147160
{
148161
case PHY_GENERIC_6DOF_CONSTRAINT:
@@ -178,6 +191,16 @@ float CcdConstraint::GetParam(int param)
178191
return 0.0f;
179192
}
180193

194+
float CcdConstraint::GetBreakingThreshold() const
195+
{
196+
return m_constraint->getBreakingImpulseThreshold();
197+
}
198+
199+
void CcdConstraint::SetBreakingThreshold(float threshold)
200+
{
201+
m_constraint->setBreakingImpulseThreshold(threshold);
202+
}
203+
181204
int CcdConstraint::GetIdentifier() const
182205
{
183206
return m_constraint->getUserConstraintId();

source/gameengine/Physics/Bullet/CcdConstraint.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@ class CcdConstraint : public PHY_IConstraint
1313
/// Disable collision between constrained objects?
1414
bool m_disableCollision;
1515
/// The constraint is added in dynamic world?
16-
bool m_enabled;
16+
bool m_active;
1717

1818
public:
1919
CcdConstraint(btTypedConstraint *constraint, bool disableCollision);
2020
virtual ~CcdConstraint();
2121

2222
bool GetDisableCollision() const;
23-
bool GetEnabled() const;
24-
void SetEnabled(bool enabled);
23+
bool GetActive() const;
24+
void SetActive(bool active);
25+
26+
virtual bool GetEnabled() const;
27+
virtual void SetEnabled(bool enabled);
2528

2629
virtual void SetParam(int param, float value0, float value1);
2730
virtual float GetParam(int param);
2831

32+
virtual float GetBreakingThreshold() const;
33+
virtual void SetBreakingThreshold(float threshold);
34+
2935
virtual int GetIdentifier() const;
3036
virtual PHY_ConstraintType GetType() const;
3137
};

source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ void CcdPhysicsEnvironment::AddCcdPhysicsController(CcdPhysicsController *ctrl)
499499
void CcdPhysicsEnvironment::RemoveConstraint(btTypedConstraint *con, bool free)
500500
{
501501
CcdConstraint *userData = (CcdConstraint *)con->getUserConstraintPtr();
502-
if (!userData->GetEnabled()) {
502+
if (!userData->GetActive()) {
503503
return;
504504
}
505505

@@ -508,7 +508,7 @@ void CcdPhysicsEnvironment::RemoveConstraint(btTypedConstraint *con, bool free)
508508
rbA.activate();
509509
rbB.activate();
510510

511-
userData->SetEnabled(false);
511+
userData->SetActive(false);
512512
m_dynamicsWorld->removeConstraint(con);
513513

514514
if (free) {
@@ -539,7 +539,7 @@ void CcdPhysicsEnvironment::RemoveVehicle(WrapperVehicle *vehicle, bool free)
539539
void CcdPhysicsEnvironment::RestoreConstraint(CcdPhysicsController *ctrl, btTypedConstraint *con)
540540
{
541541
CcdConstraint *userData = (CcdConstraint *)con->getUserConstraintPtr();
542-
if (userData->GetEnabled()) {
542+
if (userData->GetActive()) {
543543
return;
544544
}
545545

@@ -558,7 +558,7 @@ void CcdPhysicsEnvironment::RestoreConstraint(CcdPhysicsController *ctrl, btType
558558

559559
// Avoid add constraint if one of the objects are not available.
560560
if (IsActiveCcdPhysicsController(other)) {
561-
userData->SetEnabled(true);
561+
userData->SetActive(true);
562562
m_dynamicsWorld->addConstraint(con, userData->GetDisableCollision());
563563
}
564564
}
@@ -3178,6 +3178,10 @@ void CcdPhysicsEnvironment::SetupObjectConstraints(KX_GameObject *obj_src, KX_Ga
31783178
}
31793179
dofbit <<= 1;
31803180
}
3181+
3182+
if (dat->flag & CONSTRAINT_USE_BREAKING) {
3183+
constraint->SetBreakingThreshold(dat->breaking);
3184+
}
31813185
}
31823186

31833187
CcdCollData::CcdCollData(const btPersistentManifold *manifoldPoint)

source/gameengine/Physics/Common/PHY_IConstraint.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ class PHY_IConstraint
99
PHY_IConstraint() = default;
1010
virtual ~PHY_IConstraint() = default;
1111

12+
virtual bool GetEnabled() const = 0;
13+
virtual void SetEnabled(bool enabled) = 0;
14+
1215
virtual void SetParam(int param, float value, float value1) = 0;
1316
virtual float GetParam(int param) = 0;
1417

18+
virtual float GetBreakingThreshold() const = 0;
19+
virtual void SetBreakingThreshold(float threshold) = 0;
20+
1521
virtual int GetIdentifier() const = 0;
1622
virtual PHY_ConstraintType GetType() const = 0;
1723
};

0 commit comments

Comments
 (0)