Blender V5.0
grease_pencil_lineart.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 <algorithm>
10
11#include "BLI_listbase.h"
12
13#include "BKE_context.hh"
14#include "BKE_curves.hh"
15#include "BKE_global.hh"
16#include "BKE_grease_pencil.hh"
17#include "BKE_material.hh"
18#include "BKE_modifier.hh"
19#include "BKE_report.hh"
20#include "BKE_scene.hh"
21#include "BKE_screen.hh"
22
23#include "DNA_curves_types.h"
24#include "DNA_modifier_types.h"
25
26#include "RNA_access.hh"
27#include "RNA_define.hh"
28
29#include "ED_grease_pencil.hh"
30#include "ED_view3d.hh"
31
32#include "DEG_depsgraph.hh"
34
35#include "MOD_lineart.hh"
36
38
41{
42 bool is_first = true;
43 LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) {
44 if (md->type == eModifierType_GreasePencilLineart) {
45 const auto *lmd = reinterpret_cast<const GreasePencilLineartModifierData *>(md);
46 if (is_first || (lmd->flags & MOD_LINEART_USE_CACHE)) {
47 info.min_level = std::min<int>(info.min_level, lmd->level_start);
48 info.max_level = std::max<int>(
49 info.max_level, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start);
50 info.edge_types |= lmd->edge_types;
51 info.shadow_selection = std::max(info.shadow_selection, lmd->shadow_selection);
52 info.silhouette_selection = std::max(info.silhouette_selection, lmd->silhouette_selection);
53 is_first = false;
54 }
55 }
56 }
57}
58
79
81{
82 /* This function always gets the first line art modifier regardless of their visibility, because
83 * cached line art configuration are always inside the first line art modifier. */
85 if (i_md->type == eModifierType_GreasePencilLineart) {
86 return reinterpret_cast<GreasePencilLineartModifierData *>(i_md);
87 }
88 }
89 return nullptr;
90}
91
92} // namespace blender::ed::greasepencil
93
96 void *owner;
97 bool *stop, *do_update;
98 float *progress;
99
100 /* C or ob must have one != nullptr. */
104 Depsgraph *dg;
105 int frame;
111};
112
113static bool clear_strokes(Object *ob, ModifierData *md, int frame)
114{
116 return false;
117 }
119 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
120
121 blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(lmd->target_layer);
122 if (!node || !node->is_layer()) {
123 return false;
124 }
126
127 if (layer.start_frame_at(frame) == frame) {
128 blender::bke::greasepencil::Drawing *drawing = grease_pencil->get_drawing_at(layer, frame);
129 if (!drawing) {
130 return false;
131 }
132 drawing->strokes_for_write() = {};
133 }
134
135 return true;
136}
137
139{
141
142 /* Toggle on and off the baked flag as we are only interested in if something else is disabling
143 * it. We can assume that the guard function has already toggled this on for all modifiers that
144 * are sent here. */
146
149
151
152 return !enabled;
153}
154
155static bool bake_strokes(Object *ob,
156 Depsgraph *dg,
157 LineartCache **lc,
159 int frame,
160 bool is_first)
161{
162 /* Modifier data sanity check. */
164 return false;
165 }
166
167 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
168
169 blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(lmd->target_layer);
170 if (!node || !node->is_layer()) {
171 return false;
172 }
174
175 blender::bke::greasepencil::Drawing *drawing = nullptr;
176 if (layer.start_frame_at(frame) == frame) {
177 drawing = grease_pencil->get_drawing_at(layer, frame);
178 }
179 else {
180 drawing = grease_pencil->insert_frame(layer, frame);
181 }
182 if (UNLIKELY(!drawing)) {
183 return false;
184 }
185
186 LineartCache *local_lc = nullptr;
187 const bool should_compute_again = is_first || !(lmd->flags & MOD_LINEART_USE_CACHE);
188 if (!(*lc)) {
191 }
192 else {
193 if (should_compute_again) {
194 MOD_lineart_compute_feature_lines_v3(dg, *lmd, &local_lc, !(ob->dtx & OB_DRAW_IN_FRONT));
196 }
197 else {
198 /* Use the cached result, `*lc` is already valid. */
199 local_lc = *lc;
200 }
202 lmd->cache = local_lc;
203 }
204
206 lmd->cache,
207 ob->world_to_object(),
208 dg,
209 *drawing,
210 lmd->source_type,
211 lmd->source_object,
213 lmd->level_start,
214 lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
216 lmd->edge_types,
217 lmd->mask_switches,
220 lmd->radius,
221 lmd->opacity,
222 lmd->shadow_selection,
225 lmd->vgname,
226 lmd->flags,
227 lmd->calculation_flags);
228
229 if (should_compute_again) {
230 MOD_lineart_clear_cache(&local_lc);
231 }
232
233 return true;
234}
235
236static bool bake_single_target(LineartBakeJob *bj, Object *ob, int frame)
237{
238 bool touched = false;
239 if (G.is_break || ob->type != OB_GREASE_PENCIL) {
240 return false;
241 }
242
243 if (bj->overwrite_frames) {
245 if (md->type == eModifierType_GreasePencilLineart) {
246 if (clear_strokes(ob, md, frame)) {
247 touched = true;
248 }
249 }
250 }
251 }
252
255
256 LineartCache *lc = nullptr;
257 bool is_first = true;
259 if (md->type != eModifierType_GreasePencilLineart) {
260 continue;
261 }
264
265 if (bake_strokes(ob, bj->dg, &lc, lmd, frame, is_first)) {
266 touched = true;
267 is_first = false;
268 }
269 }
271
272 return touched;
273}
274
276{
277 for (const int object : bj.objects.index_range()) {
278 Object *ob = bj.objects[object];
279
281 if (md->type == eModifierType_GreasePencilLineart) {
283 md);
285 }
286 }
287 }
288}
289
290static void lineart_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
291{
292 LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
293 bj->stop = &worker_status->stop;
294 bj->do_update = &worker_status->do_update;
295 bj->progress = &worker_status->progress;
296
297 guard_modifiers(*bj);
298
300
301 for (int frame = bj->frame_begin; frame <= bj->frame_end; frame += bj->frame_increment) {
302
303 if (G.is_break) {
304 G.is_break = false;
305 break;
306 }
307
308 BKE_scene_frame_set(bj->scene, frame);
311
312 for (const int object : bj->objects.index_range()) {
313 Object *ob = bj->objects[object];
314 if (bake_single_target(bj, ob, frame)) {
315 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
317 }
318 }
319
320 /* Update and refresh the progress bar. */
321 *bj->progress = float(frame - bj->frame_begin) / (bj->frame_end - bj->frame_begin);
322 *bj->do_update = true;
323 }
324
325 /* This need to be reset manually. */
326 G.is_break = false;
327
328 /* Restore original frame. */
331}
332
333static void lineart_bake_endjob(void *customdata)
334{
335 LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
336
338
340
341 for (const int object : bj->objects.index_range()) {
342 Object *ob = bj->objects[object];
344 }
345}
346
347static void lineart_bake_job_free(void *customdata)
348{
349 LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
350 MEM_delete(bj);
351}
352
354 wmOperator *op,
355 bool bake_all_targets,
356 bool do_background)
357{
358 LineartBakeJob *bj = MEM_new<LineartBakeJob>(__func__);
359
360 if (!bake_all_targets) {
362 if (!ob || ob->type != OB_GREASE_PENCIL) {
364 "No active object, or active object isn't a Grease Pencil object");
365 return OPERATOR_CANCELLED;
366 }
367 bj->objects.append(ob);
368 }
369 else {
370 /* #CTX_DATA_BEGIN is not available for iterating in objects while using the job system. */
371 CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
372 LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
373 if (md->type == eModifierType_GreasePencilLineart) {
374 bj->objects.append(ob);
375 break;
376 }
377 }
378 }
380 }
381 bj->C = C;
382 Scene *scene = CTX_data_scene(C);
383 bj->scene = scene;
385 bj->frame_begin = scene->r.sfra;
386 bj->frame_end = scene->r.efra;
387 bj->frame_orig = scene->r.cfra;
388 bj->frame_increment = scene->r.frame_step;
389 bj->overwrite_frames = true;
390
391 if (do_background) {
394 scene,
395 "Baking Line Art...",
398
402
404
406
408
410 }
411
412 wmJobWorkerStatus worker_status = {};
413 lineart_bake_startjob(bj, &worker_status);
414
415 /* Need to call endjob manually to clear interface locking status when bake is not called as
416 * background task, otherwise spaes like 3d viewport can be unresponsive. */
418
419 MEM_delete(bj);
420
421 return OPERATOR_FINISHED;
422}
423
425 wmOperator *op,
426 const wmEvent * /*event*/)
427{
428 bool bake_all = RNA_boolean_get(op->ptr, "bake_all");
429 return lineart_bake_common(C, op, bake_all, true);
430}
432{
433 bool bake_all = RNA_boolean_get(op->ptr, "bake_all");
434 return lineart_bake_common(C, op, bake_all, false);
435}
437 wmOperator *op,
438 const wmEvent * /*event*/)
439{
440 Scene *scene = static_cast<Scene *>(op->customdata);
441
442 /* no running blender, remove handler and pass through. */
445 }
446
448}
449
451{
453 if (md->type != eModifierType_GreasePencilLineart) {
454 continue;
455 }
457 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
458
459 blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(
460 lmd->target_layer);
461 if (!node || !node->is_layer()) {
462 return;
463 }
465
466 /* Remove all the keyframes in this layer. */
467 grease_pencil->remove_frames(layer, layer.sorted_keys());
468 grease_pencil->insert_frame(layer, 0);
469
471
473 }
474 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
475}
476
478{
479 bool clear_all = RNA_boolean_get(op->ptr, "clear_all");
480
481 if (clear_all) {
482 CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
483 if (ob->type != OB_GREASE_PENCIL) {
484 continue;
485 }
488 }
490 BKE_report(op->reports, RPT_INFO, "All Line Art objects are now cleared of bakes");
491 }
492 else {
494 if (ob->type != OB_GREASE_PENCIL) {
495 return OPERATOR_CANCELLED;
496 }
499 BKE_report(op->reports, RPT_INFO, "Baked strokes are cleared");
500 }
501
502 return OPERATOR_FINISHED;
503}
504
506{
507 ot->name = "Bake Line Art";
508 ot->description = "Bake Line Art for current Grease Pencil object";
509 ot->idname = "OBJECT_OT_lineart_bake_strokes";
510
515
516 RNA_def_boolean(ot->srna, "bake_all", false, "Bake All", "Bake all Line Art modifiers");
517}
518
520{
521 ot->name = "Clear Baked Line Art";
522 ot->description = "Clear all strokes in current Grease Pencil object";
523 ot->idname = "OBJECT_OT_lineart_clear";
524
527
528 RNA_def_boolean(ot->srna, "clear_all", false, "Clear All", "Clear all Line Art modifier bakes");
529}
530
#define CTX_DATA_BEGIN(C, Type, instance, member)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define CTX_DATA_END
Low-level operations for curves.
Low-level operations for grease pencil.
General operations, lookup, etc. for materials.
int BKE_object_material_index_get(Object *ob, const Material *ma)
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_scene_frame_set(Scene *scene, float frame)
Definition scene.cc:2389
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2700
void BKE_spacedata_draw_locks(ARegionDrawLockFlags lock_flags)
Definition screen.cc:423
@ REGION_DRAW_LOCK_BAKING
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define UNLIKELY(x)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_graph_build_from_view_layer(Depsgraph *graph)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ eModifierMode_Render
@ eModifierMode_Realtime
@ MOD_LINEART_IS_BAKED
@ MOD_LINEART_USE_CACHE
@ eModifierType_GreasePencilLineart
@ OB_DRAW_IN_FRONT
@ OB_GREASE_PENCIL
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_LINEART
Definition WM_api.hh:1806
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
#define ND_DATA
Definition WM_types.hh:509
#define NC_SCENE
Definition WM_types.hh:378
#define NA_EDITED
Definition WM_types.hh:584
#define ND_FRAME
Definition WM_types.hh:434
#define NC_GPENCIL
Definition WM_types.hh:399
void append(const T &value)
IndexRange index_range() const
bke::CurvesGeometry & strokes_for_write()
std::optional< int > start_frame_at(int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
nullptr float
static wmOperatorStatus lineart_bake_strokes_invoke(bContext *C, wmOperator *op, const wmEvent *)
static bool bake_single_target(LineartBakeJob *bj, Object *ob, int frame)
static void lineart_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
static void lineart_bake_endjob(void *customdata)
static wmOperatorStatus lineart_bake_strokes_exec(bContext *C, wmOperator *op)
static bool clear_strokes(Object *ob, ModifierData *md, int frame)
static void guard_modifiers(LineartBakeJob &bj)
static void lineart_bake_job_free(void *customdata)
static bool bake_strokes(Object *ob, Depsgraph *dg, LineartCache **lc, GreasePencilLineartModifierData *lmd, int frame, bool is_first)
static bool lineart_mod_is_disabled(Scene *scene, GreasePencilLineartModifierData *md)
void ED_operatortypes_grease_pencil_lineart()
static wmOperatorStatus lineart_bake_strokes_common_modal(bContext *C, wmOperator *op, const wmEvent *)
static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
static wmOperatorStatus lineart_gpencil_clear_strokes_exec(bContext *C, wmOperator *op)
static void OBJECT_OT_lineart_bake_strokes(wmOperatorType *ot)
static void OBJECT_OT_lineart_clear(wmOperatorType *ot)
static wmOperatorStatus lineart_bake_common(bContext *C, wmOperator *op, bool bake_all_targets, bool do_background)
bool enabled
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
void MOD_lineart_clear_cache(LineartCache **lc)
void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd)
void MOD_lineart_gpencil_generate_v3(const LineartCache *cache, const blender::float4x4 &inverse_mat, Depsgraph *depsgraph, blender::bke::greasepencil::Drawing &drawing, const int8_t source_type, Object *source_object, Collection *source_collection, const int level_start, const int level_end, const int mat_nr, const int16_t edge_types, const uchar mask_switches, const uchar material_mask_bits, const uchar intersection_mask, const float thickness, const float opacity, const uchar shadow_selection, const uchar silhouette_mode, const char *source_vgname, const char *vgname, const int modifier_flags, const int modifier_calculation_flags)
bool MOD_lineart_compute_feature_lines_v3(Depsgraph *depsgraph, GreasePencilLineartModifierData &lmd, LineartCache **cached_result, bool enable_stroke_depth_offset)
#define G(x, y, z)
bool active_grease_pencil_poll(bContext *C)
void get_lineart_modifier_limits(const Object &ob, blender::ed::greasepencil::LineartLimitInfo &info)
void set_lineart_modifier_limits(GreasePencilLineartModifierData &lmd, const blender::ed::greasepencil::LineartLimitInfo &info, const bool cache_is_ready)
GreasePencilLineartModifierData * get_first_lineart_modifier(const Object &ob)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
Definition DNA_ID.h:414
blender::Vector< Object * > objects
wmWindowManager * wm
ListBase modifiers
struct RenderData r
struct ReportList * reports
struct PointerRNA * ptr
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_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
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
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:247
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))