Merge with trunk r40991
[blender.git] / source / blender / editors / physics / dynamicpaint_ops.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 #include <math.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "BLI_blenlib.h"
26
27 #include "DNA_dynamicpaint_types.h"
28 #include "DNA_modifier_types.h"
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31
32 #include "BKE_blender.h"
33 #include "BKE_context.h"
34 #include "BKE_deform.h"
35 #include "BKE_depsgraph.h"
36 #include "BKE_dynamicpaint.h"
37 #include "BKE_global.h"
38 #include "BKE_modifier.h"
39 #include "BKE_report.h"
40
41 #include "ED_mesh.h"
42 #include "ED_screen.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46 #include "RNA_enum_types.h"
47
48 /* Platform independend time    */
49 #include "PIL_time.h"
50
51 #include "WM_types.h"
52 #include "WM_api.h"
53
54 static int surface_slot_add_exec(bContext *C, wmOperator *op)
55 {
56         DynamicPaintModifierData *pmd = 0;
57         Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
58         DynamicPaintSurface *surface;
59
60         /* Make sure we're dealing with a canvas */
61         pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint);
62         if (!pmd) return OPERATOR_CANCELLED;
63         if (!pmd->canvas) return OPERATOR_CANCELLED;
64
65         surface = dynamicPaint_createNewSurface(pmd->canvas, CTX_data_scene(C));
66
67         if (!surface) return OPERATOR_CANCELLED;
68
69         /* set preview for this surface only and set active */
70         pmd->canvas->active_sur = 0;
71         for(surface=surface->prev; surface; surface=surface->prev) {
72                                 surface->flags &= ~MOD_DPAINT_PREVIEW;
73                                 pmd->canvas->active_sur++;
74         }
75
76         return OPERATOR_FINISHED;
77 }
78
79 /* add surface slot */
80 void DPAINT_OT_surface_slot_add(wmOperatorType *ot)
81 {
82         /* identifiers */
83         ot->name= "Add Surface Slot";
84         ot->idname= "DPAINT_OT_surface_slot_add";
85         ot->description="Add a new Dynamic Paint surface slot";
86         
87         /* api callbacks */
88         ot->exec= surface_slot_add_exec;
89         ot->poll= ED_operator_object_active_editable;
90
91         /* flags */
92         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
93 }
94
95 static int surface_slot_remove_exec(bContext *C, wmOperator *op)
96 {
97         DynamicPaintModifierData *pmd = 0;
98         Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
99         DynamicPaintSurface *surface;
100         int id=0;
101
102         /* Make sure we're dealing with a canvas */
103         pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint);
104         if (!pmd) return OPERATOR_CANCELLED;
105         if (!pmd->canvas) return OPERATOR_CANCELLED;
106
107         surface = pmd->canvas->surfaces.first;
108
109         /* find active surface and remove it */
110         for(; surface; surface=surface->next) {
111                 if(id == pmd->canvas->active_sur) {
112                                 pmd->canvas->active_sur -= 1;
113                                 dynamicPaint_freeSurface(surface);
114                                 break;
115                         }
116                 id++;
117         }
118
119         dynamicPaint_resetPreview(pmd->canvas);
120         DAG_id_tag_update(&cObject->id, OB_RECALC_DATA);
121         WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, cObject);
122
123         return OPERATOR_FINISHED;
124 }
125
126 /* remove surface slot */
127 void DPAINT_OT_surface_slot_remove(wmOperatorType *ot)
128 {
129         /* identifiers */
130         ot->name= "Remove Surface Slot";
131         ot->idname= "DPAINT_OT_surface_slot_remove";
132         ot->description="Remove the selected surface slot";
133         
134         /* api callbacks */
135         ot->exec= surface_slot_remove_exec;
136         ot->poll= ED_operator_object_active_editable;
137
138         /* flags */
139         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
140 }
141
142 static int type_toggle_exec(bContext *C, wmOperator *op)
143 {
144
145         Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
146         Scene *scene = CTX_data_scene(C);
147         DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint);
148         int type= RNA_enum_get(op->ptr, "type");
149
150         if (!pmd) return OPERATOR_CANCELLED;
151
152         /* if type is already enabled, toggle it off */
153         if (type == MOD_DYNAMICPAINT_TYPE_CANVAS && pmd->canvas) {
154                         dynamicPaint_freeCanvas(pmd);
155         }
156         else if (type == MOD_DYNAMICPAINT_TYPE_BRUSH && pmd->brush) {
157                         dynamicPaint_freeBrush(pmd);
158         }
159         /* else create a new type */
160         else {
161                 if (!dynamicPaint_createType(pmd, type, scene))
162                         return OPERATOR_CANCELLED;
163         }
164         
165         /* update dependancy */
166         DAG_id_tag_update(&cObject->id, OB_RECALC_DATA);
167         WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, cObject);
168         DAG_scene_sort(CTX_data_main(C), scene);
169
170         return OPERATOR_FINISHED;
171 }
172
173 void DPAINT_OT_type_toggle(wmOperatorType *ot)
174 {
175         PropertyRNA *prop;
176
177         /* identifiers */
178         ot->name= "Toggle Type Active";
179         ot->idname= "DPAINT_OT_type_toggle";
180         ot->description = "Toggles whether given type is active or not";
181         
182         /* api callbacks */
183         ot->exec= type_toggle_exec;
184         ot->poll= ED_operator_object_active_editable;
185         
186         /* flags */
187         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
188         
189         /* properties */
190         prop= RNA_def_enum(ot->srna, "type", prop_dynamicpaint_type_items, MOD_DYNAMICPAINT_TYPE_CANVAS, "Type", "");
191         ot->prop= prop;
192 }
193
194 static int output_toggle_exec(bContext *C, wmOperator *op)
195 {
196
197         Object *ob = CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
198         Scene *scene = CTX_data_scene(C);
199         DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)modifiers_findByType(ob, eModifierType_DynamicPaint);
200         int index= RNA_int_get(op->ptr, "index");
201
202         if (!pmd) return OPERATOR_CANCELLED;
203
204
205         /* if type is already enabled, toggle it off */
206         if (pmd->canvas) {
207                         DynamicPaintSurface *surface = get_activeSurface(pmd->canvas);
208
209                         if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
210                                 int exists = dynamicPaint_outputLayerExists(surface, ob, index);
211                                 char *name;
212                                 
213                                 if (index == 0)
214                                         name = surface->output_name;
215                                 else if (index == 1)
216                                         name = surface->output_name2;
217
218                                 /* Vertex Color Layer */
219                                 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
220                                         if (!exists)
221                                                 ED_mesh_color_add(C, scene, ob, ob->data, name, 1);
222                                         else 
223                                                 ED_mesh_color_remove_named(C, ob, ob->data, name);
224                                 }
225                                 /* Vertex Weight Layer */
226                                 else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
227                                         if (!exists)
228                                                 ED_vgroup_add_name(ob, name);
229                                         else {
230                                                 bDeformGroup *defgroup = defgroup_find_name(ob, name);
231                                                 if (defgroup) ED_vgroup_delete(ob, defgroup);
232                                         }
233                                 }
234                         }
235         }
236
237         return OPERATOR_FINISHED;
238 }
239
240 void DPAINT_OT_output_toggle(wmOperatorType *ot)
241 {
242         PropertyRNA *prop;
243
244         /* identifiers */
245         ot->name= "Toggle Output Layer";
246         ot->idname= "DPAINT_OT_output_toggle";
247         ot->description = "Adds or removes Dynamic Paint output data layer";
248         
249         /* api callbacks */
250         ot->exec= output_toggle_exec;
251         ot->poll= ED_operator_object_active_editable;
252         
253         /* flags */
254         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
255         
256         /* properties */
257         prop= RNA_def_int(ot->srna, "index", 0, 0, 1, "Index", "", 0, 1);
258         ot->prop= prop;
259 }
260
261
262 /***************************** Image Sequence Baking ******************************/
263
264 /*
265 *       Do actual bake operation. Loop through to-be-baked frames.
266 *       Returns 0 on failture.
267 */
268 static int dynamicPaint_bakeImageSequence(bContext *C, DynamicPaintSurface *surface, Object *cObject)
269 {
270         DynamicPaintCanvasSettings *canvas = surface->canvas;
271         Scene *scene= CTX_data_scene(C);
272         wmWindow *win = CTX_wm_window(C);
273         int frame = 1;
274         int frames;
275
276         frames = surface->end_frame - surface->start_frame + 1;
277         if (frames <= 0) {sprintf(canvas->error, "No frames to bake.");printf("DynamicPaint bake failed: %s", canvas->error);return 0;}
278
279         /*
280         *       Set frame to start point (also inits modifier data)
281         */
282         frame = surface->start_frame;
283         scene->r.cfra = (int)frame;
284         ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
285
286         /* Init surface */
287         if (!dynamicPaint_createUVSurface(surface)) return 0;
288
289         /*
290         *       Loop through selected frames
291         */
292         for (frame=surface->start_frame; frame<=surface->end_frame; frame++)
293         {
294                 float progress = (frame - surface->start_frame) / (float)frames * 100;
295                 surface->current_frame = frame;
296
297                 /* If user requested stop (esc), quit baking    */
298                 if (blender_test_break()) return 0;
299
300                 /* Update progress bar cursor */
301                 WM_timecursor(win, (int)progress);
302                 printf("DynamicPaint: Baking frame %i\n", frame);
303
304                 /* calculate a frame */
305                 scene->r.cfra = (int)frame;
306                 ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
307                 if (!dynamicPaint_calculateFrame(surface, scene, cObject, frame)) return 0;
308
309                 /*
310                 *       Save output images
311                 */
312                 {
313                         char filename[250];
314                         char pad[4];
315                         char dir_slash[2];
316                         /* OpenEXR or PNG       */
317                         short format = (surface->image_fileformat & MOD_DPAINT_IMGFORMAT_OPENEXR) ? DPOUTPUT_OPENEXR : DPOUTPUT_PNG;
318
319                         /* Add frame number padding     */
320                         if (frame<10) sprintf(pad,"000");
321                         else if (frame<100) sprintf(pad,"00");
322                         else if (frame<1000) sprintf(pad,"0");
323                         else pad[0] = '\0';
324
325                         /* make sure directory path is valid to append filename */
326                         if (surface->image_output_path[strlen(surface->image_output_path)-1] != 47 &&
327                                 surface->image_output_path[strlen(surface->image_output_path)-1] != 92)
328                                 strcpy(dir_slash,"/");
329                         else
330                                 dir_slash[0] = '\0';
331
332
333                         /* color map    */
334                         if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
335                                 if (surface->flags & MOD_DPAINT_OUT1) {
336                                         sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name, pad, (int)frame);
337                                         dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_PAINT);
338                                 }
339                                 if (surface->flags & MOD_DPAINT_OUT2) {
340                                         sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name2, pad, (int)frame);
341                                         dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_WET);
342                                 }
343                         }
344
345                         /* displacement map     */
346                         else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) {
347                                 sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name, pad, (int)frame);
348                                 dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_DISPLACE);
349                         }
350
351                         /* waves        */
352                         else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
353                                 sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name, pad, (int)frame);
354                                 dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_WAVES);
355                         }
356                 }
357         }
358         return 1;
359 }
360
361
362 /*
363 *       Bake Dynamic Paint image sequence surface
364 */
365 int dynamicPaint_initBake(struct bContext *C, struct wmOperator *op)
366 {
367         DynamicPaintModifierData *pmd = NULL;
368         Object *ob = CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
369         int status = 0;
370         double timer = PIL_check_seconds_timer();
371         DynamicPaintSurface *surface;
372
373         /*
374         *       Get modifier data
375         */
376         pmd = (DynamicPaintModifierData *)modifiers_findByType(ob, eModifierType_DynamicPaint);
377         if (!pmd) {
378                 BKE_report(op->reports, RPT_ERROR, "Bake Failed: No Dynamic Paint modifier found.");
379                 return 0;
380         }
381
382         /* Make sure we're dealing with a canvas */
383         if (!pmd->canvas) {
384                 BKE_report(op->reports, RPT_ERROR, "Bake Failed: Invalid Canvas.");
385                 return 0;
386         }
387         surface = get_activeSurface(pmd->canvas);
388
389         /* Set state to baking and init surface */
390         pmd->canvas->error[0] = '\0';
391         pmd->canvas->flags |= MOD_DPAINT_BAKING;
392         G.afbreek= 0;   /* reset blender_test_break*/
393
394         /*  Bake Dynamic Paint  */
395         status = dynamicPaint_bakeImageSequence(C, surface, ob);
396         /* Clear bake */
397         pmd->canvas->flags &= ~MOD_DPAINT_BAKING;
398         WM_cursor_restore(CTX_wm_window(C));
399         dynamicPaint_freeSurfaceData(surface);
400
401         /* Bake was successful:
402         *  Report for ended bake and how long it took */
403         if (status) {
404
405                 /* Format time string   */
406                 char timestr[30];
407                 double time = PIL_check_seconds_timer() - timer;
408                 int tmp_val;
409                 timestr[0] = '\0';
410
411                 /* days (just in case someone actually has a very slow pc)      */
412                 tmp_val = (int)floor(time / 86400.0f);
413                 if (tmp_val > 0) sprintf(timestr, "%i Day(s) - ", tmp_val);
414                 /* hours        */
415                 time -= 86400.0f * tmp_val;
416                 tmp_val = (int)floor(time / 3600.0f);
417                 if (tmp_val > 0) sprintf(timestr, "%s%i h ", timestr, tmp_val);
418                 /* minutes      */
419                 time -= 3600.0f * tmp_val;
420                 tmp_val = (int)floor(time / 60.0f);
421                 if (tmp_val > 0) sprintf(timestr, "%s%i min ", timestr, tmp_val);
422                 /* seconds      */
423                 time -= 60.0f * tmp_val;
424                 tmp_val = (int)ceil(time);
425                 sprintf(timestr, "%s%i s", timestr, tmp_val);
426
427                 /* Show bake info */
428                 sprintf(pmd->canvas->ui_info, "Bake Complete! (Time: %s)", timestr);
429                 printf("%s\n", pmd->canvas->ui_info);
430         }
431         else {
432                 if (strlen(pmd->canvas->error)) { /* If an error occured */
433                         sprintf(pmd->canvas->ui_info, "Bake Failed: %s", pmd->canvas->error);
434                         BKE_report(op->reports, RPT_ERROR, pmd->canvas->ui_info);
435                 }
436                 else {  /* User cancelled the bake */
437                         sprintf(pmd->canvas->ui_info, "Baking Cancelled!");
438                         BKE_report(op->reports, RPT_WARNING, pmd->canvas->ui_info);
439                 }
440
441                 /* Print failed bake to console */
442                 printf("Baking Cancelled!\n");
443         }
444
445         return status;
446 }
447
448 static int dynamicpaint_bake_exec(bContext *C, wmOperator *op)
449 {
450         /* Bake dynamic paint */
451         if(!dynamicPaint_initBake(C, op)) {
452                 return OPERATOR_CANCELLED;}
453
454         return OPERATOR_FINISHED;
455 }
456
457 void DPAINT_OT_bake(wmOperatorType *ot)
458 {
459         /* identifiers */
460         ot->name= "Dynamic Paint Bake";
461         ot->description= "Bake dynamic paint image sequence surface";
462         ot->idname= "DPAINT_OT_bake";
463         
464         /* api callbacks */
465         ot->exec= dynamicpaint_bake_exec;
466         ot->poll= ED_operator_object_active_editable;
467 }