Blender V5.0
MOD_grease_pencil_build.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_array.hh"
10#include "BLI_sort.hh"
11
12#include "BLT_translation.hh"
13
14#include "BLO_read_write.hh"
15
16#include "DNA_defaults.h"
17#include "DNA_object_types.h"
18#include "DNA_scene_types.h"
19#include "DNA_screen_types.h"
20
22
23#include "BKE_curves.hh"
24#include "BKE_geometry_set.hh"
25#include "BKE_grease_pencil.hh"
26#include "BKE_lib_query.hh"
27#include "BKE_modifier.hh"
28
30#include "UI_resources.hh"
31
33#include "MOD_modifiertypes.hh"
34#include "MOD_ui_common.hh"
35
36#include "RNA_access.hh"
37#include "RNA_prototypes.hh"
38
39#include "GEO_reorder.hh"
40
41namespace blender {
42
43/* The time in seconds strokes will take when the `delta_time` attribute does not exist. */
44constexpr float GP_BUILD_TIME_DEFAULT_STROKES = 1.0f;
45
55
56static void copy_data(const ModifierData *md, ModifierData *target, int flags)
57{
58 const auto *omd = reinterpret_cast<const GreasePencilBuildModifierData *>(md);
59 auto *tomd = reinterpret_cast<GreasePencilBuildModifierData *>(target);
60
62
63 BKE_modifier_copydata_generic(md, target, flags);
64 modifier::greasepencil::copy_influence_data(&omd->influence, &tomd->influence, flags);
65}
66
67static void free_data(ModifierData *md)
68{
69 auto *omd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
71}
72
73static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
74{
75 auto *omd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
76 modifier::greasepencil::foreach_influence_ID_link(&omd->influence, ob, walk, user_data);
77 walk(user_data, ob, (ID **)&omd->object, IDWALK_CB_NOP);
78}
79
81{
82 auto *mmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
83 if (mmd->object != nullptr) {
84 DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Build Modifier");
85 }
86 DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Build Modifier");
87}
88
89static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
90{
91 const auto *mmd = reinterpret_cast<const GreasePencilBuildModifierData *>(md);
92
94 modifier::greasepencil::write_influence_data(writer, &mmd->influence);
95}
96
97static void blend_read(BlendDataReader *reader, ModifierData *md)
98{
99 auto *mmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
100
101 modifier::greasepencil::read_influence_data(reader, &mmd->influence);
102}
103
105 const IndexMask &selection,
106 const int time_alignment,
107 const int transition,
108 const float factor,
109 const bool clamp_points,
110 int &r_curves_num,
111 int &r_points_num)
112{
113 const int stroke_count = curves.curves_num();
114 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
115 const VArray<bool> cyclic = curves.cyclic();
116
118 float max_length = 0;
119 for (const int stroke : curves.curves_range()) {
120 const bool stroke_cyclic = cyclic[stroke];
121 const float len = curves.evaluated_length_total_for_curve(stroke, stroke_cyclic);
122 max_length = math::max(max_length, len);
123 }
124
125 float factor_to_keep = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW ? factor :
126 1.0f - factor;
127 if (clamp_points) {
128 r_curves_num = r_points_num = 0;
129 factor_to_keep = std::clamp(factor_to_keep, 0.0f, 1.0f);
130 }
131
132 auto get_stroke_factor = [&](const float factor, const int index) {
133 const bool stroke_cyclic = cyclic[index];
134 const float total_length = curves.evaluated_length_total_for_curve(index, stroke_cyclic);
135 if (total_length == 0) {
136 return factor > 0.5f ? 1.0f : 0.0f;
137 }
138 const float max_factor = max_length / total_length;
139 if (time_alignment == MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START) {
140 if (clamp_points) {
141 return std::clamp(factor * max_factor, 0.0f, 1.0f);
142 }
143 return factor * max_factor;
144 }
145 if (time_alignment == MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END) {
146 const float min_factor = max_factor - 1.0f;
147 const float use_factor = factor * max_factor;
148 if (clamp_points) {
149 return std::clamp(use_factor - min_factor, 0.0f, 1.0f);
150 }
151 return use_factor - min_factor;
152 }
153 return 0.0f;
154 };
155
156 Array<bool> select(stroke_count);
157 selection.to_bools(select.as_mutable_span());
158 Array<int> result(stroke_count);
159 for (const int curve : curves.curves_range()) {
160 const float local_factor = select[curve] ? get_stroke_factor(factor_to_keep, curve) : 1.0f;
161 const int num_points = points_by_curve[curve].size() * local_factor;
162 result[curve] = num_points;
163 if (clamp_points) {
164 r_points_num += num_points;
165 if (num_points > 0) {
166 r_curves_num++;
167 }
168 }
169 }
170 return result;
171}
172
174 bke::CurvesGeometry &curves,
175 const IndexMask &selection,
176 const int time_alignment,
177 const int transition,
178 const float factor,
179 const float factor_start,
180 const float factor_opacity,
181 const float factor_radii,
182 StringRefNull target_vgname)
183{
184 int dst_curves_num, dst_points_num;
185 const bool has_fade = factor_start != factor;
186 const Array<int> point_counts_to_keep = point_counts_to_keep_concurrent(
187 curves, selection, time_alignment, transition, factor, true, dst_curves_num, dst_points_num);
188 if (dst_curves_num == 0) {
189 return {};
190 }
191 const Array<int> starts_per_curve = has_fade ? point_counts_to_keep_concurrent(curves,
192 selection,
193 time_alignment,
194 transition,
195 factor_start,
196 false,
197 dst_curves_num,
198 dst_points_num) :
199 Array<int>(0);
200 const Array<int> ends_per_curve = has_fade ? point_counts_to_keep_concurrent(curves,
201 selection,
202 time_alignment,
203 transition,
204 factor,
205 false,
206 dst_curves_num,
207 dst_points_num) :
208 Array<int>(0);
209
210 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
211 MutableSpan<float> opacities = drawing.opacities_for_write();
212 MutableSpan<float> radii = drawing.radii_for_write();
214 bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(target_vgname);
215
216 const bool is_vanishing = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
217
218 bke::CurvesGeometry dst_curves(dst_points_num, dst_curves_num);
219 Array<int> dst_to_src_point(dst_points_num);
220 Array<int> dst_to_src_curve(dst_curves_num);
221 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
222 dst_offsets[0] = 0;
223
224 int next_curve = 0;
225 int next_point = 0;
226 for (const int curve : curves.curves_range()) {
227 if (!point_counts_to_keep[curve]) {
228 continue;
229 }
230 const IndexRange points = points_by_curve[curve];
231 dst_offsets[next_curve] = point_counts_to_keep[curve];
232 const int curve_size = points.size();
233
234 auto get_fade_weight = [&](const int local_index) {
235 const float fade_range = std::abs(ends_per_curve[curve] - starts_per_curve[curve]);
236 if (is_vanishing) {
237 const float factor_from_start = local_index - curve_size + ends_per_curve[curve];
238 return 1.0f - std::clamp(factor_from_start / fade_range, 0.0f, 1.0f);
239 }
240 const float factor_from_start = local_index - starts_per_curve[curve];
241 return std::clamp(factor_from_start / fade_range, 0.0f, 1.0f);
242 };
243
244 const int extra_offset = is_vanishing ? points.size() - point_counts_to_keep[curve] : 0;
245 for (const int stroke_point : IndexRange(point_counts_to_keep[curve])) {
246 const int src_point_index = points.first() + extra_offset + stroke_point;
247 if (has_fade) {
248 const float fade_weight = get_fade_weight(extra_offset + stroke_point);
249 opacities[src_point_index] = opacities[src_point_index] *
250 (1.0f - fade_weight * factor_opacity);
251 radii[src_point_index] = radii[src_point_index] * (1.0f - fade_weight * factor_radii);
252 if (!weights.span.is_empty()) {
253 weights.span[src_point_index] = fade_weight;
254 }
255 }
256 dst_to_src_point[next_point] = src_point_index;
257 next_point++;
258 }
259 dst_to_src_curve[next_curve] = curve;
260 next_curve++;
261 }
262 weights.finish();
263
265
266 const bke::AttributeAccessor src_attributes = curves.attributes();
267 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
268
269 gather_attributes(src_attributes,
272 {},
273 dst_to_src_point,
274 dst_attributes);
275 gather_attributes(src_attributes,
278 {},
279 dst_to_src_curve,
280 dst_attributes);
281
282 dst_curves.update_curve_types();
283
284 return dst_curves;
285}
286
288 const IndexMask &selection,
289 const int transition,
290 const float factor,
291 const bool clamp_points,
292 int &r_curves_num,
293 int &r_points_num)
294{
295 const int stroke_count = curves.curves_num();
296 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
297
298 float factor_to_keep = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW ? factor :
299 (1.0f - factor);
300 if (clamp_points) {
301 factor_to_keep = std::clamp(factor_to_keep, 0.0f, 1.0f);
302 }
303
304 const bool is_vanishing = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
305
306 int effective_points_num = offset_indices::sum_group_sizes(points_by_curve, selection);
307
308 const int untouched_points_num = points_by_curve.total_size() - effective_points_num;
309 effective_points_num *= factor_to_keep;
310 effective_points_num += untouched_points_num;
311
312 r_points_num = effective_points_num;
313 r_curves_num = 0;
314
315 Array<bool> select(stroke_count);
316 selection.to_bools(select.as_mutable_span());
317
318 int counted_points_num = 0;
319 for (const int i : curves.curves_range()) {
320 const int stroke = is_vanishing ? stroke_count - i - 1 : i;
321 if (select[stroke] && counted_points_num >= effective_points_num) {
322 continue;
323 }
324 counted_points_num += points_by_curve[stroke].size();
325 r_curves_num++;
326 }
327}
328
330 bke::CurvesGeometry &curves,
331 const IndexMask &selection,
332 const int transition,
333 const float factor,
334 const float factor_start,
335 const float factor_opacity,
336 const float factor_radii,
337 StringRefNull target_vgname)
338{
339 const bool has_fade = factor_start != factor;
340 int dst_curves_num, dst_points_num;
341 int start_points_num, end_points_num, dummy_curves_num;
343 curves, selection, transition, factor, true, dst_curves_num, dst_points_num);
344
345 if (dst_curves_num == 0) {
346 return {};
347 }
348
350 curves, selection, transition, factor_start, false, dummy_curves_num, start_points_num);
352 curves, selection, transition, factor, false, dummy_curves_num, end_points_num);
353
354 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
355 MutableSpan<float> opacities = drawing.opacities_for_write();
356 MutableSpan<float> radii = drawing.radii_for_write();
358 bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(target_vgname);
359
360 const bool is_vanishing = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
361
362 bke::CurvesGeometry dst_curves(dst_points_num, dst_curves_num);
363 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
364 Array<int> dst_to_src_point(dst_points_num);
365 Array<int> dst_to_src_curve(dst_curves_num);
366
367 dst_offsets[0] = 0;
368
369 int next_curve = 1, next_point = 0;
370 IndexMaskMemory memory;
371 selection.complement(curves.curves_range(), memory).foreach_index([&](const int stroke) {
372 for (const int point : points_by_curve[stroke]) {
373 dst_to_src_point[next_point] = point;
374 next_point++;
375 }
376 dst_to_src_curve[next_curve - 1] = stroke;
377 dst_offsets[next_curve] = next_point;
378 next_curve++;
379 });
380
381 const int stroke_count = curves.curves_num();
382 bool done_scanning = false;
383 selection.foreach_index([&](const int i) {
384 const int stroke = is_vanishing ? stroke_count - i - 1 : i;
385 if (done_scanning || next_point >= dst_points_num) {
386 done_scanning = true;
387 return;
388 }
389
390 auto get_fade_weight = [&](const int next_point_count) {
391 return std::clamp(float(next_point_count - start_points_num) /
392 float(abs(end_points_num - start_points_num)),
393 0.0f,
394 1.0f);
395 };
396
397 const IndexRange points = points_by_curve[stroke];
398 for (const int point : points) {
399 const int local_index = point - points.first();
400 const int src_point_index = is_vanishing ? points.last() - local_index : point;
401 dst_to_src_point[next_point] = src_point_index;
402
403 if (has_fade) {
404 const float fade_weight = get_fade_weight(next_point);
405 opacities[src_point_index] = opacities[src_point_index] *
406 (1.0f - fade_weight * factor_opacity);
407 radii[src_point_index] = radii[src_point_index] * (1.0f - fade_weight * factor_radii);
408 if (!weights.span.is_empty()) {
409 weights.span[src_point_index] = fade_weight;
410 }
411 }
412
413 next_point++;
414 if (next_point >= dst_points_num) {
415 done_scanning = true;
416 break;
417 }
418 }
419 dst_offsets[next_curve] = next_point;
420 dst_to_src_curve[next_curve - 1] = i;
421 next_curve++;
422 });
423 weights.finish();
424
425 BLI_assert(next_curve == (dst_curves_num + 1));
426 BLI_assert(next_point == dst_points_num);
427
428 const bke::AttributeAccessor src_attributes = curves.attributes();
429 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
430
431 gather_attributes(src_attributes,
434 {},
435 dst_to_src_point,
436 dst_attributes);
437 gather_attributes(src_attributes,
440 {},
441 dst_to_src_curve,
442 dst_attributes);
443
444 dst_curves.update_curve_types();
445
446 return dst_curves;
447}
448
450 const Span<bool> select,
451 const Object &object,
452 MutableSpan<bool> r_selection)
453{
454 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
455 const Span<float3> positions = curves.positions();
456 const float3 center = object.object_to_world().location();
457
458 struct Pair {
459 float value;
460 int index;
461 bool selected;
462 };
463
464 Array<Pair> distances(curves.curves_num());
465 for (const int stroke : curves.curves_range()) {
466 const IndexRange points = points_by_curve[stroke];
467 const float3 p1 = positions[points.first()];
468 const float3 p2 = positions[points.last()];
469 distances[stroke].value = math::max(math::distance(p1, center), math::distance(p2, center));
470 distances[stroke].index = stroke;
471 distances[stroke].selected = select[stroke];
472 }
473
475 distances.begin(), distances.end(), [](Pair &a, Pair &b) { return a.value < b.value; });
476
477 Array<int> new_order(curves.curves_num());
478 for (const int i : curves.curves_range()) {
479 new_order[i] = distances[i].index;
480 r_selection[i] = distances[i].selected;
481 }
482
483 return geometry::reorder_curves_geometry(curves, new_order.as_span(), {});
484}
485
487 const float time_elapsed,
488 const float speed_fac,
489 const float max_gap,
490 const float frame_duration)
491{
492 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
493 const bke::AttributeAccessor attributes = curves.attributes();
494 const VArray<float> init_times = *attributes.lookup_or_default<float>(
495 "init_time", bke::AttrDomain::Curve, 0.0f);
496 const VArray<float> src_delta_times = *attributes.lookup_or_default<float>(
497 "delta_time", bke::AttrDomain::Point, 0.0f);
498
499 Array<float> delta_times(curves.points_num());
500
501 if (const std::optional<float> src_delta_time = src_delta_times.get_if_single()) {
502 delta_times.fill(*src_delta_time);
503 }
504 else {
505 array_utils::copy(src_delta_times, delta_times.as_mutable_span());
506 }
507
512 for (const int curve : curves.curves_range()) {
513 const IndexRange points = points_by_curve[curve];
514 if (delta_times[points.last()] == 0.0f && points.size() != 1) {
515 for (const int point_id : points.index_range()) {
516 const int point_i = points[point_id];
517 delta_times[point_i] = GP_BUILD_TIME_DEFAULT_STROKES * float(point_id) /
518 float(points.size() - 1);
519 }
520 }
521 }
522
523 Array<float> start_times(curves.curves_num());
524 start_times[0] = 0;
525 float accumulated_shift_delta_time = init_times[0];
526 for (const int curve : curves.curves_range().drop_front(1)) {
527 const float previous_start_time = start_times[curve - 1];
528 const float init_time = init_times[curve];
529 const float previous_delta_time = delta_times[points_by_curve[curve - 1].last()];
530 const float previous_end_time = previous_start_time + previous_delta_time;
531 float shifted_start_time = init_time - accumulated_shift_delta_time;
532
533 /* Make each stroke have no gap, if the `init_time` is at the default. */
534 if (init_time == 0.0f) {
535 shifted_start_time = previous_end_time;
536 }
537
538 const float gap_delta_time = math::min(math::abs(shifted_start_time - previous_end_time),
539 max_gap);
540
541 start_times[curve] = previous_end_time + gap_delta_time;
542 accumulated_shift_delta_time += math::max(shifted_start_time - start_times[curve], 0.0f);
543 }
544
545 /* Calculates the maximum time of this frame, which is the time between the beginning of the
546 * first stroke and the end of the last stroke. `start_times.last()` gives the starting time of
547 * the last stroke related to frame beginning, and `delta_time.last()` gives how long that stroke
548 * lasted. */
549 const float max_time = start_times.last() + delta_times.last();
550
551 /* If the time needed for building the frame is shorter than frame length, this gives the
552 * percentage of time it needs to be compared to original drawing time. `max_time/speed_fac`
553 * gives time after speed scaling, then divided by `frame_duration` gives the percentage. */
554 const float time_compress_factor = math::max(max_time / speed_fac / frame_duration, 1.0f);
555
556 /* Finally actual building limit is then scaled with speed factor and time compress factor. */
557 const float limit = time_elapsed * speed_fac * time_compress_factor;
558
559 for (const int curve : curves.curves_range()) {
560 const float start_time = start_times[curve];
561 for (const int point : points_by_curve[curve]) {
562 if (start_time + delta_times[point] >= limit) {
563 return math::clamp(float(point) / float(curves.points_num()), 0.0f, 1.0f);
564 }
565 }
566 }
567
568 return 1.0f;
569}
570
571static float get_build_factor(const GreasePencilBuildTimeMode time_mode,
572 const int current_frame,
573 const int start_frame,
574 const int frame_duration,
575 const int length,
576 const float percentage,
577 const bke::CurvesGeometry &curves,
578 const float scene_fps,
579 const float speed_fac,
580 const float max_gap,
581 const float fade)
582{
583 const float use_time = blender::math::round(
584 float(current_frame) / float(math::min(frame_duration, length)) * float(length));
585 const float build_factor_frames = math::clamp(
586 float(use_time - start_frame) / length, 0.0f, 1.0f) *
587 (1.0f + fade);
588 switch (time_mode) {
590 return build_factor_frames;
592 return percentage * (1.0f + fade);
594 return get_factor_from_draw_speed(curves,
595 float(current_frame) / scene_fps,
596 speed_fac,
597 max_gap,
598 float(frame_duration) / scene_fps) *
599 (1.0f + fade);
600 }
602 return 0.0f;
603}
604
606 const Object &ob,
608 const bke::greasepencil::Drawing *previous_drawing,
609 const int current_time,
610 const int frame_duration,
611 const float scene_fps)
612{
614 bke::CurvesGeometry &curves = drawing.strokes_for_write();
615
616 if (curves.is_empty()) {
617 return;
618 }
619
620 IndexMaskMemory memory;
622 &ob, curves, mmd.influence, memory);
623
624 /* Remove a count of #prev_strokes. */
625 if (mmd.mode == MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE && previous_drawing != nullptr) {
626 const bke::CurvesGeometry &prev_curves = previous_drawing->strokes();
627 const int prev_strokes = prev_curves.curves_num();
628 const int added_strokes = curves.curves_num() - prev_strokes;
629 if (added_strokes > 0) {
630 Array<bool> work_on_select(curves.curves_num());
631 selection.to_bools(work_on_select.as_mutable_span());
632 work_on_select.as_mutable_span().take_front(prev_strokes).fill(false);
633 selection = IndexMask::from_bools(work_on_select, memory);
634 }
635 }
636
637 if (mmd.object) {
638 const int curves_num = curves.curves_num();
639 Array<bool> select(curves_num), reordered_select(curves_num);
640 selection.to_bools(select);
641 curves = reorder_strokes(
642 curves, select.as_span(), *mmd.object, reordered_select.as_mutable_span());
643 selection = IndexMask::from_bools(reordered_select, memory);
644 }
645
646 const float fade_factor = ((mmd.flag & MOD_GREASE_PENCIL_BUILD_USE_FADING) != 0) ? mmd.fade_fac :
647 0.0f;
649 current_time,
650 mmd.start_delay,
651 frame_duration,
652 mmd.length,
653 mmd.percentage_fac,
654 curves,
655 scene_fps,
656 mmd.speed_fac,
657 mmd.speed_maxgap,
658 fade_factor);
659 float factor_start = factor - fade_factor;
661 std::swap(factor, factor_start);
662 }
663
664 const float use_time_alignment = mmd.transition != MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW ?
665 !mmd.time_alignment :
666 mmd.time_alignment;
667 switch (mmd.mode) {
668 default:
670 curves = build_sequential(drawing,
671 curves,
672 selection,
673 mmd.transition,
674 factor,
675 factor_start,
678 mmd.target_vgname);
679 break;
681 curves = build_concurrent(drawing,
682 curves,
683 selection,
684 use_time_alignment,
685 mmd.transition,
686 factor,
687 factor_start,
690 mmd.target_vgname);
691 break;
693 curves = build_sequential(drawing,
694 curves,
695 selection,
696 mmd.transition,
697 factor,
698 factor_start,
701 mmd.target_vgname);
702 break;
703 }
704
705 drawing.tag_topology_changed();
706}
707
709 const ModifierEvalContext *ctx,
710 blender::bke::GeometrySet *geometry_set)
711{
712 const auto *mmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
713
714 if (!geometry_set->has_grease_pencil()) {
715 return;
716 }
717
718 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
719 const int eval_frame = grease_pencil.runtime->eval_frame;
720
721 IndexMaskMemory mask_memory;
723 grease_pencil, mmd->influence, mask_memory);
725 modifier::greasepencil::get_drawing_infos_by_layer(grease_pencil, layer_mask, eval_frame);
726
728 if (eval_frame < mmd->start_frame || eval_frame > mmd->end_frame) {
729 return;
730 }
731 }
732
733 const Scene &scene = *DEG_get_evaluated_scene(ctx->depsgraph);
734 const float scene_fps = float(scene.r.frs_sec) / scene.r.frs_sec_base;
735 const Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
736
738 drawing_infos, [&](modifier::greasepencil::LayerDrawingInfo drawing_info) {
739 const bke::greasepencil::Layer &layer = *layers[drawing_info.layer_index];
740
741 /* This will always return a valid start frame because we're iterating over the valid
742 * drawings on `eval_frame`. Each drawing will have a start frame. */
743 const int start_frame = *layer.start_frame_at(eval_frame);
744 BLI_assert(start_frame <= eval_frame);
745
746 const bke::greasepencil::Drawing *prev_drawing = grease_pencil.get_drawing_at(
747 layer, start_frame - 1);
748
749 const int relative_start_frame = eval_frame - start_frame;
750
751 const int frame_index = layer.sorted_keys_index_at(eval_frame);
752 BLI_assert(frame_index != -1);
753
754 int frame_duration = INT_MAX;
755 if (frame_index != layer.sorted_keys().index_range().last()) {
756 const int next_frame = layer.sorted_keys()[frame_index + 1];
757 frame_duration = math::distance(start_frame, next_frame);
758 }
759
760 build_drawing(*mmd,
761 *ctx->object,
762 *drawing_info.drawing,
763 prev_drawing,
764 relative_start_frame,
765 frame_duration,
766 scene_fps);
767 });
768}
769
770static void panel_draw(const bContext *C, Panel *panel)
771{
772 uiLayout *layout = panel->layout;
773
774 PointerRNA ob_ptr;
776
779
780 layout->use_property_split_set(true);
781
782 /* First: Build mode and build settings. */
783 layout->prop(ptr, "mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
785 layout->prop(ptr, "transition", UI_ITEM_NONE, std::nullopt, ICON_NONE);
786 }
788 /* Concurrent mode doesn't support MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED, so unset it. */
792 }
793 layout->prop(ptr, "transition", UI_ITEM_NONE, std::nullopt, ICON_NONE);
794 }
795 layout->separator();
796
797 /* Second: Time mode and time settings. */
798
799 layout->prop(ptr, "time_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
801 layout->prop(ptr, "concurrent_time_alignment", UI_ITEM_NONE, std::nullopt, ICON_NONE);
802 }
803 switch (time_mode) {
805 layout->prop(ptr, "speed_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
806 layout->prop(ptr, "speed_maxgap", UI_ITEM_NONE, std::nullopt, ICON_NONE);
807 break;
809 layout->prop(ptr, "length", UI_ITEM_NONE, IFACE_("Frames"), ICON_NONE);
811 layout->prop(ptr, "start_delay", UI_ITEM_NONE, std::nullopt, ICON_NONE);
812 }
813 break;
815 layout->prop(ptr, "percentage_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
816 break;
817 default:
818 break;
819 }
820 layout->separator();
821 layout->prop(ptr, "object", UI_ITEM_NONE, std::nullopt, ICON_NONE);
822 PanelLayout restrict_frame_range_layout = layout->panel_prop_with_bool_header(
823 C,
824 ptr,
825 "open_frame_range_panel",
826 ptr,
827 "use_restrict_frame_range",
828 IFACE_("Effective Range"));
829 if (uiLayout *panel = restrict_frame_range_layout.body) {
830 const bool active = RNA_boolean_get(ptr, "use_restrict_frame_range");
831 uiLayout *col = &panel->column(false);
832 col->active_set(active);
833 col->prop(ptr, "frame_start", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
834 col->prop(ptr, "frame_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
835 }
836 PanelLayout fading_layout = layout->panel_prop_with_bool_header(
837 C, ptr, "open_fading_panel", ptr, "use_fading", IFACE_("Fading"));
838 if (uiLayout *panel = fading_layout.body) {
839 const bool active = RNA_boolean_get(ptr, "use_fading");
840 uiLayout *col = &panel->column(false);
841 col->active_set(active);
842
843 col->prop(ptr, "fade_factor", UI_ITEM_NONE, IFACE_("Factor"), ICON_NONE);
844
845 uiLayout *subcol = &col->column(true);
846 subcol->prop(ptr, "fade_thickness_strength", UI_ITEM_NONE, IFACE_("Thickness"), ICON_NONE);
847 subcol->prop(ptr, "fade_opacity_strength", UI_ITEM_NONE, IFACE_("Opacity"), ICON_NONE);
848
849 col->prop_search(
850 ptr, "target_vertex_group", &ob_ptr, "vertex_groups", IFACE_("Weight Output"), ICON_NONE);
851 }
852
853 if (uiLayout *influence_panel = layout->panel_prop(
854 C, ptr, "open_influence_panel", IFACE_("Influence")))
855 {
858 }
859
861}
862
867
868} // namespace blender
869
871 /*idname*/ "GreasePencilBuildModifier",
872 /*name*/ N_("Build"),
873 /*struct_name*/ "GreasePencilBuildModifierData",
874 /*struct_size*/ sizeof(GreasePencilBuildModifierData),
875 /*srna*/ &RNA_GreasePencilBuildModifier,
877 /*flags*/
880 /*icon*/ ICON_MOD_LENGTH,
881
882 /*copy_data*/ blender::copy_data,
883
884 /*deform_verts*/ nullptr,
885 /*deform_matrices*/ nullptr,
886 /*deform_verts_EM*/ nullptr,
887 /*deform_matrices_EM*/ nullptr,
888 /*modify_mesh*/ nullptr,
889 /*modify_geometry_set*/ blender::modify_geometry_set,
890
891 /*init_data*/ blender::init_data,
892 /*required_data_mask*/ nullptr,
893 /*free_data*/ blender::free_data,
894 /*is_disabled*/ nullptr,
895 /*update_depsgraph*/ blender::update_depsgraph,
896 /*depends_on_time*/ nullptr,
897 /*depends_on_normals*/ nullptr,
898 /*foreach_ID_link*/ blender::foreach_ID_link,
899 /*foreach_tex_link*/ nullptr,
900 /*free_runtime_data*/ nullptr,
901 /*panel_register*/ blender::panel_register,
902 /*blend_write*/ blender::blend_write,
903 /*blend_read*/ blender::blend_read,
904};
Low-level operations for curves.
Low-level operations for grease pencil.
@ IDWALK_CB_NOP
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
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#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 IFACE_(msgid)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_TRANSFORM
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
#define DNA_struct_default_get(struct_name)
GreasePencilBuildTimeMode
@ MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE
@ MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED
@ MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES
@ MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START
@ MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END
GreasePencilBuildMode
@ MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL
@ MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE
@ MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT
@ MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME
@ MOD_GREASE_PENCIL_BUILD_USE_FADING
@ eModifierType_GreasePencilBuild
@ MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH
@ MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW
Object is a sort of wrapper for general info.
ModifierTypeInfo modifierType_GreasePencilBuild
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)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * end() const
Definition BLI_array.hh:325
const T & last(const int64_t n=0) const
Definition BLI_array.hh:296
const T * begin() const
Definition BLI_array.hh:321
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr IndexRange drop_front(int64_t n) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > positions() const
AttributeAccessor attributes() const
float evaluated_length_total_for_curve(int curve_index, bool cyclic) const
MutableSpan< int > offsets_for_write()
VArray< bool > cyclic() const
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
MutableSpan< float > opacities_for_write()
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
int sorted_keys_index_at(int frame_number) const
std::optional< int > start_frame_at(int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void to_bools(MutableSpan< bool > r_bools) const
void foreach_index(Fn &&fn) const
nullptr float
uint col
#define active
#define abs
#define select(A, B, C)
float length(VecOp< float, D >) RET
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
bke::CurvesGeometry reorder_curves_geometry(const bke::CurvesGeometry &src_curves, Span< int > old_by_new_map, const bke::AttributeFilter &attribute_filter)
Definition reorder.cc:348
T clamp(const T &a, const T &min, const T &max)
T distance(const T &a, const T &b)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
T abs(const T &a)
T round(const T &a)
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
Vector< LayerDrawingInfo > get_drawing_infos_by_layer(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
static IndexMask get_filtered_stroke_mask(const Object *ob, const bke::CurvesGeometry &curves, const Material *material_filter, const std::optional< int > material_pass_filter, const bool material_filter_invert, const bool material_pass_filter_invert, IndexMaskMemory &memory)
void write_influence_data(BlendWriter *writer, const GreasePencilModifierInfluenceData *influence_data)
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, const std::optional< StringRef > tree_node_name_filter, const std::optional< int > layer_pass_filter, const bool layer_filter_invert, const bool layer_pass_filter_invert, IndexMaskMemory &memory)
void draw_material_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_layer_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, Object *ob, IDWalkFunc walk, void *user_data)
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, GreasePencilModifierInfluenceData *influence_data_dst, const int)
void ensure_no_bezier_curves(Drawing &drawing)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
int sum_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
constexpr float GP_BUILD_TIME_DEFAULT_STROKES
static Array< int > point_counts_to_keep_concurrent(const bke::CurvesGeometry &curves, const IndexMask &selection, const int time_alignment, const int transition, const float factor, const bool clamp_points, int &r_curves_num, int &r_points_num)
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
static bke::CurvesGeometry build_sequential(bke::greasepencil::Drawing &drawing, bke::CurvesGeometry &curves, const IndexMask &selection, const int transition, const float factor, const float factor_start, const float factor_opacity, const float factor_radii, StringRefNull target_vgname)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void points_info_sequential(const bke::CurvesGeometry &curves, const IndexMask &selection, const int transition, const float factor, const bool clamp_points, int &r_curves_num, int &r_points_num)
static void panel_draw(const bContext *C, Panel *panel)
static float get_factor_from_draw_speed(const bke::CurvesGeometry &curves, const float time_elapsed, const float speed_fac, const float max_gap, const float frame_duration)
static float get_build_factor(const GreasePencilBuildTimeMode time_mode, const int current_frame, const int start_frame, const int frame_duration, const int length, const float percentage, const bke::CurvesGeometry &curves, const float scene_fps, const float speed_fac, const float max_gap, const float fade)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
Definition BLI_sort.hh:23
static bke::CurvesGeometry reorder_strokes(const bke::CurvesGeometry &curves, const Span< bool > select, const Object &object, MutableSpan< bool > r_selection)
static void free_data(ModifierData *md)
static void build_drawing(const GreasePencilBuildModifierData &mmd, const Object &ob, bke::greasepencil::Drawing &drawing, const bke::greasepencil::Drawing *previous_drawing, const int current_time, const int frame_duration, const float scene_fps)
static void panel_register(ARegionType *region_type)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
VecBase< float, 3 > float3
static void blend_read(BlendDataReader *reader, ModifierData *md)
static bke::CurvesGeometry build_concurrent(bke::greasepencil::Drawing &drawing, bke::CurvesGeometry &curves, const IndexMask &selection, const int time_alignment, const int transition, const float factor, const float factor_start, const float factor_opacity, const float factor_radii, StringRefNull target_vgname)
CCL_NAMESPACE_BEGIN ccl_device float fade(const float t)
Definition noise.h:18
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
GreasePencilModifierInfluenceData influence
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:414
struct uiLayout * layout
struct RenderData r
GreasePencil * get_grease_pencil_for_write()
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
PanelLayout panel_prop_with_bool_header(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name, PointerRNA *bool_prop_owner, blender::StringRefNull bool_prop_name, std::optional< blender::StringRef > label)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void use_property_split_set(bool value)
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
uint len
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238