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