Blender V4.3
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
9#include <algorithm>
10#include <limits>
11
12#include "BLI_array_utils.hh"
14#include "BLI_kdtree.h"
15#include "BLI_listbase.h"
16#include "BLI_math_vector.hh"
17#include "BLI_offset_indices.hh"
18#include "BLI_rect.h"
19#include "BLI_stack.hh"
20#include "BLI_task.hh"
21
22#include "BKE_context.hh"
23#include "BKE_curves.hh"
24#include "BKE_global.hh"
25#include "BKE_grease_pencil.hh"
26#include "BKE_material.h"
27#include "BKE_modifier.hh"
28#include "BKE_report.hh"
29#include "BKE_scene.hh"
30
31#include "DNA_curves_types.h"
32#include "DNA_modifier_types.h"
33
34#include "RNA_access.hh"
35#include "RNA_define.hh"
36
37#include "ED_grease_pencil.hh"
38#include "ED_view3d.hh"
39
40#include "DEG_depsgraph.hh"
42
43#include "MOD_lineart.hh"
44
46
49{
50 bool is_first = true;
51 LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) {
52 if (md->type == eModifierType_GreasePencilLineart) {
53 const auto *lmd = reinterpret_cast<const GreasePencilLineartModifierData *>(md);
54 if (is_first || (lmd->flags & MOD_LINEART_USE_CACHE)) {
55 info.min_level = std::min<int>(info.min_level, lmd->level_start);
56 info.max_level = std::max<int>(
57 info.max_level, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start);
58 info.edge_types |= lmd->edge_types;
59 info.shadow_selection = std::max(info.shadow_selection, lmd->shadow_selection);
60 info.silhouette_selection = std::max(info.silhouette_selection, lmd->silhouette_selection);
61 is_first = false;
62 }
63 }
64 }
65}
66
87
89{
90 /* This function always gets the first line art modifier regardless of their visibility, because
91 * cached line art configuration are always inside the first line art modifier. */
93 if (i_md->type == eModifierType_GreasePencilLineart) {
94 return reinterpret_cast<GreasePencilLineartModifierData *>(i_md);
95 }
96 }
97 return nullptr;
98}
99
100} // namespace blender::ed::greasepencil
101
120
121static bool clear_strokes(Object *ob, ModifierData *md, int frame)
122{
124 return false;
125 }
127 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
128
129 blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(lmd->target_layer);
130 if (!node || !node->is_layer()) {
131 return false;
132 }
133 blender::bke::greasepencil::Layer &layer = node->as_layer();
134
135 if (layer.start_frame_at(frame) == frame) {
136 blender::bke::greasepencil::Drawing *drawing = grease_pencil->get_drawing_at(layer, frame);
137 if (!drawing) {
138 return false;
139 }
140 drawing->strokes_for_write() = {};
141 }
142
143 return true;
144}
145
147{
149
150 /* Toggle on and off the baked flag as we are only interested in if something else is disabling
151 * it. We can assume that the guard function has already toggled this on for all modifiers that
152 * are sent here. */
153 md->flags &= (~MOD_LINEART_IS_BAKED);
154
155 bool enabled = BKE_modifier_is_enabled(
157
159
160 return !enabled;
161}
162
163static bool bake_strokes(Object *ob,
164 Depsgraph *dg,
165 LineartCache **lc,
167 int frame,
168 bool is_first)
169{
170 /* Modifier data sanity check. */
172 return false;
173 }
174
176 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
177
178 blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(lmd->target_layer);
179 if (!node || !node->is_layer()) {
180 return false;
181 }
182 blender::bke::greasepencil::Layer &layer = node->as_layer();
183
184 blender::bke::greasepencil::Drawing *drawing = nullptr;
185 if (layer.start_frame_at(frame) == frame) {
186 drawing = grease_pencil->get_drawing_at(layer, frame);
187 }
188 else {
189 drawing = grease_pencil->insert_frame(layer, frame);
190 }
191 if (UNLIKELY(!drawing)) {
192 return false;
193 }
194
195 LineartCache *local_lc = *lc;
196 if (!(*lc)) {
199 }
200 else {
201 if (is_first || !(lmd->flags & MOD_LINEART_USE_CACHE)) {
202 MOD_lineart_compute_feature_lines_v3(dg, *lmd, &local_lc, !(ob->dtx & OB_DRAW_IN_FRONT));
204 }
206 lmd->cache = local_lc;
207 }
208
210 lmd->cache,
211 ob->object_to_world(),
212 dg,
213 *drawing,
214 lmd->source_type,
215 lmd->source_object,
217 lmd->level_start,
218 lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
220 lmd->edge_types,
221 lmd->mask_switches,
224 float(lmd->thickness) / 1000.0f,
225 lmd->opacity,
226 lmd->shadow_selection,
229 lmd->vgname,
230 lmd->flags,
231 lmd->calculation_flags);
232
233 if (!(lmd->flags & MOD_LINEART_USE_CACHE)) {
234 /* Clear local cache. */
235 if (!is_first) {
236 MOD_lineart_clear_cache(&local_lc);
237 }
238 }
239
240 return true;
241}
242
243static bool bake_single_target(LineartBakeJob *bj, Object *ob, int frame)
244{
245 bool touched = false;
246 if (G.is_break || ob->type != OB_GREASE_PENCIL) {
247 return false;
248 }
249
250 if (bj->overwrite_frames) {
252 if (md->type == eModifierType_GreasePencilLineart) {
253 if (clear_strokes(ob, md, frame)) {
254 touched = true;
255 }
256 }
257 }
258 }
259
262
263 LineartCache *lc = nullptr;
264 bool is_first = true;
266 if (md->type != eModifierType_GreasePencilLineart) {
267 continue;
268 }
271
272 if (bake_strokes(ob, bj->dg, &lc, lmd, frame, is_first)) {
273 touched = true;
274 is_first = false;
275 }
276 }
278
279 return touched;
280}
281
283{
284 for (const int object : bj.objects.index_range()) {
285 Object *ob = bj.objects[object];
286
288 if (md->type == eModifierType_GreasePencilLineart) {
290 md);
292 }
293 }
294 }
295}
296
297static void lineart_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
298{
299 LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
300 bj->stop = &worker_status->stop;
301 bj->do_update = &worker_status->do_update;
302 bj->progress = &worker_status->progress;
303
304 guard_modifiers(*bj);
305
306 for (int frame = bj->frame_begin; frame <= bj->frame_end; frame += bj->frame_increment) {
307
308 if (G.is_break) {
309 G.is_break = false;
310 break;
311 }
312
313 BKE_scene_frame_set(bj->scene, frame);
316
317 for (const int object : bj->objects.index_range()) {
318 Object *ob = bj->objects[object];
319 if (bake_single_target(bj, ob, frame)) {
320 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
322 }
323 }
324
325 /* Update and refresh the progress bar. */
326 *bj->progress = float(frame - bj->frame_begin) / (bj->frame_end - bj->frame_begin);
327 *bj->do_update = true;
328 }
329
330 /* This need to be reset manually. */
331 G.is_break = false;
332
333 /* Restore original frame. */
336}
337
338static void lineart_bake_endjob(void *customdata)
339{
340 LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
341
343
345
346 for (const int object : bj->objects.index_range()) {
347 Object *ob = bj->objects[object];
349 }
350}
351
352static void lineart_bake_job_free(void *customdata)
353{
354 LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
355 MEM_delete(bj);
356}
357
359 wmOperator *op,
360 bool bake_all_targets,
361 bool do_background)
362{
363 LineartBakeJob *bj = MEM_new<LineartBakeJob>(__func__);
364
365 if (!bake_all_targets) {
367 if (!ob || ob->type != OB_GREASE_PENCIL) {
368 WM_report(RPT_ERROR, "No active object, or active object isn't a Grease Pencil object");
369 return OPERATOR_CANCELLED;
370 }
371 bj->objects.append(ob);
372 }
373 else {
374 /* #CTX_DATA_BEGIN is not available for iterating in objects while using the job system. */
375 CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
376 LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
377 if (md->type == eModifierType_GreasePencilLineart) {
378 bj->objects.append(ob);
379 break;
380 }
381 }
382 }
384 }
385 bj->C = C;
386 Scene *scene = CTX_data_scene(C);
387 bj->scene = scene;
389 bj->frame_begin = scene->r.sfra;
390 bj->frame_end = scene->r.efra;
391 bj->frame_orig = scene->r.cfra;
392 bj->frame_increment = scene->r.frame_step;
393 bj->overwrite_frames = true;
394
395 if (do_background) {
396 wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
397 CTX_wm_window(C),
398 scene,
399 "Line Art",
402
406
408
409 WM_jobs_start(CTX_wm_manager(C), wm_job);
410
412
414 }
415
416 wmJobWorkerStatus worker_status = {};
417 lineart_bake_startjob(bj, &worker_status);
418
419 MEM_delete(bj);
420
421 return OPERATOR_FINISHED;
422}
423
424static int lineart_bake_strokes_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
425{
426 bool bake_all = RNA_boolean_get(op->ptr, "bake_all");
427 return lineart_bake_common(C, op, bake_all, true);
428}
430{
431 bool bake_all = RNA_boolean_get(op->ptr, "bake_all");
432 return lineart_bake_common(C, op, bake_all, false);
433}
435 wmOperator *op,
436 const wmEvent * /*event*/)
437{
438 Scene *scene = static_cast<Scene *>(op->customdata);
439
440 /* no running blender, remove handler and pass through. */
441 if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LINEART) == 0) {
443 }
444
446}
447
449{
451 if (md->type != eModifierType_GreasePencilLineart) {
452 continue;
453 }
455 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
456
457 blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(
458 lmd->target_layer);
459 if (!node || !node->is_layer()) {
460 return;
461 }
462 blender::bke::greasepencil::Layer &layer = node->as_layer();
463
464 /* Remove all the keyframes in this layer. */
465 grease_pencil->remove_frames(layer, layer.sorted_keys());
466 grease_pencil->insert_frame(layer, 0);
467
469
470 lmd->flags &= (~MOD_LINEART_IS_BAKED);
471 }
472 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
473}
474
476{
477 bool clear_all = RNA_boolean_get(op->ptr, "clear_all");
478
479 if (clear_all) {
480 CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
481 if (ob->type != OB_GREASE_PENCIL) {
482 continue;
483 }
486 }
488 BKE_report(op->reports, RPT_INFO, "All Line Art objects are now cleared of bakes");
489 }
490 else {
492 if (ob->type != OB_GREASE_PENCIL) {
493 return OPERATOR_CANCELLED;
494 }
497 BKE_report(op->reports, RPT_INFO, "Baked strokes are cleared");
498 }
499
500 return OPERATOR_FINISHED;
501}
502
504{
505 ot->name = "Bake Line Art";
506 ot->description = "Bake Line Art for current Grease Pencil object";
507 ot->idname = "OBJECT_OT_lineart_bake_strokes";
508
513
514 RNA_def_boolean(ot->srna, "bake_all", false, "Bake All", "Bake all line art modifiers");
515}
516
518{
519 ot->name = "Clear Baked Line Art";
520 ot->description = "Clear all strokes in current Grease Pencil object";
521 ot->idname = "OBJECT_OT_lineart_clear";
522
525
526 RNA_def_boolean(ot->srna, "clear_all", false, "Clear All", "Clear all line art modifier bakes");
527}
528
#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)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
void BKE_scene_frame_set(Scene *scene, float frame)
Definition scene.cc:2336
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2647
#define BLI_assert(a)
Definition BLI_assert.h:50
A KD-tree for nearest neighbor search.
#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:1041
@ eModifierMode_Render
@ eModifierMode_Realtime
@ MOD_LINEART_IS_BAKED
@ MOD_LINEART_USE_CACHE
@ eModifierType_GreasePencilLineart
@ OB_GREASE_PENCIL
@ OB_DRAW_IN_FRONT
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_LINEART
Definition WM_api.hh:1606
@ WM_JOB_PROGRESS
Definition WM_api.hh:1566
#define ND_DATA
Definition WM_types.hh:475
#define NC_SCENE
Definition WM_types.hh:345
#define NA_EDITED
Definition WM_types.hh:550
#define ND_FRAME
Definition WM_types.hh:401
#define NC_GPENCIL
Definition WM_types.hh:366
void append(const T &value)
IndexRange index_range() const
bke::CurvesGeometry & strokes_for_write()
draw_view in_light_buf[] float
static bool bake_strokes(Object *ob, Depsgraph *dg, LineartCache **lc, GreasePencilLineartModifierData *md, int frame, bool is_first)
static int 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 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 lineart_mod_is_disabled(Scene *scene, GreasePencilLineartModifierData *md)
static int lineart_bake_strokes_exec(bContext *C, wmOperator *op)
void ED_operatortypes_grease_pencil_lineart()
static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
static void OBJECT_OT_lineart_bake_strokes(wmOperatorType *ot)
static int lineart_bake_common(bContext *C, wmOperator *op, bool bake_all_targets, bool do_background)
static int lineart_bake_strokes_common_modal(bContext *C, wmOperator *op, const wmEvent *)
static int lineart_gpencil_clear_strokes_exec(bContext *C, wmOperator *op)
static void OBJECT_OT_lineart_clear(wmOperatorType *ot)
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:413
blender::Vector< Object * > objects
wmWindowManager * wm
ListBase modifiers
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
void WM_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)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:352
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:455
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:189
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:364
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:223
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:336
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))