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)
This commit is contained in:
Sergej Reich
2013-01-23 05:56:44 +00:00
parent 089cf12435
commit 2d8637946b
18 changed files with 1246 additions and 11 deletions

View File

@@ -1166,6 +1166,9 @@ def get_panels():
types.PARTICLE_PT_velocity, types.PARTICLE_PT_velocity,
types.PARTICLE_PT_rotation, types.PARTICLE_PT_rotation,
types.PARTICLE_PT_physics, 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_boidbrain,
types.PARTICLE_PT_render, types.PARTICLE_PT_render,
types.PARTICLE_PT_draw, types.PARTICLE_PT_draw,

View File

@@ -35,6 +35,7 @@ _modules = (
"object_randomize_transform", "object_randomize_transform",
"object_quick_effects", "object_quick_effects",
"presets", "presets",
"rigidbody",
"screen_play_rendered_anim", "screen_play_rendered_anim",
"sequencer", "sequencer",
"uvcalc_follow_active", "uvcalc_follow_active",

View File

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

View File

@@ -44,6 +44,12 @@ def physics_add(self, layout, md, name, type, typeicon, toggles):
else: else:
sub.operator("object.modifier_add", text=name, icon=typeicon).type = type 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): class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
bl_label = "" 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.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True)
physics_add(self, col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', 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 # 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) layout.context_pointer_set("point_cache", cache)
row = layout.row() if not cachetype == 'RIGID_BODY':
row.template_list("UI_UL_list", "", cache, "point_caches", cache.point_caches, "active_index", rows=2) row = layout.row()
col = row.column(align=True) row.template_list("UI_UL_list", "", cache, "point_caches", cache.point_caches, "active_index", rows=2)
col.operator("ptcache.add", icon='ZOOMIN', text="") col = row.column(align=True)
col.operator("ptcache.remove", icon='ZOOMOUT', text="") col.operator("ptcache.add", icon='ZOOMIN', text="")
col.operator("ptcache.remove", icon='ZOOMOUT', text="")
row = layout.row() row = layout.row()
if cachetype in {'PSYS', 'HAIR', 'SMOKE'}: if cachetype in {'PSYS', 'HAIR', 'SMOKE'}:
@@ -131,13 +144,13 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
row.enabled = enabled row.enabled = enabled
row.prop(cache, "frame_start") row.prop(cache, "frame_start")
row.prop(cache, "frame_end") 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") row.prop(cache, "frame_step")
if cachetype != 'SMOKE': if cachetype != 'SMOKE':
layout.label(text=cache.info) 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 = layout.split()
split.enabled = enabled and bpy.data.is_saved split.enabled = enabled and bpy.data.is_saved

View File

@@ -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__)

View File

@@ -21,6 +21,10 @@ import bpy
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from rna_prop_ui import PropertyPanel 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): class SCENE_UL_keying_set_paths(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): 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="") 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): class SCENE_PT_simplify(SceneButtonsPanel, Panel):
bl_label = "Simplify" bl_label = "Simplify"
COMPAT_ENGINES = {'BLENDER_RENDER'} COMPAT_ENGINES = {'BLENDER_RENDER'}

View File

@@ -108,6 +108,29 @@ class VIEW3D_PT_tools_objectmode(View3DPanel, Panel):
draw_repeat_tools(context, layout) draw_repeat_tools(context, layout)
draw_gpencil_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 **************** # ********** default tools for editmode_mesh ****************

View File

@@ -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_object_to_mat4(ob, ob->obmat);
} }
BKE_rigidbody_sync_transforms(scene, ob, ctime);
/* solve constraints */ /* solve constraints */
if (ob->constraints.first && !(ob->transflag & OB_NO_CONSTRAINTS)) { if (ob->constraints.first && !(ob->transflag & OB_NO_CONSTRAINTS)) {
bConstraintOb *cob; bConstraintOb *cob;

View File

@@ -751,7 +751,35 @@ static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, int r
/* Sync rigid body and object transformations */ /* Sync rigid body and object transformations */
void BKE_rigidbody_sync_transforms(Scene *scene, Object *ob, float ctime) 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) 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 */ /* Run RigidBody simulation for the specified physics world */
void BKE_rigidbody_do_simulation(Scene *scene, float ctime) 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;
}
} }
/* ************************************** */ /* ************************************** */

View File

@@ -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); BKE_animsys_evaluate_all_animation(bmain, sce, ctime);
/*...done with recusrive funcs */ /*...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 /* clear "LIB_DOIT" flag from all materials, to prevent infinite recursion problems later
* when trying to find materials with drivers that need evaluating [#32017] * when trying to find materials with drivers that need evaluating [#32017]
*/ */

View File

@@ -32,13 +32,22 @@
#ifndef __ED_PHYSICS_H__ #ifndef __ED_PHYSICS_H__
#define __ED_PHYSICS_H__ #define __ED_PHYSICS_H__
struct bContext;
struct wmOperator;
struct wmKeyConfig; struct wmKeyConfig;
struct Scene;
struct Object;
/* particle_edit.c */ /* particle_edit.c */
int PE_poll(struct bContext *C); int PE_poll(struct bContext *C);
int PE_hair_poll(struct bContext *C); int PE_hair_poll(struct bContext *C);
int PE_poll_view3d(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 */ /* operators */
void ED_operatortypes_physics(void); void ED_operatortypes_physics(void);
void ED_keymap_physics(struct wmKeyConfig *keyconf); void ED_keymap_physics(struct wmKeyConfig *keyconf);

View File

@@ -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 */ BLI_addhead(&scene->base, basen); /* addhead: prevent eternal loop */
basen->object = obn; 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; Group *group;
for (group = bmain->group.first; group; group = group->id.next) { for (group = bmain->group.first; group; group = group->id.next) {
if (object_in_group(ob, group)) if (object_in_group(ob, group))

View File

@@ -27,6 +27,7 @@ set(INC
../../makesdna ../../makesdna
../../makesrna ../../makesrna
../../windowmanager ../../windowmanager
../../rigidbody
../../../../intern/elbeem/extern ../../../../intern/elbeem/extern
../../../../intern/guardedalloc ../../../../intern/guardedalloc
) )
@@ -43,6 +44,8 @@ set(SRC
physics_fluid.c physics_fluid.c
physics_ops.c physics_ops.c
physics_pointcache.c physics_pointcache.c
rigidbody_object.c
rigidbody_world.c
physics_intern.h physics_intern.h
) )

View File

@@ -33,6 +33,7 @@ incs = '../include ../../blenfont ../../blenlib ../../blenkernel ../../makesdna
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../gpu ../../blenloader ../../bmesh' incs += ' ../../gpu ../../blenloader ../../bmesh'
incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern' incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern'
incs += ' ../../rigidbody'
defs = [] defs = []

View File

@@ -105,5 +105,19 @@ void PTCACHE_OT_bake_from_cache(struct wmOperatorType *ot);
void PTCACHE_OT_add(struct wmOperatorType *ot); void PTCACHE_OT_add(struct wmOperatorType *ot);
void PTCACHE_OT_remove(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__ */

View File

@@ -86,6 +86,19 @@ static void operatortypes_particle(void)
WM_operatortype_append(PARTICLE_OT_dupliob_remove); WM_operatortype_append(PARTICLE_OT_dupliob_remove);
WM_operatortype_append(PARTICLE_OT_dupliob_move_up); WM_operatortype_append(PARTICLE_OT_dupliob_move_up);
WM_operatortype_append(PARTICLE_OT_dupliob_move_down); 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) static void keymap_particle(wmKeyConfig *keyconf)

View File

@@ -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);
}
/* ********************************************** */

View File

@@ -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);
}