Depsgraph: Remove duplicated sets of recalc/update flags
[blender.git] / source / blender / editors / physics / rigidbody_object.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2013 Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joshua Leung, Sergej Reich
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file rigidbody_object.c
29  *  \ingroup editor_physics
30  *  \brief Rigid Body object editing operators
31  */
32
33 #include <stdlib.h>
34 #include <string.h>
35
36
37 #include "DNA_object_types.h"
38 #include "DNA_rigidbody_types.h"
39 #include "DNA_scene_types.h"
40
41 #include "BLI_blenlib.h"
42
43 #include "BLT_translation.h"
44
45 #include "BKE_collection.h"
46 #include "BKE_context.h"
47 #include "BKE_library.h"
48 #include "BKE_main.h"
49 #include "BKE_report.h"
50 #include "BKE_rigidbody.h"
51
52 #include "DEG_depsgraph.h"
53 #include "DEG_depsgraph_build.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57 #include "RNA_enum_types.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "ED_object.h"
63 #include "ED_physics.h"
64 #include "ED_screen.h"
65
66 #include "physics_intern.h"
67
68 /* ********************************************** */
69 /* Helper API's for RigidBody Objects Editing */
70
71 static bool ED_operator_rigidbody_active_poll(bContext *C)
72 {
73         if (ED_operator_object_active_editable(C)) {
74                 Object *ob = ED_object_active_context(C);
75                 return (ob && ob->rigidbody_object);
76         }
77         else
78                 return 0;
79 }
80
81 static bool ED_operator_rigidbody_add_poll(bContext *C)
82 {
83         if (ED_operator_object_active_editable(C)) {
84                 Object *ob = ED_object_active_context(C);
85                 return (ob && ob->type == OB_MESH);
86         }
87         else
88                 return 0;
89 }
90
91 /* ----------------- */
92
93 bool ED_rigidbody_object_add(Main *bmain, Scene *scene, Object *ob, int type, ReportList *reports)
94 {
95         RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
96
97         if (ob->type != OB_MESH) {
98                 BKE_report(reports, RPT_ERROR, "Can't add Rigid Body to non mesh object");
99                 return false;
100         }
101
102         /* Add rigid body world and group if they don't exist for convenience */
103         if (rbw == NULL) {
104                 rbw = BKE_rigidbody_create_world(scene);
105                 if (rbw == NULL) {
106                         BKE_report(reports, RPT_ERROR, "Can't create Rigid Body world");
107                         return false;
108                 }
109                 BKE_rigidbody_validate_sim_world(scene, rbw, false);
110                 scene->rigidbody_world = rbw;
111         }
112         if (rbw->group == NULL) {
113                 rbw->group = BKE_collection_add(bmain, NULL, "RigidBodyWorld");
114                 id_fake_user_set(&rbw->group->id);
115         }
116
117         /* make rigidbody object settings */
118         if (ob->rigidbody_object == NULL) {
119                 ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, type);
120         }
121         ob->rigidbody_object->type = type;
122         ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE;
123
124         /* add object to rigid body group */
125         BKE_collection_object_add(bmain, rbw->group, ob);
126
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);
130
131         return true;
132 }
133
134 void ED_rigidbody_object_remove(Main *bmain, Scene *scene, Object *ob)
135 {
136         BKE_rigidbody_remove_object(bmain, scene, ob);
137
138         DEG_relations_tag_update(bmain);
139         DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
140 }
141
142 /* ********************************************** */
143 /* Active Object Add/Remove Operators */
144
145 /* ************ Add Rigid Body ************** */
146
147 static int rigidbody_object_add_exec(bContext *C, wmOperator *op)
148 {
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");
153         bool changed;
154
155         /* apply to active object */
156         changed = ED_rigidbody_object_add(bmain, scene, ob, type, op->reports);
157
158         if (changed) {
159                 /* send updates */
160                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
161                 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
162
163                 /* done */
164                 return OPERATOR_FINISHED;
165         }
166         else {
167                 return OPERATOR_CANCELLED;
168         }
169 }
170
171 void RIGIDBODY_OT_object_add(wmOperatorType *ot)
172 {
173         /* identifiers */
174         ot->idname = "RIGIDBODY_OT_object_add";
175         ot->name = "Add Rigid Body";
176         ot->description = "Add active object as Rigid Body";
177
178         /* callbacks */
179         ot->exec = rigidbody_object_add_exec;
180         ot->poll = ED_operator_rigidbody_add_poll;
181
182         /* flags */
183         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
184
185         /* properties */
186         ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_rigidbody_object_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
187 }
188
189 /* ************ Remove Rigid Body ************** */
190
191 static int rigidbody_object_remove_exec(bContext *C, wmOperator *op)
192 {
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;
197
198         /* apply to active object */
199         if (!ELEM(NULL, ob, ob->rigidbody_object)) {
200                 ED_rigidbody_object_remove(bmain, scene, ob);
201                 changed = true;
202         }
203
204         if (changed) {
205                 /* send updates */
206                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
207                 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
208
209                 /* done */
210                 return OPERATOR_FINISHED;
211         }
212         else {
213                 BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
214                 return OPERATOR_CANCELLED;
215         }
216 }
217
218 void RIGIDBODY_OT_object_remove(wmOperatorType *ot)
219 {
220         /* identifiers */
221         ot->idname = "RIGIDBODY_OT_object_remove";
222         ot->name = "Remove Rigid Body";
223         ot->description = "Remove Rigid Body settings from Object";
224
225         /* callbacks */
226         ot->exec = rigidbody_object_remove_exec;
227         ot->poll = ED_operator_rigidbody_active_poll;
228
229         /* flags */
230         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
231 }
232
233 /* ********************************************** */
234 /* Selected Object Add/Remove Operators */
235
236 /* ************ Add Rigid Bodies ************** */
237
238 static int rigidbody_objects_add_exec(bContext *C, wmOperator *op)
239 {
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;
244
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);
248         }
249         CTX_DATA_END;
250
251         if (changed) {
252                 /* send updates */
253                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
254                 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
255
256                 /* done */
257                 return OPERATOR_FINISHED;
258         }
259         else {
260                 return OPERATOR_CANCELLED;
261         }
262 }
263
264 void RIGIDBODY_OT_objects_add(wmOperatorType *ot)
265 {
266         /* identifiers */
267         ot->idname = "RIGIDBODY_OT_objects_add";
268         ot->name = "Add Rigid Bodies";
269         ot->description = "Add selected objects as Rigid Bodies";
270
271         /* callbacks */
272         ot->exec = rigidbody_objects_add_exec;
273         ot->poll = ED_operator_rigidbody_add_poll;
274
275         /* flags */
276         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
277
278         /* properties */
279         ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_rigidbody_object_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
280 }
281
282 /* ************ Remove Rigid Bodies ************** */
283
284 static int rigidbody_objects_remove_exec(bContext *C, wmOperator *UNUSED(op))
285 {
286         Main *bmain = CTX_data_main(C);
287         Scene *scene = CTX_data_scene(C);
288         bool changed = false;
289
290         /* apply this to all selected objects... */
291         CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
292         {
293                 if (ob->rigidbody_object) {
294                         ED_rigidbody_object_remove(bmain, scene, ob);
295                         changed = true;
296                 }
297         }
298         CTX_DATA_END;
299
300         if (changed) {
301                 /* send updates */
302                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
303                 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
304
305                 /* done */
306                 return OPERATOR_FINISHED;
307         }
308         else {
309                 return OPERATOR_CANCELLED;
310         }
311 }
312
313 void RIGIDBODY_OT_objects_remove(wmOperatorType *ot)
314 {
315         /* identifiers */
316         ot->idname = "RIGIDBODY_OT_objects_remove";
317         ot->name = "Remove Rigid Bodies";
318         ot->description = "Remove selected objects from Rigid Body simulation";
319
320         /* callbacks */
321         ot->exec = rigidbody_objects_remove_exec;
322         ot->poll = ED_operator_scene_editable;
323
324         /* flags */
325         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
326 }
327
328 /* ********************************************** */
329 /* Utility Operators */
330
331 /* ************ Change Collision Shapes ************** */
332
333 static int rigidbody_objects_shape_change_exec(bContext *C, wmOperator *op)
334 {
335         int shape = RNA_enum_get(op->ptr, "type");
336         bool changed = false;
337
338         /* apply this to all selected objects... */
339         CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
340         {
341                 if (ob->rigidbody_object) {
342                         PointerRNA ptr;
343
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);
347
348                         DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
349
350                         changed = true;
351                 }
352         }
353         CTX_DATA_END;
354
355         if (changed) {
356                 /* send updates */
357                 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
358                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
359
360                 /* done */
361                 return OPERATOR_FINISHED;
362         }
363         else {
364                 return OPERATOR_CANCELLED;
365         }
366 }
367
368 void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
369 {
370         /* identifiers */
371         ot->idname = "RIGIDBODY_OT_shape_change";
372         ot->name = "Change Collision Shape";
373         ot->description = "Change collision shapes for selected Rigid Body Objects";
374
375         /* callbacks */
376         ot->invoke = WM_menu_invoke;
377         ot->exec = rigidbody_objects_shape_change_exec;
378         ot->poll = ED_operator_scene_editable;
379
380         /* flags */
381         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
382
383         /* properties */
384         ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_rigidbody_object_shape_items, RB_SHAPE_TRIMESH, "Rigid Body Shape", "");
385 }
386
387 /* ************ Calculate Mass ************** */
388
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;
394
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
400  */
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}
450 };
451 static const int NUM_RB_MATERIAL_PRESETS = sizeof(RB_MATERIAL_DENSITY_TABLE) / sizeof(rbMaterialDensityItem);
452
453
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...
457  */
458 static const EnumPropertyItem *rigidbody_materials_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
459 {
460         EnumPropertyItem item_tmp = {0};
461         EnumPropertyItem *item = NULL;
462         int totitem = 0;
463         int i = 0;
464
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];
468
469                 item_tmp.identifier = preset->name;
470                 item_tmp.name = IFACE_(preset->name);
471                 item_tmp.value = i;
472                 RNA_enum_item_add(&item, &totitem, &item_tmp);
473         }
474
475         /* add special "custom" entry to the end of the list */
476         {
477                 item_tmp.identifier = "Custom";
478                 item_tmp.name = IFACE_("Custom");
479                 item_tmp.value = -1;
480                 RNA_enum_item_add(&item, &totitem, &item_tmp);
481         }
482
483         RNA_enum_item_end(&item, &totitem);
484         *r_free = true;
485
486         return item;
487 }
488
489 /* ------------------------------------------ */
490
491 static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op)
492 {
493         int material = RNA_enum_get(op->ptr, "material");
494         float density;
495         bool changed = false;
496
497         /* get density (kg/m^3) to apply */
498         if (material >= 0) {
499                 /* get density from table, and store in props for later repeating */
500                 if (material >= NUM_RB_MATERIAL_PRESETS)
501                         material = 0;
502
503                 density = RB_MATERIAL_DENSITY_TABLE[material].density;
504                 RNA_float_set(op->ptr, "density", density);
505         }
506         else {
507                 /* custom - grab from whatever value is set */
508                 density = RNA_float_get(op->ptr, "density");
509         }
510
511         /* apply this to all selected objects (with rigidbodies)... */
512         CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
513         {
514                 if (ob->rigidbody_object) {
515                         PointerRNA ptr;
516
517                         float volume; /* m^3 */
518                         float mass;   /* kg */
519
520                         /* mass is calculated from the approximate volume of the object,
521                          * and the density of the material we're simulating
522                          */
523                         BKE_rigidbody_calc_volume(ob, &volume);
524                         mass = volume * density;
525
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);
529
530                         DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
531
532                         changed = true;
533                 }
534         }
535         CTX_DATA_END;
536
537         if (changed) {
538                 /* send updates */
539                 WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
540
541                 /* done */
542                 return OPERATOR_FINISHED;
543         }
544         else {
545                 return OPERATOR_CANCELLED;
546         }
547 }
548
549 void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
550 {
551         PropertyRNA *prop;
552
553         /* identifiers */
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";
557
558         /* callbacks */
559         ot->invoke = WM_menu_invoke; // XXX
560         ot->exec = rigidbody_objects_calc_mass_exec;
561         ot->poll = ED_operator_scene_editable;
562
563         /* flags */
564         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
565
566         /* properties */
567         ot->prop = prop = RNA_def_enum(ot->srna, "material",
568                                        DummyRNA_DEFAULT_items, 0,
569                                        "Material Preset",
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);
573
574         RNA_def_float(ot->srna, "density", 1.0, FLT_MIN, FLT_MAX,
575                       "Density",
576                       "Custom density value (kg/m^3) to use instead of material preset",
577                       1.0f, 2500.0f);
578 }
579
580 /* ********************************************** */