2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2013 Blender Foundation
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Joshua Leung, Sergej Reich
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file rigidbody_object.c
29 * \ingroup editor_physics
30 * \brief Rigid Body object editing operators
37 #include "DNA_object_types.h"
38 #include "DNA_rigidbody_types.h"
39 #include "DNA_scene_types.h"
41 #include "BLI_blenlib.h"
43 #include "BLT_translation.h"
45 #include "BKE_collection.h"
46 #include "BKE_context.h"
47 #include "BKE_library.h"
49 #include "BKE_report.h"
50 #include "BKE_rigidbody.h"
52 #include "DEG_depsgraph.h"
53 #include "DEG_depsgraph_build.h"
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57 #include "RNA_enum_types.h"
62 #include "ED_object.h"
63 #include "ED_physics.h"
64 #include "ED_screen.h"
66 #include "physics_intern.h"
68 /* ********************************************** */
69 /* Helper API's for RigidBody Objects Editing */
71 static bool ED_operator_rigidbody_active_poll(bContext *C)
73 if (ED_operator_object_active_editable(C)) {
74 Object *ob = ED_object_active_context(C);
75 return (ob && ob->rigidbody_object);
81 static bool ED_operator_rigidbody_add_poll(bContext *C)
83 if (ED_operator_object_active_editable(C)) {
84 Object *ob = ED_object_active_context(C);
85 return (ob && ob->type == OB_MESH);
91 /* ----------------- */
93 bool ED_rigidbody_object_add(Main *bmain, Scene *scene, Object *ob, int type, ReportList *reports)
95 RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
97 if (ob->type != OB_MESH) {
98 BKE_report(reports, RPT_ERROR, "Can't add Rigid Body to non mesh object");
102 /* Add rigid body world and group if they don't exist for convenience */
104 rbw = BKE_rigidbody_create_world(scene);
106 BKE_report(reports, RPT_ERROR, "Can't create Rigid Body world");
109 BKE_rigidbody_validate_sim_world(scene, rbw, false);
110 scene->rigidbody_world = rbw;
112 if (rbw->group == NULL) {
113 rbw->group = BKE_collection_add(bmain, NULL, "RigidBodyWorld");
114 id_fake_user_set(&rbw->group->id);
117 /* make rigidbody object settings */
118 if (ob->rigidbody_object == NULL) {
119 ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, type);
121 ob->rigidbody_object->type = type;
122 ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE;
124 /* add object to rigid body group */
125 BKE_collection_object_add(bmain, rbw->group, ob);
127 DEG_relations_tag_update(bmain);
128 DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
129 DEG_id_tag_update(&rbw->group->id, ID_RECALC_COPY_ON_WRITE);
134 void ED_rigidbody_object_remove(Main *bmain, Scene *scene, Object *ob)
136 BKE_rigidbody_remove_object(bmain, scene, ob);
138 DEG_relations_tag_update(bmain);
139 DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
142 /* ********************************************** */
143 /* Active Object Add/Remove Operators */
145 /* ************ Add Rigid Body ************** */
147 static int rigidbody_object_add_exec(bContext *C, wmOperator *op)
149 Main *bmain = CTX_data_main(C);
150 Scene *scene = CTX_data_scene(C);
151 Object *ob = ED_object_active_context(C);
152 int type = RNA_enum_get(op->ptr, "type");
155 /* apply to active object */
156 changed = ED_rigidbody_object_add(bmain, scene, ob, type, op->reports);
160 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
161 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
164 return OPERATOR_FINISHED;
167 return OPERATOR_CANCELLED;
171 void RIGIDBODY_OT_object_add(wmOperatorType *ot)
174 ot->idname = "RIGIDBODY_OT_object_add";
175 ot->name = "Add Rigid Body";
176 ot->description = "Add active object as Rigid Body";
179 ot->exec = rigidbody_object_add_exec;
180 ot->poll = ED_operator_rigidbody_add_poll;
183 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
186 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_rigidbody_object_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
189 /* ************ Remove Rigid Body ************** */
191 static int rigidbody_object_remove_exec(bContext *C, wmOperator *op)
193 Main *bmain = CTX_data_main(C);
194 Scene *scene = CTX_data_scene(C);
195 Object *ob = ED_object_active_context(C);
196 bool changed = false;
198 /* apply to active object */
199 if (!ELEM(NULL, ob, ob->rigidbody_object)) {
200 ED_rigidbody_object_remove(bmain, scene, ob);
206 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
207 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
210 return OPERATOR_FINISHED;
213 BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
214 return OPERATOR_CANCELLED;
218 void RIGIDBODY_OT_object_remove(wmOperatorType *ot)
221 ot->idname = "RIGIDBODY_OT_object_remove";
222 ot->name = "Remove Rigid Body";
223 ot->description = "Remove Rigid Body settings from Object";
226 ot->exec = rigidbody_object_remove_exec;
227 ot->poll = ED_operator_rigidbody_active_poll;
230 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
233 /* ********************************************** */
234 /* Selected Object Add/Remove Operators */
236 /* ************ Add Rigid Bodies ************** */
238 static int rigidbody_objects_add_exec(bContext *C, wmOperator *op)
240 Main *bmain = CTX_data_main(C);
241 Scene *scene = CTX_data_scene(C);
242 int type = RNA_enum_get(op->ptr, "type");
243 bool changed = false;
245 /* create rigid body objects and add them to the world's group */
246 CTX_DATA_BEGIN(C, Object *, ob, selected_objects) {
247 changed |= ED_rigidbody_object_add(bmain, scene, ob, type, op->reports);
253 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
254 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
257 return OPERATOR_FINISHED;
260 return OPERATOR_CANCELLED;
264 void RIGIDBODY_OT_objects_add(wmOperatorType *ot)
267 ot->idname = "RIGIDBODY_OT_objects_add";
268 ot->name = "Add Rigid Bodies";
269 ot->description = "Add selected objects as Rigid Bodies";
272 ot->exec = rigidbody_objects_add_exec;
273 ot->poll = ED_operator_rigidbody_add_poll;
276 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
279 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_rigidbody_object_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
282 /* ************ Remove Rigid Bodies ************** */
284 static int rigidbody_objects_remove_exec(bContext *C, wmOperator *UNUSED(op))
286 Main *bmain = CTX_data_main(C);
287 Scene *scene = CTX_data_scene(C);
288 bool changed = false;
290 /* apply this to all selected objects... */
291 CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
293 if (ob->rigidbody_object) {
294 ED_rigidbody_object_remove(bmain, scene, ob);
302 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
303 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
306 return OPERATOR_FINISHED;
309 return OPERATOR_CANCELLED;
313 void RIGIDBODY_OT_objects_remove(wmOperatorType *ot)
316 ot->idname = "RIGIDBODY_OT_objects_remove";
317 ot->name = "Remove Rigid Bodies";
318 ot->description = "Remove selected objects from Rigid Body simulation";
321 ot->exec = rigidbody_objects_remove_exec;
322 ot->poll = ED_operator_scene_editable;
325 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
328 /* ********************************************** */
329 /* Utility Operators */
331 /* ************ Change Collision Shapes ************** */
333 static int rigidbody_objects_shape_change_exec(bContext *C, wmOperator *op)
335 int shape = RNA_enum_get(op->ptr, "type");
336 bool changed = false;
338 /* apply this to all selected objects... */
339 CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
341 if (ob->rigidbody_object) {
344 /* use RNA-system to change the property and perform all necessary changes */
345 RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
346 RNA_enum_set(&ptr, "collision_shape", shape);
348 DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
357 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
358 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
361 return OPERATOR_FINISHED;
364 return OPERATOR_CANCELLED;
368 void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
371 ot->idname = "RIGIDBODY_OT_shape_change";
372 ot->name = "Change Collision Shape";
373 ot->description = "Change collision shapes for selected Rigid Body Objects";
376 ot->invoke = WM_menu_invoke;
377 ot->exec = rigidbody_objects_shape_change_exec;
378 ot->poll = ED_operator_scene_editable;
381 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
384 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_rigidbody_object_shape_items, RB_SHAPE_TRIMESH, "Rigid Body Shape", "");
387 /* ************ Calculate Mass ************** */
389 /* Entry in material density table */
390 typedef struct rbMaterialDensityItem {
391 const char *name; /* Name of material */
392 float density; /* Density (kg/m^3) */
393 } rbMaterialDensityItem;
395 /* Preset density values for materials (kg/m^3)
396 * Selected values obtained from:
397 * 1) http://www.jaredzone.info/2010/09/densities.html
398 * 2) http://www.avlandesign.com/density_construction.htm
399 * 3) http://www.avlandesign.com/density_metal.htm
401 static rbMaterialDensityItem RB_MATERIAL_DENSITY_TABLE[] = {
402 {N_("Air"), 1.0f}, /* not quite; adapted from 1.43 for oxygen for use as default */
403 {N_("Acrylic"), 1400.0f},
404 {N_("Asphalt (Crushed)"), 721.0f},
405 {N_("Bark"), 240.0f},
406 {N_("Beans (Cocoa)"), 593.0f},
407 {N_("Beans (Soy)"), 721.0f},
408 {N_("Brick (Pressed)"), 2400.0f},
409 {N_("Brick (Common)"), 2000.0f},
410 {N_("Brick (Soft)"), 1600.0f},
411 {N_("Brass"), 8216.0f},
412 {N_("Bronze"), 8860.0f},
413 {N_("Carbon (Solid)"), 2146.0f},
414 {N_("Cardboard"), 689.0f},
415 {N_("Cast Iron"), 7150.0f},
416 /* {N_("Cement"), 1442.0f}, */
417 {N_("Chalk (Solid)"), 2499.0f},
418 /* {N_("Coffee (Fresh/Roast)"), ~500}, */
419 {N_("Concrete"), 2320.0f},
420 {N_("Charcoal"), 208.0f},
421 {N_("Cork"), 240.0f},
422 {N_("Copper"), 8933.0f},
423 {N_("Garbage"), 481.0f},
424 {N_("Glass (Broken)"), 1940.0f},
425 {N_("Glass (Solid)"), 2190.0f},
426 {N_("Gold"), 19282.0f},
427 {N_("Granite (Broken)"), 1650.0f},
428 {N_("Granite (Solid)"), 2691.0f},
429 {N_("Gravel"), 2780.0f},
430 {N_("Ice (Crushed)"), 593.0f},
431 {N_("Ice (Solid)"), 919.0f},
432 {N_("Iron"), 7874.0f},
433 {N_("Lead"), 11342.0f},
434 {N_("Limestone (Broken)"), 1554.0f},
435 {N_("Limestone (Solid)"), 2611.0f},
436 {N_("Marble (Broken)"), 1570.0f},
437 {N_("Marble (Solid)"), 2563.0f},
438 {N_("Paper"), 1201.0f},
439 {N_("Peanuts (Shelled)"), 641.0f},
440 {N_("Peanuts (Not Shelled)"), 272.0f},
441 {N_("Plaster"), 849.0f},
442 {N_("Plastic"), 1200.0f},
443 {N_("Polystyrene"), 1050.0f},
444 {N_("Rubber"), 1522.0f},
445 {N_("Silver"), 10501.0f},
446 {N_("Steel"), 7860.0f},
447 {N_("Stone"), 2515.0f},
448 {N_("Stone (Crushed)"), 1602.0f},
449 {N_("Timber"), 610.0f}
451 static const int NUM_RB_MATERIAL_PRESETS = sizeof(RB_MATERIAL_DENSITY_TABLE) / sizeof(rbMaterialDensityItem);
454 /* dynamically generate list of items
455 * - Although there is a runtime cost, this has a lower maintenance cost
456 * in the long run than other two-list solutions...
458 static const EnumPropertyItem *rigidbody_materials_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
460 EnumPropertyItem item_tmp = {0};
461 EnumPropertyItem *item = NULL;
465 /* add each preset to the list */
466 for (i = 0; i < NUM_RB_MATERIAL_PRESETS; i++) {
467 rbMaterialDensityItem *preset = &RB_MATERIAL_DENSITY_TABLE[i];
469 item_tmp.identifier = preset->name;
470 item_tmp.name = IFACE_(preset->name);
472 RNA_enum_item_add(&item, &totitem, &item_tmp);
475 /* add special "custom" entry to the end of the list */
477 item_tmp.identifier = "Custom";
478 item_tmp.name = IFACE_("Custom");
480 RNA_enum_item_add(&item, &totitem, &item_tmp);
483 RNA_enum_item_end(&item, &totitem);
489 /* ------------------------------------------ */
491 static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op)
493 int material = RNA_enum_get(op->ptr, "material");
495 bool changed = false;
497 /* get density (kg/m^3) to apply */
499 /* get density from table, and store in props for later repeating */
500 if (material >= NUM_RB_MATERIAL_PRESETS)
503 density = RB_MATERIAL_DENSITY_TABLE[material].density;
504 RNA_float_set(op->ptr, "density", density);
507 /* custom - grab from whatever value is set */
508 density = RNA_float_get(op->ptr, "density");
511 /* apply this to all selected objects (with rigidbodies)... */
512 CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
514 if (ob->rigidbody_object) {
517 float volume; /* m^3 */
520 /* mass is calculated from the approximate volume of the object,
521 * and the density of the material we're simulating
523 BKE_rigidbody_calc_volume(ob, &volume);
524 mass = volume * density;
526 /* use RNA-system to change the property and perform all necessary changes */
527 RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
528 RNA_float_set(&ptr, "mass", mass);
530 DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
539 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
542 return OPERATOR_FINISHED;
545 return OPERATOR_CANCELLED;
549 void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
554 ot->idname = "RIGIDBODY_OT_mass_calculate";
555 ot->name = "Calculate Mass";
556 ot->description = "Automatically calculate mass values for Rigid Body Objects based on volume";
559 ot->invoke = WM_menu_invoke; // XXX
560 ot->exec = rigidbody_objects_calc_mass_exec;
561 ot->poll = ED_operator_scene_editable;
564 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
567 ot->prop = prop = RNA_def_enum(ot->srna, "material",
568 DummyRNA_DEFAULT_items, 0,
570 "Type of material that objects are made of (determines material density)");
571 RNA_def_enum_funcs(prop, rigidbody_materials_itemf);
572 RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
574 RNA_def_float(ot->srna, "density", 1.0, FLT_MIN, FLT_MAX,
576 "Custom density value (kg/m^3) to use instead of material preset",
580 /* ********************************************** */