Blender V4.3
grease_pencil_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_attribute.hh"
10#include "BKE_brush.hh"
11#include "BKE_colortools.hh"
12#include "BKE_context.hh"
13#include "BKE_grease_pencil.hh"
14#include "BKE_material.h"
15#include "BKE_paint.hh"
16#include "BKE_report.hh"
17#include "BKE_scene.hh"
18
19#include "BLI_math_geom.h"
20#include "BLI_math_numbers.hh"
21#include "BLI_math_vector.hh"
22#include "BLI_vector_set.hh"
23
24#include "DNA_brush_types.h"
25#include "DNA_material_types.h"
26#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28#include "DNA_view3d_types.h"
29
30#include "RNA_prototypes.hh"
31
32#include "ED_curves.hh"
33#include "ED_grease_pencil.hh"
34#include "ED_view3d.hh"
35
37
39 const ARegion &region,
40 const View3D &view3d,
41 const Object &eval_object,
42 const bke::greasepencil::Layer *layer)
43 : region_(&region), view3d_(&view3d)
44{
45 layer_space_to_world_space_ = (layer != nullptr) ? layer->to_world_space(eval_object) :
46 eval_object.object_to_world();
47 world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
48 /* Initialize DrawingPlacementPlane from toolsettings. */
49 switch (scene.toolsettings->gp_sculpt.lock_axis) {
52 break;
53 case GP_LOCKAXIS_Y:
55 placement_normal_ = float3(0, 1, 0);
56 break;
57 case GP_LOCKAXIS_X:
59 placement_normal_ = float3(1, 0, 0);
60 break;
61 case GP_LOCKAXIS_Z:
63 placement_normal_ = float3(0, 0, 1);
64 break;
65 case GP_LOCKAXIS_CURSOR: {
67 placement_normal_ = scene.cursor.matrix<float3x3>() * float3(0, 0, 1);
68 break;
69 }
70 }
71
72 /* Account for layer transform. */
73 if (!ELEM(scene.toolsettings->gp_sculpt.lock_axis, GP_LOCKAXIS_VIEW, GP_LOCKAXIS_CURSOR)) {
74 /* Use the transpose inverse for normal. */
75 placement_normal_ = math::transform_direction(math::transpose(world_space_to_layer_space_),
76 placement_normal_);
77 }
78
79 /* Initialize DrawingPlacementDepth from toolsettings. */
80 const char align_flag = scene.toolsettings->gpencil_v3d_align;
81 if (align_flag & GP_PROJECT_VIEWSPACE) {
82 if (align_flag & GP_PROJECT_CURSOR) {
84 surface_offset_ = 0.0f;
85 placement_loc_ = float3(scene.cursor.location);
86 }
87 else if (align_flag & GP_PROJECT_DEPTH_VIEW) {
89 if (align_flag & GP_PROJECT_DEPTH_ONLY_SELECTED) {
90 use_project_only_selected_ = true;
91 }
92 surface_offset_ = scene.toolsettings->gpencil_surface_offset;
93 /* Default to view placement with the object origin if we don't hit a surface. */
94 placement_loc_ = layer_space_to_world_space_.location();
95 }
96 else if (align_flag & GP_PROJECT_DEPTH_STROKE) {
98 surface_offset_ = 0.0f;
99 /* Default to view placement with the object origin if we don't hit a stroke. */
100 placement_loc_ = layer_space_to_world_space_.location();
101 }
102 else {
104 surface_offset_ = 0.0f;
105 placement_loc_ = layer_space_to_world_space_.location();
106 }
107 }
108 else {
110 surface_offset_ = 0.0f;
111 placement_loc_ = float3(0.0f);
112 }
113
114 if (plane_ != DrawingPlacementPlane::View) {
115 plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
116 }
117}
118
120 const ARegion &region,
121 const View3D &view3d,
122 const Object &eval_object,
123 const bke::greasepencil::Layer *layer,
124 const ReprojectMode reproject_mode,
125 const float surface_offset,
126 ViewDepths *view_depths)
127 : region_(&region),
128 view3d_(&view3d),
129 depth_cache_(view_depths),
130 surface_offset_(surface_offset)
131{
132 layer_space_to_world_space_ = (layer != nullptr) ? layer->to_world_space(eval_object) :
133 eval_object.object_to_world();
134 world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
135 /* Initialize DrawingPlacementPlane from mode. */
136 switch (reproject_mode) {
139 break;
142 placement_normal_ = float3(0, 1, 0);
143 break;
146 placement_normal_ = float3(1, 0, 0);
147 break;
150 placement_normal_ = float3(0, 0, 1);
151 break;
154 placement_normal_ = scene.cursor.matrix<float3x3>() * float3(0, 0, 1);
155 break;
156 }
157 default:
158 break;
159 }
160
161 /* Account for layer transform. */
162 if (!ELEM(reproject_mode, ReprojectMode::View, ReprojectMode::Cursor)) {
163 /* Use the transpose inverse for normal. */
164 placement_normal_ = math::transform_direction(math::transpose(world_space_to_layer_space_),
165 placement_normal_);
166 }
167
168 /* Initialize DrawingPlacementDepth from mode. */
169 switch (reproject_mode) {
172 surface_offset_ = 0.0f;
173 placement_loc_ = float3(scene.cursor.location);
174 break;
177 surface_offset_ = 0.0f;
178 placement_loc_ = layer_space_to_world_space_.location();
179 break;
182 placement_loc_ = layer_space_to_world_space_.location();
183 break;
184 default:
186 surface_offset_ = 0.0f;
187 placement_loc_ = layer_space_to_world_space_.location();
188 break;
189 }
190
191 if (plane_ != DrawingPlacementPlane::View) {
192 plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
193 }
194}
195
197{
198 region_ = other.region_;
199 view3d_ = other.view3d_;
200
201 depth_ = other.depth_;
202 plane_ = other.plane_;
203
204 if (other.depth_cache_ != nullptr) {
205 depth_cache_ = static_cast<ViewDepths *>(MEM_dupallocN(other.depth_cache_));
206 depth_cache_->depths = static_cast<float *>(MEM_dupallocN(other.depth_cache_->depths));
207 }
208 use_project_only_selected_ = other.use_project_only_selected_;
209
210 surface_offset_ = other.surface_offset_;
211
212 placement_loc_ = other.placement_loc_;
213 placement_normal_ = other.placement_normal_;
214 placement_plane_ = other.placement_plane_;
215
216 layer_space_to_world_space_ = other.layer_space_to_world_space_;
217 world_space_to_layer_space_ = other.world_space_to_layer_space_;
218}
219
221{
222 region_ = other.region_;
223 view3d_ = other.view3d_;
224
225 depth_ = other.depth_;
226 plane_ = other.plane_;
227
228 std::swap(depth_cache_, other.depth_cache_);
229 use_project_only_selected_ = other.use_project_only_selected_;
230
231 surface_offset_ = other.surface_offset_;
232
233 placement_loc_ = other.placement_loc_;
234 placement_normal_ = other.placement_normal_;
235 placement_plane_ = other.placement_plane_;
236
237 layer_space_to_world_space_ = other.layer_space_to_world_space_;
238 world_space_to_layer_space_ = other.world_space_to_layer_space_;
239}
240
242{
243 if (this == &other) {
244 return *this;
245 }
246 std::destroy_at(this);
247 new (this) DrawingPlacement(other);
248 return *this;
249}
250
252{
253 if (this == &other) {
254 return *this;
255 }
256 std::destroy_at(this);
257 new (this) DrawingPlacement(std::move(other));
258 return *this;
259}
260
262{
263 if (depth_cache_ != nullptr) {
264 ED_view3d_depths_free(depth_cache_);
265 }
266}
267
272
277
279{
281
283 if (use_project_only_selected_) {
285 }
286 else {
288 }
289 }
290 ED_view3d_depth_override(depsgraph, region, view3d, nullptr, mode, &this->depth_cache_);
291}
292
294{
295 BLI_assert(depth_cache_ != nullptr);
296 float depth;
297 if (ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth)) {
298 float3 origin;
299 ED_view3d_depth_unproject_v3(region_, int2(co), depth, origin);
300
301 placement_loc_ = origin;
302 }
303 else {
304 /* If nothing was hit, use origin. */
305 placement_loc_ = layer_space_to_world_space_.location();
306 }
307 plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
308}
309
310float3 DrawingPlacement::project_depth(const float2 co) const
311{
312 float3 proj_point;
313 float depth;
314 if (depth_cache_ != nullptr && ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth)) {
315 ED_view3d_depth_unproject_v3(region_, int2(co), depth, proj_point);
316 float3 view_normal;
317 ED_view3d_win_to_vector(region_, co, view_normal);
318 proj_point -= view_normal * surface_offset_;
319 }
320 else {
321 /* Fallback to `View` placement. */
322 ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
323 }
324 return proj_point;
325}
326
328{
329 float3 proj_point;
330 if (depth_ == DrawingPlacementDepth::Surface) {
331 /* Project using the viewport depth cache. */
332 proj_point = this->project_depth(co);
333 }
334 else {
335 if (plane_ == DrawingPlacementPlane::View) {
336 ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
337 }
338 else {
339 ED_view3d_win_to_3d_on_plane(region_, placement_plane_, co, false, proj_point);
340 }
341 }
342 return math::transform_point(world_space_to_layer_space_, proj_point);
343}
344
346{
347 threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
348 for (const int i : range) {
349 dst[i] = this->project(src[i]);
350 }
351 });
352}
353
354float3 DrawingPlacement::reproject(const float3 pos) const
355{
356 const float3 world_pos = math::transform_point(layer_space_to_world_space_, pos);
357 float3 proj_point;
358 if (depth_ == DrawingPlacementDepth::Surface) {
359 /* First project the position into view space. */
360 float2 co;
361 if (ED_view3d_project_float_global(region_, world_pos, co, V3D_PROJ_TEST_NOP)) {
362 /* Can't reproject the point. */
363 return pos;
364 }
365 /* Project using the viewport depth cache. */
366 proj_point = this->project_depth(co);
367 }
368 else {
369 /* Reproject the point onto the `placement_plane_` from the current view. */
370 RegionView3D *rv3d = static_cast<RegionView3D *>(region_->regiondata);
371
372 float3 ray_co, ray_no;
373 if (rv3d->is_persp) {
374 ray_co = float3(rv3d->viewinv[3]);
375 ray_no = math::normalize(ray_co - world_pos);
376 }
377 else {
378 ray_co = world_pos;
379 ray_no = -float3(rv3d->viewinv[2]);
380 }
381 float4 plane;
382 if (plane_ == DrawingPlacementPlane::View) {
383 plane = float4(rv3d->viewinv[2]);
384 }
385 else {
386 plane = placement_plane_;
387 }
388
389 float lambda;
390 if (isect_ray_plane_v3(ray_co, ray_no, plane, &lambda, false)) {
391 proj_point = ray_co + ray_no * lambda;
392 }
393 else {
394 return pos;
395 }
396 }
397 return math::transform_point(world_space_to_layer_space_, proj_point);
398}
399
400void DrawingPlacement::reproject(const Span<float3> src, MutableSpan<float3> dst) const
401{
402 threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
403 for (const int i : range) {
404 dst[i] = this->reproject(src[i]);
405 }
406 });
407}
408
409float4x4 DrawingPlacement::to_world_space() const
410{
411 return layer_space_to_world_space_;
412}
413
414static float get_multi_frame_falloff(const int frame_number,
415 const int center_frame,
416 const int min_frame,
417 const int max_frame,
418 const CurveMapping *falloff_curve)
419{
420 if (falloff_curve == nullptr) {
421 return 1.0f;
422 }
423
424 /* Frame right of the center frame. */
425 if (frame_number > center_frame) {
426 const float frame_factor = 0.5f * float(center_frame - min_frame) / (frame_number - min_frame);
427 return BKE_curvemapping_evaluateF(falloff_curve, 0, frame_factor);
428 }
429 /* Frame left of the center frame. */
430 if (frame_number < center_frame) {
431 const float frame_factor = 0.5f * float(center_frame - frame_number) /
432 (max_frame - frame_number);
433 return BKE_curvemapping_evaluateF(falloff_curve, 0, frame_factor + 0.5f);
434 }
435 /* Frame at center. */
436 return BKE_curvemapping_evaluateF(falloff_curve, 0, 0.5f);
437}
438
439static std::pair<int, int> get_minmax_selected_frame_numbers(const GreasePencil &grease_pencil,
440 const int current_frame)
441{
442 using namespace blender::bke::greasepencil;
443 int frame_min = current_frame;
444 int frame_max = current_frame;
445 Span<const Layer *> layers = grease_pencil.layers();
446 for (const int layer_i : layers.index_range()) {
447 const Layer &layer = *layers[layer_i];
448 if (!layer.is_editable()) {
449 continue;
450 }
451 for (const auto [frame_number, frame] : layer.frames().items()) {
452 if (frame_number != current_frame && frame.is_selected()) {
453 frame_min = math::min(frame_min, frame_number);
454 frame_max = math::max(frame_max, frame_number);
455 }
456 }
457 }
458 return std::pair<int, int>(frame_min, frame_max);
459}
460
461static std::optional<int> get_frame_id(const bke::greasepencil::Layer &layer,
462 const GreasePencilFrame &frame,
463 const int frame_number,
464 const int frame_index,
465 const int current_frame,
466 const int current_frame_index,
467 const int last_frame,
468 const int last_frame_index,
469 const bool use_multi_frame_editing,
470 const bool do_onion_skinning,
471 const bool is_before_first,
472 const GreasePencilOnionSkinningSettings onion_settings)
473{
474 if (use_multi_frame_editing) {
475 if (frame.is_selected()) {
476 if (do_onion_skinning) {
477 return (frame_number < current_frame) ? -1 : 1;
478 }
479 return 0;
480 }
481 return {};
482 }
483 if (do_onion_skinning && layer.use_onion_skinning()) {
484 /* Keyframe type filter. */
485 if (onion_settings.filter != 0 && (onion_settings.filter & (1 << frame.type)) == 0) {
486 return {};
487 }
488 /* Selected mode filter. */
489 if (onion_settings.mode == GP_ONION_SKINNING_MODE_SELECTED && !frame.is_selected()) {
490 return {};
491 }
492
493 int delta = 0;
494 if (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) {
495 delta = frame_number - current_frame;
496 }
497 else {
498 delta = frame_index - current_frame_index;
499 }
500
501 if (is_before_first) {
502 delta++;
503 }
504 if ((onion_settings.flag & GP_ONION_SKINNING_SHOW_LOOP) != 0 &&
505 (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after))
506 {
507 /* We wrap the value using the last frame and 0 as reference. */
508 /* FIXME: This might not be good for animations not starting at 0. */
509 int shift = 0;
510 if (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) {
511 shift = last_frame;
512 }
513 else {
514 shift = last_frame_index;
515 }
516 delta += (delta < 0) ? (shift + 1) : -(shift + 1);
517 }
518 /* Frame range filter. */
519 if (ELEM(onion_settings.mode,
522 (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after))
523 {
524 return {};
525 }
526
527 return delta;
528 }
529 return {};
530}
531
533 const GreasePencil &grease_pencil,
534 const bke::greasepencil::Layer &layer,
535 const int current_frame,
536 const bool use_multi_frame_editing,
537 const bool do_onion_skinning)
538{
539 GreasePencilOnionSkinningSettings onion_settings = grease_pencil.onion_skinning_settings;
540 Vector<std::pair<int, int>> frame_numbers;
541 const Span<int> sorted_keys = layer.sorted_keys();
542 if (sorted_keys.is_empty()) {
543 return {};
544 }
545 const int current_frame_index = std::max(layer.sorted_keys_index_at(current_frame), 0);
546 const int last_frame = sorted_keys.last();
547 const int last_frame_index = sorted_keys.index_range().last();
548 const bool is_before_first = (current_frame < sorted_keys.first());
549 const std::optional<int> current_start_frame = layer.start_frame_at(current_frame);
550 for (const int frame_i : sorted_keys.index_range()) {
551 const int frame_number = sorted_keys[frame_i];
552 if (current_start_frame && *current_start_frame == frame_number) {
553 continue;
554 }
555 const GreasePencilFrame &frame = layer.frames().lookup(frame_number);
556 const std::optional<int> frame_id = get_frame_id(layer,
557 frame,
558 frame_number,
559 frame_i,
560 current_frame,
561 current_frame_index,
562 last_frame,
563 last_frame_index,
564 use_multi_frame_editing,
565 do_onion_skinning,
566 is_before_first,
567 onion_settings);
568 if (!frame_id.has_value()) {
569 /* Drawing on this frame is not visible. */
570 continue;
571 }
572
573 frame_numbers.append({frame_number, *frame_id});
574 }
575
576 frame_numbers.append({current_frame, 0});
577
578 return frame_numbers.as_span();
579}
580
582 const bke::greasepencil::Layer &layer,
583 const int current_frame,
584 const bool use_multi_frame_editing)
585{
586 using namespace blender::bke::greasepencil;
587 Vector<int> frame_numbers;
588 Set<const Drawing *> added_drawings;
589 if (use_multi_frame_editing) {
590 const Drawing *current_drawing = grease_pencil.get_drawing_at(layer, current_frame);
591 for (const auto [frame_number, frame] : layer.frames().items()) {
592 if (!frame.is_selected()) {
593 continue;
594 }
595 frame_numbers.append(frame_number);
596 added_drawings.add(grease_pencil.get_drawing_at(layer, frame_number));
597 }
598 if (added_drawings.contains(current_drawing)) {
599 return frame_numbers.as_span();
600 }
601 }
602
603 frame_numbers.append(current_frame);
604 return frame_numbers.as_span();
605}
606
608 GreasePencil &grease_pencil)
609{
610 using namespace blender::bke::greasepencil;
611 const int current_frame = scene.r.cfra;
612 const ToolSettings *toolsettings = scene.toolsettings;
613 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
615
616 Vector<MutableDrawingInfo> editable_drawings;
617 Span<const Layer *> layers = grease_pencil.layers();
618 for (const int layer_i : layers.index_range()) {
619 const Layer &layer = *layers[layer_i];
620 if (!layer.is_editable()) {
621 continue;
622 }
623 const Array<int> frame_numbers = get_editable_frames_for_layer(
624 grease_pencil, layer, current_frame, use_multi_frame_editing);
625 for (const int frame_number : frame_numbers) {
626 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
627 editable_drawings.append({*drawing, layer_i, frame_number, 1.0f});
628 }
629 }
630 }
631
632 return editable_drawings;
633}
634
636 GreasePencil &grease_pencil)
637{
638 using namespace blender::bke::greasepencil;
639 const int current_frame = scene.r.cfra;
640 const ToolSettings *toolsettings = scene.toolsettings;
641 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
643 const bool use_multi_frame_falloff = use_multi_frame_editing &&
644 (toolsettings->gp_sculpt.flag &
646 int center_frame;
647 std::pair<int, int> minmax_frame;
648 if (use_multi_frame_falloff) {
650 minmax_frame = get_minmax_selected_frame_numbers(grease_pencil, current_frame);
651 center_frame = math::clamp(current_frame, minmax_frame.first, minmax_frame.second);
652 }
653
654 Vector<MutableDrawingInfo> editable_drawings;
655 Span<const Layer *> layers = grease_pencil.layers();
656 for (const int layer_i : layers.index_range()) {
657 const Layer &layer = *layers[layer_i];
658 if (!layer.is_editable()) {
659 continue;
660 }
661 const Array<int> frame_numbers = get_editable_frames_for_layer(
662 grease_pencil, layer, current_frame, use_multi_frame_editing);
663 for (const int frame_number : frame_numbers) {
664 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
665 const float falloff = use_multi_frame_falloff ?
666 get_multi_frame_falloff(frame_number,
667 center_frame,
668 minmax_frame.first,
669 minmax_frame.second,
670 toolsettings->gp_sculpt.cur_falloff) :
671 1.0f;
672 editable_drawings.append({*drawing, layer_i, frame_number, falloff});
673 }
674 }
675 }
676
677 return editable_drawings;
678}
679
681 const Scene &scene, GreasePencil &grease_pencil)
682{
683 using namespace blender::bke::greasepencil;
684 int current_frame = scene.r.cfra;
685 const ToolSettings *toolsettings = scene.toolsettings;
686 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
688 const bool use_multi_frame_falloff = use_multi_frame_editing &&
689 (toolsettings->gp_sculpt.flag &
691 if (use_multi_frame_falloff) {
693 }
694
695 /* Get a set of unique frame numbers with editable drawings on them. */
696 VectorSet<int> selected_frames;
697 int frame_min = current_frame, frame_max = current_frame;
698 Span<const Layer *> layers = grease_pencil.layers();
699 if (use_multi_frame_editing) {
700 for (const int layer_i : layers.index_range()) {
701 const Layer &layer = *layers[layer_i];
702 if (!layer.is_editable()) {
703 continue;
704 }
705 for (const auto [frame_number, frame] : layer.frames().items()) {
706 if (frame_number != current_frame && frame.is_selected()) {
707 selected_frames.add(frame_number);
708 frame_min = math::min(frame_min, frame_number);
709 frame_max = math::max(frame_max, frame_number);
710 }
711 }
712 }
713 }
714 selected_frames.add(current_frame);
715
716 /* Get multi frame falloff factor per selected frame. */
717 Array<float> falloff_per_selected_frame(selected_frames.size(), 1.0f);
718 if (use_multi_frame_falloff) {
719 int frame_group = 0;
720 for (const int frame_number : selected_frames) {
721 falloff_per_selected_frame[frame_group] = get_multi_frame_falloff(
722 frame_number, current_frame, frame_min, frame_max, toolsettings->gp_sculpt.cur_falloff);
723 frame_group++;
724 }
725 }
726
727 /* Get drawings grouped per frame. */
728 Array<Vector<MutableDrawingInfo>> drawings_grouped_per_frame(selected_frames.size());
729 Set<const Drawing *> added_drawings;
730 for (const int layer_i : layers.index_range()) {
731 const Layer &layer = *layers[layer_i];
732 if (!layer.is_editable()) {
733 continue;
734 }
735 /* In multi frame editing mode, add drawings at selected frames. */
736 if (use_multi_frame_editing) {
737 for (const auto [frame_number, frame] : layer.frames().items()) {
738 Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number);
739 if (!frame.is_selected() || drawing == nullptr || added_drawings.contains(drawing)) {
740 continue;
741 }
742 const int frame_group = selected_frames.index_of(frame_number);
743 drawings_grouped_per_frame[frame_group].append(
744 {*drawing, layer_i, frame_number, falloff_per_selected_frame[frame_group]});
745 added_drawings.add_new(drawing);
746 }
747 }
748
749 /* Add drawing at current frame. */
750 Drawing *current_drawing = grease_pencil.get_drawing_at(layer, current_frame);
751 if (current_drawing != nullptr && !added_drawings.contains(current_drawing)) {
752 const int frame_group = selected_frames.index_of(current_frame);
753 drawings_grouped_per_frame[frame_group].append(
754 {*current_drawing, layer_i, current_frame, falloff_per_selected_frame[frame_group]});
755 added_drawings.add_new(current_drawing);
756 }
757 }
758
759 return drawings_grouped_per_frame;
760}
761
763 const Scene &scene,
764 GreasePencil &grease_pencil,
766{
767 using namespace blender::bke::greasepencil;
768 const int current_frame = scene.r.cfra;
769 const ToolSettings *toolsettings = scene.toolsettings;
770 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
772 const int layer_index = *grease_pencil.get_layer_index(layer);
773
774 Vector<MutableDrawingInfo> editable_drawings;
775 const Array<int> frame_numbers = get_editable_frames_for_layer(
776 grease_pencil, layer, current_frame, use_multi_frame_editing);
777 for (const int frame_number : frame_numbers) {
778 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
779 editable_drawings.append({*drawing, layer_index, frame_number, 1.0f});
780 }
781 }
782
783 return editable_drawings;
784}
785
787 const Scene &scene,
788 GreasePencil &grease_pencil,
790{
791 using namespace blender::bke::greasepencil;
792 const int current_frame = scene.r.cfra;
793 const ToolSettings *toolsettings = scene.toolsettings;
794 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
796 const bool use_multi_frame_falloff = use_multi_frame_editing &&
797 (toolsettings->gp_sculpt.flag &
799 const int layer_index = *grease_pencil.get_layer_index(layer);
800 int center_frame;
801 std::pair<int, int> minmax_frame;
802 if (use_multi_frame_falloff) {
804 minmax_frame = get_minmax_selected_frame_numbers(grease_pencil, current_frame);
805 center_frame = math::clamp(current_frame, minmax_frame.first, minmax_frame.second);
806 }
807
808 Vector<MutableDrawingInfo> editable_drawings;
809 const Array<int> frame_numbers = get_editable_frames_for_layer(
810 grease_pencil, layer, current_frame, use_multi_frame_editing);
811 for (const int frame_number : frame_numbers) {
812 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
813 const float falloff = use_multi_frame_falloff ?
814 get_multi_frame_falloff(frame_number,
815 center_frame,
816 minmax_frame.first,
817 minmax_frame.second,
818 toolsettings->gp_sculpt.cur_falloff) :
819 1.0f;
820 editable_drawings.append({*drawing, layer_index, frame_number, falloff});
821 }
822 }
823
824 return editable_drawings;
825}
826
828 const GreasePencil &grease_pencil,
829 const bool do_onion_skinning)
830{
831 using namespace blender::bke::greasepencil;
832 const int current_frame = scene.r.cfra;
833 const ToolSettings *toolsettings = scene.toolsettings;
834 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
836
837 Vector<DrawingInfo> visible_drawings;
838 Span<const Layer *> layers = grease_pencil.layers();
839 for (const int layer_i : layers.index_range()) {
840 const Layer &layer = *layers[layer_i];
841 if (!layer.is_visible()) {
842 continue;
843 }
845 grease_pencil, layer, current_frame, use_multi_frame_editing, do_onion_skinning);
846 for (const auto &[frame_number, onion_id] : frames) {
847 if (const Drawing *drawing = grease_pencil.get_drawing_at(layer, frame_number)) {
848 visible_drawings.append({*drawing, layer_i, frame_number, onion_id});
849 }
850 }
851 }
852
853 return visible_drawings;
854}
855
857{
858 BLI_assert(object.type == OB_GREASE_PENCIL);
859 VectorSet<int> editable_material_indices;
860 for (const int mat_i : IndexRange(object.totcol)) {
861 Material *material = BKE_object_material_get(&object, mat_i + 1);
862 /* The editable materials are unlocked and not hidden. */
863 if (material != nullptr && material->gp_style != nullptr &&
864 (material->gp_style->flag & GP_MATERIAL_LOCKED) == 0 &&
865 (material->gp_style->flag & GP_MATERIAL_HIDE) == 0)
866 {
867 editable_material_indices.add_new(mat_i);
868 }
869 }
870 return editable_material_indices;
871}
872
874{
875 BLI_assert(object.type == OB_GREASE_PENCIL);
876 VectorSet<int> hidden_material_indices;
877 for (const int mat_i : IndexRange(object.totcol)) {
878 Material *material = BKE_object_material_get(&object, mat_i + 1);
879 if (material != nullptr && material->gp_style != nullptr &&
880 (material->gp_style->flag & GP_MATERIAL_HIDE) != 0)
881 {
882 hidden_material_indices.add_new(mat_i);
883 }
884 }
885 return hidden_material_indices;
886}
887
889{
890 BLI_assert(object.type == OB_GREASE_PENCIL);
891 VectorSet<int> fill_material_indices;
892 for (const int mat_i : IndexRange(object.totcol)) {
893 Material *material = BKE_object_material_get(&object, mat_i + 1);
894 if (material != nullptr && material->gp_style != nullptr &&
895 (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0)
896 {
897 fill_material_indices.add_new(mat_i);
898 }
899 }
900 return fill_material_indices;
901}
902
904 const bke::greasepencil::Drawing &drawing,
905 int layer_index,
906 IndexMaskMemory &memory)
907{
908 using namespace blender;
909 const bke::CurvesGeometry &curves = drawing.strokes();
910 const IndexRange curves_range = curves.curves_range();
911
912 if (object.totcol == 0) {
913 return IndexMask(curves_range);
914 }
915
916 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
917 const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
918
919 /* If we're not using material locking, the entire curves range is editable. */
920 if (layer.ignore_locked_materials()) {
921 return IndexMask(curves_range);
922 }
923
924 /* Get all the editable material indices */
925 VectorSet<int> editable_material_indices = get_editable_material_indices(object);
926 if (editable_material_indices.is_empty()) {
927 return {};
928 }
929
930 const bke::AttributeAccessor attributes = curves.attributes();
931 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
932 if (!materials) {
933 /* If the attribute does not exist then the default is the first material. */
934 if (editable_material_indices.contains(0)) {
935 return curves_range;
936 }
937 return {};
938 }
939 /* Get all the strokes that have their material unlocked. */
941 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
942 return editable_material_indices.contains(materials[curve_i]);
943 });
944}
945
947 const bke::greasepencil::Drawing &drawing,
948 int layer_index,
949 IndexMaskMemory &memory)
950{
951 using namespace blender;
952 const IndexMask editable_strokes = retrieve_editable_strokes(
953 object, drawing, layer_index, memory);
954 if (editable_strokes.is_empty()) {
955 return {};
956 }
957
958 const bke::CurvesGeometry &curves = drawing.strokes();
959 const IndexRange curves_range = curves.curves_range();
960
961 const bke::AttributeAccessor attributes = curves.attributes();
962 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
963 const VectorSet<int> fill_material_indices = get_fill_material_indices(object);
964 if (!materials) {
965 /* If the attribute does not exist then the default is the first material. */
966 if (editable_strokes.contains(0) && fill_material_indices.contains(0)) {
967 return curves_range;
968 }
969 return {};
970 }
972 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
973 const int material_index = materials[curve_i];
974 return fill_material_indices.contains(material_index);
975 });
976 return IndexMask::from_intersection(editable_strokes, fill_strokes, memory);
977}
978
980 const bke::greasepencil::Drawing &drawing,
981 const int mat_i,
982 IndexMaskMemory &memory)
983{
984 using namespace blender;
985
986 /* Get all the editable material indices */
987 VectorSet<int> editable_material_indices = get_editable_material_indices(object);
988 if (editable_material_indices.is_empty()) {
989 return {};
990 }
991
992 const bke::CurvesGeometry &curves = drawing.strokes();
993 const IndexRange curves_range = drawing.strokes().curves_range();
994 const bke::AttributeAccessor attributes = curves.attributes();
995
996 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
997 if (!materials) {
998 /* If the attribute does not exist then the default is the first material. */
999 if (editable_material_indices.contains(0)) {
1000 return curves_range;
1001 }
1002 return {};
1003 }
1004 /* Get all the strokes that share the same material and have it unlocked. */
1006 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1007 const int material_index = materials[curve_i];
1008 if (material_index == mat_i) {
1009 return editable_material_indices.contains(material_index);
1010 }
1011 return false;
1012 });
1013}
1014
1016 const bke::greasepencil::Drawing &drawing,
1017 int layer_index,
1018 IndexMaskMemory &memory)
1019{
1020 const bke::CurvesGeometry &curves = drawing.strokes();
1021 const IndexRange points_range = curves.points_range();
1022
1023 if (object.totcol == 0) {
1024 return IndexMask(points_range);
1025 }
1026
1027 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1028 const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
1029
1030 /* If we're not using material locking, the entire points range is editable. */
1031 if (layer.ignore_locked_materials()) {
1032 return IndexMask(points_range);
1033 }
1034
1035 /* Get all the editable material indices */
1036 VectorSet<int> editable_material_indices = get_editable_material_indices(object);
1037 if (editable_material_indices.is_empty()) {
1038 return {};
1039 }
1040
1041 /* Propagate the material index to the points. */
1042 const bke::AttributeAccessor attributes = curves.attributes();
1043 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Point);
1044 if (!materials) {
1045 /* If the attribute does not exist then the default is the first material. */
1046 if (editable_material_indices.contains(0)) {
1047 return points_range;
1048 }
1049 return {};
1050 }
1051 /* Get all the points that are part of a stroke with an unlocked material. */
1053 points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
1054 return editable_material_indices.contains(materials[point_i]);
1055 });
1056}
1057
1059 const MutableDrawingInfo &info,
1060 const bke::AttrDomain selection_domain,
1061 IndexMaskMemory &memory)
1062{
1063
1064 const bke::greasepencil::Drawing &drawing = info.drawing;
1065 if (selection_domain == bke::AttrDomain::Curve) {
1066 return ed::greasepencil::retrieve_editable_strokes(object, drawing, info.layer_index, memory);
1067 }
1068 else if (selection_domain == bke::AttrDomain::Point) {
1069 return ed::greasepencil::retrieve_editable_points(object, drawing, info.layer_index, memory);
1070 }
1071 return {};
1072}
1073
1075 const bke::greasepencil::Drawing &drawing,
1076 IndexMaskMemory &memory)
1077{
1078 using namespace blender;
1079
1080 /* Get all the hidden material indices. */
1081 VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
1082
1083 if (hidden_material_indices.is_empty()) {
1084 return drawing.strokes().curves_range();
1085 }
1086
1087 const bke::CurvesGeometry &curves = drawing.strokes();
1088 const IndexRange curves_range = drawing.strokes().curves_range();
1089 const bke::AttributeAccessor attributes = curves.attributes();
1090
1091 /* Get all the strokes that have their material visible. */
1092 const VArray<int> materials = *attributes.lookup_or_default<int>(
1093 "material_index", bke::AttrDomain::Curve, 0);
1095 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1096 const int material_index = materials[curve_i];
1097 return !hidden_material_indices.contains(material_index);
1098 });
1099}
1100
1102 const bke::greasepencil::Drawing &drawing,
1103 IndexMaskMemory &memory)
1104{
1105 /* Get all the hidden material indices. */
1106 VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
1107
1108 if (hidden_material_indices.is_empty()) {
1109 return drawing.strokes().points_range();
1110 }
1111
1112 const bke::CurvesGeometry &curves = drawing.strokes();
1113 const IndexRange points_range = curves.points_range();
1114 const bke::AttributeAccessor attributes = curves.attributes();
1115
1116 /* Propagate the material index to the points. */
1117 const VArray<int> materials = *attributes.lookup_or_default<int>(
1118 "material_index", bke::AttrDomain::Point, 0);
1119 if (const std::optional<int> single_material = materials.get_if_single()) {
1120 if (!hidden_material_indices.contains(*single_material)) {
1121 return points_range;
1122 }
1123 return {};
1124 }
1125
1126 /* Get all the points that are part of a stroke with a visible material. */
1128 points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
1129 const int material_index = materials[point_i];
1130 return !hidden_material_indices.contains(material_index);
1131 });
1132}
1133
1135 const bke::greasepencil::Drawing &drawing,
1136 const int layer_index,
1137 IndexMaskMemory &memory)
1138{
1139 const bke::CurvesGeometry &curves = drawing.strokes();
1140
1141 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
1142 return IndexMask(0);
1143 }
1144
1145 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1146 const VArray<int8_t> types = curves.curve_types();
1147
1148 const VArray<bool> selected_point = *curves.attributes().lookup_or_default<bool>(
1149 ".selection", bke::AttrDomain::Point, true);
1150 const VArray<bool> selected_left = *curves.attributes().lookup_or_default<bool>(
1151 ".selection_handle_left", bke::AttrDomain::Point, true);
1152 const VArray<bool> selected_right = *curves.attributes().lookup_or_default<bool>(
1153 ".selection_handle_right", bke::AttrDomain::Point, true);
1154
1156 object, drawing, layer_index, memory);
1157
1158 const IndexMask selected_points = IndexMask::from_predicate(
1159 curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
1160 const bool is_selected = selected_point[point_i] || selected_left[point_i] ||
1161 selected_right[point_i];
1162 const bool is_bezier = types[point_to_curve_map[point_i]] == CURVE_TYPE_BEZIER;
1163 return is_selected && is_bezier;
1164 });
1165
1166 return IndexMask::from_intersection(editable_points, selected_points, memory);
1167}
1168
1170 const bke::greasepencil::Drawing &drawing,
1171 const int layer_index,
1172 const bke::AttrDomain selection_domain,
1173 IndexMaskMemory &memory)
1174{
1175 if (selection_domain == bke::AttrDomain::Curve) {
1177 object, drawing, layer_index, memory);
1178 }
1179 else if (selection_domain == bke::AttrDomain::Point) {
1181 object, drawing, layer_index, memory);
1182 }
1183 return {};
1184}
1185
1187 const bke::greasepencil::Drawing &drawing,
1188 int layer_index,
1189 IndexMaskMemory &memory)
1190{
1191 using namespace blender;
1192 const bke::CurvesGeometry &curves = drawing.strokes();
1193
1194 const IndexMask editable_strokes = retrieve_editable_strokes(
1195 object, drawing, layer_index, memory);
1196 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1197
1198 return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
1199}
1200
1202 const bke::greasepencil::Drawing &drawing,
1203 int layer_index,
1204 IndexMaskMemory &memory)
1205{
1206 using namespace blender;
1207 const bke::CurvesGeometry &curves = drawing.strokes();
1208
1209 const IndexMask editable_strokes = retrieve_editable_fill_strokes(
1210 object, drawing, layer_index, memory);
1211 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1212
1213 return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
1214}
1215
1217 const bke::greasepencil::Drawing &drawing,
1218 int layer_index,
1219 IndexMaskMemory &memory)
1220{
1221 const bke::CurvesGeometry &curves = drawing.strokes();
1222
1223 const IndexMask editable_points = retrieve_editable_points(object, drawing, layer_index, memory);
1224 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
1225
1226 return IndexMask::from_intersection(editable_points, selected_points, memory);
1227}
1228
1230 const bke::greasepencil::Drawing &drawing,
1231 int layer_index,
1232 const bke::AttrDomain selection_domain,
1233 IndexMaskMemory &memory)
1234{
1235 if (selection_domain == bke::AttrDomain::Curve) {
1237 object, drawing, layer_index, memory);
1238 }
1239 else if (selection_domain == bke::AttrDomain::Point) {
1241 object, drawing, layer_index, memory);
1242 }
1243 return {};
1244}
1245
1247 const bke::CurvesGeometry &src,
1249 const Span<Vector<PointTransferData>> src_to_dst_points,
1250 const bool keep_caps)
1251{
1252 const int src_curves_num = src.curves_num();
1253 const OffsetIndices<int> src_points_by_curve = src.points_by_curve();
1254 const VArray<bool> src_cyclic = src.cyclic();
1255
1256 int dst_points_num = 0;
1257 for (const Vector<PointTransferData> &src_transfer_data : src_to_dst_points) {
1258 dst_points_num += src_transfer_data.size();
1259 }
1260 if (dst_points_num == 0) {
1261 dst.resize(0, 0);
1262 return Array<PointTransferData>(0);
1263 }
1264
1265 /* Set the intersection parameters in the destination domain : a pair of int and float
1266 * numbers for which the integer is the index of the corresponding segment in the
1267 * source curves, and the float part is the (0,1) factor representing its position in
1268 * the segment.
1269 */
1270 Array<PointTransferData> dst_transfer_data(dst_points_num);
1271
1272 Array<int> src_pivot_point(src_curves_num, -1);
1273 Array<int> dst_interm_curves_offsets(src_curves_num + 1, 0);
1274 int dst_point = -1;
1275 for (const int src_curve : src.curves_range()) {
1276 const IndexRange src_points = src_points_by_curve[src_curve];
1277
1278 for (const int src_point : src_points) {
1279 for (const PointTransferData &dst_point_transfer : src_to_dst_points[src_point]) {
1280 if (dst_point_transfer.is_src_point) {
1281 dst_transfer_data[++dst_point] = dst_point_transfer;
1282 continue;
1283 }
1284
1285 /* Add an intersection with the eraser and mark it as a cut. */
1286 dst_transfer_data[++dst_point] = dst_point_transfer;
1287
1288 /* For cyclic curves, mark the pivot point as the last intersection with the eraser
1289 * that starts a new segment in the destination.
1290 */
1291 if (src_cyclic[src_curve] && dst_point_transfer.is_cut) {
1292 src_pivot_point[src_curve] = dst_point;
1293 }
1294 }
1295 }
1296 /* We store intermediate curve offsets represent an intermediate state of the
1297 * destination curves before cutting the curves at eraser's intersection. Thus, it
1298 * contains the same number of curves than in the source, but the offsets are
1299 * different, because points may have been added or removed. */
1300 dst_interm_curves_offsets[src_curve + 1] = dst_point + 1;
1301 }
1302
1303 /* Cyclic curves. */
1304 Array<bool> src_now_cyclic(src_curves_num);
1305 threading::parallel_for(src.curves_range(), 4096, [&](const IndexRange src_curves) {
1306 for (const int src_curve : src_curves) {
1307 const int pivot_point = src_pivot_point[src_curve];
1308
1309 if (pivot_point == -1) {
1310 /* Either the curve was not cyclic or it wasn't cut : no need to change it. */
1311 src_now_cyclic[src_curve] = src_cyclic[src_curve];
1312 continue;
1313 }
1314
1315 /* A cyclic curve was cut :
1316 * - this curve is not cyclic anymore,
1317 * - and we have to shift points to keep the closing segment.
1318 */
1319 src_now_cyclic[src_curve] = false;
1320
1321 const int dst_interm_first = dst_interm_curves_offsets[src_curve];
1322 const int dst_interm_last = dst_interm_curves_offsets[src_curve + 1];
1323 std::rotate(dst_transfer_data.begin() + dst_interm_first,
1324 dst_transfer_data.begin() + pivot_point,
1325 dst_transfer_data.begin() + dst_interm_last);
1326 }
1327 });
1328
1329 /* Compute the destination curve offsets. */
1330 Vector<int> dst_curves_offset;
1331 Vector<int> dst_to_src_curve;
1332 dst_curves_offset.append(0);
1333 for (int src_curve : src.curves_range()) {
1334 const IndexRange dst_points(dst_interm_curves_offsets[src_curve],
1335 dst_interm_curves_offsets[src_curve + 1] -
1336 dst_interm_curves_offsets[src_curve]);
1337 int length_of_current = 0;
1338
1339 for (int dst_point : dst_points) {
1340
1341 if ((length_of_current > 0) && dst_transfer_data[dst_point].is_cut) {
1342 /* This is the new first point of a curve. */
1343 dst_curves_offset.append(dst_point);
1344 dst_to_src_curve.append(src_curve);
1345 length_of_current = 0;
1346 }
1347 ++length_of_current;
1348 }
1349
1350 if (length_of_current != 0) {
1351 /* End of a source curve. */
1352 dst_curves_offset.append(dst_points.one_after_last());
1353 dst_to_src_curve.append(src_curve);
1354 }
1355 }
1356 const int dst_curves_num = dst_curves_offset.size() - 1;
1357 if (dst_curves_num == 0) {
1358 dst.resize(0, 0);
1359 return dst_transfer_data;
1360 }
1361
1362 /* Build destination curves geometry. */
1363 dst.resize(dst_points_num, dst_curves_num);
1364 array_utils::copy(dst_curves_offset.as_span(), dst.offsets_for_write());
1365 const OffsetIndices<int> dst_points_by_curve = dst.points_by_curve();
1366
1367 /* Attributes. */
1368 const bke::AttributeAccessor src_attributes = src.attributes();
1369 bke::MutableAttributeAccessor dst_attributes = dst.attributes_for_write();
1370
1371 /* Copy curves attributes. */
1372 bke::gather_attributes(src_attributes,
1376 dst_to_src_curve,
1377 dst_attributes);
1378 if (src_cyclic.get_if_single().value_or(true)) {
1380 src_now_cyclic.as_span(), dst_to_src_curve.as_span(), dst.cyclic_for_write());
1381 }
1382
1383 dst.update_curve_types();
1384
1385 /* Display intersections with flat caps. */
1386 if (!keep_caps) {
1387 bke::SpanAttributeWriter<int8_t> dst_start_caps =
1388 dst_attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve);
1391
1392 threading::parallel_for(dst.curves_range(), 4096, [&](const IndexRange dst_curves) {
1393 for (const int dst_curve : dst_curves) {
1394 const IndexRange dst_curve_points = dst_points_by_curve[dst_curve];
1395 const PointTransferData &start_point_transfer =
1396 dst_transfer_data[dst_curve_points.first()];
1397 const PointTransferData &end_point_transfer = dst_transfer_data[dst_curve_points.last()];
1398
1399 if (start_point_transfer.is_cut) {
1400 dst_start_caps.span[dst_curve] = GP_STROKE_CAP_TYPE_FLAT;
1401 }
1402 /* The is_cut flag does not work for end points, but any end point that isn't the source
1403 * point must also be a cut. */
1404 if (!end_point_transfer.is_src_end_point()) {
1405 dst_end_caps.span[dst_curve] = GP_STROKE_CAP_TYPE_FLAT;
1406 }
1407 }
1408 });
1409
1410 dst_start_caps.finish();
1411 dst_end_caps.finish();
1412 }
1413
1414 /* Copy/Interpolate point attributes. */
1416 src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT))
1417 {
1418 bke::attribute_math::convert_to_static_type(attribute.dst.span.type(), [&](auto dummy) {
1419 using T = decltype(dummy);
1420 auto src_attr = attribute.src.typed<T>();
1421 auto dst_attr = attribute.dst.span.typed<T>();
1422
1423 threading::parallel_for(dst.points_range(), 4096, [&](const IndexRange dst_points) {
1424 for (const int dst_point : dst_points) {
1425 const PointTransferData &point_transfer = dst_transfer_data[dst_point];
1426 if (point_transfer.is_src_point) {
1427 dst_attr[dst_point] = src_attr[point_transfer.src_point];
1428 }
1429 else {
1430 dst_attr[dst_point] = bke::attribute_math::mix2<T>(
1431 point_transfer.factor,
1432 src_attr[point_transfer.src_point],
1433 src_attr[point_transfer.src_next_point]);
1434 }
1435 }
1436 });
1437
1438 attribute.dst.finish();
1439 });
1440 }
1441
1442 return dst_transfer_data;
1443}
1444
1446 const ARegion *region,
1447 const float3 center,
1448 const float4x4 to_world,
1449 const float pixel_radius)
1450{
1451 const float2 xy_delta = float2(pixel_radius, 0.0f);
1452 const float3 loc = math::transform_point(to_world, center);
1453
1454 const float zfac = ED_view3d_calc_zfac(rv3d, loc);
1455 float3 delta;
1456 ED_view3d_win_to_delta(region, xy_delta, zfac, delta);
1457
1458 const float scale = math::length(
1460
1461 return math::safe_divide(math::length(delta), scale);
1462}
1463
1465 const ARegion *region,
1466 const Brush *brush,
1467 const float3 location,
1468 const float4x4 to_world)
1469{
1470 if ((brush->flag & BRUSH_LOCK_SIZE) == 0) {
1471 return pixel_radius_to_world_space_radius(rv3d, region, location, to_world, brush->size);
1472 }
1473 return brush->unprojected_radius;
1474}
1475
1477 const ARegion *region,
1478 const Brush *brush,
1479 const float pressure,
1480 const float3 location,
1481 const float4x4 to_world,
1482 const BrushGpencilSettings *settings)
1483{
1484 float radius = brush_radius_at_location(rv3d, region, brush, location, to_world);
1485 if (BKE_brush_use_size_pressure(brush)) {
1486 radius *= BKE_curvemapping_evaluateF(settings->curve_sensitivity, 0, pressure);
1487 }
1488 return radius;
1489}
1490
1491float opacity_from_input_sample(const float pressure,
1492 const Brush *brush,
1493 const BrushGpencilSettings *settings)
1494{
1495 float opacity = brush->alpha;
1496 if (BKE_brush_use_alpha_pressure(brush)) {
1497 opacity *= BKE_curvemapping_evaluateF(settings->curve_strength, 0, pressure);
1498 }
1499 return opacity;
1500}
1501
1503 wmOperator *op,
1504 const bool use_duplicate_previous_key)
1505{
1506 const Scene *scene = CTX_data_scene(C);
1507 const Object *object = CTX_data_active_object(C);
1508 if (!object || object->type != OB_GREASE_PENCIL) {
1509 return OPERATOR_CANCELLED;
1510 }
1511
1512 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1513 if (!grease_pencil.has_active_layer()) {
1514 BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
1515 return OPERATOR_CANCELLED;
1516 }
1517
1518 const Paint *paint = BKE_paint_get_active_from_context(C);
1519 const Brush *brush = BKE_paint_brush_for_read(paint);
1520 if (brush == nullptr) {
1521 return OPERATOR_CANCELLED;
1522 }
1523
1524 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1525
1526 if (!active_layer.is_editable()) {
1527 BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
1528 return OPERATOR_CANCELLED;
1529 }
1530
1531 /* Ensure a drawing at the current keyframe. */
1532 bool inserted_keyframe = false;
1534 *scene, grease_pencil, active_layer, use_duplicate_previous_key, inserted_keyframe))
1535 {
1536 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
1537 return OPERATOR_CANCELLED;
1538 }
1539 if (inserted_keyframe) {
1541 }
1543}
1544
1546 const ARegion *region,
1547 const float2 &mouse,
1548 const DrawingPlacement &placement)
1549{
1550 float3 u_dir;
1551 float3 v_dir;
1552 /* Set the texture space origin to be the first point. */
1553 float3 origin = placement.project(mouse);
1554 /* Align texture with the drawing plane. */
1555 switch (scene->toolsettings->gp_sculpt.lock_axis) {
1556 case GP_LOCKAXIS_VIEW:
1557 u_dir = math::normalize(placement.project(float2(region->winx, 0.0f) + mouse) - origin);
1558 v_dir = math::normalize(placement.project(float2(0.0f, region->winy) + mouse) - origin);
1559 break;
1560 case GP_LOCKAXIS_Y:
1561 u_dir = float3(1.0f, 0.0f, 0.0f);
1562 v_dir = float3(0.0f, 0.0f, 1.0f);
1563 break;
1564 case GP_LOCKAXIS_X:
1565 u_dir = float3(0.0f, 1.0f, 0.0f);
1566 v_dir = float3(0.0f, 0.0f, 1.0f);
1567 break;
1568 case GP_LOCKAXIS_Z:
1569 u_dir = float3(1.0f, 0.0f, 0.0f);
1570 v_dir = float3(0.0f, 1.0f, 0.0f);
1571 break;
1572 case GP_LOCKAXIS_CURSOR: {
1573 const float3x3 mat = scene->cursor.matrix<float3x3>();
1574 u_dir = mat * float3(1.0f, 0.0f, 0.0f);
1575 v_dir = mat * float3(0.0f, 1.0f, 0.0f);
1576 origin = float3(scene->cursor.location);
1577 break;
1578 }
1579 }
1580
1581 return math::transpose(float2x4(float4(u_dir, -math::dot(u_dir, origin)),
1582 float4(v_dir, -math::dot(v_dir, origin))));
1583}
1584
1586{
1587 GreasePencil *grease_pencil = static_cast<GreasePencil *>(
1588 CTX_data_pointer_get_type(&C, "grease_pencil", &RNA_GreasePencilv3).data);
1589
1590 if (grease_pencil == nullptr) {
1591 Object *object = CTX_data_active_object(&C);
1592 if (object && object->type == OB_GREASE_PENCIL) {
1593 grease_pencil = static_cast<GreasePencil *>(object->data);
1594 }
1595 }
1596 return grease_pencil;
1597}
1598
1599} // namespace blender::ed::greasepencil
@ ATTR_DOMAIN_MASK_POINT
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1096
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1091
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Low-level operations for grease pencil.
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:215
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, bool clip)
#define ELEM(...)
@ BRUSH_LOCK_SIZE
@ CURVE_TYPE_BEZIER
@ GP_ONION_SKINNING_MODE_ABSOLUTE
@ GP_ONION_SKINNING_MODE_SELECTED
@ GP_ONION_SKINNING_MODE_RELATIVE
@ GP_ONION_SKINNING_SHOW_LOOP
@ GP_MATERIAL_LOCKED
@ GP_MATERIAL_HIDE
@ GP_MATERIAL_FILL_SHOW
Object is a sort of wrapper for general info.
@ OB_GREASE_PENCIL
@ GP_SCULPT_SETT_FLAG_FRAME_FALLOFF
@ GP_LOCKAXIS_X
@ GP_LOCKAXIS_VIEW
@ GP_LOCKAXIS_Y
@ GP_LOCKAXIS_Z
@ GP_LOCKAXIS_CURSOR
@ GP_PROJECT_VIEWSPACE
@ GP_PROJECT_DEPTH_VIEW
@ GP_PROJECT_CURSOR
@ GP_PROJECT_DEPTH_STROKE
@ GP_PROJECT_DEPTH_ONLY_SELECTED
@ GP_USE_MULTI_FRAME_EDITING
@ OPERATOR_RUNNING_MODAL
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:266
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, ViewDepths **r_depths)
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3])
float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3])
void ED_view3d_depths_free(ViewDepths *depths)
eV3DDepthOverrideMode
Definition ED_view3d.hh:184
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:194
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:188
@ V3D_DEPTH_GPENCIL_ONLY
Definition ED_view3d.hh:190
bool ED_view3d_win_to_3d_on_plane(const ARegion *region, const float plane[4], const float mval[2], bool do_clip, float r_out[3])
void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r_out[3])
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
constexpr int64_t last(const int64_t n=0) const
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
void add_new(const Key &key)
Definition BLI_set.hh:233
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t index_of(const Key &key) const
bool add(const Key &key)
void add_new(const Key &key)
int64_t size() const
bool contains(const Key &key) const
void append(const T &value)
Span< T > as_span() const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
IndexRange points_range() const
void resize(int points_num, int curves_num)
VArray< bool > cyclic() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
const bke::CurvesGeometry & strokes() const
DrawingPlacement & operator=(const DrawingPlacement &other)
void cache_viewport_depths(Depsgraph *depsgraph, ARegion *region, View3D *view3d)
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_intersection(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
bool contains(int64_t query_index) const
const Depsgraph * depsgraph
draw_view in_light_buf[] float
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_elements(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op, const bool use_duplicate_previous_key)
static Array< int > get_editable_frames_for_layer(const GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer, const int current_frame, const bool use_multi_frame_editing)
static std::optional< int > get_frame_id(const bke::greasepencil::Layer &layer, const GreasePencilFrame &frame, const int frame_number, const int frame_index, const int current_frame, const int current_frame_index, const int last_frame, const int last_frame_index, const bool use_multi_frame_editing, const bool do_onion_skinning, const bool is_before_first, const GreasePencilOnionSkinningSettings onion_settings)
IndexMask retrieve_editable_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const bool duplicate_previous_key, bool &r_inserted_keyframe)
GreasePencil * from_context(bContext &C)
IndexMask retrieve_editable_fill_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
float opacity_from_input_sample(const float pressure, const Brush *brush, const BrushGpencilSettings *settings)
IndexMask retrieve_editable_elements(Object &object, const MutableDrawingInfo &info, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_handle_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory)
static float get_multi_frame_falloff(const int frame_number, const int center_frame, const int min_frame, const int max_frame, const CurveMapping *falloff_curve)
bke::CurvesGeometry fill_strokes(const ViewContext &view_context, const Brush &brush, const Scene &scene, const bke::greasepencil::Layer &layer, const VArray< bool > &boundary_layers, Span< DrawingInfo > src_drawings, bool invert, const std::optional< float > alpha_threshold, const float2 &fill_point, const ExtensionData &extensions, FillToolFitMethod fit_method, int stroke_material_index, bool keep_images)
IndexMask retrieve_visible_points(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
float radius_from_input_sample(const RegionView3D *rv3d, const ARegion *region, const Brush *brush, const float pressure, const float3 location, const float4x4 to_world, const BrushGpencilSettings *settings)
IndexMask retrieve_editable_strokes_by_material(Object &object, const bke::greasepencil::Drawing &drawing, const int mat_i, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_fill_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static std::pair< int, int > get_minmax_selected_frame_numbers(const GreasePencil &grease_pencil, const int current_frame)
static float pixel_radius_to_world_space_radius(const RegionView3D *rv3d, const ARegion *region, const float3 center, const float4x4 to_world, const float pixel_radius)
static float brush_radius_at_location(const RegionView3D *rv3d, const ARegion *region, const Brush *brush, const float3 location, const float4x4 to_world)
static Array< std::pair< int, int > > get_visible_frames_for_layer(const GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer, const int current_frame, const bool use_multi_frame_editing, const bool do_onion_skinning)
Array< PointTransferData > compute_topology_change(const bke::CurvesGeometry &src, bke::CurvesGeometry &dst, const Span< Vector< PointTransferData > > src_to_dst_points, const bool keep_caps)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static VectorSet< int > get_editable_material_indices(Object &object)
static VectorSet< int > get_fill_material_indices(Object &object)
static VectorSet< int > get_hidden_material_indices(Object &object)
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
IndexMask retrieve_visible_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer_with_falloff(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
IndexMask retrieve_visible_bezier_handle_elements(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings_with_falloff(const Scene &scene, GreasePencil &grease_pencil)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
float4x2 calculate_texture_space(const Scene *scene, const ARegion *region, const float2 &mouse, const DrawingPlacement &placement)
Array< Vector< MutableDrawingInfo > > retrieve_editable_drawings_grouped_per_frame(const Scene &scene, GreasePencil &grease_pencil)
constexpr double inv_sqrt3
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
T clamp(const T &a, const T &min, const T &max)
T safe_divide(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T min(const T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T max(const T &a, const T &b)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
MatBase< float, 2, 4 > float2x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
float alpha
float unprojected_radius
struct CurveMapping * cur_falloff
GreasePencilOnionSkinningSettings onion_skinning_settings
void * data
Definition RNA_types.hh:42
float viewinv[4][4]
struct GP_Sculpt_Settings gp_sculpt
float * depths
Definition ED_view3d.hh:85
struct ReportList * reports
void WM_event_add_notifier(const bContext *C, uint type, void *reference)