rigidbody: Add rigid body constraints
authorSergej Reich <sergej.reich@googlemail.com>
Wed, 23 Jan 2013 05:56:56 +0000 (05:56 +0000)
committerSergej Reich <sergej.reich@googlemail.com>
Wed, 23 Jan 2013 05:56:56 +0000 (05:56 +0000)
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).

22 files changed:
release/scripts/startup/bl_operators/rigidbody.py
release/scripts/startup/bl_ui/__init__.py
release/scripts/startup/bl_ui/properties_physics_common.py
release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py [new file with mode: 0644]
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/pointcache.c
source/blender/blenkernel/intern/rigidbody.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/include/ED_physics.h
source/blender/editors/object/object_add.c
source/blender/editors/physics/CMakeLists.txt
source/blender/editors/physics/physics_intern.h
source/blender/editors/physics/physics_ops.c
source/blender/editors/physics/rigidbody_constraint.c [new file with mode: 0644]
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/DNA_rigidbody_types.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/rna_object.c
source/blender/makesrna/intern/rna_rigidbody.c

index 55a2cef4535bb7e6e51df25420f550a728a4b3d2..5363eba642060d273cd20c27785c0d7167dc144d 100644 (file)
@@ -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)
index 80d9b4f046d3d1415efe3d9b7cfa0067ef3b42c8..982c723f08ea56a22024bc873f65610c6519ad7e 100644 (file)
@@ -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",
index 1131ea997daca752f63f09c7ab0b22e5e99e8c77..68c04cfb4c5805d189052a8c261ae69a7d48c15d 100644 (file)
@@ -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
 
diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py
new file mode 100644 (file)
index 0000000..8bd64d0
--- /dev/null
@@ -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__)
index ba9b32310b2bb4a781a5adab17fa1ccd83558904..ca79c6cb7fa25bfd58a31bb7d8e96005dbbaf50b 100644 (file)
@@ -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 ****************
 
index cf1b21e91dee357d4da7d7ce9bfba41b4f4d9095..0c1a23bf41da6e68c2cd8ded0b8d64273fbc3419 100644 (file)
@@ -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;
index afc552ae7f30af13e86f55535c61c8163befde22..1670389566e1b6bf4d0affc3201ef69f43e2f055 100644 (file)
@@ -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);
index 359c46f2b0e4102653e1da082d92cad707006935..d4a7013fb8f703cda0a5e9c31967d1145a1e4a2a 100644 (file)
 /* 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 */
index b0938f784355ddcd4cd9c922d9b579207951145a..fa3dac67a7f99566bd10a972f13b2ecfcabf4a33 100644 (file)
@@ -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);
index edb033e80f87ad206115d5db888c37ec657a18d4..070cb4676a1e5c10f9cb7ce270fba3444b4ecf87 100644 (file)
@@ -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
index ebd752c0118612c54c5c0d14b602070f904d63b2..e7a5367084984cdbb0c655ab23ecfad2bd41aa2c 100644 (file)
@@ -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);
                }
index d5aee5bbb9e875e1e20997d9bf3a8b76643da09f..192bcd8f712688c3644a96f0f21ccca20c625f7d 100644 (file)
@@ -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);
index b7ac93c5996cc9a33bf872c2612373ecdff75f70..9c829a06cd2fb41ec42c332daedacbed0596cc9f 100644 (file)
@@ -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))
index f14882884c0fbb27890fa0687a4238ad55758b7f..7f3eaa892d0b35dccb7e9fd79aa5ff9723246858 100644 (file)
@@ -44,6 +44,7 @@ set(SRC
        physics_fluid.c
        physics_ops.c
        physics_pointcache.c
+       rigidbody_constraint.c
        rigidbody_object.c
        rigidbody_world.c
 
index b4b4b5e54f587ce3dc91bab6cc9916f9d4e56bc4..4b4855c9922cd255a5f3947daf9091d8738328cd 100644 (file)
@@ -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);
index 7a893ab47a4c4d355b3fca28b935ddd8199e1faf..fc6a0e587520986d80be6978082018ab952e005c 100644 (file)
@@ -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);
diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c
new file mode 100644 (file)
index 0000000..e4db1c9
--- /dev/null
@@ -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;
+}
index 16859e5975a0c4f4155152b03602d46e3ec3354a..a1bb9fb9c3d6d937464301e637cf2ae511a73b73 100644 (file)
@@ -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;
index e08162fedd633f249d003c13c5b54e9efd63455d..5b7ffe4209c257711b0a858bf4287d62aa05dc2a 100644 (file)
@@ -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__ */
index f0de36bbdfecd5b1bdb771162dfb2c85a4fb9343..eabf0391c0117853e0131d4b65e026ae8c608116 100644 (file)
@@ -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[];
 
index 99cecb01140bff863dd6f6a2db913de265c12832..47b775801f6e3bfbbc1f335dea813560fcb008a8 100644 (file)
@@ -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);
index 1e0e250ac664398b000d7cd0afb704ce6caff3ce..206c6e19c4113aa8f14fdad3fdd29a01ea136ac3 100644 (file)
@@ -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);
 }