Rigid body UI: add translation to materials' names (density collection).
[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 #include "MEM_guardedalloc.h"
37
38 #include "DNA_group_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_mesh_types.h"
41 #include "DNA_rigidbody_types.h"
42 #include "DNA_scene_types.h"
43
44 #include "BLI_blenlib.h"
45 #include "BLI_math.h"
46
47 #include "BLF_translation.h"
48
49 #include "BKE_context.h"
50 #include "BKE_depsgraph.h"
51 #include "BKE_global.h"
52 #include "BKE_group.h"
53 #include "BKE_object.h"
54 #include "BKE_report.h"
55 #include "BKE_rigidbody.h"
56
57 #include "RNA_access.h"
58 #include "RNA_define.h"
59 #include "RNA_enum_types.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "ED_physics.h"
65 #include "ED_screen.h"
66
67 #include "physics_intern.h"
68
69 /* ********************************************** */
70 /* Helper API's for RigidBody Objects Editing */
71
72 static int ED_operator_rigidbody_active_poll(bContext *C)
73 {
74         if (ED_operator_object_active_editable(C)) {
75                 Object *ob = CTX_data_active_object(C);
76                 return (ob && ob->rigidbody_object);
77         }
78         else
79                 return 0;
80 }
81
82 static int ED_operator_rigidbody_add_poll(bContext *C)
83 {
84         if (ED_operator_object_active_editable(C)) {
85                 Object *ob = CTX_data_active_object(C);
86                 return (ob && ob->type == OB_MESH);
87         }
88         else
89                 return 0;
90 }
91
92 /* ----------------- */
93
94 void ED_rigidbody_ob_add(wmOperator *op, Scene *scene, Object *ob, int type)
95 {
96         RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
97
98         if (ob->type != OB_MESH) {
99                 BKE_report(op->reports, RPT_ERROR, "Can't add Rigid Body to non mesh object");
100                 return;
101         }
102         if (((Mesh *)ob->data)->totpoly == 0) {
103                 BKE_report(op->reports, RPT_ERROR, "Can't create Rigid Body from mesh with no polygons");
104                 return;
105         }
106
107         /* Add rigid body world and group if they don't exist for convenience */
108         if (rbw == NULL) {
109                 rbw = BKE_rigidbody_create_world(scene);
110                 BKE_rigidbody_validate_sim_world(scene, rbw, false);
111                 scene->rigidbody_world = rbw;
112         }
113         if (rbw->group == NULL) {
114                 rbw->group = add_group(G.main, "RigidBodyWorld");
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         add_to_group(rbw->group, ob, scene, NULL);
126
127         DAG_id_tag_update(&ob->id, OB_RECALC_OB);
128 }
129
130 void ED_rigidbody_ob_remove(Scene *scene, Object *ob)
131 {
132         RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
133
134         BKE_rigidbody_remove_object(scene, ob);
135         if (rbw)
136                 rem_from_group(rbw->group, ob, scene, NULL);
137
138         DAG_id_tag_update(&ob->id, OB_RECALC_OB);
139 }
140
141 /* ********************************************** */
142 /* Active Object Add/Remove Operators */
143
144 /* ************ Add Rigid Body ************** */
145
146 static int rigidbody_ob_add_exec(bContext *C, wmOperator *op)
147 {
148         Scene *scene = CTX_data_scene(C);
149         Object *ob = (scene) ? OBACT : NULL;
150         int type = RNA_enum_get(op->ptr, "type");
151
152         /* apply to active object */
153         ED_rigidbody_ob_add(op, scene, ob, type);
154
155         /* send updates */
156         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
157
158         /* done */
159         return OPERATOR_FINISHED;
160 }
161
162 void RIGIDBODY_OT_object_add(wmOperatorType *ot)
163 {
164         /* identifiers */
165         ot->idname = "RIGIDBODY_OT_object_add";
166         ot->name = "Add Rigid Body";
167         ot->description = "Add active object as Rigid Body";
168
169         /* callbacks */
170         ot->exec = rigidbody_ob_add_exec;
171         ot->poll = ED_operator_rigidbody_add_poll;
172
173         /* flags */
174         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
175
176         /* properties */
177         ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
178 }
179
180 /* ************ Remove Rigid Body ************** */
181
182 static int rigidbody_ob_remove_exec(bContext *C, wmOperator *op)
183 {
184         Scene *scene = CTX_data_scene(C);
185         Object *ob = (scene) ? OBACT : NULL;
186
187         /* sanity checks */
188         if (scene == NULL)
189                 return OPERATOR_CANCELLED;
190
191         /* apply to active object */
192         if (ELEM(NULL, ob, ob->rigidbody_object)) {
193                 BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
194                 return OPERATOR_CANCELLED;
195         }
196         else
197                 ED_rigidbody_ob_remove(scene, ob);
198
199         /* send updates */
200         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
201
202         /* done */
203         return OPERATOR_FINISHED;
204 }
205
206 void RIGIDBODY_OT_object_remove(wmOperatorType *ot)
207 {
208         /* identifiers */
209         ot->idname = "RIGIDBODY_OT_object_remove";
210         ot->name = "Remove Rigid Body";
211         ot->description = "Remove Rigid Body settings from Object";
212
213         /* callbacks */
214         ot->exec = rigidbody_ob_remove_exec;
215         ot->poll = ED_operator_rigidbody_active_poll;
216
217         /* flags */
218         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
219 }
220
221 /* ********************************************** */
222 /* Selected Object Add/Remove Operators */
223
224 /* ************ Add Rigid Bodies ************** */
225
226 static int rigidbody_obs_add_exec(bContext *C, wmOperator *op)
227 {
228         Scene *scene = CTX_data_scene(C);
229         int type = RNA_enum_get(op->ptr, "type");
230
231         /* sanity check */
232         if (scene == NULL) {
233                 BKE_report(op->reports, RPT_ERROR, "No Scene to add Rigid Bodies to");
234                 return OPERATOR_CANCELLED;
235         }
236         /* create rigid body objects and add them to the world's group */
237         CTX_DATA_BEGIN(C, Object *, ob, selected_objects) {
238                 ED_rigidbody_ob_add(op, scene, ob, type);
239         }
240         CTX_DATA_END;
241
242         /* send updates */
243         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
244         WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
245
246         /* done */
247         return OPERATOR_FINISHED;
248 }
249
250 void RIGIDBODY_OT_objects_add(wmOperatorType *ot)
251 {
252         /* identifiers */
253         ot->idname = "RIGIDBODY_OT_objects_add";
254         ot->name = "Add Rigid Bodies";
255         ot->description = "Add selected objects as Rigid Bodies";
256
257         /* callbacks */
258         ot->exec = rigidbody_obs_add_exec;
259         ot->poll = ED_operator_rigidbody_add_poll;
260
261         /* flags */
262         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
263
264         /* properties */
265         ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
266 }
267
268 /* ************ Remove Rigid Bodies ************** */
269
270 static int rigidbody_obs_remove_exec(bContext *C, wmOperator *UNUSED(op))
271 {
272         Scene *scene = CTX_data_scene(C);
273
274         /* sanity checks */
275         if (scene == NULL)
276                 return OPERATOR_CANCELLED;
277
278         /* apply this to all selected objects... */
279         CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
280         {
281                 if (ob->rigidbody_object) {
282                         ED_rigidbody_ob_remove(scene, ob);
283                 }
284         }
285         CTX_DATA_END;
286
287         /* send updates */
288         WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
289
290         /* done */
291         return OPERATOR_FINISHED;
292 }
293
294 void RIGIDBODY_OT_objects_remove(wmOperatorType *ot)
295 {
296         /* identifiers */
297         ot->idname = "RIGIDBODY_OT_objects_remove";
298         ot->name = "Remove Rigid Bodies";
299         ot->description = "Remove selected objects from Rigid Body simulation";
300
301         /* callbacks */
302         ot->exec = rigidbody_obs_remove_exec;
303         ot->poll = ED_operator_rigidbody_active_poll;
304
305         /* flags */
306         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
307 }
308
309 /* ********************************************** */
310 /* Utility Operators */
311
312 /* ************ Change Collision Shapes ************** */
313
314 static int rigidbody_obs_shape_change_exec(bContext *C, wmOperator *op)
315 {
316         Scene *scene = CTX_data_scene(C);
317         int shape = RNA_enum_get(op->ptr, "type");
318
319         /* sanity checks */
320         if (scene == NULL)
321                 return OPERATOR_CANCELLED;
322
323         /* apply this to all selected objects... */
324         CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
325         {
326                 if (ob->rigidbody_object) {
327                         PointerRNA ptr;
328
329                         /* use RNA-system to change the property and perform all necessary changes */
330                         RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
331                         RNA_enum_set(&ptr, "collision_shape", shape);
332
333                         DAG_id_tag_update(&ob->id, OB_RECALC_OB);
334                 }
335         }
336         CTX_DATA_END;
337
338         /* send updates */
339         WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
340
341         /* done */
342         return OPERATOR_FINISHED;
343 }
344
345 void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
346 {
347         /* identifiers */
348         ot->idname = "RIGIDBODY_OT_shape_change";
349         ot->name = "Change Collision Shape";
350         ot->description = "Change collision shapes for selected Rigid Body Objects";
351
352         /* callbacks */
353         ot->invoke = WM_menu_invoke;
354         ot->exec = rigidbody_obs_shape_change_exec;
355         ot->poll = ED_operator_rigidbody_active_poll;
356
357         /* flags */
358         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
359
360         /* properties */
361         ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_shape_items, RB_SHAPE_TRIMESH, "Rigid Body Shape", "");
362 }
363
364 /* ************ Calculate Mass ************** */
365
366 /* Entry in material density table */
367 typedef struct rbMaterialDensityItem {
368         const char *name;   /* Name of material */
369         float density;      /* Density (kg/m^3) */
370 } rbMaterialDensityItem;
371
372 /* Preset density values for materials (kg/m^3)
373  * Selected values obtained from:
374  * 1) http://www.jaredzone.info/2010/09/densities.html
375  * 2) http://www.avlandesign.com/density_construction.htm
376  * 3) http://www.avlandesign.com/density_metal.htm
377  */
378 static rbMaterialDensityItem RB_MATERIAL_DENSITY_TABLE[] = {
379         {N_("Air"), 1.0f}, /* not quite; adapted from 1.43 for oxygen for use as default */
380         {N_("Acrylic"), 1400.0f},
381         {N_("Asphalt (Crushed)"), 721.0f},
382         {N_("Bark"), 240.0f},
383         {N_("Beans (Cocoa)"), 593.0f},
384         {N_("Beans (Soy)"), 721.0f},
385         {N_("Brick (Pressed)"), 2400.0f},
386         {N_("Brick (Common)"), 2000.0f},
387         {N_("Brick (Soft)"), 1600.0f},
388         {N_("Brass"), 8216.0f},
389         {N_("Bronze"), 8860.0f},
390         {N_("Carbon (Solid)"), 2146.0f},
391         {N_("Cardboard"), 689.0f},
392         {N_("Cast Iron"), 7150.0f},
393         /* {N_("Cement"), 1442.0f}, */
394         {N_("Chalk (Solid)"), 2499.0f},
395         /* {N_("Coffee (Fresh/Roast)"), ~500}, */
396         {N_("Concrete"), 2320.0f},
397         {N_("Charcoal"), 208.0f},
398         {N_("Cork"), 240.0f},
399         {N_("Copper"), 8933.0f},
400         {N_("Garbage"), 481.0f},
401         {N_("Glass (Broken)"), 1940.0f},
402         {N_("Glass (Solid)"), 2190.0f},
403         {N_("Gold"), 19282.0f},
404         {N_("Granite (Broken)"), 1650.0f},
405         {N_("Granite (Solid)"), 2691.0f},
406         {N_("Gravel"), 2780.0f},
407         {N_("Ice (Crushed)"), 593.0f},
408         {N_("Ice (Solid)"), 919.0f},
409         {N_("Iron"), 7874.0f},
410         {N_("Lead"), 11342.0f},
411         {N_("Limestone (Broken)"), 1554.0f},
412         {N_("Limestone (Solid)"), 2611.0f},
413         {N_("Marble (Broken)"), 1570.0f},
414         {N_("Marble (Solid)"), 2563.0f},
415         {N_("Paper"), 1201.0f},
416         {N_("Peanuts (Shelled)"), 641.0f},
417         {N_("Peanuts (Not Shelled)"), 272.0f},
418         {N_("Plaster"), 849.0f},
419         {N_("Plastic"), 1200.0f},
420         {N_("Polystyrene"), 1050.0f},
421         {N_("Rubber"), 1522.0f},
422         {N_("Silver"), 10501.0f},
423         {N_("Steel"), 7860.0f},
424         {N_("Stone"), 2515.0f},
425         {N_("Stone (Crushed)"), 1602.0f},
426         {N_("Timber"), 610.0f}
427 };
428 static const int NUM_RB_MATERIAL_PRESETS = sizeof(RB_MATERIAL_DENSITY_TABLE) / sizeof(rbMaterialDensityItem);
429
430
431 /* dynamically generate list of items
432  * - Although there is a runtime cost, this has a lower maintenance cost
433  *   in the long run than other two-list solutions...
434  */
435 static EnumPropertyItem *rigidbody_materials_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
436 {
437         EnumPropertyItem item_tmp = {0};
438         EnumPropertyItem *item = NULL;
439         int totitem = 0;
440         int i = 0;
441
442         /* add each preset to the list */
443         for (i = 0; i < NUM_RB_MATERIAL_PRESETS; i++) {
444                 rbMaterialDensityItem *preset = &RB_MATERIAL_DENSITY_TABLE[i];
445
446                 item_tmp.identifier = preset->name;
447                 item_tmp.name = IFACE_(preset->name);
448                 item_tmp.value = i;
449                 RNA_enum_item_add(&item, &totitem, &item_tmp);
450         }
451
452         /* add special "custom" entry to the end of the list */
453         {
454                 item_tmp.identifier = "Custom";
455                 item_tmp.name = IFACE_("Custom");
456                 item_tmp.value = -1;
457                 RNA_enum_item_add(&item, &totitem, &item_tmp);
458         }
459
460         RNA_enum_item_end(&item, &totitem);
461         *free = 1;
462
463         return item;
464 }
465
466 /* ------------------------------------------ */
467
468 /* helper function to calculate volume of rigidbody object */
469 // TODO: allow a parameter to specify method used to calculate this?
470 static float calc_rigidbody_ob_volume(Object *ob)
471 {
472         RigidBodyOb *rbo = ob->rigidbody_object;
473
474         float size[3]  = {1.0f, 1.0f, 1.0f};
475         float radius = 1.0f;
476         float height = 1.0f;
477
478         float volume = 0.0f;
479
480         /* if automatically determining dimensions, use the Object's boundbox
481          *      - assume that all quadrics are standing upright on local z-axis
482          *      - assume even distribution of mass around the Object's pivot
483          *        (i.e. Object pivot is centralised in boundbox)
484          *      - boundbox gives full width
485          */
486         // XXX: all dimensions are auto-determined now... later can add stored settings for this
487         BKE_object_dimensions_get(ob, size);
488
489         if (ELEM3(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) {
490                 /* take radius as largest x/y dimension, and height as z-dimension */
491                 radius = MAX2(size[0], size[1]) * 0.5f;
492                 height = size[2];
493         }
494         else if (rbo->shape == RB_SHAPE_SPHERE) {
495                 /* take radius to the the largest dimension to try and encompass everything */
496                 radius = max_fff(size[0], size[1], size[2]) * 0.5f;
497         }
498
499         /* calculate volume as appropriate  */
500         switch (rbo->shape) {
501                 case RB_SHAPE_BOX:
502                         volume = size[0] * size[1] * size[2];
503                         break;
504
505                 case RB_SHAPE_SPHERE:
506                         volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius;
507                         break;
508
509                 /* for now, assume that capsule is close enough to a cylinder... */
510                 case RB_SHAPE_CAPSULE:
511                 case RB_SHAPE_CYLINDER:
512                         volume = (float)M_PI * radius * radius * height;
513                         break;
514
515                 case RB_SHAPE_CONE:
516                         volume = (float)M_PI / 3.0f * radius * radius * height;
517                         break;
518
519                 /* for now, all mesh shapes are just treated as boxes...
520                  * NOTE: this may overestimate the volume, but other methods are overkill
521                  */
522                 case RB_SHAPE_CONVEXH:
523                 case RB_SHAPE_TRIMESH:
524                         volume = size[0] * size[1] * size[2];
525                         break;
526
527 #if 0 // XXX: not defined yet
528                 case RB_SHAPE_COMPOUND:
529                         volume = 0.0f;
530                         break;
531 #endif
532         }
533
534         /* return the volume calculated */
535         return volume;
536 }
537
538 /* ------------------------------------------ */
539
540 static int rigidbody_obs_calc_mass_exec(bContext *C, wmOperator *op)
541 {
542         Scene *scene = CTX_data_scene(C);
543         int material = RNA_enum_get(op->ptr, "material");
544         float density;
545
546         /* sanity checks */
547         if (scene == NULL)
548                 return OPERATOR_CANCELLED;
549
550         /* get density (kg/m^3) to apply */
551         if (material >= 0) {
552                 /* get density from table, and store in props for later repeating */
553                 if (material >= NUM_RB_MATERIAL_PRESETS)
554                         material = 0;
555
556                 density = RB_MATERIAL_DENSITY_TABLE[material].density;
557                 RNA_float_set(op->ptr, "density", density);
558         }
559         else {
560                 /* custom - grab from whatever value is set */
561                 density = RNA_float_get(op->ptr, "density");
562         }
563
564         /* apply this to all selected objects (with rigidbodies)... */
565         CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
566         {
567                 if (ob->rigidbody_object) {
568                         PointerRNA ptr;
569
570                         float volume; /* m^3 */
571                         float mass;   /* kg */
572
573                         /* mass is calculated from the approximate volume of the object,
574                          * and the density of the material we're simulating
575                          */
576                         volume = calc_rigidbody_ob_volume(ob);
577                         mass = volume * density;
578
579                         /* use RNA-system to change the property and perform all necessary changes */
580                         RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
581                         RNA_float_set(&ptr, "mass", mass);
582
583                         DAG_id_tag_update(&ob->id, OB_RECALC_OB);
584                 }
585         }
586         CTX_DATA_END;
587
588         /* send updates */
589         WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL);
590
591         /* done */
592         return OPERATOR_FINISHED;
593 }
594
595 void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
596 {
597         PropertyRNA *prop;
598
599         /* identifiers */
600         ot->idname = "RIGIDBODY_OT_mass_calculate";
601         ot->name = "Calculate Mass";
602         ot->description = "Automatically calculate mass values for Rigid Body Objects based on volume";
603
604         /* callbacks */
605         ot->invoke = WM_menu_invoke; // XXX
606         ot->exec = rigidbody_obs_calc_mass_exec;
607         ot->poll = ED_operator_rigidbody_active_poll;
608
609         /* flags */
610         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
611
612         /* properties */
613         ot->prop = prop = RNA_def_enum(ot->srna, "material",
614                                        DummyRNA_DEFAULT_items, 0,
615                                        "Material Preset",
616                                        "Type of material that objects are made of (determines material density)");
617         RNA_def_enum_funcs(prop, rigidbody_materials_itemf);
618
619         RNA_def_float(ot->srna, "density", 1.0, FLT_MIN, FLT_MAX,
620                       "Density",
621                       "Custom density value (kg/m^3) to use instead of material preset",
622                       1.0f, 2500.0f);
623 }
624
625 /* ********************************************** */