From 2d8637946b047a8a9cc3fb6fe6d146b9961f92a6 Mon Sep 17 00:00:00 2001 From: Sergej Reich Date: Wed, 23 Jan 2013 05:56:44 +0000 Subject: 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) --- source/blender/blenkernel/intern/object.c | 2 + source/blender/blenkernel/intern/rigidbody.c | 103 +++- source/blender/blenkernel/intern/scene.c | 6 + source/blender/editors/include/ED_physics.h | 9 + source/blender/editors/object/object_add.c | 6 +- source/blender/editors/physics/CMakeLists.txt | 3 + source/blender/editors/physics/SConscript | 1 + source/blender/editors/physics/physics_intern.h | 16 +- source/blender/editors/physics/physics_ops.c | 13 + source/blender/editors/physics/rigidbody_object.c | 622 ++++++++++++++++++++++ source/blender/editors/physics/rigidbody_world.c | 210 ++++++++ 11 files changed, 987 insertions(+), 4 deletions(-) create mode 100644 source/blender/editors/physics/rigidbody_object.c create mode 100644 source/blender/editors/physics/rigidbody_world.c (limited to 'source') 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 +#include + +#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 +#include + +#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); +} -- cgit v1.2.3