Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorSergej Reich <sergej.reich@googlemail.com>2013-01-23 09:56:44 +0400
committerSergej Reich <sergej.reich@googlemail.com>2013-01-23 09:56:44 +0400
commit2d8637946b047a8a9cc3fb6fe6d146b9961f92a6 (patch)
treeff566a91ca6c45d67b310fec8c990b801a7f0ba2 /source
parent089cf12435dfbc4b934a73b73ecd617b27ac678d (diff)
rigidbody: Add rigid body simulation
Add operators to add/remove rigid body world and objects. Add UI scripts. The rigid body simulation works on scene level and overrides the position/orientation of rigid bodies when active. It does not deform meshes or generate data so there is no modifier. Usage: * Add rigid body world in the scene tab * Create a group * Add objects to the group * Assign group to the rigid body world * Play animation For convenience the rigid body tools operators in the tools panel of the 3d view will add a world, group and add objects to the group automatically so you only have to press one button to add/remove rigid bodies to the simulation. Part of GSoC 2010 and 2012. Authors: Joshua Leung (aligorith), Sergej Reich (sergof)
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/intern/object.c2
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c103
-rw-r--r--source/blender/blenkernel/intern/scene.c6
-rw-r--r--source/blender/editors/include/ED_physics.h9
-rw-r--r--source/blender/editors/object/object_add.c6
-rw-r--r--source/blender/editors/physics/CMakeLists.txt3
-rw-r--r--source/blender/editors/physics/SConscript1
-rw-r--r--source/blender/editors/physics/physics_intern.h16
-rw-r--r--source/blender/editors/physics/physics_ops.c13
-rw-r--r--source/blender/editors/physics/rigidbody_object.c622
-rw-r--r--source/blender/editors/physics/rigidbody_world.c210
11 files changed, 987 insertions, 4 deletions
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 2738c546ce1..cf1b21e91de 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -2146,6 +2146,8 @@ void BKE_object_where_is_calc_time(Scene *scene, Object *ob, float ctime)
BKE_object_to_mat4(ob, ob->obmat);
}
+ BKE_rigidbody_sync_transforms(scene, ob, ctime);
+
/* solve constraints */
if (ob->constraints.first && !(ob->transflag & OB_NO_CONSTRAINTS)) {
bConstraintOb *cob;
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 17ced6fa54d..359c46f2b0e 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -751,7 +751,35 @@ static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, int r
/* Sync rigid body and object transformations */
void BKE_rigidbody_sync_transforms(Scene *scene, Object *ob, float ctime)
{
-// RB_TODO implement this
+ RigidBodyWorld *rbw = scene->rigidbody_world;
+ RigidBodyOb *rbo = ob->rigidbody_object;
+
+ /* keep original transform for kinematic and passive objects */
+ if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE)
+ return;
+
+ /* use rigid body transform after cache start frame */
+ if (ctime > rbw->pointcache->startframe) {
+ float mat[4][4], size_mat[4][4], size[3];
+
+ /* keep original transform when the simulation is muted */
+ if (rbw->flag & RBW_FLAG_MUTED)
+ return;
+
+ normalize_qt(rbo->orn); // RB_TODO investigate why quaternion isn't normalized at this point
+ quat_to_mat4(mat, rbo->orn);
+ copy_v3_v3(mat[3], rbo->pos);
+
+ mat4_to_size(size, ob->obmat);
+ size_to_mat4(size_mat, size);
+ mult_m4_m4m4(mat, mat, size_mat);
+
+ copy_m4_m4(ob->obmat, mat);
+ }
+ /* otherwise set rigid body transform to current obmat */
+ else {
+ mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat);
+ }
}
void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
@@ -765,6 +793,77 @@ void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
/* Run RigidBody simulation for the specified physics world */
void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
{
-// RB_TODO implement this
+ float timestep;
+ RigidBodyWorld *rbw = scene->rigidbody_world;
+ PointCache *cache;
+ PTCacheID pid;
+ int startframe, endframe;
+
+ BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw);
+ BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL);
+ cache = rbw->pointcache;
+
+ /* flag cache as outdated if we don't have a world or number of objects in the simulation has changed */
+ if (rbw->physics_world == NULL || rbw->numbodies != BLI_countlist(&rbw->group->gobject)) {
+ cache->flag |= PTCACHE_OUTDATED;
+ }
+
+ if (ctime <= startframe) {
+ rbw->ltime = startframe;
+ /* reset and rebuild simulation if necessary */
+ if (cache->flag & PTCACHE_OUTDATED) {
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
+ rigidbody_update_simulation(scene, rbw, true);
+ BKE_ptcache_validate(cache, (int)ctime);
+ cache->last_exact = 0;
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+ }
+ return;
+ }
+ /* rebuild world if it's outdated on second frame */
+ else if (ctime == startframe + 1 && rbw->ltime == startframe && cache->flag & PTCACHE_OUTDATED) {
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
+ rigidbody_update_simulation(scene, rbw, true);
+ }
+ /* make sure we don't go out of cache frame range */
+ else if (ctime > endframe) {
+ ctime = endframe;
+ }
+
+ /* don't try to run the simulation if we don't have a world yet but allow reading baked cache */
+ if (rbw->physics_world == NULL && !(cache->flag & PTCACHE_BAKED))
+ return;
+ else if (rbw->objects == NULL)
+ rigidbody_update_ob_array(rbw);
+
+ /* try to read from cache */
+ // RB_TODO deal with interpolated, old and baked results
+ if (BKE_ptcache_read(&pid, ctime)) {
+ BKE_ptcache_validate(cache, (int)ctime);
+ rbw->ltime = ctime;
+ return;
+ }
+
+ /* advance simulation, we can only step one frame forward */
+ if (ctime == rbw->ltime + 1) {
+ /* write cache for first frame when on second frame */
+ if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
+ BKE_ptcache_write(&pid, startframe);
+ }
+
+ /* update and validate simulation */
+ rigidbody_update_simulation(scene, rbw, false);
+
+ /* calculate how much time elapsed since last step in seconds */
+ timestep = 1.0f / (float)FPS * (ctime - rbw->ltime) * rbw->time_scale;
+ /* step simulation by the requested timestep, steps per second are adjusted to take time scale into account */
+ RB_dworld_step_simulation(rbw->physics_world, timestep, INT_MAX, 1.0f / (float)rbw->steps_per_second * min_ff(rbw->time_scale, 1.0f));
+
+ /* write cache for current frame */
+ BKE_ptcache_validate(cache, (int)ctime);
+ BKE_ptcache_write(&pid, (unsigned int)ctime);
+
+ rbw->ltime = ctime;
+ }
}
/* ************************************** */
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index e48cf369d4f..b0938f78435 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -1206,6 +1206,12 @@ void BKE_scene_update_for_newframe(Main *bmain, Scene *sce, unsigned int lay)
BKE_animsys_evaluate_all_animation(bmain, sce, ctime);
/*...done with recusrive funcs */
+ /* run rigidbody sim */
+ // XXX: this position may still change, objects not being updated correctly before simulation is run
+ // NOTE: current position is so that rigidbody sim affects other objects
+ if (BKE_scene_check_rigidbody_active(sce))
+ BKE_rigidbody_do_simulation(sce, ctime);
+
/* clear "LIB_DOIT" flag from all materials, to prevent infinite recursion problems later
* when trying to find materials with drivers that need evaluating [#32017]
*/
diff --git a/source/blender/editors/include/ED_physics.h b/source/blender/editors/include/ED_physics.h
index cd9ddd3d7d1..d5aee5bbb9e 100644
--- a/source/blender/editors/include/ED_physics.h
+++ b/source/blender/editors/include/ED_physics.h
@@ -32,13 +32,22 @@
#ifndef __ED_PHYSICS_H__
#define __ED_PHYSICS_H__
+struct bContext;
+struct wmOperator;
struct wmKeyConfig;
+struct Scene;
+struct Object;
+
/* particle_edit.c */
int PE_poll(struct bContext *C);
int PE_hair_poll(struct bContext *C);
int PE_poll_view3d(struct bContext *C);
+/* rigidbody_object.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);
+
/* operators */
void ED_operatortypes_physics(void);
void ED_keymap_physics(struct wmKeyConfig *keyconf);
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 897aeb74482..b7ac93c5996 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1697,7 +1697,11 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
BLI_addhead(&scene->base, basen); /* addhead: prevent eternal loop */
basen->object = obn;
- if (basen->flag & OB_FROMGROUP) {
+ /* 1) duplis should end up in same group as the original
+ * 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) {
Group *group;
for (group = bmain->group.first; group; group = group->id.next) {
if (object_in_group(ob, group))
diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt
index da12a26e747..f14882884c0 100644
--- a/source/blender/editors/physics/CMakeLists.txt
+++ b/source/blender/editors/physics/CMakeLists.txt
@@ -27,6 +27,7 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
+ ../../rigidbody
../../../../intern/elbeem/extern
../../../../intern/guardedalloc
)
@@ -43,6 +44,8 @@ set(SRC
physics_fluid.c
physics_ops.c
physics_pointcache.c
+ rigidbody_object.c
+ rigidbody_world.c
physics_intern.h
)
diff --git a/source/blender/editors/physics/SConscript b/source/blender/editors/physics/SConscript
index 293f7769a6a..b68cc944925 100644
--- a/source/blender/editors/physics/SConscript
+++ b/source/blender/editors/physics/SConscript
@@ -33,6 +33,7 @@ incs = '../include ../../blenfont ../../blenlib ../../blenkernel ../../makesdna
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../gpu ../../blenloader ../../bmesh'
incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern'
+incs += ' ../../rigidbody'
defs = []
diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h
index 75779cf6102..b4b4b5e54f5 100644
--- a/source/blender/editors/physics/physics_intern.h
+++ b/source/blender/editors/physics/physics_intern.h
@@ -105,5 +105,19 @@ void PTCACHE_OT_bake_from_cache(struct wmOperatorType *ot);
void PTCACHE_OT_add(struct wmOperatorType *ot);
void PTCACHE_OT_remove(struct wmOperatorType *ot);
-#endif /* __PHYSICS_INTERN_H__ */
+/* rigidbody_object.c */
+void RIGIDBODY_OT_object_add(struct wmOperatorType *ot);
+void RIGIDBODY_OT_object_remove(struct wmOperatorType *ot);
+
+void RIGIDBODY_OT_objects_add(struct wmOperatorType *ot);
+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_world.c */
+void RIGIDBODY_OT_world_add(struct wmOperatorType *ot);
+void RIGIDBODY_OT_world_remove(struct wmOperatorType *ot);
+void RIGIDBODY_OT_world_export(struct wmOperatorType *ot);
+
+#endif /* __PHYSICS_INTERN_H__ */
diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c
index fb99d296a54..7a893ab47a4 100644
--- a/source/blender/editors/physics/physics_ops.c
+++ b/source/blender/editors/physics/physics_ops.c
@@ -86,6 +86,19 @@ static void operatortypes_particle(void)
WM_operatortype_append(PARTICLE_OT_dupliob_remove);
WM_operatortype_append(PARTICLE_OT_dupliob_move_up);
WM_operatortype_append(PARTICLE_OT_dupliob_move_down);
+
+ WM_operatortype_append(RIGIDBODY_OT_object_add);
+ WM_operatortype_append(RIGIDBODY_OT_object_remove);
+
+ WM_operatortype_append(RIGIDBODY_OT_objects_add);
+ WM_operatortype_append(RIGIDBODY_OT_objects_remove);
+
+ WM_operatortype_append(RIGIDBODY_OT_shape_change);
+ WM_operatortype_append(RIGIDBODY_OT_mass_calculate);
+
+ WM_operatortype_append(RIGIDBODY_OT_world_add);
+ WM_operatortype_append(RIGIDBODY_OT_world_remove);
+// WM_operatortype_append(RIGIDBODY_OT_world_export);
}
static void keymap_particle(wmKeyConfig *keyconf)
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
new file mode 100644
index 00000000000..c61d4c33866
--- /dev/null
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -0,0 +1,622 @@
+/*
+ * ***** 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): Joshua Leung, Sergej Reich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file rigidbody_object.c
+ * \ingroup editor_physics
+ * \brief Rigid Body object editing operators
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_group_types.h"
+#include "DNA_object_types.h"
+#include "DNA_mesh_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 Objects Editing */
+
+static int ED_operator_rigidbody_active_poll(bContext *C)
+{
+ if (ED_operator_object_active_editable(C)) {
+ Object *ob = CTX_data_active_object(C);
+ return (ob && ob->rigidbody_object);
+ }
+ else
+ return 0;
+}
+
+static int ED_operator_rigidbody_add_poll(bContext *C)
+{
+ if (ED_operator_object_active_editable(C)) {
+ Object *ob = CTX_data_active_object(C);
+ return (ob && ob->type == OB_MESH);
+ }
+ else
+ return 0;
+}
+
+/* ----------------- */
+
+void ED_rigidbody_ob_add(wmOperator *op, Scene *scene, Object *ob, int type)
+{
+ /* check that object doesn't already belong to the current simulation */
+ if (ob->rigidbody_object) {
+ BKE_reportf(op->reports, RPT_INFO, "Object '%s' already has a Rigid Body", ob->id.name + 2);
+ return;
+ }
+ if (ob->type != OB_MESH) {
+ BKE_report(op->reports, RPT_ERROR, "Can't add Rigid Body to non mesh object");
+ return;
+ }
+ if (((Mesh *)ob->data)->totpoly == 0) {
+ BKE_report(op->reports, RPT_ERROR, "Can't create Rigid Body from mesh with no polygons");
+ return;
+ }
+
+ /* make rigidbody object settings */
+ ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, type);
+ ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE;
+}
+
+void ED_rigidbody_ob_remove(Scene *scene, Object *ob)
+{
+ BKE_rigidbody_remove_object(scene, ob);
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+}
+
+/* ********************************************** */
+/* Active Object Add/Remove Operators */
+
+/* ************ Add Rigid Body ************** */
+
+static int rigidbody_ob_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_ob_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_object_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_object_add";
+ ot->name = "Add Rigid Body";
+ ot->description = "Add active object as Rigid Body";
+
+ /* callbacks */
+ ot->exec = rigidbody_ob_add_exec;
+ ot->poll = ED_operator_rigidbody_add_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
+}
+
+/* ************ Remove Rigid Body ************** */
+
+static int rigidbody_ob_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_object)) {
+ BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
+ return OPERATOR_CANCELLED;
+ }
+ else
+ ED_rigidbody_ob_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_object_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_object_remove";
+ ot->name = "Remove Rigid Body";
+ ot->description = "Remove Rigid Body settings from Object";
+
+ /* callbacks */
+ ot->exec = rigidbody_ob_remove_exec;
+ ot->poll = ED_operator_rigidbody_active_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************************** */
+/* Selected Object Add/Remove Operators */
+
+/* ************ Add Rigid Bodies ************** */
+
+static int rigidbody_obs_add_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
+ int type = RNA_enum_get(op->ptr, "type");
+
+ /* sanity check */
+ if (scene == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No Scene to add Rigid Bodies to");
+ return OPERATOR_CANCELLED;
+ }
+ /* Add rigid body world and group if they don't exist for convenience */
+ if (rbw == NULL) {
+ rbw = BKE_rigidbody_create_world(scene);
+ BKE_rigidbody_validate_sim_world(scene, rbw, false);
+ scene->rigidbody_world = rbw;
+ }
+ if (rbw->group == NULL) {
+ rbw->group = add_group("RigidBodyWorld");
+ }
+ /* create rigid body objects and add them to the world's group */
+ CTX_DATA_BEGIN(C, Object *, ob, selected_objects) {
+ ED_rigidbody_ob_add(op, scene, ob, type);
+ add_to_group(rbw->group, ob, scene, NULL);
+ }
+ CTX_DATA_END;
+
+ /* 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_objects_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_objects_add";
+ ot->name = "Add Rigid Bodies";
+ ot->description = "Add selected objects as Rigid Bodies";
+
+ /* callbacks */
+ ot->exec = rigidbody_obs_add_exec;
+ ot->poll = ED_operator_rigidbody_add_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
+}
+
+/* ************ Remove Rigid Bodies ************** */
+
+static int rigidbody_obs_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
+
+ /* sanity checks */
+ if (scene == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* apply this to all selected objects... */
+ CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
+ {
+ if (ob->rigidbody_object) {
+ ED_rigidbody_ob_remove(scene, ob);
+ if (rbw)
+ rem_from_group(rbw->group, ob, scene, NULL);
+ }
+ }
+ CTX_DATA_END;
+
+ /* 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_objects_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_objects_remove";
+ ot->name = "Remove Rigid Bodies";
+ ot->description = "Remove selected objects from Rigid Body simulation";
+
+ /* callbacks */
+ ot->exec = rigidbody_obs_remove_exec;
+ ot->poll = ED_operator_rigidbody_active_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************************************** */
+/* Utility Operators */
+
+/* ************ Change Collision Shapes ************** */
+
+static int rigidbody_obs_shape_change_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ int shape = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (scene == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* apply this to all selected objects... */
+ CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
+ {
+ if (ob->rigidbody_object) {
+ PointerRNA ptr;
+
+ /* use RNA-system to change the property and perform all necessary changes */
+ RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
+ RNA_enum_set(&ptr, "collision_shape", shape);
+ }
+ }
+ CTX_DATA_END;
+
+ /* send updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); // XXX: wrong notifiers for now, but these also do the job...
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_shape_change";
+ ot->name = "Change Collision Shape";
+ ot->description = "Change collision shapes for selected Rigid Body Objects";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = rigidbody_obs_shape_change_exec;
+ ot->poll = ED_operator_rigidbody_active_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_shape_items, RB_SHAPE_TRIMESH, "Rigid Body Shape", "");
+}
+
+/* ************ Calculate Mass ************** */
+
+/* Entry in material density table */
+typedef struct rbMaterialDensityItem {
+ const char *name; /* Name of material */
+ float density; /* Density (kg/m^3) */
+} rbMaterialDensityItem;
+
+/* Preset density values for materials (kg/m^3)
+ * Selected values obtained from:
+ * 1) http://www.jaredzone.info/2010/09/densities.html
+ * 2) http://www.avlandesign.com/density_construction.htm
+ * 3) http://www.avlandesign.com/density_metal.htm
+ */
+static rbMaterialDensityItem RB_MATERIAL_DENSITY_TABLE[] = {
+ {"Air", 1.0f}, /* not quite; adapted from 1.43 for oxygen for use as default */
+ {"Acrylic", 1400.0f},
+ {"Asphalt (Crushed)", 721.0f},
+ {"Bark", 240.0f},
+ {"Beans (Cocoa)", 593.0f},
+ {"Beans (Soy)", 721.0f},
+ {"Brick (Pressed)", 2400.0f},
+ {"Brick (Common)", 2000.0f},
+ {"Brick (Soft)", 1600.0f},
+ {"Brass", 8216.0f},
+ {"Bronze", 8860.0f},
+ {"Carbon (Solid)", 2146.0f},
+ {"Cardboard", 689.0f},
+ {"Cast Iron", 7150.0f},
+ //{"Cement", 1442.0f},
+ {"Chalk (Solid)", 2499.0f},
+ //{"Coffee (Fresh/Roast)", ~500},
+ {"Concrete", 2320.0f},
+ {"Charcoal", 208.0f},
+ {"Cork", 240.0f},
+ {"Copper", 8933.0f},
+ {"Garbage", 481.0f},
+ {"Glass (Broken)", 1940.0f},
+ {"Glass (Solid)", 2190.0f},
+ {"Gold", 19282.0f},
+ {"Granite (Broken)", 1650.0f},
+ {"Granite (Solid)", 2691.0f},
+ {"Gravel", 2780.0f},
+ {"Ice (Crushed)", 593.0f},
+ {"Ice (Solid)", 919.0f},
+ {"Iron", 7874.0f},
+ {"Lead", 11342.0f},
+ {"Limestone (Broken)", 1554.0f},
+ {"Limestone (Solid)", 2611.0f},
+ {"Marble (Broken)", 1570.0f},
+ {"Marble (Solid)", 2563.0f},
+ {"Paper", 1201.0f},
+ {"Peanuts (Shelled)", 641.0f},
+ {"Peanuts (Not Shelled)", 272.0f},
+ {"Plaster", 849.0f},
+ {"Plastic", 1200.0f},
+ {"Polystyrene", 1050.0f},
+ {"Rubber", 1522.0f},
+ {"Silver", 10501.0f},
+ {"Steel", 7860.0f},
+ {"Stone", 2515.0f},
+ {"Stone (Crushed)", 1602.0f},
+ {"Timber", 610.0f}
+};
+static const int NUM_RB_MATERIAL_PRESETS = sizeof(RB_MATERIAL_DENSITY_TABLE) / sizeof(rbMaterialDensityItem);
+
+
+/* dynamically generate list of items
+ * - Although there is a runtime cost, this has a lower maintenance cost
+ * in the long run than other two-list solutions...
+ */
+static EnumPropertyItem *rigidbody_materials_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
+{
+ EnumPropertyItem item_tmp = {0};
+ EnumPropertyItem *item = NULL;
+ int totitem = 0;
+ int i = 0;
+
+ /* add each preset to the list */
+ for (i = 0; i < NUM_RB_MATERIAL_PRESETS; i++) {
+ rbMaterialDensityItem *preset = &RB_MATERIAL_DENSITY_TABLE[i];
+
+ item_tmp.identifier = item_tmp.name = preset->name;
+ item_tmp.value = i;
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+
+ /* add special "custom" entry to the end of the list */
+ {
+ item_tmp.identifier = item_tmp.name = "Custom";
+ item_tmp.value = -1;
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *free = 1;
+
+ return item;
+}
+
+/* ------------------------------------------ */
+
+/* helper function to calculate volume of rigidbody object */
+// TODO: allow a parameter to specify method used to calculate this?
+static float calc_rigidbody_ob_volume(Object *ob)
+{
+ RigidBodyOb *rbo = ob->rigidbody_object;
+
+ float size[3] = {1.0f, 1.0f, 1.0f};
+ float radius = 1.0f;
+ float height = 1.0f;
+
+ float volume = 0.0f;
+
+ /* if automatically determining dimensions, use the Object's boundbox
+ * - assume that all quadrics are standing upright on local z-axis
+ * - assume even distribution of mass around the Object's pivot
+ * (i.e. Object pivot is centralised in boundbox)
+ * - boundbox gives full width
+ */
+ // XXX: all dimensions are auto-determined now... later can add stored settings for this
+ BKE_object_dimensions_get(ob, size);
+
+ if (ELEM3(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) {
+ /* take radius as largest x/y dimension, and height as z-dimension */
+ radius = MAX2(size[0], size[1]) * 0.5f;
+ height = size[2];
+ }
+ else if (rbo->shape == RB_SHAPE_SPHERE) {
+ /* take radius to the the largest dimension to try and encompass everything */
+ radius = max_fff(size[0], size[1], size[2]) * 0.5f;
+ }
+
+ /* calculate volume as appropriate */
+ switch (rbo->shape) {
+ case RB_SHAPE_BOX:
+ volume = size[0] * size[1] * size[2];
+ break;
+
+ case RB_SHAPE_SPHERE:
+ volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius;
+ break;
+
+ /* for now, assume that capsule is close enough to a cylinder... */
+ case RB_SHAPE_CAPSULE:
+ case RB_SHAPE_CYLINDER:
+ volume = (float)M_PI * radius * radius * height;
+ break;
+
+ case RB_SHAPE_CONE:
+ volume = (float)M_PI / 3.0f * radius * radius * height;
+ break;
+
+ /* for now, all mesh shapes are just treated as boxes...
+ * NOTE: this may overestimate the volume, but other methods are overkill
+ */
+ case RB_SHAPE_CONVEXH:
+ case RB_SHAPE_TRIMESH:
+ volume = size[0] * size[1] * size[2];
+ break;
+
+#if 0 // XXX: not defined yet
+ case RB_SHAPE_COMPOUND:
+ volume = 0.0f;
+ break;
+#endif
+ }
+
+ /* return the volume calculated */
+ return volume;
+}
+
+/* ------------------------------------------ */
+
+static int rigidbody_obs_calc_mass_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ int material = RNA_enum_get(op->ptr, "material");
+ float density;
+
+ /* sanity checks */
+ if (scene == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* get density (kg/m^3) to apply */
+ if (material >= 0) {
+ /* get density from table, and store in props for later repeating */
+ if (material >= NUM_RB_MATERIAL_PRESETS)
+ material = 0;
+
+ density = RB_MATERIAL_DENSITY_TABLE[material].density;
+ RNA_float_set(op->ptr, "density", density);
+ }
+ else {
+ /* custom - grab from whatever value is set */
+ density = RNA_float_get(op->ptr, "density");
+ }
+
+ /* apply this to all selected objects (with rigidbodies)... */
+ CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
+ {
+ if (ob->rigidbody_object) {
+ PointerRNA ptr;
+
+ float volume; /* m^3 */
+ float mass; /* kg */
+
+ /* mass is calculated from the approximate volume of the object,
+ * and the density of the material we're simulating
+ */
+ volume = calc_rigidbody_ob_volume(ob);
+ mass = volume * density;
+
+ /* use RNA-system to change the property and perform all necessary changes */
+ RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
+ RNA_float_set(&ptr, "mass", mass);
+ }
+ }
+ CTX_DATA_END;
+
+ /* send updates */
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); // XXX: wrong notifiers for now, but these also do the job...
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_mass_calculate";
+ ot->name = "Calculate Mass";
+ ot->description = "Automatically calculate mass values for Rigid Body Objects based on volume";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke; // XXX
+ ot->exec = rigidbody_obs_calc_mass_exec;
+ ot->poll = ED_operator_rigidbody_active_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = prop = RNA_def_enum(ot->srna, "material",
+ DummyRNA_DEFAULT_items, 0,
+ "Material Preset",
+ "Type of material that objects are made of. "
+ "Determines material density");
+ RNA_def_enum_funcs(prop, rigidbody_materials_itemf);
+
+ RNA_def_float(ot->srna, "density", 1.0, FLT_MIN, FLT_MAX,
+ "Density",
+ "Custom density value (kg/m^3) to use instead of material preset",
+ 1.0f, 2500.0f);
+}
+
+/* ********************************************** */
diff --git a/source/blender/editors/physics/rigidbody_world.c b/source/blender/editors/physics/rigidbody_world.c
new file mode 100644
index 00000000000..068f8decef5
--- /dev/null
+++ b/source/blender/editors/physics/rigidbody_world.c
@@ -0,0 +1,210 @@
+/*
+ * ***** 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): Joshua Leung, Sergej Reich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file rigidbody_world.c
+ * \ingroup editor_physics
+ * \brief Rigid Body world editing operators
+ */
+
+#include <stdlib.h>
+#include <string.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 "RBI_api.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_group.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_rigidbody.h"
+#include "BKE_utildefines.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"
+
+/* ********************************************** */
+/* API */
+
+/* check if there is an active rigid body world */
+static int ED_rigidbody_world_active_poll(bContext *C)
+{
+ Scene *scene = CTX_data_scene(C);
+ return (scene && scene->rigidbody_world);
+}
+static int ED_rigidbody_world_add_poll(bContext *C)
+{
+ Scene *scene = CTX_data_scene(C);
+ return (scene && scene->rigidbody_world == NULL);
+}
+
+/* ********************************************** */
+/* OPERATORS - Management */
+
+/* ********** Add RigidBody World **************** */
+
+static int rigidbody_world_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ RigidBodyWorld *rbw;
+
+ rbw = BKE_rigidbody_create_world(scene);
+// BKE_rigidbody_validate_sim_world(scene, rbw, false);
+ scene->rigidbody_world = rbw;
+
+ return OPERATOR_FINISHED;
+}
+
+void RIGIDBODY_OT_world_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_world_add";
+ ot->name = "Add Rigid Body World";
+ ot->description = "Add Rigid Body simulation world to the current scene";
+
+ /* callbacks */
+ ot->exec = rigidbody_world_add_exec;
+ ot->poll = ED_rigidbody_world_add_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ********** Remove RigidBody World ************* */
+
+static int rigidbody_world_remove_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ RigidBodyWorld *rbw = scene->rigidbody_world;
+
+ /* sanity checks */
+ if (ELEM(NULL, scene, rbw)) {
+ BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to remove");
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_rigidbody_free_world(rbw);
+ scene->rigidbody_world = NULL;
+
+ /* send updates */
+ WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+void RIGIDBODY_OT_world_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_world_remove";
+ ot->name = "Remove Rigid Body World";
+ ot->description = "Remove Rigid Body simulation world from the current scene";
+
+ /* callbacks */
+ ot->exec = rigidbody_world_remove_exec;
+ ot->poll = ED_rigidbody_world_active_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ********************************************** */
+/* UTILITY OPERATORS */
+
+/* ********** Export RigidBody World ************* */
+
+static int rigidbody_world_export_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ RigidBodyWorld *rbw = scene->rigidbody_world;
+ char path[FILE_MAX];
+
+ /* sanity checks */
+ if ELEM(NULL, scene, rbw) {
+ BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to export");
+ return OPERATOR_CANCELLED;
+ }
+ if (rbw->physics_world == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Rigid Body World has no associated physics data to export");
+ return OPERATOR_CANCELLED;
+ }
+
+ RNA_string_get(op->ptr, "filepath", path);
+ RB_dworld_export(rbw->physics_world, path);
+
+ return OPERATOR_FINISHED;
+}
+
+static int rigidbody_world_export_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
+{
+ if (!RNA_struct_property_is_set(op->ptr, "relative_path"))
+ RNA_boolean_set(op->ptr, "relative_path", (U.flag & USER_RELPATHS));
+
+ if (RNA_struct_property_is_set(op->ptr, "filepath"))
+ return rigidbody_world_export_exec(C, op);
+
+ // TODO: use the actual rigidbody world's name + .bullet instead of this temp crap
+ RNA_string_set(op->ptr, "filepath", "rigidbodyworld_export.bullet");
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void RIGIDBODY_OT_world_export(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->idname = "RIGIDBODY_OT_world_export";
+ ot->name = "Export Rigid Body World";
+ ot->description = "Export Rigid Body world to simulator's own fileformat (i.e. '.bullet' for Bullet Physics)";
+
+ /* callbacks */
+ ot->invoke = rigidbody_world_export_invoke;
+ ot->exec = rigidbody_world_export_exec;
+ ot->poll = ED_rigidbody_world_active_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_filesel(ot, FOLDERFILE, FILE_SPECIAL, FILE_SAVE, FILE_RELPATH, FILE_DEFAULTDISPLAY);
+}