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
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/blender/addon/ui.py3
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_ui/__init__.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py27
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_rigidbody.py130
-rw-r--r--release/scripts/startup/bl_ui/properties_scene.py81
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py23
-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
18 files changed, 1246 insertions, 11 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index f9985f326cd..dff7dfc9c21 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -1166,6 +1166,9 @@ def get_panels():
types.PARTICLE_PT_velocity,
types.PARTICLE_PT_rotation,
types.PARTICLE_PT_physics,
+ bpy.types.SCENE_PT_rigid_body_world,
+ bpy.types.SCENE_PT_rigid_body_cache,
+ bpy.types.SCENE_PT_rigid_body_field_weights,
types.PARTICLE_PT_boidbrain,
types.PARTICLE_PT_render,
types.PARTICLE_PT_draw,
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index ecbbe34dbb4..3ff02420bbd 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -35,6 +35,7 @@ _modules = (
"object_randomize_transform",
"object_quick_effects",
"presets",
+ "rigidbody",
"screen_play_rendered_anim",
"sequencer",
"uvcalc_follow_active",
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index 9ed12943bf8..80d9b4f046d 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -48,6 +48,7 @@ _modules = (
"properties_physics_dynamicpaint",
"properties_physics_field",
"properties_physics_fluid",
+ "properties_physics_rigidbody",
"properties_physics_smoke",
"properties_physics_softbody",
"properties_render",
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index db8dfd7a542..1131ea997da 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -44,6 +44,12 @@ def physics_add(self, layout, md, name, type, typeicon, toggles):
else:
sub.operator("object.modifier_add", text=name, icon=typeicon).type = type
+def physics_add_special(self, layout, data, name, addop, removeop, typeicon):
+ sub = layout.row(align=True)
+ if data:
+ sub.operator(removeop, text=name, icon='X')
+ else:
+ sub.operator(addop, text=name, icon=typeicon)
class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
bl_label = ""
@@ -76,6 +82,12 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
physics_add(self, col, context.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True)
physics_add(self, col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', True)
+ if(ob.type == 'MESH'):
+ physics_add_special(self, col, ob.rigid_body, "Rigid Body",
+ "rigidbody.object_add",
+ "rigidbody.object_remove",
+ 'MESH_ICOSPHERE') # XXX: need dedicated icon
+
# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc
@@ -84,11 +96,12 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
layout.context_pointer_set("point_cache", cache)
- row = layout.row()
- row.template_list("UI_UL_list", "", cache, "point_caches", cache.point_caches, "active_index", rows=2)
- col = row.column(align=True)
- col.operator("ptcache.add", icon='ZOOMIN', text="")
- col.operator("ptcache.remove", icon='ZOOMOUT', text="")
+ if not cachetype == 'RIGID_BODY':
+ row = layout.row()
+ row.template_list("UI_UL_list", "", cache, "point_caches", cache.point_caches, "active_index", rows=2)
+ col = row.column(align=True)
+ col.operator("ptcache.add", icon='ZOOMIN', text="")
+ col.operator("ptcache.remove", icon='ZOOMOUT', text="")
row = layout.row()
if cachetype in {'PSYS', 'HAIR', 'SMOKE'}:
@@ -131,13 +144,13 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
row.enabled = enabled
row.prop(cache, "frame_start")
row.prop(cache, "frame_end")
- if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT'}:
+ if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
row.prop(cache, "frame_step")
if cachetype != 'SMOKE':
layout.label(text=cache.info)
- if cachetype not in {'SMOKE', 'DYNAMIC_PAINT'}:
+ if cachetype not in {'SMOKE', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
split = layout.split()
split.enabled = enabled and bpy.data.is_saved
diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
new file mode 100644
index 00000000000..87a4c2fdbee
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
@@ -0,0 +1,130 @@
+# ##### 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_panel():
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "physics"
+
+
+class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel):
+ bl_label = "Rigid Body"
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ rd = context.scene.render
+ return (ob and ob.rigid_body and (not rd.use_game_engine))
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ rbo = ob.rigid_body
+
+ if rbo:
+ layout.prop(rbo, "type", text="Type")
+
+ row = layout.row()
+ row.prop(rbo, "enabled");
+ row.prop(rbo, "kinematic", text="Animated")
+
+ if rbo.type == 'ACTIVE':
+ col = layout.column()
+ col.prop(rbo, "mass")
+
+
+class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel):
+ bl_label = "Rigid Body Collisions"
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object and context.object.rigid_body and
+ (not context.scene.render.use_game_engine))
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ rbo = ob.rigid_body
+
+ layout.prop(rbo, "collision_shape", text="Shape")
+
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Surface Response:")
+ col.prop(rbo, "friction")
+ col.prop(rbo, "restitution", text="Bounciness")
+
+ col = split.column()
+ col.label(text="Sensitivity:")
+ if rbo.collision_shape in {'MESH', 'CONE'}:
+ col.prop(rbo, "collision_margin", text="Margin")
+ else:
+ col.prop(rbo, "use_margin");
+ sub = col.column()
+ sub.active = rbo.use_margin
+ sub.prop(rbo, "collision_margin", text="Margin")
+ layout.prop(rbo, "collision_groups")
+
+
+class PHYSICS_PT_rigid_body_dynamics(PHYSICS_PT_rigidbody_panel, Panel):
+ bl_label = "Rigid Body Dynamics"
+ bl_default_closed = True
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object and context.object.rigid_body and
+ context.object.rigid_body.type == 'ACTIVE' and
+ (not context.scene.render.use_game_engine))
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ rbo = ob.rigid_body
+
+ #col = layout.column(align=1)
+ #col.label(text="Activation:")
+ # XXX: settings such as activate on collison/etc.
+
+ split = layout.split();
+
+ col = split.column()
+ col.label(text="Deactivation:")
+ col.prop(rbo, "use_deactivation")
+ sub = col.column()
+ sub.active = rbo.use_deactivation
+ sub.prop(rbo, "start_deactivated")
+ sub.prop(rbo, "deactivate_linear_velocity", text="Linear Vel")
+ sub.prop(rbo, "deactivate_angular_velocity", text="Angular Vel")
+ # TODO: other params such as time?
+
+ col = split.column()
+ col.label(text="Damping:")
+ col.prop(rbo, "linear_damping", text="Translation")
+ col.prop(rbo, "angular_damping", text="Rotation")
+
+if __name__ == "__main__": # only for live edit.
+ bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py
index 5fe3923f0e9..73de00fdd40 100644
--- a/release/scripts/startup/bl_ui/properties_scene.py
+++ b/release/scripts/startup/bl_ui/properties_scene.py
@@ -21,6 +21,10 @@ import bpy
from bpy.types import Panel, UIList
from rna_prop_ui import PropertyPanel
+from bl_ui.properties_physics_common import (
+ point_cache_ui,
+ effector_weights_ui,
+ )
class SCENE_UL_keying_set_paths(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
@@ -242,6 +246,83 @@ class SCENE_PT_physics(SceneButtonsPanel, Panel):
layout.prop(scene, "gravity", text="")
+class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
+ bl_label = "Rigid Body World"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ rd = context.scene.render
+ scene = context.scene
+ return scene and (rd.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+
+ rbw = scene.rigidbody_world
+
+ if not rbw:
+ layout.operator("rigidbody.world_add")
+ else:
+ split = layout.split()
+ split.operator("rigidbody.world_remove")
+ layout.separator()
+ layout.prop(context.scene.rigidbody_world, "enabled")
+ layout.active = rbw.enabled
+
+ col = layout.column()
+ col.prop(rbw, "group")
+ col.prop(rbw, "constraints")
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(rbw, "time_scale", text="Speed")
+ col.prop(rbw, "use_split_impulse")
+
+ col = split.column()
+ col.prop(rbw, "steps_per_second", text="Steps Per Second")
+ col.prop(rbw, "num_solver_iterations", text="Solver Iterations")
+
+
+class SCENE_PT_rigid_body_cache(SceneButtonsPanel, Panel):
+ bl_label = "Rigid Body Cache"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ rd = context.scene.render
+ scene = context.scene
+ return scene and scene.rigidbody_world and (rd.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ scene = context.scene
+ rbw = scene.rigidbody_world
+
+ point_cache_ui(self, context, rbw.point_cache, rbw.point_cache.is_baked is False and rbw.enabled, 'RIGID_BODY')
+
+
+class SCENE_PT_rigid_body_field_weights(SceneButtonsPanel, Panel):
+ bl_label = "Rigid Body Field Weights"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ rd = context.scene.render
+ scene = context.scene
+ return scene and scene.rigidbody_world and (rd.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ scene = context.scene
+ rbw = scene.rigidbody_world
+
+ effector_weights_ui(self, context, rbw.effector_weights, 'RIGID_BODY')
+
+
class SCENE_PT_simplify(SceneButtonsPanel, Panel):
bl_label = "Simplify"
COMPAT_ENGINES = {'BLENDER_RENDER'}
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 1653e8fab7e..2df156abd55 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -108,6 +108,29 @@ class VIEW3D_PT_tools_objectmode(View3DPanel, Panel):
draw_repeat_tools(context, layout)
draw_gpencil_tools(context, layout)
+ col = layout.column(align=True)
+
+
+class VIEW3D_PT_tools_rigidbody(View3DPanel, Panel):
+ bl_context = "objectmode"
+ bl_label = "Rigidbody Tools"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ col = layout.column(align=True)
+ col.label(text="Add/Remove:")
+ row = col.row()
+ row.operator("rigidbody.objects_add", text="Add Active").type = 'ACTIVE'
+ row.operator("rigidbody.objects_add", text="Add Passive").type = 'PASSIVE'
+ row = col.row()
+ row.operator("rigidbody.objects_remove", text="Remove")
+
+ col = layout.column(align=True)
+ col.label(text="Object Tools:")
+ col.operator("rigidbody.shape_change", text="Change Shape")
+ col.operator("rigidbody.mass_calculate", text="Calculate Mass")
# ********** default tools for editmode_mesh ****************
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);
+}