Blender V4.5
MOD_lineart.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLT_translation.hh"
10
11#include "BLO_read_write.hh"
12
14#include "DNA_defaults.h"
16#include "DNA_scene_types.h"
17
18#include "BKE_collection.hh"
19#include "BKE_geometry_set.hh"
20#include "BKE_grease_pencil.hh"
21#include "BKE_lib_query.hh"
22#include "BKE_material.hh"
23#include "BKE_modifier.hh"
24
25#include "UI_interface.hh"
26#include "UI_resources.hh"
27
29#include "MOD_lineart.hh"
30#include "MOD_modifiertypes.hh"
31#include "MOD_ui_common.hh"
32
33#include "RNA_access.hh"
34#include "RNA_prototypes.hh"
35
37
38#include "ED_grease_pencil.hh"
39
40namespace blender {
41
43{
45 return false;
46 }
47 ModifierData *imd = md.modifier.prev;
48 while (imd != nullptr) {
50 return false;
51 }
52 imd = imd->prev;
53 }
54 return true;
55}
56
57static bool is_last_line_art(const GreasePencilLineartModifierData &md, const bool use_render)
58{
60 return false;
61 }
62 ModifierData *imd = md.modifier.next;
63 while (imd != nullptr) {
65 if (use_render && (imd->mode & eModifierMode_Render)) {
66 return false;
67 }
68 if ((!use_render) && (imd->mode & eModifierMode_Realtime)) {
69 return false;
70 }
71 }
72 imd = imd->next;
73 }
74 return true;
75}
76
85
86static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
87{
89
90 const GreasePencilLineartModifierData *source_lmd =
91 reinterpret_cast<const GreasePencilLineartModifierData *>(md);
92 const LineartModifierRuntime *source_runtime = source_lmd->runtime;
93
95 reinterpret_cast<GreasePencilLineartModifierData *>(target);
96
97 if (source_runtime) {
98 /* `source_runtime` can be nullptr when line art is imported from asset. `target_lmd->runtime`
99 * Will also re-init before calculation/depsgraph evaluation if it's nullptr anyway. */
100 target_lmd->runtime = MEM_new<LineartModifierRuntime>(__func__, *source_runtime);
101 }
102}
103
104static void free_data(ModifierData *md)
105{
107 if (LineartModifierRuntime *runtime = lmd->runtime) {
108 MEM_delete(runtime);
109 lmd->runtime = nullptr;
110 }
111}
112
113static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
114{
116
117 if (lmd->target_layer[0] == '\0' || !lmd->target_material) {
118 return true;
119 }
120 if (lmd->source_type == LINEART_SOURCE_OBJECT && !lmd->source_object) {
121 return true;
122 }
124 return true;
125 }
126 /* Preventing calculation in depsgraph when baking frames. */
127 if (lmd->flags & MOD_LINEART_IS_BAKED) {
128 return true;
129 }
130
131 return false;
132}
133
134static void add_this_collection(Collection &collection,
136 const int mode,
137 Set<const Object *> &object_dependencies)
138{
139 bool default_add = true;
140 /* Do not do nested collection usage check, this is consistent with lineart calculation, because
141 * collection usage doesn't have a INHERIT mode. This might initially be derived from the fact
142 * that an object can be inside multiple collections, but might be irrelevant now with the way
143 * objects are iterated. Keep this logic for now. */
144 if (collection.lineart_usage & COLLECTION_LRT_EXCLUDE) {
145 default_add = false;
146 }
149 if ((ob->lineart.usage == OBJECT_LRT_INHERIT && default_add) ||
150 ob->lineart.usage != OBJECT_LRT_EXCLUDE)
151 {
152 DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
153 DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
154 object_dependencies.add(ob);
155 }
156 }
157 if (ob->type == OB_EMPTY && (ob->transflag & OB_DUPLICOLLECTION)) {
158 if (!ob->instance_collection) {
159 continue;
160 }
161 add_this_collection(*ob->instance_collection, ctx, mode, object_dependencies);
162 object_dependencies.add(ob);
163 }
164 }
166}
167
169{
170 DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
171
173
174 /* Always add whole master collection because line art will need the whole scene for
175 * visibility computation. Line art exclusion is handled inside #add_this_collection. */
176
177 /* Do we need to distinguish DAG_EVAL_VIEWPORT or DAG_EVAL_RENDER here? */
178
179 LineartModifierRuntime *runtime = reinterpret_cast<LineartModifierRuntime *>(lmd->runtime);
180 if (!runtime) {
181 runtime = MEM_new<LineartModifierRuntime>(__func__);
182 lmd->runtime = runtime;
183 }
184 Set<const Object *> &object_dependencies = runtime->object_dependencies;
185 object_dependencies.clear();
186
187 add_this_collection(*ctx->scene->master_collection, ctx, DAG_EVAL_VIEWPORT, object_dependencies);
188
189 /* No need to add any non-geometry objects into `lmd->object_dependencies` because we won't be
190 * loading... */
193 ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
195 ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier");
196 }
197 else if (ctx->scene->camera) {
199 ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
201 ctx->node, ctx->scene->camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier");
202 DEG_add_scene_relation(ctx->node, ctx->scene, DEG_SCENE_COMP_PARAMETERS, "Line Art Modifier");
203 }
204 if (lmd->light_contour_object) {
206 ctx->node, lmd->light_contour_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
207 }
208}
209
210static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
211{
213
214 walk(user_data, ob, (ID **)&lmd->target_material, IDWALK_CB_USER);
215 walk(user_data, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP);
216
217 walk(user_data, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP);
218 walk(user_data, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP);
219 walk(user_data, ob, (ID **)&lmd->light_contour_object, IDWALK_CB_NOP);
220}
221
222static void panel_draw(const bContext * /*C*/, Panel *panel)
223{
224 uiLayout *layout = panel->layout;
225
226 PointerRNA ob_ptr;
228
229 PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
230
231 const int source_type = RNA_enum_get(ptr, "source_type");
232 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
233
234 uiLayoutSetPropSep(layout, true);
235 uiLayoutSetEnabled(layout, !is_baked);
236
237 if (!is_first_lineart(*static_cast<const GreasePencilLineartModifierData *>(ptr->data))) {
238 layout->prop(ptr, "use_cache", UI_ITEM_NONE, std::nullopt, ICON_NONE);
239 }
240
241 layout->prop(ptr, "source_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
242
243 if (source_type == LINEART_SOURCE_OBJECT) {
244 layout->prop(ptr, "source_object", UI_ITEM_NONE, std::nullopt, ICON_OBJECT_DATA);
245 }
246 else if (source_type == LINEART_SOURCE_COLLECTION) {
247 uiLayout *sub = &layout->row(true);
248 sub->prop(ptr, "source_collection", UI_ITEM_NONE, std::nullopt, ICON_OUTLINER_COLLECTION);
249 sub->prop(ptr, "use_invert_collection", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
250 }
251 else {
252 /* Source is Scene. */
253 }
254
255 uiLayout *col = &layout->column(false);
257 ptr,
258 "target_layer",
259 &obj_data_ptr,
260 "layers",
261 std::nullopt,
262 ICON_OUTLINER_DATA_GP_LAYER);
264 col, ptr, "target_material", &obj_data_ptr, "materials", std::nullopt, ICON_MATERIAL);
265
266 col = &layout->column(false);
267 col->prop(ptr, "radius", UI_ITEM_R_SLIDER, IFACE_("Line Radius"), ICON_NONE);
268 col->prop(ptr, "opacity", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
269
271}
272
273static void edge_types_panel_draw(const bContext * /*C*/, Panel *panel)
274{
275 uiLayout *layout = panel->layout;
276 PointerRNA ob_ptr;
278
279 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
280 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
281 const bool is_first = is_first_lineart(
282 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
283 const bool has_light = RNA_pointer_get(ptr, "light_contour_object").data != nullptr;
284
285 uiLayoutSetEnabled(layout, !is_baked);
286
287 uiLayoutSetPropSep(layout, true);
288
289 uiLayout *sub = &layout->row(false);
290 uiLayoutSetActive(sub, has_light);
291 sub->prop(
292 ptr, "shadow_region_filtering", UI_ITEM_NONE, IFACE_("Illumination Filtering"), ICON_NONE);
293
294 uiLayout *col = &layout->column(true);
295
296 sub = &col->row(false, IFACE_("Create"));
297 sub->prop(ptr, "use_contour", UI_ITEM_NONE, "", ICON_NONE);
298
299 uiLayout *entry = &sub->row(true);
300 uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_contour"));
301 entry->prop(ptr, "silhouette_filtering", UI_ITEM_NONE, "", ICON_NONE);
302
303 const int silhouette_filtering = RNA_enum_get(ptr, "silhouette_filtering");
304 if (silhouette_filtering != LINEART_SILHOUETTE_FILTER_NONE) {
305 entry->prop(ptr, "use_invert_silhouette", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
306 }
307
308 sub = &col->row(false);
309 if (use_cache && !is_first) {
310 sub->prop(ptr, "use_crease", UI_ITEM_NONE, IFACE_("Crease (Angle Cached)"), ICON_NONE);
311 }
312 else {
313 sub->prop(ptr, "use_crease", UI_ITEM_NONE, "", ICON_NONE);
314 sub->prop(ptr,
315 "crease_threshold",
317 std::nullopt,
318 ICON_NONE);
319 }
320
321 col->prop(ptr, "use_intersection", UI_ITEM_NONE, IFACE_("Intersections"), ICON_NONE);
322 col->prop(ptr, "use_material", UI_ITEM_NONE, IFACE_("Material Borders"), ICON_NONE);
323 col->prop(ptr, "use_edge_mark", UI_ITEM_NONE, IFACE_("Edge Marks"), ICON_NONE);
324 col->prop(ptr, "use_loose", UI_ITEM_NONE, IFACE_("Loose"), ICON_NONE);
325
326 entry = &col->column(false);
327 uiLayoutSetActive(entry, has_light);
328
329 sub = &entry->row(false);
330 sub->prop(ptr, "use_light_contour", UI_ITEM_NONE, IFACE_("Light Contour"), ICON_NONE);
331
332 entry->prop(ptr,
333 "use_shadow",
336 ICON_NONE);
337
338 layout->label(IFACE_("Options"), ICON_NONE);
339
340 sub = &layout->column(false);
341 if (use_cache && !is_first) {
342 sub->label(IFACE_("Type overlapping cached"), ICON_INFO);
343 }
344 else {
345 sub->prop(ptr,
346 "use_overlap_edge_type_support",
348 IFACE_("Allow Overlapping Types"),
349 ICON_NONE);
350 }
351}
352
353static void options_light_reference_draw(const bContext * /*C*/, Panel *panel)
354{
355 uiLayout *layout = panel->layout;
356 PointerRNA ob_ptr;
358
359 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
360 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
361 const bool has_light = RNA_pointer_get(ptr, "light_contour_object").data != nullptr;
362 const bool is_first = is_first_lineart(
363 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
364
365 uiLayoutSetPropSep(layout, true);
366 uiLayoutSetEnabled(layout, !is_baked);
367
368 if (use_cache && !is_first) {
369 layout->label(RPT_("Cached from the first Line Art modifier."), ICON_INFO);
370 return;
371 }
372
373 layout->prop(ptr, "light_contour_object", UI_ITEM_NONE, std::nullopt, ICON_NONE);
374
375 uiLayout *remaining = &layout->column(false);
376 uiLayoutSetActive(remaining, has_light);
377
378 remaining->prop(ptr, "shadow_camera_size", UI_ITEM_NONE, std::nullopt, ICON_NONE);
379
380 uiLayout *col = &remaining->column(true);
381 col->prop(ptr, "shadow_camera_near", UI_ITEM_NONE, IFACE_("Near"), ICON_NONE);
382 col->prop(ptr, "shadow_camera_far", UI_ITEM_NONE, IFACE_("Far"), ICON_NONE);
383}
384
385static void options_panel_draw(const bContext * /*C*/, Panel *panel)
386{
387 uiLayout *layout = panel->layout;
388 PointerRNA ob_ptr;
390
391 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
392 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
393 const bool is_first = is_first_lineart(
394 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
395
396 uiLayoutSetPropSep(layout, true);
397 uiLayoutSetEnabled(layout, !is_baked);
398
399 if (use_cache && !is_first) {
400 layout->label(TIP_("Cached from the first Line Art modifier"), ICON_INFO);
401 return;
402 }
403
404 uiLayout *row = &layout->row(false, IFACE_("Custom Camera"));
405 row->prop(ptr, "use_custom_camera", UI_ITEM_NONE, "", ICON_NONE);
406 uiLayout *subrow = &row->row(true);
407 uiLayoutSetActive(subrow, RNA_boolean_get(ptr, "use_custom_camera"));
408 uiLayoutSetPropSep(subrow, true);
409 subrow->prop(ptr, "source_camera", UI_ITEM_NONE, "", ICON_OBJECT_DATA);
410
411 uiLayout *col = &layout->column(true);
412
413 col->prop(
414 ptr, "use_edge_overlap", UI_ITEM_NONE, IFACE_("Overlapping Edges As Contour"), ICON_NONE);
415 col->prop(ptr, "use_object_instances", UI_ITEM_NONE, std::nullopt, ICON_NONE);
416 col->prop(ptr, "use_clip_plane_boundaries", UI_ITEM_NONE, std::nullopt, ICON_NONE);
417 col->prop(ptr, "use_crease_on_smooth", UI_ITEM_NONE, IFACE_("Crease On Smooth"), ICON_NONE);
418 col->prop(ptr, "use_crease_on_sharp", UI_ITEM_NONE, IFACE_("Crease On Sharp"), ICON_NONE);
419 col->prop(
420 ptr, "use_back_face_culling", UI_ITEM_NONE, IFACE_("Force Backface Culling"), ICON_NONE);
421}
422
423static void occlusion_panel_draw(const bContext * /*C*/, Panel *panel)
424{
425 uiLayout *layout = panel->layout;
426 PointerRNA ob_ptr;
428
429 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
430
431 const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
432 const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
433
434 uiLayoutSetPropSep(layout, true);
435 uiLayoutSetEnabled(layout, !is_baked);
436
437 if (!show_in_front) {
438 layout->label(TIP_("Object is not in front"), ICON_INFO);
439 }
440
441 layout = &layout->column(false);
442 uiLayoutSetActive(layout, show_in_front);
443
444 layout->prop(ptr, "use_multiple_levels", UI_ITEM_NONE, IFACE_("Range"), ICON_NONE);
445
446 if (use_multiple_levels) {
447 uiLayout *col = &layout->column(true);
448 col->prop(ptr, "level_start", UI_ITEM_NONE, std::nullopt, ICON_NONE);
449 col->prop(ptr, "level_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
450 }
451 else {
452 layout->prop(ptr, "level_start", UI_ITEM_NONE, IFACE_("Level"), ICON_NONE);
453 }
454}
455
457{
458 const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
459 const int level_start = RNA_int_get(ptr, "level_start");
460 const int level_end = RNA_int_get(ptr, "level_end");
461 if (use_multiple_levels) {
462 return std::max(level_start, level_end) > 0;
463 }
464 return level_start > 0;
465}
466
467static void material_mask_panel_draw_header(const bContext * /*C*/, Panel *panel)
468{
469 uiLayout *layout = panel->layout;
470 PointerRNA ob_ptr;
472
473 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
474 const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
475
476 uiLayoutSetEnabled(layout, !is_baked);
477 uiLayoutSetActive(layout, show_in_front && anything_showing_through(ptr));
478
479 layout->prop(ptr, "use_material_mask", UI_ITEM_NONE, IFACE_("Material Mask"), ICON_NONE);
480}
481
482static void material_mask_panel_draw(const bContext * /*C*/, Panel *panel)
483{
484 uiLayout *layout = panel->layout;
486
487 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
488 uiLayoutSetEnabled(layout, !is_baked);
490
491 uiLayoutSetPropSep(layout, true);
492
493 uiLayoutSetEnabled(layout, RNA_boolean_get(ptr, "use_material_mask"));
494
495 uiLayout *col = &layout->column(true);
496 uiLayout *sub = &col->row(true, IFACE_("Masks"));
497
498 PropertyRNA *prop = RNA_struct_find_property(ptr, "use_material_mask_bits");
499 for (int i = 0; i < 8; i++) {
500 sub->prop(ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE);
501 if (i == 3) {
502 sub = &col->row(true);
503 }
504 }
505
506 layout->prop(ptr, "use_material_mask_match", UI_ITEM_NONE, IFACE_("Exact Match"), ICON_NONE);
507}
508
509static void intersection_panel_draw(const bContext * /*C*/, Panel *panel)
510{
511 uiLayout *layout = panel->layout;
513
514 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
515 uiLayoutSetEnabled(layout, !is_baked);
516
517 uiLayoutSetPropSep(layout, true);
518
519 uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_intersection"));
520
521 uiLayout *col = &layout->column(true);
522 uiLayout *sub = &col->row(true, IFACE_("Collection Masks"));
523
524 PropertyRNA *prop = RNA_struct_find_property(ptr, "use_intersection_mask");
525 for (int i = 0; i < 8; i++) {
526 sub->prop(ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE);
527 if (i == 3) {
528 sub = &col->row(true);
529 }
530 }
531
532 layout->prop(ptr, "use_intersection_match", UI_ITEM_NONE, IFACE_("Exact Match"), ICON_NONE);
533}
534
535static void face_mark_panel_draw_header(const bContext * /*C*/, Panel *panel)
536{
537 uiLayout *layout = panel->layout;
538 PointerRNA ob_ptr;
540
541 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
542 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
543 const bool is_first = is_first_lineart(
544 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
545
546 if (!use_cache || is_first) {
547 uiLayoutSetEnabled(layout, !is_baked);
548 layout->prop(ptr, "use_face_mark", UI_ITEM_NONE, IFACE_("Face Mark Filtering"), ICON_NONE);
549 }
550 else {
551 layout->label(IFACE_("Face Mark Filtering"), ICON_NONE);
552 }
553}
554
555static void face_mark_panel_draw(const bContext * /*C*/, Panel *panel)
556{
557 uiLayout *layout = panel->layout;
558 PointerRNA ob_ptr;
560
561 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
562 const bool use_mark = RNA_boolean_get(ptr, "use_face_mark");
563 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
564 const bool is_first = is_first_lineart(
565 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
566
567 uiLayoutSetEnabled(layout, !is_baked);
568
569 if (use_cache && !is_first) {
570 layout->label(TIP_("Cached from the first Line Art modifier"), ICON_INFO);
571 return;
572 }
573
574 uiLayoutSetPropSep(layout, true);
575
576 uiLayoutSetActive(layout, use_mark);
577
578 layout->prop(ptr, "use_face_mark_invert", UI_ITEM_NONE, std::nullopt, ICON_NONE);
579 layout->prop(ptr, "use_face_mark_boundaries", UI_ITEM_NONE, std::nullopt, ICON_NONE);
580 layout->prop(ptr, "use_face_mark_keep_contour", UI_ITEM_NONE, std::nullopt, ICON_NONE);
581}
582
583static void chaining_panel_draw(const bContext * /*C*/, Panel *panel)
584{
585 PointerRNA ob_ptr;
587
588 uiLayout *layout = panel->layout;
589
590 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
591 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
592 const bool is_first = is_first_lineart(
593 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
594 const bool is_geom = RNA_boolean_get(ptr, "use_geometry_space_chain");
595
596 uiLayoutSetPropSep(layout, true);
597 uiLayoutSetEnabled(layout, !is_baked);
598
599 if (use_cache && !is_first) {
600 layout->label(TIP_("Cached from the first Line Art modifier"), ICON_INFO);
601 return;
602 }
603
604 uiLayout *col = &layout->column(true, IFACE_("Chain"));
605 col->prop(ptr, "use_fuzzy_intersections", UI_ITEM_NONE, std::nullopt, ICON_NONE);
606 col->prop(ptr, "use_fuzzy_all", UI_ITEM_NONE, std::nullopt, ICON_NONE);
607 col->prop(ptr, "use_loose_edge_chain", UI_ITEM_NONE, IFACE_("Loose Edges"), ICON_NONE);
608 col->prop(
609 ptr, "use_loose_as_contour", UI_ITEM_NONE, IFACE_("Loose Edges As Contour"), ICON_NONE);
610 col->prop(ptr, "use_detail_preserve", UI_ITEM_NONE, std::nullopt, ICON_NONE);
611 col->prop(ptr, "use_geometry_space_chain", UI_ITEM_NONE, IFACE_("Geometry Space"), ICON_NONE);
612
613 layout->prop(ptr,
614 "chaining_image_threshold",
616 is_geom ? std::make_optional<StringRefNull>(IFACE_("Geometry Threshold")) :
617 std::nullopt,
618 ICON_NONE);
619
620 layout->prop(ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
621 layout->prop(ptr, "split_angle", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
622}
623
624static void vgroup_panel_draw(const bContext * /*C*/, Panel *panel)
625{
626 PointerRNA ob_ptr;
628
629 uiLayout *layout = panel->layout;
630
631 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
632 const bool use_cache = RNA_boolean_get(ptr, "use_cache");
633 const bool is_first = is_first_lineart(
634 *static_cast<const GreasePencilLineartModifierData *>(ptr->data));
635
636 uiLayoutSetPropSep(layout, true);
637 uiLayoutSetEnabled(layout, !is_baked);
638
639 if (use_cache && !is_first) {
640 layout->label(TIP_("Cached from the first Line Art modifier"), ICON_INFO);
641 return;
642 }
643
644 uiLayout *col = &layout->column(true);
645
646 uiLayout *row = &col->row(true);
647
648 row->prop(ptr, "source_vertex_group", UI_ITEM_NONE, IFACE_("Filter Source"), ICON_GROUP_VERTEX);
649 row->prop(ptr, "invert_source_vertex_group", UI_ITEM_R_TOGGLE, "", ICON_ARROW_LEFTRIGHT);
650
651 col->prop(ptr, "use_output_vertex_group_match_by_name", UI_ITEM_NONE, std::nullopt, ICON_NONE);
652
653 uiItemPointerR(col, ptr, "vertex_group", &ob_ptr, "vertex_groups", IFACE_("Target"), ICON_NONE);
654}
655
656static void bake_panel_draw(const bContext * /*C*/, Panel *panel)
657{
658 uiLayout *layout = panel->layout;
659 PointerRNA ob_ptr;
661
662 const bool is_baked = RNA_boolean_get(ptr, "is_baked");
663
664 uiLayoutSetPropSep(layout, true);
665
666 if (is_baked) {
667 uiLayout *col = &layout->column(false);
668 uiLayoutSetPropSep(col, false);
669 col->label(TIP_("Modifier has baked data"), ICON_NONE);
670 col->prop(ptr, "is_baked", UI_ITEM_R_TOGGLE, IFACE_("Continue Without Clearing"), ICON_NONE);
671 }
672
673 uiLayout *col = &layout->column(false);
674 uiLayoutSetEnabled(col, !is_baked);
675 col->op("OBJECT_OT_lineart_bake_strokes", std::nullopt, ICON_NONE);
676 PointerRNA op_ptr = col->op("OBJECT_OT_lineart_bake_strokes", IFACE_("Bake All"), ICON_NONE);
677 RNA_boolean_set(&op_ptr, "bake_all", true);
678
679 col = &layout->column(false);
680 col->op("OBJECT_OT_lineart_clear", std::nullopt, ICON_NONE);
681 op_ptr = col->op("OBJECT_OT_lineart_clear", IFACE_("Clear All"), ICON_NONE);
682 RNA_boolean_set(&op_ptr, "clear_all", true);
683}
684
685static void composition_panel_draw(const bContext * /*C*/, Panel *panel)
686{
687 PointerRNA ob_ptr;
689
690 uiLayout *layout = panel->layout;
691
692 const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
693
694 uiLayoutSetPropSep(layout, true);
695
696 layout->prop(ptr, "overscan", UI_ITEM_NONE, std::nullopt, ICON_NONE);
697 layout->prop(ptr, "use_image_boundary_trimming", UI_ITEM_NONE, std::nullopt, ICON_NONE);
698
699 if (show_in_front) {
700 layout->label(TIP_("Object is shown in front"), ICON_ERROR);
701 }
702
703 uiLayout *col = &layout->column(false);
704 uiLayoutSetActive(col, !show_in_front);
705
706 col->prop(ptr, "stroke_depth_offset", UI_ITEM_NONE, IFACE_("Depth Offset"), ICON_NONE);
707 col->prop(ptr,
708 "use_offset_towards_custom_camera",
710 IFACE_("Towards Custom Camera"),
711 ICON_NONE);
712}
713
714static void panel_register(ARegionType *region_type)
715{
718
720 region_type, "edge_types", "Edge Types", nullptr, edge_types_panel_draw, panel_type);
721 modifier_subpanel_register(region_type,
722 "light_reference",
723 "Light Reference",
724 nullptr,
726 panel_type);
728 region_type, "geometry", "Geometry Processing", nullptr, options_panel_draw, panel_type);
729 PanelType *occlusion_panel = modifier_subpanel_register(
730 region_type, "occlusion", "Occlusion", nullptr, occlusion_panel_draw, panel_type);
731 modifier_subpanel_register(region_type,
732 "material_mask",
733 "",
736 occlusion_panel);
738 region_type, "intersection", "Intersection", nullptr, intersection_panel_draw, panel_type);
740 region_type, "face_mark", "", face_mark_panel_draw_header, face_mark_panel_draw, panel_type);
742 region_type, "chaining", "Chaining", nullptr, chaining_panel_draw, panel_type);
744 region_type, "vgroup", "Vertex Weight Transfer", nullptr, vgroup_panel_draw, panel_type);
746 region_type, "composition", "Composition", nullptr, composition_panel_draw, panel_type);
747 modifier_subpanel_register(region_type, "bake", "Bake", nullptr, bake_panel_draw, panel_type);
748}
749
751 const ModifierEvalContext &ctx,
752 GreasePencil &grease_pencil,
753 GreasePencilLineartModifierData &first_lineart,
754 const bool force_compute)
755{
756 using namespace bke::greasepencil;
757 auto &lmd = reinterpret_cast<GreasePencilLineartModifierData &>(md);
758
759 TreeNode *node = grease_pencil.find_node_by_name(lmd.target_layer);
760 if (!node || !node->is_layer()) {
761 return;
762 }
763
764 const bool is_first_lineart = (&first_lineart == &lmd);
765 const bool use_cache = (lmd.flags & MOD_LINEART_USE_CACHE);
766 LineartCache *local_lc = (is_first_lineart || use_cache) ? first_lineart.shared_cache : nullptr;
767
768 /* Only calculate strokes in these three conditions:
769 * 1. It's the very first line art modifier in the stack.
770 * 2. This line art modifier doesn't want to use globally cached data.
771 * 3. This modifier is not the first line art in stack, but it's the first that's visible (so we
772 * need to do a `force_compute`). */
773 if (is_first_lineart || (!use_cache) || force_compute) {
775 ctx.depsgraph, lmd, &local_lc, !(ctx.object->dtx & OB_DRAW_IN_FRONT));
777 }
779 lmd.cache = local_lc;
780
781 const int current_frame = grease_pencil.runtime->eval_frame;
782
783 /* Ensure we have a frame in the selected layer to put line art result in. */
784 Layer &layer = node->as_layer();
785
786 const float4x4 &mat = ctx.object->world_to_object();
787
788 /* `drawing` can be nullptr if current frame is before any of the key frames, in which case no
789 * strokes are generated. We still allow cache operations to run at the end of this function
790 * because there might be other line art modifiers in the same stack. */
791 Drawing *drawing = [&]() -> Drawing * {
792 if (Drawing *drawing = grease_pencil.get_drawing_at(layer, current_frame)) {
793 return drawing;
794 }
795 return grease_pencil.insert_frame(layer, current_frame);
796 }();
797
798 if (drawing) {
800 lmd.cache,
801 mat,
802 ctx.depsgraph,
803 *drawing,
804 lmd.source_type,
805 lmd.source_object,
806 lmd.source_collection,
807 lmd.level_start,
808 lmd.use_multiple_levels ? lmd.level_end : lmd.level_start,
809 lmd.target_material ? BKE_object_material_index_get(ctx.object, lmd.target_material) : 0,
810 lmd.edge_types,
811 lmd.mask_switches,
812 lmd.material_mask_bits,
813 lmd.intersection_mask,
814 lmd.radius,
815 lmd.opacity,
816 lmd.shadow_selection,
817 lmd.silhouette_selection,
818 lmd.source_vertex_group,
819 lmd.vgname,
820 lmd.flags,
821 lmd.calculation_flags);
822 }
823
824 if ((!is_first_lineart) && (!use_cache)) {
825 /* We only clear local cache, not global cache from the first line art modifier. */
826 BLI_assert(local_lc != first_lineart.shared_cache);
827 MOD_lineart_clear_cache(&local_lc);
828 /* Restore the original cache pointer so the modifiers below still have access to the "global"
829 * cache. */
830 lmd.cache = first_lineart.shared_cache;
831 }
832}
833
835 const ModifierEvalContext *ctx,
836 bke::GeometrySet *geometry_set)
837{
838 if (!geometry_set->has_grease_pencil()) {
839 return;
840 }
841 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
842 auto *mmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
843
844 GreasePencilLineartModifierData *first_lineart =
846 BLI_assert(first_lineart);
847
848 /* Since settings for line art cached data are always in the first line art modifier, we need to
849 * get and set overall calculation limits on the first modifier regardless of its visibility
850 * state. If line art cache doesn't exist, it means line art hasn't done any calculation. */
851 const bool cache_ready = (first_lineart->shared_cache != nullptr);
852 if (!cache_ready) {
853 first_lineart->shared_cache = MOD_lineart_init_cache();
855 first_lineart->shared_cache->LimitInfo);
856 }
858 *mmd, first_lineart->shared_cache->LimitInfo, cache_ready);
859
860 generate_strokes(*md, *ctx, grease_pencil, *first_lineart, (!cache_ready));
861
862 const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER);
863 if (is_last_line_art(*mmd, use_render_params)) {
864 MOD_lineart_clear_cache(&first_lineart->shared_cache);
865 }
866
867 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
868}
869
870static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
871{
872 const auto *lmd = reinterpret_cast<const GreasePencilLineartModifierData *>(md);
873
875}
876
877static void blend_read(BlendDataReader * /*reader*/, ModifierData *md)
878{
880 lmd->runtime = MEM_new<LineartModifierRuntime>(__func__);
881}
882
883} // namespace blender
884
886 /*idname*/ "Lineart Modifier",
887 /*name*/ N_("Lineart"),
888 /*struct_name*/ "GreasePencilLineartModifierData",
889 /*struct_size*/ sizeof(GreasePencilLineartModifierData),
890 /*srna*/ &RNA_GreasePencilLineartModifier,
893 /*icon*/ ICON_MOD_LINEART,
894
895 /*copy_data*/ blender::copy_data,
896
897 /*deform_verts*/ nullptr,
898 /*deform_matrices*/ nullptr,
899 /*deform_verts_EM*/ nullptr,
900 /*deform_matrices_EM*/ nullptr,
901 /*modify_mesh*/ nullptr,
902 /*modify_geometry_set*/ blender::modify_geometry_set,
903
904 /*init_data*/ blender::init_data,
905 /*required_data_mask*/ nullptr,
906 /*free_data*/ blender::free_data,
907 /*is_disabled*/ blender::is_disabled,
908 /*update_depsgraph*/ blender::update_depsgraph,
909 /*depends_on_time*/ nullptr,
910 /*depends_on_normals*/ nullptr,
911 /*foreach_ID_link*/ blender::foreach_ID_link,
912 /*foreach_tex_link*/ nullptr,
913 /*free_runtime_data*/ nullptr,
914 /*panel_register*/ blender::panel_register,
915 /*blend_write*/ blender::blend_write,
916 /*blend_read*/ blender::blend_read,
917};
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN(_collection, _object, _mode)
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END
Low-level operations for grease pencil.
@ IDWALK_CB_USER
@ IDWALK_CB_NOP
General operations, lookup, etc. for materials.
int BKE_object_material_index_get(Object *ob, const Material *ma)
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_AcceptsGreasePencil
@ MOD_APPLY_RENDER
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define RPT_(msgid)
#define TIP_(msgid)
#define BLT_I18NCONTEXT_ID_GPENCIL
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ DAG_EVAL_VIEWPORT
@ DEG_SCENE_COMP_PARAMETERS
void DEG_add_scene_relation(DepsNodeHandle *node_handle, Scene *scene, eDepsSceneComponentType component, const char *description)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_GEOMETRY
@ DEG_OB_COMP_TRANSFORM
@ DEG_OB_COMP_PARAMETERS
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
Object groups, one object can be in many groups at once.
@ COLLECTION_LRT_EXCLUDE
#define DNA_struct_default_get(struct_name)
@ MOD_LINEART_USE_CUSTOM_CAMERA
@ eModifierMode_Render
@ eModifierMode_Realtime
@ LINEART_SILHOUETTE_FILTER_NONE
@ MOD_LINEART_IS_BAKED
@ MOD_LINEART_USE_CACHE
@ LINEART_SOURCE_OBJECT
@ LINEART_SOURCE_COLLECTION
@ eModifierType_GreasePencilLineart
@ OB_MBALL
@ OB_EMPTY
@ OB_SURF
@ OB_FONT
@ OB_MESH
@ OB_CURVES_LEGACY
@ OBJECT_LRT_EXCLUDE
@ OBJECT_LRT_INHERIT
@ OB_DUPLICOLLECTION
@ OB_DRAW_IN_FRONT
static bool is_disabled
ModifierTypeInfo modifierType_GreasePencilLineart
PanelType * modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
@ UI_ITEM_R_TOGGLE
@ UI_ITEM_R_FORCE_BLANK_DECORATE
@ UI_ITEM_R_SLIDER
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
void uiItemPointerR(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, PointerRNA *searchptr, blender::StringRefNull searchpropname, std::optional< blender::StringRefNull > name, int icon)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
bool is_layer() const
const Layer & as_layer() const
bool add(const Key &key)
Definition BLI_set.hh:248
void clear()
Definition BLI_set.hh:551
uint col
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
LineartCache * MOD_lineart_init_cache()
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)
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)
static void material_mask_panel_draw_header(const bContext *, Panel *panel)
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
static void material_mask_panel_draw(const bContext *, Panel *panel)
static void chaining_panel_draw(const bContext *, Panel *panel)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void occlusion_panel_draw(const bContext *, Panel *panel)
static void panel_draw(const bContext *C, Panel *panel)
MatBase< float, 4, 4 > float4x4
static void add_this_collection(Collection &collection, const ModifierUpdateDepsgraphContext *ctx, const int mode, Set< const Object * > &object_dependencies)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static bool anything_showing_through(PointerRNA *ptr)
static void generate_strokes(ModifierData &md, const ModifierEvalContext &ctx, GreasePencil &grease_pencil, GreasePencilLineartModifierData &first_lineart, const bool force_compute)
static void free_data(ModifierData *md)
static void bake_panel_draw(const bContext *, Panel *panel)
static void options_panel_draw(const bContext *, Panel *panel)
static void panel_register(ARegionType *region_type)
static bool is_last_line_art(const GreasePencilLineartModifierData &md, const bool use_render)
static void edge_types_panel_draw(const bContext *, Panel *panel)
static void face_mark_panel_draw_header(const bContext *, Panel *panel)
static void intersection_panel_draw(const bContext *, Panel *panel)
static void composition_panel_draw(const bContext *, Panel *panel)
static void vgroup_panel_draw(const bContext *, Panel *panel)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static bool is_disabled(const Scene *, ModifierData *md, bool)
static void face_mark_panel_draw(const bContext *, Panel *panel)
static void blend_read(BlendDataReader *reader, ModifierData *md)
static bool is_first_lineart(const GreasePencilLineartModifierData &md)
static void options_light_reference_draw(const bContext *, Panel *panel)
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
struct LineartModifierRuntime * runtime
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:404
blender::ed::greasepencil::LineartLimitInfo LimitInfo
blender::Set< const Object * > object_dependencies
struct ModifierData * next
struct ModifierData * prev
ModifierApplyFlag flag
struct uiLayout * layout
void * data
Definition RNA_types.hh:53
struct Collection * master_collection
struct Object * camera
GreasePencil * get_grease_pencil_for_write()
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
uiLayout & row(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4227
uint8_t flag
Definition wm_window.cc:139