rigidbody: Add rigid body constraints

Constraints connect two rigid bodies.
Depending on which constraint is used different degrees of freedom
are limited, e.g. a hinge constraint only allows the objects to rotate
around a common axis.

Constraints are implemented as individual objects and bahave similar to
rigid bodies in terms of adding/removing/validating.

The position and orientation of the constraint object is the pivot point
of the constraint.

Constraints have their own group in the rigid body world.

To make connecting rigid bodies easier, there is a "Connect" operator that
creates an empty objects with a rigid body constraint connecting the selected
objects to active.

Currently the following constraints are implemented:
* Fixed
* Point
* Hinge
* Slider
* Piston
* Generic

Note: constraint limits aren't animatable yet).
This commit is contained in:
Sergej Reich 2013-01-23 05:56:56 +00:00
parent cdc8ed24bf
commit 47c96081d0
22 changed files with 1260 additions and 7 deletions

@ -188,3 +188,63 @@ class BakeToKeyframes(Operator):
wm = context.window_manager
return wm.invoke_props_dialog(self)
class ConnectRigidBodies(Operator):
'''Connect selected rigid bodies to active'''
bl_idname = "rigidbody.connect"
bl_label = "ConnectRigidBodies"
bl_options = {'REGISTER', 'UNDO'}
con_type = EnumProperty(
name="Type",
description="Type of generated contraint",
items=(('FIXED', "Fixed", "Glues ridig bodies together"),
('POINT', "Point", "Constrains rigid bodies to move aound common pivot point"),
('HINGE', "Hinge", "Restricts rigid body rotation to one axis"),
('SLIDER', "Slider", "Restricts rigid boddy translation to one axis"),
('PISTON', "Piston", "Restricts rigid boddy translation and rotation to one axis"),
('GENERIC', "Generic", "Restricts translation and rotation to specified axes"),
default='FIXED',)
pivot_type = EnumProperty(
name="Location",
description="Constraint pivot location",
items=(('CENTER', "Center", "Pivot location is between the constrained rigid bodies"),
('ACTIVE', "Active", "Pivot location is at the active object position"),
('SELECTED', "Selected", "Pivot location is at the slected object position")),
default='CENTER',)
@classmethod
def poll(cls, context):
obj = bpy.context.object
objs = bpy.context.selected_objects
return (obj and obj.rigid_body and (len(objs) > 1))
def execute(self, context):
objs = bpy.context.selected_objects
ob_act = bpy.context.active_object
for ob in objs:
if ob == ob_act:
continue
if self.pivot_type == 'ACTIVE':
loc = ob_act.location
elif self.pivot_type == 'SELECTED':
loc = ob.location
else:
loc = (ob_act.location + ob.location) / 2
bpy.ops.object.add(type='EMPTY', view_align=False, enter_editmode=False, location=loc)
bpy.ops.rigidbody.constraint_group_add()
con = bpy.context.active_object.rigid_body_constraint
con.type = self.con_type
con.object1 = ob_act
con.object2 = ob
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)

@ -49,6 +49,7 @@ _modules = (
"properties_physics_field",
"properties_physics_fluid",
"properties_physics_rigidbody",
"properties_physics_rigidbody_constraint",
"properties_physics_smoke",
"properties_physics_softbody",
"properties_render",

@ -88,6 +88,11 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
"rigidbody.object_remove",
'MESH_ICOSPHERE') # XXX: need dedicated icon
physics_add_special(self, col, ob.rigid_body_constraint, "Rigid Body Constraint",
"rigidbody.constraint_add",
"rigidbody.constraint_remove",
'CONSTRAINT') # RB_TODO needs better icon
# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc

@ -0,0 +1,215 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.types import Panel
class PHYSICS_PT_rigidbody_constraint_panel():
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "physics"
class PHYSICS_PT_rigid_body_constraint(PHYSICS_PT_rigidbody_constraint_panel, Panel):
bl_label = "Rigid Body Constraint"
@classmethod
def poll(cls, context):
ob = context.object
rd = context.scene.render
return (ob and ob.rigid_body_constraint and (not rd.use_game_engine))
def draw(self, context):
layout = self.layout
ob = context.object
rbc = ob.rigid_body_constraint
if rbc:
layout.prop(rbc, "type")
split = layout.split()
row = split.row()
row.prop(rbc, "enabled")
row.prop(rbc, "disable_collisions")
split = layout.split()
split.prop(rbc, "object1")
split = layout.split()
split.prop(rbc, "object2")
split = layout.split()
col = split.row()
col.prop(rbc, "use_breaking")
sub = col.column()
sub.active = rbc.use_breaking
sub.prop(rbc, "breaking_threshold", text="Threshold")
split = layout.split()
col = split.row()
col.prop(rbc, "override_solver_iterations", text="Override Iterations")
sub = col.split()
sub.active = rbc.override_solver_iterations
sub.prop(rbc, "num_solver_iterations", text="Iterations")
if rbc.type == 'HINGE':
col = layout.column(align=True)
col.label("Limits:")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_ang_z", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_ang_z
sub.prop(rbc, "limit_ang_z_lower", text="Lower")
sub.prop(rbc, "limit_ang_z_upper", text="Upper")
elif rbc.type == 'SLIDER':
col = layout.column(align=True)
col.label("Limits:")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_lin_x", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_lin_x
sub.prop(rbc, "limit_lin_x_lower", text="Lower")
sub.prop(rbc, "limit_lin_x_upper", text="Upper")
elif rbc.type == 'PISTON':
col = layout.column(align=True)
col.label("Limits:")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_lin_x", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_lin_x
sub.prop(rbc, "limit_lin_x_lower", text="Lower")
sub.prop(rbc, "limit_lin_x_upper", text="Upper")
col = layout.column(align=True)
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_ang_x", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_ang_x
sub.prop(rbc, "limit_ang_x_lower", text="Lower")
sub.prop(rbc, "limit_ang_x_upper", text="Upper")
elif rbc.type in {'GENERIC', 'GENERIC_SPRING'}:
col = layout.column(align=True)
col.label("Limits:")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_lin_x", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_lin_x
sub.prop(rbc, "limit_lin_x_lower", text="Lower")
sub.prop(rbc, "limit_lin_x_upper", text="Upper")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_lin_y", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_lin_y
sub.prop(rbc, "limit_lin_y_lower", text="Lower")
sub.prop(rbc, "limit_lin_y_upper", text="Upper")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_lin_z", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_lin_z
sub.prop(rbc, "limit_lin_z_lower", text="Lower")
sub.prop(rbc, "limit_lin_z_upper", text="Upper")
col = layout.column(align=True)
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_ang_x", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_ang_x
sub.prop(rbc, "limit_ang_x_lower", text="Lower")
sub.prop(rbc, "limit_ang_x_upper", text="Upper")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_ang_y", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_ang_y
sub.prop(rbc, "limit_ang_y_lower", text="Lower")
sub.prop(rbc, "limit_ang_y_upper", text="Upper")
row = col.row()
sub = row.row()
sub.scale_x = 0.5
sub.prop(rbc, "use_limit_ang_z", toggle=True)
sub = row.row()
sub.active = rbc.use_limit_ang_z
sub.prop(rbc, "limit_ang_z_lower", text="Lower")
sub.prop(rbc, "limit_ang_z_upper", text="Upper")
if rbc.type == 'GENERIC_SPRING':
col = layout.column(align=True)
col.label("Springs:")
row = col.row()
sub = row.row()
sub.scale_x = 0.1
sub.prop(rbc, "use_spring_x", toggle=True, text="X")
sub = row.row()
sub.active = rbc.use_spring_x
sub.prop(rbc, "spring_stiffness_x", text="Stiffness")
sub.prop(rbc, "spring_damping_x")
row = col.row()
sub = row.row()
sub.scale_x = 0.1
sub.prop(rbc, "use_spring_y", toggle=True, text="Y")
sub = row.row()
sub.active = rbc.use_spring_y
sub.prop(rbc, "spring_stiffness_y", text="Stiffness")
sub.prop(rbc, "spring_damping_y")
row = col.row()
sub = row.row()
sub.scale_x = 0.1
sub.prop(rbc, "use_spring_z", toggle=True, text="Z")
sub = row.row()
sub.active = rbc.use_spring_z
sub.prop(rbc, "spring_stiffness_z", text="Stiffness")
sub.prop(rbc, "spring_damping_z")
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)

@ -133,6 +133,8 @@ class VIEW3D_PT_tools_rigidbody(View3DPanel, Panel):
col.operator("rigidbody.mass_calculate", text="Calculate Mass")
col.operator("rigidbody.object_settings_copy", text="Copy from Active")
col.operator("rigidbody.bake_to_keyframes", text="Bake To Keyframes")
col.label(text="Constraints:")
col.operator("rigidbody.connect", text="Connect")
# ********** default tools for editmode_mesh ****************

@ -388,6 +388,7 @@ void BKE_object_free(Object *ob)
free_partdeflect(ob->pd);
BKE_rigidbody_free_object(ob);
BKE_rigidbody_free_constraint(ob);
if (ob->soft) sbFree(ob->soft);
if (ob->bsoft) bsbFree(ob->bsoft);
@ -1288,9 +1289,10 @@ static Object *object_copy_do(Object *ob, int copy_caches)
}
obn->soft = copy_softbody(ob->soft, copy_caches);
obn->bsoft = copy_bulletsoftbody(ob->bsoft);
obn->rigidbody_object = BKE_rigidbody_copy_object(ob);
obn->rigidbody_constraint = BKE_rigidbody_copy_constraint(ob);
BKE_object_copy_particlesystems(obn, ob);
obn->rigidbody_object = BKE_rigidbody_copy_object(ob);
obn->derivedDeform = NULL;
obn->derivedFinal = NULL;

@ -2777,7 +2777,7 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode)
}
}
if (scene->rigidbody_world && ob->rigidbody_object) {
if (scene->rigidbody_world && (ob->rigidbody_object || ob->rigidbody_constraint)) {
if (ob->rigidbody_object)
ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_RESHAPE;
BKE_ptcache_id_from_rigidbody(&pid, ob, scene->rigidbody_world);

@ -74,13 +74,23 @@
/* Free rigidbody world */
void BKE_rigidbody_free_world(RigidBodyWorld *rbw)
{
GroupObject *go;
/* sanity check */
if (!rbw)
return;
if (rbw->physics_world) {
/* free physics references, we assume that all physics objects in will have been added to the world */
GroupObject *go;
if (rbw->constraints) {
for (go = rbw->constraints->gobject.first; go; go = go->next) {
if (go->ob && go->ob->rigidbody_constraint) {
RigidBodyCon *rbc = go->ob->rigidbody_constraint;
if (rbc->physics_constraint)
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
}
}
}
if (rbw->group) {
for (go = rbw->group->gobject.first; go; go = go->next) {
if (go->ob && go->ob->rigidbody_object) {
@ -134,6 +144,26 @@ void BKE_rigidbody_free_object(Object *ob)
ob->rigidbody_object = NULL;
}
/* Free RigidBody constraint and sim instance */
void BKE_rigidbody_free_constraint(Object *ob)
{
RigidBodyCon *rbc = (ob) ? ob->rigidbody_constraint : NULL;
/* sanity check */
if (rbc == NULL)
return;
/* free physics reference */
if (rbc->physics_constraint) {
RB_constraint_delete(rbc->physics_constraint);
rbc->physics_constraint = NULL;
}
/* free data itself */
MEM_freeN(rbc);
ob->rigidbody_constraint = NULL;
}
/* Copying Methods --------------------- */
/* These just copy the data, clearing out references to physics objects.
@ -161,6 +191,27 @@ RigidBodyOb *BKE_rigidbody_copy_object(Object *ob)
return rboN;
}
RigidBodyCon *BKE_rigidbody_copy_constraint(Object *ob)
{
RigidBodyCon *rbcN = NULL;
if (ob->rigidbody_constraint) {
/* just duplicate the whole struct first (to catch all the settings) */
rbcN = MEM_dupallocN(ob->rigidbody_constraint);
// RB_TODO be more clever about copying constrained objects
/* tag object as needing to be verified */
rbcN->flag |= RBC_FLAG_NEEDS_VALIDATE;
/* clear out all the fields which need to be revalidated later */
rbcN->physics_constraint = NULL;
}
/* return new copy of settings */
return rbcN;
}
/* ************************************** */
/* Setup Utilities - Validate Sim Instances */
@ -381,6 +432,7 @@ void BKE_rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, short re
return;
/* make sure collision shape exists */
/* FIXME we shouldn't always have to rebuild collision shapes when rebuilding objects, but it's needed for constraints to update correctly */
if (rbo->physics_shape == NULL || rebuild)
BKE_rigidbody_validate_sim_shape(ob, true);
@ -428,6 +480,151 @@ void BKE_rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, short re
/* --------------------- */
/* Create physics sim representation of constraint given rigid body constraint settings
* < rebuild: even if an instance already exists, replace it
*/
void BKE_rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, short rebuild)
{
RigidBodyCon *rbc = (ob) ? ob->rigidbody_constraint : NULL;
float loc[3];
float rot[4];
float lin_lower;
float lin_upper;
float ang_lower;
float ang_upper;
/* sanity checks:
* - object should have a rigid body constraint
* - rigid body constraint should have at least one constrained object
*/
if (rbc == NULL) {
return;
}
if (ELEM4(NULL, rbc->ob1, rbc->ob1->rigidbody_object, rbc->ob2, rbc->ob2->rigidbody_object)) {
if (rbc->physics_constraint) {
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
RB_constraint_delete(rbc->physics_constraint);
rbc->physics_constraint = NULL;
}
return;
}
if (rbc->physics_constraint) {
if (rebuild == false)
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
}
if (rbc->physics_constraint == NULL || rebuild) {
rbRigidBody *rb1 = rbc->ob1->rigidbody_object->physics_object;
rbRigidBody *rb2 = rbc->ob2->rigidbody_object->physics_object;
/* remove constraint if it already exists before creating a new one */
if (rbc->physics_constraint) {
RB_constraint_delete(rbc->physics_constraint);
rbc->physics_constraint = NULL;
}
mat4_to_loc_quat(loc, rot, ob->obmat);
if (rb1 && rb2) {
switch (rbc->type) {
case RBC_TYPE_POINT:
rbc->physics_constraint = RB_constraint_new_point(loc, rb1, rb2);
break;
case RBC_TYPE_FIXED:
rbc->physics_constraint = RB_constraint_new_fixed(loc, rot, rb1, rb2);
break;
case RBC_TYPE_HINGE:
rbc->physics_constraint = RB_constraint_new_hinge(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_Z) {
RB_constraint_set_limits_hinge(rbc->physics_constraint, rbc->limit_ang_z_lower, rbc->limit_ang_z_upper);
}
else
RB_constraint_set_limits_hinge(rbc->physics_constraint, 0.0f, -1.0f);
break;
case RBC_TYPE_SLIDER:
rbc->physics_constraint = RB_constraint_new_slider(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X)
RB_constraint_set_limits_slider(rbc->physics_constraint, rbc->limit_lin_x_lower, rbc->limit_lin_x_upper);
else
RB_constraint_set_limits_slider(rbc->physics_constraint, 0.0f, -1.0f);
break;
case RBC_TYPE_PISTON:
rbc->physics_constraint = RB_constraint_new_piston(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X) {
lin_lower = rbc->limit_lin_x_lower;
lin_upper = rbc->limit_lin_x_upper;
}
else {
lin_lower = 0.0f;
lin_upper = -1.0f;
}
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_X) {
ang_lower = rbc->limit_ang_x_lower;
ang_upper = rbc->limit_ang_x_upper;
}
else {
ang_lower = 0.0f;
ang_upper = -1.0f;
}
RB_constraint_set_limits_piston(rbc->physics_constraint, lin_lower, lin_upper, ang_lower, ang_upper);
break;
case RBC_TYPE_6DOF:
rbc->physics_constraint = RB_constraint_new_6dof(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->limit_lin_x_lower, rbc->limit_lin_x_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_X, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_Y)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->limit_lin_y_lower, rbc->limit_lin_y_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Y, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_Z)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->limit_lin_z_lower, rbc->limit_lin_z_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Z, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_X)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->limit_ang_x_lower, rbc->limit_ang_x_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_X, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_Y)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->limit_ang_y_lower, rbc->limit_ang_y_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Y, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_Z)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->limit_ang_z_lower, rbc->limit_ang_z_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Z, 0.0f, -1.0f);
break;
}
}
RB_constraint_set_enabled(rbc->physics_constraint, rbc->flag & RBC_FLAG_ENABLED);
if (rbc->flag & RBC_FLAG_USE_BREAKING)
RB_constraint_set_breaking_threshold(rbc->physics_constraint, rbc->breaking_threshold);
else
RB_constraint_set_breaking_threshold(rbc->physics_constraint, FLT_MAX);
if (rbc->flag & RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS)
RB_constraint_set_solver_iterations(rbc->physics_constraint, rbc->num_solver_iterations);
else
RB_constraint_set_solver_iterations(rbc->physics_constraint, -1);
}
if (rbw && rbw->physics_world && rbc->physics_constraint) {
RB_dworld_add_constraint(rbw->physics_world, rbc->physics_constraint, rbc->flag & RBC_FLAG_DISABLE_COLLISIONS);
}
}
/* --------------------- */
/* Create physics sim world given RigidBody world settings */
// NOTE: this does NOT update object references that the scene uses, in case those aren't ready yet!
void BKE_rigidbody_validate_sim_world(Scene *scene, RigidBodyWorld *rbw, short rebuild)
@ -536,6 +733,55 @@ RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type)
return rbo;
}
/* Add rigid body constraint to the specified object */
RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short type)
{
RigidBodyCon *rbc;
RigidBodyWorld *rbw = scene->rigidbody_world;
/* sanity checks
* - rigidbody world must exist
* - object must exist
* - cannot add constraint if it already exists
*/
if (ob == NULL || (ob->rigidbody_constraint != NULL))
return NULL;
/* create new settings data, and link it up */
rbc = MEM_callocN(sizeof(RigidBodyCon), "RigidBodyCon");
/* set default settings */
rbc->type = type;
rbc->ob1 = NULL;
rbc->ob2 = NULL;
rbc->flag |= RBC_FLAG_ENABLED;
rbc->flag |= RBC_FLAG_DISABLE_COLLISIONS;
rbc->breaking_threshold = 10.0f; /* no good default here, just use 10 for now */
rbc->num_solver_iterations = 10; /* 10 is Bullet default */
rbc->limit_lin_x_lower = -1.0f;
rbc->limit_lin_x_upper = 1.0f;
rbc->limit_lin_y_lower = -1.0f;
rbc->limit_lin_y_upper = 1.0f;
rbc->limit_lin_z_lower = -1.0f;
rbc->limit_lin_z_upper = 1.0f;
rbc->limit_ang_x_lower = -M_PI_4;
rbc->limit_ang_x_upper = M_PI_4;
rbc->limit_ang_y_lower = -M_PI_4;
rbc->limit_ang_y_upper = M_PI_4;
rbc->limit_ang_z_lower = -M_PI_4;
rbc->limit_ang_z_upper = M_PI_4;
/* flag cache as outdated */
BKE_rigidbody_cache_reset(rbw);
/* return this object */
return rbc;
}
/* ************************************** */
/* Utilities API */
@ -555,6 +801,7 @@ void BKE_rigidbody_remove_object(Scene *scene, Object *ob)
{
RigidBodyWorld *rbw = scene->rigidbody_world;
RigidBodyOb *rbo = ob->rigidbody_object;
RigidBodyCon *rbc;
GroupObject *go;
int i;
@ -572,9 +819,46 @@ void BKE_rigidbody_remove_object(Scene *scene, Object *ob)
}
}
}
/* remove object from rigid body constraints */
if (rbw->constraints) {
for (go = rbw->constraints->gobject.first; go; go = go->next) {
Object *obt = go->ob;
if (obt) {
rbc = obt->rigidbody_constraint;
if (rbc->ob1 == ob) {
rbc->ob1 = NULL;
rbc->flag |= RBC_FLAG_NEEDS_VALIDATE;
}
if (rbc->ob2 == ob) {
rbc->ob2 = NULL;
rbc->flag |= RBC_FLAG_NEEDS_VALIDATE;
}
}
}
}
}
/* remove object's settings */
BKE_rigidbody_free_object(ob);
/* flag cache as outdated */
BKE_rigidbody_cache_reset(rbw);
}
void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob)
{
RigidBodyWorld *rbw = scene->rigidbody_world;
RigidBodyCon *rbc = ob->rigidbody_constraint;
if (rbw) {
/* remove from rigidbody world, free object won't do this */
if (rbw && rbw->physics_world && rbc->physics_constraint)
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
}
/* remove object's settings */
BKE_rigidbody_free_constraint(ob);
/* flag cache as outdated */
BKE_rigidbody_cache_reset(rbw);
}
@ -746,6 +1030,40 @@ static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, int r
rigidbody_update_sim_ob(scene, rbw, ob, rbo);
}
}
/* update constraints */
if (rbw->constraints == NULL) /* no constraints, move on */
return;
for (go = rbw->constraints->gobject.first; go; go = go->next) {
Object *ob = go->ob;
if (ob) {
/* validate that we've got valid object set up here... */
RigidBodyCon *rbc = ob->rigidbody_constraint;
/* update transformation matrix of the object so we don't get a frame of lag for simple animations */
BKE_object_where_is_calc(scene, ob);
if (rbc == NULL) {
/* Since this object is included in the group but doesn't have
* constraint settings (perhaps it was added manually), add!
*/
ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, RBC_TYPE_FIXED);
BKE_rigidbody_validate_sim_constraint(rbw, ob, true);
rbc = ob->rigidbody_constraint;
}
else {
/* perform simulation data updates as tagged */
if (rebuild) {
/* World has been rebuilt so rebuild constraint */
BKE_rigidbody_validate_sim_constraint(rbw, ob, true);
}
else if (rbc->flag & RBC_FLAG_NEEDS_VALIDATE) {
BKE_rigidbody_validate_sim_constraint(rbw, ob, false);
}
rbc->flag &= ~RBC_FLAG_NEEDS_VALIDATE;
}
}
}
}
/* Sync rigid body and object transformations */

@ -943,6 +943,9 @@ Base *BKE_scene_base_add(Scene *sce, Object *ob)
void BKE_scene_base_remove(Scene *sce, Base *base)
{
/* remove rigid body constraint from world before removing object */
if (base->object->rigidbody_constraint)
BKE_rigidbody_remove_constraint(sce, base->object);
/* remove rigid body object from world before removing object */
if (base->object->rigidbody_object)
BKE_rigidbody_remove_object(sce, base->object);

@ -4363,6 +4363,11 @@ static void lib_link_object(FileData *fd, Main *main)
lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem);
lib_link_modifiers(fd, ob);
if (ob->rigidbody_constraint) {
ob->rigidbody_constraint->ob1 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob1);
ob->rigidbody_constraint->ob2 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob2);
}
}
}
@ -4797,7 +4802,10 @@ static void direct_link_object(FileData *fd, Object *ob)
rbo->physics_object = NULL;
rbo->physics_shape = NULL;
}
ob->rigidbody_constraint = newdataadr(fd, ob->rigidbody_constraint);
if (ob->rigidbody_constraint)
ob->rigidbody_constraint->physics_constraint = NULL;
link_list(fd, &ob->particlesystem);
direct_link_particlesystems(fd, &ob->particlesystem);
@ -5021,6 +5029,8 @@ static void lib_link_scene(FileData *fd, Main *main)
RigidBodyWorld *rbw = sce->rigidbody_world;
if (rbw->group)
rbw->group = newlibadr(fd, sce->id.lib, rbw->group);
if (rbw->constraints)
rbw->constraints = newlibadr(fd, sce->id.lib, rbw->constraints);
if (rbw->effector_weights)
rbw->effector_weights->group = newlibadr(fd, sce->id.lib, rbw->effector_weights->group);
}
@ -9679,7 +9689,12 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
if (ob->pd && ob->pd->tex)
expand_doit(fd, mainvar, ob->pd->tex);
if (ob->rigidbody_constraint) {
expand_doit(fd, mainvar, ob->rigidbody_constraint->ob1);
expand_doit(fd, mainvar, ob->rigidbody_constraint->ob2);
}
}
static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
@ -9728,6 +9743,7 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
if (sce->rigidbody_world) {
expand_doit(fd, mainvar, sce->rigidbody_world->group);
expand_doit(fd, mainvar, sce->rigidbody_world->constraints);
}
#ifdef DURIAN_CAMERA_SWITCH

@ -1492,7 +1492,10 @@ static void write_objects(WriteData *wd, ListBase *idbase)
// TODO: if any extra data is added to handle duplis, will need separate function then
writestruct(wd, DATA, "RigidBodyOb", 1, ob->rigidbody_object);
}
if (ob->rigidbody_constraint) {
writestruct(wd, DATA, "RigidBodyCon", 1, ob->rigidbody_constraint);
}
write_particlesystems(wd, &ob->particlesystem);
write_modifiers(wd, &ob->modifiers);
}

@ -48,6 +48,10 @@ int PE_poll_view3d(struct bContext *C);
void ED_rigidbody_ob_add(struct wmOperator *op, struct Scene *scene, struct Object *ob, int type);
void ED_rigidbody_ob_remove(struct Scene *scene, struct Object *ob);
/* rigidbody_constraint.c */
void ED_rigidbody_con_add(struct wmOperator *op, struct Scene *scene, struct Object *ob, int type);
void ED_rigidbody_con_remove(struct Scene *scene, struct Object *ob);
/* operators */
void ED_operatortypes_physics(void);
void ED_keymap_physics(struct wmKeyConfig *keyconf);

@ -1701,7 +1701,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
* 2) Rigid Body sim participants MUST always be part of a group...
*/
// XXX: is 2) really a good measure here?
if ((basen->flag & OB_FROMGROUP) || ob->rigidbody_object) {
if ((basen->flag & OB_FROMGROUP) || ob->rigidbody_object || ob->rigidbody_constraint) {
Group *group;
for (group = bmain->group.first; group; group = group->id.next) {
if (object_in_group(ob, group))

@ -44,6 +44,7 @@ set(SRC
physics_fluid.c
physics_ops.c
physics_pointcache.c
rigidbody_constraint.c
rigidbody_object.c
rigidbody_world.c

@ -115,6 +115,11 @@ void RIGIDBODY_OT_objects_remove(struct wmOperatorType *ot);
void RIGIDBODY_OT_shape_change(struct wmOperatorType *ot);
void RIGIDBODY_OT_mass_calculate(struct wmOperatorType *ot);
/* rigidbody_constraint.c */
void RIGIDBODY_OT_constraint_add(struct wmOperatorType *ot);
void RIGIDBODY_OT_constraint_group_add(struct wmOperatorType *ot);
void RIGIDBODY_OT_constraint_remove(struct wmOperatorType *ot);
/*rigidbody_world.c */
void RIGIDBODY_OT_world_add(struct wmOperatorType *ot);
void RIGIDBODY_OT_world_remove(struct wmOperatorType *ot);

@ -96,6 +96,10 @@ static void operatortypes_particle(void)
WM_operatortype_append(RIGIDBODY_OT_shape_change);
WM_operatortype_append(RIGIDBODY_OT_mass_calculate);
WM_operatortype_append(RIGIDBODY_OT_constraint_add);
WM_operatortype_append(RIGIDBODY_OT_constraint_group_add);
WM_operatortype_append(RIGIDBODY_OT_constraint_remove);
WM_operatortype_append(RIGIDBODY_OT_world_add);
WM_operatortype_append(RIGIDBODY_OT_world_remove);
// WM_operatortype_append(RIGIDBODY_OT_world_export);

@ -0,0 +1,230 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2013 Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Sergej Reich
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file rigidbody_constraint.c
* \ingroup editor_physics
* \brief Rigid Body constraint editing operators
*/
#include <stdlib.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_group_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_group.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_rigidbody.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_physics.h"
#include "ED_screen.h"
#include "physics_intern.h"
/* ********************************************** */
/* Helper API's for RigidBody Constraint Editing */
static int ED_operator_rigidbody_con_active_poll(bContext *C)
{
if (ED_operator_object_active_editable(C)) {
Object *ob = CTX_data_active_object(C);
return (ob && ob->rigidbody_constraint);
}
else
return 0;
}
void ED_rigidbody_con_add(wmOperator *op, Scene *scene, Object *ob, int type)
{
/* check that object doesn't already have a constraint */
if (ob->rigidbody_constraint) {
BKE_reportf(op->reports, RPT_INFO, "Object '%s' already has a Rigid Body Constraint", ob->id.name + 2);
return;
}
/* make rigidbody constraint settings */
ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, type);
ob->rigidbody_constraint->flag |= RBC_FLAG_NEEDS_VALIDATE;
}
void ED_rigidbody_con_remove(Scene *scene, Object *ob)
{
BKE_rigidbody_remove_constraint(scene, ob);
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
}
/* ********************************************** */
/* Active Object Add/Remove Operators */
/* ************ Add Rigid Body Constraint ************** */
static int rigidbody_con_add_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = (scene) ? OBACT : NULL;
int type = RNA_enum_get(op->ptr, "type");
/* apply to active object */
ED_rigidbody_con_add(op, scene, ob, type);
/* send updates */
DAG_ids_flush_update(CTX_data_main(C), 0);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
/* done */
return OPERATOR_FINISHED;
}
void RIGIDBODY_OT_constraint_add(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_constraint_add";
ot->name = "Add Rigid Body Constraint";
ot->description = "Add Rigid Body Constraint to active object";
/* callbacks */
ot->exec = rigidbody_con_add_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_con_type_items, RBC_TYPE_FIXED, "Rigid Body Constraint Type", "");
}
static int rigidbody_con_group_add_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
Object *ob = (scene) ? OBACT : NULL;
int type = RNA_enum_get(op->ptr, "type");
/* sanity checks */
if (ELEM(NULL, scene, rbw)) {
BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to add Rigid Body Constraint to");
return OPERATOR_CANCELLED;
}
if (rbw->constraints == NULL) {
rbw->constraints = add_group("RigidBodyConstraints");
}
/* apply to active object */
ED_rigidbody_con_add(op, scene, ob, type);
add_to_group(rbw->constraints, ob, scene, NULL);
/* send updates */
DAG_ids_flush_update(CTX_data_main(C), 0);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
/* done */
return OPERATOR_FINISHED;
}
void RIGIDBODY_OT_constraint_group_add(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_constraint_group_add";
ot->name = "Add Rigid Body Constraint";
ot->description = "Add Rigid Body Constraint to active object and world's group";
/* callbacks */
ot->exec = rigidbody_con_group_add_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_con_type_items, RBC_TYPE_FIXED, "Rigid Body Constraint Type", "");
}
/* ************ Remove Rigid Body Constraint ************** */
static int rigidbody_con_remove_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = (scene) ? OBACT : NULL;
/* sanity checks */
if (scene == NULL)
return OPERATOR_CANCELLED;
/* apply to active object */
if (ELEM(NULL, ob, ob->rigidbody_constraint)) {
BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body Constraint to remove");
return OPERATOR_CANCELLED;
}
else {
ED_rigidbody_con_remove(scene, ob);
}
/* send updates */
DAG_ids_flush_update(CTX_data_main(C), 0);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
/* done */
return OPERATOR_FINISHED;
}
void RIGIDBODY_OT_constraint_remove(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_constraint_remove";
ot->name = "Remove Rigid Body Constraint";
ot->description = "Remove Rigid Body Constraint from Object";
/* callbacks */
ot->exec = rigidbody_con_remove_exec;
ot->poll = ED_operator_rigidbody_con_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

@ -273,6 +273,7 @@ typedef struct Object {
ListBase *duplilist; /* for temporary dupli list storage, only for use by RNA API */
struct RigidBodyOb *rigidbody_object; /* settings for Bullet rigid body */
struct RigidBodyCon *rigidbody_constraint; /* settings for Bullet constraint */
float ima_ofs[2]; /* offset for image empties */
} Object;

@ -53,6 +53,8 @@ typedef struct RigidBodyWorld {
struct Group *group; /* Group containing objects to use for Rigid Bodies */
struct Object **objects; /* Array to access group objects by index, only used at runtime */
struct Group *constraints; /* Group containing objects to use for Rigid Body Constraints*/
int pad;
float ltime; /* last frame world was evaluated for (internal) */
@ -171,6 +173,92 @@ typedef enum eRigidBody_Shape {
//RB_SHAPE_COMPOUND,
} eRigidBody_Shape;
/* ******************************** */
/* RigidBody Constraint */
/* RigidBodyConstraint (rbc)
*
* Represents an constraint connecting two rigid bodies.
*/
typedef struct RigidBodyCon {
struct Object *ob1; /* First object influenced by the constraint */
struct Object *ob2; /* Second object influenced by the constraint */
/* General Settings for this RigidBodyCon */
short type; /* (eRigidBodyCon_Type) role of RigidBody in sim */
short num_solver_iterations;/* number of constraint solver iterations made per simulation step */
int flag; /* (eRigidBodyCon_Flag) */
float breaking_threshold; /* breaking impulse threshold */
float pad;
/* limits */
float limit_lin_x_lower; /* lower limit for x axis translation */
float limit_lin_x_upper; /* upper limit for x axis translation */
float limit_lin_y_lower; /* lower limit for y axis translation */
float limit_lin_y_upper; /* upper limit for y axis translation */
float limit_lin_z_lower; /* lower limit for z axis translation */
float limit_lin_z_upper; /* upper limit for z axis translation */
float limit_ang_x_lower; /* lower limit for x axis rotation */
float limit_ang_x_upper; /* upper limit for x axis rotation */
float limit_ang_y_lower; /* lower limit for y axis rotation */
float limit_ang_y_upper; /* upper limit for y axis rotation */
float limit_ang_z_lower; /* lower limit for z axis rotation */
float limit_ang_z_upper; /* upper limit for z axis rotation */
/* References to Physics Sim object. Exist at runtime only */
void *physics_constraint; /* Physics object representation (i.e. btTypedConstraint) */
} RigidBodyCon;
/* Participation types for RigidBodyOb */
typedef enum eRigidBodyCon_Type {
/* lets bodies rotate around a specified point */
RBC_TYPE_POINT = 0,
/* lets bodies rotate around a specified axis */
RBC_TYPE_HINGE,
/* simulates wheel suspension */
RBC_TYPE_HINGE2,
/* restricts movent to a specified axis */
RBC_TYPE_SLIDER,
/* lets object rotate within a cpecified cone */
RBC_TYPE_CONE_TWIST,
/* allows user to specify constraint axes */
RBC_TYPE_6DOF,
/* like 6DOF but has springs */
RBC_TYPE_6DOF_SPRING,
/* simulates a universal joint */
RBC_TYPE_UNIVERSAL,
/* glues two bodies together */
RBC_TYPE_FIXED,
/* similar to slider but also allows rotation around slider axis */
RBC_TYPE_PISTON,
/* Simplified spring constraint with only once axis that's automatically placed between the connected bodies */
RBC_TYPE_SPRING
} eRigidBodyCon_Type;
/* Flags for RigidBodyCon */
typedef enum eRigidBodyCon_Flag {
/* constraint influences rigid body motion */
RBC_FLAG_ENABLED = (1<<0),
/* constraint needs to be validated */
RBC_FLAG_NEEDS_VALIDATE = (1<<1),
/* allow constrained bodies to collide */
RBC_FLAG_DISABLE_COLLISIONS = (1<<2),
/* constraint can break */
RBC_FLAG_USE_BREAKING = (1<<3),
/* constraint use custom number of constraint solver iterations */
RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS = (1<<4),
/* limits */
RBC_FLAG_USE_LIMIT_LIN_X = (1<<5),
RBC_FLAG_USE_LIMIT_LIN_Y = (1<<6),
RBC_FLAG_USE_LIMIT_LIN_Z = (1<<7),
RBC_FLAG_USE_LIMIT_ANG_X = (1<<8),
RBC_FLAG_USE_LIMIT_ANG_Y = (1<<9),
RBC_FLAG_USE_LIMIT_ANG_Z = (1<<10),
} eRigidBodyCon_Flag;
/* ******************************** */
#endif /* __DNA_RIGIDBODY_TYPES_H__ */

@ -104,6 +104,7 @@ extern EnumPropertyItem object_type_curve_items[];
extern EnumPropertyItem rigidbody_ob_type_items[];
extern EnumPropertyItem rigidbody_ob_shape_items[];
extern EnumPropertyItem rigidbody_con_type_items[];
extern EnumPropertyItem object_axis_items[];

@ -2433,6 +2433,11 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "rigidbody_object");
RNA_def_property_struct_type(prop, "RigidBodyObject");
RNA_def_property_ui_text(prop, "Rigid Body Settings", "Settings for rigid body simulation");
prop = RNA_def_property(srna, "rigid_body_constraint", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "rigidbody_constraint");
RNA_def_property_struct_type(prop, "RigidBodyConstraint");
RNA_def_property_ui_text(prop, "Rigid Body Constraint", "Constraint constraining rigid bodies");
/* restrict */
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);

@ -59,6 +59,16 @@ EnumPropertyItem rigidbody_ob_shape_items[] = {
{RB_SHAPE_TRIMESH, "MESH", ICON_MESH_MONKEY, "Mesh", "Mesh consisting of triangles only, allowing for more detailed interactions than convex hulls"},
{0, NULL, 0, NULL, NULL}};
/* collision shapes of constraints in rigid body sim */
EnumPropertyItem rigidbody_con_type_items[] = {
{RBC_TYPE_FIXED, "FIXED", ICON_FORCE_FORCE, "Fixed", "Glues rigid bodies together"},
{RBC_TYPE_POINT, "POINT", ICON_FORCE_FORCE, "Point", "Constrains rigid bodies to move aound common pivot point"},
{RBC_TYPE_HINGE, "HINGE", ICON_FORCE_FORCE, "Hinge", "Restricts rigid body rotation to one axis"},
{RBC_TYPE_SLIDER, "SLIDER", ICON_FORCE_FORCE, "Slider", "Restricts rigid boddy translation to one axis"},
{RBC_TYPE_PISTON, "PISTON", ICON_FORCE_FORCE, "Piston", "Restricts rigid boddy translation and rotation to one axis"},
{RBC_TYPE_6DOF, "GENERIC", ICON_FORCE_FORCE, "Generic", "Restricts translation and rotation to specified axes"},
{0, NULL, 0, NULL, NULL}};
#ifdef RNA_RUNTIME
@ -286,6 +296,91 @@ static void rna_RigidBodyOb_angular_damping_set(PointerRNA *ptr, float value)
RB_body_set_angular_damping(rbo->physics_object, value);
}
static char *rna_RigidBodyCon_path(PointerRNA *ptr)
{
/* NOTE: this hardcoded path should work as long as only Objects have this */
return BLI_sprintfN("rigid_body_constraint");
}
static void rna_RigidBodyCon_type_set(PointerRNA *ptr, int value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
rbc->type = value;
rbc->flag |= RBC_FLAG_NEEDS_VALIDATE;
}
static void rna_RigidBodyCon_enabled_set(PointerRNA *ptr, int value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
RB_FLAG_SET(rbc->flag, value, RBC_FLAG_ENABLED);
if (rbc->physics_constraint)
RB_constraint_set_enabled(rbc->physics_constraint, value);
}
static void rna_RigidBodyCon_disable_collisions_set(PointerRNA *ptr, int value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
RB_FLAG_SET(rbc->flag, value, RBC_FLAG_DISABLE_COLLISIONS);
rbc->flag |= RBC_FLAG_NEEDS_VALIDATE;
}
static void rna_RigidBodyCon_use_breaking_set(PointerRNA *ptr, int value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
if (value) {
rbc->flag |= RBC_FLAG_USE_BREAKING;
if (rbc->physics_constraint)
RB_constraint_set_breaking_threshold(rbc->physics_constraint, rbc->breaking_threshold);
}
else {
rbc->flag &= ~RBC_FLAG_USE_BREAKING;
if (rbc->physics_constraint)
RB_constraint_set_breaking_threshold(rbc->physics_constraint, FLT_MAX);
}
}
static void rna_RigidBodyCon_breaking_threshold_set(PointerRNA *ptr, float value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
rbc->breaking_threshold = value;
if (rbc->physics_constraint && (rbc->flag & RBC_FLAG_USE_BREAKING))
RB_constraint_set_breaking_threshold(rbc->physics_constraint, value);
}
static void rna_RigidBodyCon_override_solver_iterations_set(PointerRNA *ptr, int value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
if (value) {
rbc->flag |= RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS;
if (rbc->physics_constraint)
RB_constraint_set_solver_iterations(rbc->physics_constraint, rbc->num_solver_iterations);
}
else {
rbc->flag &= ~RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS;
if (rbc->physics_constraint)
RB_constraint_set_solver_iterations(rbc->physics_constraint, -1);
}
}
static void rna_RigidBodyCon_num_solver_iterations_set(PointerRNA *ptr, int value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
rbc->num_solver_iterations = value;
if (rbc->physics_constraint && (rbc->flag & RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS))
RB_constraint_set_solver_iterations(rbc->physics_constraint, value);
}
#else
static void rna_def_rigidbody_world(BlenderRNA *brna)
@ -304,6 +399,12 @@ static void rna_def_rigidbody_world(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE|PROP_ID_SELF_CHECK);
RNA_def_property_ui_text(prop, "Group", "Group containing objects participating in this simulation");
RNA_def_property_update(prop, NC_SCENE, "rna_RigidBodyWorld_reset");
prop = RNA_def_property(srna, "constraints", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Group");
RNA_def_property_flag(prop, PROP_EDITABLE|PROP_ID_SELF_CHECK);
RNA_def_property_ui_text(prop, "Constraints", "Group containing rigid body constraint objects");
RNA_def_property_update(prop, NC_SCENE, "rna_RigidBodyWorld_reset");
/* booleans */
prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
@ -501,10 +602,198 @@ static void rna_def_rigidbody_object(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
}
static void rna_def_rigidbody_constraint(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "RigidBodyConstraint", NULL);
RNA_def_struct_sdna(srna, "RigidBodyCon");
RNA_def_struct_ui_text(srna, "Rigid Body Constraint", "Constraint influencing Objects inside Rigid Body Simulation");
RNA_def_struct_path_func(srna, "rna_RigidBodyCon_path");
/* Enums */
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, rigidbody_con_type_items);
RNA_def_property_enum_funcs(prop, NULL, "rna_RigidBodyCon_type_set", NULL);
RNA_def_property_ui_text(prop, "Type", "Type of Rigid Body Constraint");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_ENABLED);
RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_enabled_set");
RNA_def_property_ui_text(prop, "Enabled", "Enable this constraint");
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "disable_collisions", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_DISABLE_COLLISIONS);
RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_disable_collisions_set");
RNA_def_property_ui_text(prop, "Disable Collisions", "Disables collisions between constrained ridid bodies");
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "object1", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "ob1");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Object 1", "First Rigid Body Object to be constrained");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "object2", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "ob2");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Object 2", "Second Rigid Body Object to be constrained");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
/* Breaking Threshold */
prop = RNA_def_property(srna, "use_breaking", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_BREAKING);
RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_use_breaking_set");
RNA_def_property_ui_text(prop, "Breakable", "Constraint can be broaken if it receives an impulse above the threshold");
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "breaking_threshold", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "breaking_threshold");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 100.0, 2);
RNA_def_property_float_default(prop, 10.0f);
RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_breaking_threshold_set", NULL);
RNA_def_property_ui_text(prop, "Breaking Threshold", "Impulse threshold that must be reached for the constraint to break");
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
/* Solver Iterations */
prop = RNA_def_property(srna, "override_solver_iterations", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS);
RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_override_solver_iterations_set");
RNA_def_property_ui_text(prop, "Override Solver Iterations", "Overrides the number of solver iterations for this constraint");
RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "num_solver_iterations", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "num_solver_iterations");
RNA_def_property_range(prop, 1, 1000);
RNA_def_property_ui_range(prop, 1, 100, 1, 0);
RNA_def_property_int_default(prop, 10);
RNA_def_property_int_funcs(prop, NULL, "rna_RigidBodyCon_num_solver_iterations_set", NULL);
RNA_def_property_ui_text(prop, "Solver Iterations", "Number of constraint solver iterations made per simulation step (higher values are more accurate but slower)");
RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
/* Limits */
prop = RNA_def_property(srna, "use_limit_lin_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_LIMIT_LIN_X);
RNA_def_property_ui_text(prop, "X Axis", "Limits translation on x axis");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "use_limit_lin_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_LIMIT_LIN_Y);
RNA_def_property_ui_text(prop, "Y Axis", "Limits translation on y axis");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "use_limit_lin_z", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_LIMIT_LIN_Z);
RNA_def_property_ui_text(prop, "Z Axis", "Limits translation on z axis");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "use_limit_ang_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_LIMIT_ANG_X);
RNA_def_property_ui_text(prop, "X Angle", "Limits rotation around x axis");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "use_limit_ang_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_LIMIT_ANG_Y);
RNA_def_property_ui_text(prop, "Y Angle", "Limits rotation around y axis");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "use_limit_ang_z", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_LIMIT_ANG_Z);
RNA_def_property_ui_text(prop, "Z Angle", "Limits rotation around z axis");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_lin_x_lower", PROP_FLOAT, PROP_UNIT_LENGTH);
RNA_def_property_float_sdna(prop, NULL, "limit_lin_x_lower");
RNA_def_property_float_default(prop, -1.0f);
RNA_def_property_ui_text(prop, "Lower X Limit", "Lower limit of x axis translation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_lin_x_upper", PROP_FLOAT, PROP_UNIT_LENGTH);
RNA_def_property_float_sdna(prop, NULL, "limit_lin_x_upper");
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Upper X Limit", "Upper limit of x axis translation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_lin_y_lower", PROP_FLOAT, PROP_UNIT_LENGTH);
RNA_def_property_float_sdna(prop, NULL, "limit_lin_y_lower");
RNA_def_property_float_default(prop, -1.0f);
RNA_def_property_ui_text(prop, "Lower Y Limit", "Lower limit of y axis translation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_lin_y_upper", PROP_FLOAT, PROP_UNIT_LENGTH);
RNA_def_property_float_sdna(prop, NULL, "limit_lin_y_upper");
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Upper Y Limit", "Upper limit of y axis translation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_lin_z_lower", PROP_FLOAT, PROP_UNIT_LENGTH);
RNA_def_property_float_sdna(prop, NULL, "limit_lin_z_lower");
RNA_def_property_float_default(prop, -1.0f);
RNA_def_property_ui_text(prop, "Lower Z Limit", "Lower limit of z axis translation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_lin_z_upper", PROP_FLOAT, PROP_UNIT_LENGTH);
RNA_def_property_float_sdna(prop, NULL, "limit_lin_z_upper");
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Upper Z Limit", "Upper limit of z axis translation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_ang_x_lower", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "limit_ang_x_lower");
RNA_def_property_range(prop, -M_PI * 2, M_PI * 2);
RNA_def_property_float_default(prop, -M_PI_4);
RNA_def_property_ui_text(prop, "Lower X Angle Limit", "Lower limit of x axis rotation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_ang_x_upper", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "limit_ang_x_upper");
RNA_def_property_range(prop, -M_PI * 2, M_PI * 2);
RNA_def_property_float_default(prop, M_PI_4);
RNA_def_property_ui_text(prop, "Upper X Angle Limit", "Upper limit of x axis rotation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_ang_y_lower", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "limit_ang_y_lower");
RNA_def_property_range(prop, -M_PI * 2, M_PI * 2);
RNA_def_property_float_default(prop, -M_PI_4);
RNA_def_property_ui_text(prop, "Lower Y Angle Limit", "Lower limit of y axis rotation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_ang_y_upper", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "limit_ang_y_upper");
RNA_def_property_range(prop, -M_PI * 2, M_PI * 2);
RNA_def_property_float_default(prop, M_PI_4);
RNA_def_property_ui_text(prop, "Upper Y Angle Limit", "Upper limit of y axis rotation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_ang_z_lower", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "limit_ang_z_lower");
RNA_def_property_range(prop, -M_PI * 2, M_PI * 2);
RNA_def_property_float_default(prop, -M_PI_4);
RNA_def_property_ui_text(prop, "Lower Z Angle Limit", "Lower limit of z axis rotation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
prop = RNA_def_property(srna, "limit_ang_z_upper", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "limit_ang_z_upper");
RNA_def_property_range(prop, -M_PI * 2, M_PI * 2);
RNA_def_property_float_default(prop, M_PI_4);
RNA_def_property_ui_text(prop, "Upper Z Angle Limit", "Upper limit of z axis rotation");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_RigidBodyOb_reset");
}
void RNA_def_rigidbody(BlenderRNA *brna)
{
rna_def_rigidbody_world(brna);
rna_def_rigidbody_object(brna);
rna_def_rigidbody_constraint(brna);
}