Blender V5.0
dynamicpaint_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstring>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_path_utils.hh"
14#include "BLI_string_utf8.h"
15#include "BLI_time.h"
16
17#include "BLT_translation.hh"
18
20#include "DNA_modifier_types.h"
21#include "DNA_object_types.h"
22#include "DNA_scene_types.h"
23
24#include "BKE_attribute.hh"
25#include "BKE_context.hh"
26#include "BKE_deform.hh"
27#include "BKE_dynamicpaint.h"
28#include "BKE_global.hh"
29#include "BKE_main.hh"
30#include "BKE_modifier.hh"
31#include "BKE_object_deform.h"
32#include "BKE_report.hh"
33#include "BKE_screen.hh"
34
35#include "DEG_depsgraph.hh"
38
39#include "ED_mesh.hh"
40#include "ED_object.hh"
41#include "ED_screen.hh"
42
43#include "RNA_access.hh"
44#include "RNA_define.hh"
45#include "RNA_enum_types.hh"
46
47#include "WM_api.hh"
48#include "WM_types.hh"
49
50#include "physics_intern.hh" /* own include */
51
53{
54 DynamicPaintModifierData *pmd = nullptr;
57 DynamicPaintSurface *surface;
58
59 /* Make sure we're dealing with a canvas */
61 if (!pmd || !pmd->canvas) {
62 return OPERATOR_CANCELLED;
63 }
64
65 canvas = pmd->canvas;
67
68 if (!surface) {
69 return OPERATOR_CANCELLED;
70 }
71
72 canvas->active_sur = 0;
73 for (surface = surface->prev; surface; surface = surface->prev) {
74 canvas->active_sur++;
75 }
76
77 return OPERATOR_FINISHED;
78}
79
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. */
90
91 /* flags */
93}
94
96{
97 DynamicPaintModifierData *pmd = nullptr;
100 DynamicPaintSurface *surface;
101 int id = 0;
102
103 /* Make sure we're dealing with a canvas */
105 if (!pmd || !pmd->canvas) {
106 return OPERATOR_CANCELLED;
107 }
108
109 canvas = pmd->canvas;
110 surface = static_cast<DynamicPaintSurface *>(canvas->surfaces.first);
111
112 /* find active surface and remove it */
113 for (; surface; surface = surface->next) {
114 if (id == canvas->active_sur) {
115 canvas->active_sur -= 1;
116 dynamicPaint_freeSurface(pmd, surface);
117 break;
118 }
119 id++;
120 }
121
124
125 return OPERATOR_FINISHED;
126}
127
129{
130 /* identifiers */
131 ot->name = "Remove Surface Slot";
132 ot->idname = "DPAINT_OT_surface_slot_remove";
133 ot->description = "Remove the selected surface slot";
134
135 /* API callbacks. */
138
139 /* flags */
141}
142
144{
145
147 Scene *scene = CTX_data_scene(C);
150 int type = RNA_enum_get(op->ptr, "type");
151
152 if (!pmd) {
153 return OPERATOR_CANCELLED;
154 }
155
156 /* if type is already enabled, toggle it off */
157 if (type == MOD_DYNAMICPAINT_TYPE_CANVAS && pmd->canvas) {
159 }
160 else if (type == MOD_DYNAMICPAINT_TYPE_BRUSH && pmd->brush) {
162 }
163 /* else create a new type */
164 else {
165 if (!dynamicPaint_createType(pmd, type, scene)) {
166 return OPERATOR_CANCELLED;
167 }
168 }
169
170 /* update dependency */
174
175 return OPERATOR_FINISHED;
176}
177
179{
180 PropertyRNA *prop;
181
182 /* identifiers */
183 ot->name = "Toggle Type Active";
184 ot->idname = "DPAINT_OT_type_toggle";
185 ot->description = "Toggle whether given type is active or not";
186
187 /* API callbacks. */
188 ot->exec = type_toggle_exec;
190
191 /* flags */
193
194 /* properties */
195 prop = RNA_def_enum(ot->srna,
196 "type",
199 "Type",
200 "");
202 ot->prop = prop;
203}
204
206{
208 DynamicPaintSurface *surface;
211 int output = RNA_enum_get(op->ptr, "output"); /* currently only 1/0 */
212
213 if (!pmd || !pmd->canvas) {
214 return OPERATOR_CANCELLED;
215 }
216 surface = get_activeSurface(pmd->canvas);
217
218 /* if type is already enabled, toggle it off */
219 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
220 bool exists = dynamicPaint_outputLayerExists(surface, ob, output);
221 const char *name;
222
223 if (output == 0) {
224 name = surface->output_name;
225 }
226 else {
227 name = surface->output_name2;
228 }
229
230 /* Vertex Color Layer */
231 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
232 if (!exists) {
233 ED_mesh_color_add(static_cast<Mesh *>(ob->data), name, true, true, op->reports);
234 }
235 else {
236 AttributeOwner owner = AttributeOwner::from_id(static_cast<ID *>(ob->data));
237 BKE_attribute_remove(owner, name, nullptr);
238 }
239 }
240 /* Vertex Weight Layer */
241 else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
242 if (!exists) {
245 }
246 else {
248 if (defgroup) {
249 BKE_object_defgroup_remove(ob, defgroup);
251 }
252 }
253 }
254 }
255
256 return OPERATOR_FINISHED;
257}
258
260{
261 static const EnumPropertyItem prop_output_toggle_types[] = {
262 {0, "A", 0, "Output A", ""},
263 {1, "B", 0, "Output B", ""},
264 {0, nullptr, 0, nullptr, nullptr},
265 };
266
267 /* identifiers */
268 ot->name = "Toggle Output Layer";
269 ot->idname = "DPAINT_OT_output_toggle";
270 ot->description = "Add or remove Dynamic Paint output data layer";
271
272 /* API callbacks. */
273 ot->exec = output_toggle_exec;
275
276 /* flags */
278
279 /* properties */
280 ot->prop = RNA_def_enum(ot->srna, "output", prop_output_toggle_types, 0, "Output Toggle", "");
281}
282
283/***************************** Image Sequence Baking ******************************/
284
302
303static void dpaint_bake_free(void *customdata)
304{
305 DynamicPaintBakeJob *job = static_cast<DynamicPaintBakeJob *>(customdata);
306 MEM_freeN(job);
307}
308
309static void dpaint_bake_endjob(void *customdata)
310{
311 DynamicPaintBakeJob *job = static_cast<DynamicPaintBakeJob *>(customdata);
312 DynamicPaintCanvasSettings *canvas = job->canvas;
313
314 canvas->flags &= ~MOD_DPAINT_BAKING;
315
317
318 G.is_rendering = false;
319
320 WM_locked_interface_set(static_cast<wmWindowManager *>(G_MAIN->wm.first), false);
321
322 /* Bake was successful:
323 * Report for ended bake and how long it took */
324 if (job->success) {
325 /* Show bake info */
327 RPT_INFO, "DynamicPaint: Bake complete! (%.2f)", BLI_time_now_seconds() - job->start);
328 }
329 else {
330 if (strlen(canvas->error)) { /* If an error occurred */
331 WM_global_reportf(RPT_ERROR, "DynamicPaint: Bake failed: %s", canvas->error);
332 }
333 else { /* User canceled the bake */
334 WM_global_report(RPT_WARNING, "Baking canceled!");
335 }
336 }
337}
338
339/*
340 * Do actual bake operation. Loop through to-be-baked frames.
341 * Returns 0 on failure.
342 */
344{
345 DynamicPaintSurface *surface = job->surface;
346 Object *cObject = job->ob;
347 DynamicPaintCanvasSettings *canvas = surface->canvas;
348 Scene *input_scene = DEG_get_input_scene(job->depsgraph);
349 Scene *scene = job->scene;
350 int frame = 1, orig_frame;
351 int frames;
352
353 frames = surface->end_frame - surface->start_frame + 1;
354 if (frames <= 0) {
355 STRNCPY_UTF8(canvas->error, N_("No frames to bake"));
356 return;
357 }
358
359 /* Show progress bar. */
360 *(job->do_update) = true;
361
362 /* Set frame to start point (also initializes modifier data). */
363 frame = surface->start_frame;
364 orig_frame = input_scene->r.cfra;
365 input_scene->r.cfra = frame;
367
368 /* Init surface */
369 if (!dynamicPaint_createUVSurface(scene, surface, job->progress, job->do_update)) {
370 job->success = 0;
371 return;
372 }
373
374 /* Loop through selected frames */
375 for (frame = surface->start_frame; frame <= surface->end_frame; frame++) {
376 /* The first 10% are for createUVSurface... */
377 const float progress = 0.1f + 0.9f * (frame - surface->start_frame) / float(frames);
378 surface->current_frame = frame;
379
380 /* If user requested stop, quit baking */
381 if (G.is_break) {
382 job->success = 0;
383 return;
384 }
385
386 /* Update progress bar */
387 *(job->do_update) = true;
388 *(job->progress) = progress;
389
390 /* calculate a frame */
391 input_scene->r.cfra = frame;
393 if (!dynamicPaint_calculateFrame(surface, job->depsgraph, scene, cObject, frame)) {
394 job->success = 0;
395 return;
396 }
397
398 /*
399 * Save output images
400 */
401 {
402 char filepath[FILE_MAX];
403
404 /* primary output layer */
405 if (surface->flags & MOD_DPAINT_OUT1) {
406 /* set filepath */
408 filepath, sizeof(filepath), surface->image_output_path, surface->output_name);
409 BLI_path_frame(filepath, sizeof(filepath), frame, 4);
410
411 /* save image */
412 dynamicPaint_outputSurfaceImage(surface, filepath, 0);
413 }
414 /* secondary output */
415 if (surface->flags & MOD_DPAINT_OUT2 && surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
416 /* set filepath */
418 filepath, sizeof(filepath), surface->image_output_path, surface->output_name2);
419 BLI_path_frame(filepath, sizeof(filepath), frame, 4);
420
421 /* save image */
422 dynamicPaint_outputSurfaceImage(surface, filepath, 1);
423 }
424 }
425 }
426
427 input_scene->r.cfra = orig_frame;
429}
430
431static void dpaint_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
432{
433 DynamicPaintBakeJob *job = static_cast<DynamicPaintBakeJob *>(customdata);
434
435 job->stop = &worker_status->stop;
436 job->do_update = &worker_status->do_update;
437 job->progress = &worker_status->progress;
439 job->success = 1;
440
441 G.is_break = false;
442
443 /* XXX annoying hack: needed to prevent data corruption when changing
444 * scene frame in separate threads
445 */
446 G.is_rendering = true;
448
450
451 worker_status->do_update = true;
452 worker_status->stop = false;
453}
454
455/*
456 * Bake Dynamic Paint image sequence surface
457 */
459{
462 Object *object_eval = DEG_get_evaluated(depsgraph, ob_);
464
465 DynamicPaintSurface *surface;
466
467 /*
468 * Get modifier data
469 */
471 object_eval, eModifierType_DynamicPaint);
472 if (pmd == nullptr) {
473 BKE_report(op->reports, RPT_ERROR, "Bake failed: no Dynamic Paint modifier found");
474 return OPERATOR_CANCELLED;
475 }
476
477 /* Make sure we're dealing with a canvas */
478 DynamicPaintCanvasSettings *canvas = pmd->canvas;
479 if (canvas == nullptr) {
480 BKE_report(op->reports, RPT_ERROR, "Bake failed: invalid canvas");
481 return OPERATOR_CANCELLED;
482 }
483 surface = get_activeSurface(canvas);
484
485 /* Set state to baking and init surface */
486 canvas->error[0] = '\0';
487 canvas->flags |= MOD_DPAINT_BAKING;
488
489 DynamicPaintBakeJob *job = MEM_mallocN<DynamicPaintBakeJob>("DynamicPaintBakeJob");
490 job->bmain = CTX_data_main(C);
491 job->scene = scene_eval;
492 job->depsgraph = depsgraph;
493 job->ob = object_eval;
494 job->canvas = canvas;
495 job->surface = surface;
496
500 "Baking Dynamic Paint...",
503
507
509
510 /* Bake Dynamic Paint */
512
513 return OPERATOR_FINISHED;
514}
515
517{
518 /* identifiers */
519 ot->name = "Dynamic Paint Bake";
520 ot->description = "Bake dynamic paint image sequence surface";
521 ot->idname = "DPAINT_OT_bake";
522
523 /* API callbacks. */
526}
bool BKE_attribute_remove(AttributeOwner &owner, blender::StringRef name, struct ReportList *reports)
Definition attribute.cc:512
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
support for deformation groups and hooks.
bDeformGroup * BKE_object_defgroup_find_name(const Object *ob, blender::StringRef name)
Definition deform.cc:526
struct DynamicPaintSurface * get_activeSurface(struct DynamicPaintCanvasSettings *canvas)
bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, struct Object *ob, int output)
void dynamicPaint_outputSurfaceImage(struct DynamicPaintSurface *surface, const char *filepath, short output_layer)
void dynamicPaint_freeCanvas(struct DynamicPaintModifierData *pmd)
bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, struct Scene *scene)
int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface, float *progress, bool *do_update)
void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd)
struct DynamicPaintSurface * dynamicPaint_createNewSurface(struct DynamicPaintCanvasSettings *canvas, struct Scene *scene)
int dynamicPaint_calculateFrame(struct DynamicPaintSurface *surface, struct Depsgraph *depsgraph, struct Scene *scene, struct Object *cObject, int frame)
void dynamicPaint_freeSurfaceData(struct DynamicPaintSurface *surface)
void dynamicPaint_freeSurface(const struct DynamicPaintModifierData *pmd, struct DynamicPaintSurface *surface)
#define G_MAIN
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
Functions for dealing with objects and deform verts, used by painting and tools.
void BKE_object_defgroup_remove(struct Object *ob, struct bDeformGroup *defgroup)
struct bDeformGroup * BKE_object_defgroup_add_name(struct Object *ob, const char *name)
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_spacedata_draw_locks(ARegionDrawLockFlags lock_flags)
Definition screen.cc:423
@ REGION_DRAW_LOCK_BAKING
#define FILE_MAX
#define BLI_path_join(...)
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1)
#define STRNCPY_UTF8(dst, src)
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define BLT_I18NCONTEXT_ID_SIMULATION
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
Scene * DEG_get_input_scene(const Depsgraph *graph)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ MOD_DPAINT_OUT2
@ MOD_DPAINT_OUT1
@ MOD_DPAINT_SURFACE_T_WEIGHT
@ MOD_DPAINT_SURFACE_T_PAINT
@ MOD_DPAINT_BAKING
@ MOD_DPAINT_SURFACE_F_VERTEX
@ MOD_DYNAMICPAINT_TYPE_BRUSH
@ MOD_DYNAMICPAINT_TYPE_CANVAS
@ eModifierType_DynamicPaint
Object is a sort of wrapper for general info.
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
int ED_mesh_color_add(Mesh *mesh, const char *name, bool active_set, bool do_init, ReportList *reports)
Definition mesh_data.cc:340
bool ED_operator_object_active_local_editable(bContext *C)
void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_DPAINT_BAKE
Definition WM_api.hh:1795
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_MODIFIER
Definition WM_types.hh:462
#define NC_OBJECT
Definition WM_types.hh:379
BPy_StructRNA * depsgraph
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
void DPAINT_OT_type_toggle(wmOperatorType *ot)
void DPAINT_OT_output_toggle(wmOperatorType *ot)
static void dpaint_bake_endjob(void *customdata)
static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job)
static wmOperatorStatus type_toggle_exec(bContext *C, wmOperator *op)
void DPAINT_OT_surface_slot_add(wmOperatorType *ot)
static wmOperatorStatus output_toggle_exec(bContext *C, wmOperator *op)
static void dpaint_bake_free(void *customdata)
static void dpaint_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
void DPAINT_OT_surface_slot_remove(wmOperatorType *ot)
static wmOperatorStatus surface_slot_add_exec(bContext *C, wmOperator *)
static wmOperatorStatus surface_slot_remove_exec(bContext *C, wmOperator *)
static wmOperatorStatus dynamicpaint_bake_exec(bContext *C, wmOperator *op)
void DPAINT_OT_bake(wmOperatorType *ot)
#define output
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
Object * context_active_object(const bContext *C)
const char * name
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[]
DynamicPaintCanvasSettings * canvas
DynamicPaintSurface * surface
struct DynamicPaintCanvasSettings * canvas
struct DynamicPaintBrushSettings * brush
struct DynamicPaintSurface * prev
struct DynamicPaintCanvasSettings * canvas
struct DynamicPaintSurface * next
Definition DNA_ID.h:414
void * first
struct RenderData r
struct ReportList * reports
struct PointerRNA * ptr
#define N_(msgid)
void WM_locked_interface_set(wmWindowManager *wm, bool lock)
void WM_locked_interface_set_with_flags(wmWindowManager *wm, short lock_flags)
void WM_global_report(eReportType type, const char *message)
void WM_global_reportf(eReportType type, const char *format,...)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:211
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:388
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360