Blender V4.3
grease_pencil_paint.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
5#include "BKE_action.hh"
6#include "BKE_attribute.hh"
7#include "BKE_brush.hh"
8#include "BKE_colortools.hh"
9#include "BKE_context.hh"
10#include "BKE_curves.hh"
11#include "BKE_deform.hh"
12#include "BKE_geometry_set.hh"
13#include "BKE_grease_pencil.hh"
15#include "BKE_material.h"
16#include "BKE_paint.hh"
17#include "BKE_scene.hh"
18
19#include "BLI_bounds.hh"
20#include "BLI_color.hh"
22#include "BLI_math_base.hh"
23#include "BLI_math_color.h"
24#include "BLI_math_geom.h"
25#include "BLI_noise.hh"
26#include "BLI_rand.hh"
27#include "BLI_rect.h"
28#include "BLI_time.h"
29
31
32#include "DNA_brush_enums.h"
33#include "DNA_material_types.h"
34#include "DNA_modifier_types.h"
35
36#include "DNA_scene_types.h"
37#include "ED_curves.hh"
38#include "ED_grease_pencil.hh"
39#include "ED_view3d.hh"
40
43#include "GEO_smooth_curves.hh"
44
45#include "WM_api.hh"
46#include "WM_types.hh"
47
49
50#include <optional>
51
53
55 const Brush *brush,
56 const float3 pos)
57{
58 if ((brush->flag & BRUSH_LOCK_SIZE) != 0) {
59 const float pixel_size = ED_view3d_pixel_size(rv3d, pos);
60 return brush->unprojected_radius / pixel_size;
61 }
62 return float(brush->size);
63}
64
65template<typename T>
66static inline void linear_interpolation(const T &a,
67 const T &b,
69 const bool include_first_point)
70{
71 if (include_first_point) {
72 const float step = math::safe_rcp(float(dst.size() - 1));
73 for (const int i : dst.index_range()) {
74 dst[i] = bke::attribute_math::mix2(float(i) * step, a, b);
75 }
76 }
77 else {
78 const float step = 1.0f / float(dst.size());
79 for (const int i : dst.index_range()) {
80 dst[i] = bke::attribute_math::mix2(float(i + 1) * step, a, b);
81 }
82 }
83}
84
86{
87 return std::accumulate(values.begin(), values.end(), float2(0)) / values.size();
88}
89
91static Array<float2> sample_curve_2d(Span<float2> positions, const int64_t resolution)
92{
93 BLI_assert(positions.size() % 3 == 0);
94 const int64_t num_handles = positions.size() / 3;
95 if (num_handles == 1) {
96 return Array<float2>(resolution, positions[1]);
97 }
98 const int64_t num_segments = num_handles - 1;
99 const int64_t num_points = num_segments * resolution;
100
101 Array<float2> points(num_points);
102 const Span<float2> curve_segments = positions.drop_front(1).drop_back(1);
103 threading::parallel_for(IndexRange(num_segments), 32 * resolution, [&](const IndexRange range) {
104 for (const int64_t segment_i : range) {
105 IndexRange segment_range(segment_i * resolution, resolution);
106 bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0],
107 curve_segments[segment_i * 3 + 1],
108 curve_segments[segment_i * 3 + 2],
109 curve_segments[segment_i * 3 + 3],
110 points.as_mutable_span().slice(segment_range));
111 }
112 });
113 return points;
114}
115
121{
122 BLI_assert(src.size() == dst.size());
123 Array<float> accumulated_lengths_src(src.size() - 1);
124 length_parameterize::accumulate_lengths<float2>(src, false, accumulated_lengths_src);
125
126 Array<float> accumulated_lengths_target(target.size() - 1);
127 length_parameterize::accumulate_lengths<float2>(target, false, accumulated_lengths_target);
128
129 Array<int> segment_indices(accumulated_lengths_src.size());
130 Array<float> segment_factors(accumulated_lengths_src.size());
132 accumulated_lengths_target, accumulated_lengths_src, segment_indices, segment_factors);
133
135 target, segment_indices, segment_factors, dst.drop_back(1));
136 dst.last() = src.last();
137}
138
143static void create_blank_curve(bke::CurvesGeometry &curves, const bool on_back)
144{
145 if (!on_back) {
146 const int num_old_points = curves.points_num();
147 curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
148 curves.offsets_for_write().last(1) = num_old_points;
149 return;
150 }
151
152 curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
153 MutableSpan<int> offsets = curves.offsets_for_write();
154 offsets.first() = 0;
155
156 /* Loop through backwards to not overwrite the data. */
157 for (int i = curves.curves_num() - 2; i >= 0; i--) {
158 offsets[i + 1] = offsets[i] + 1;
159 }
160
161 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
162
163 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
165 GMutableSpan attribute_data = dst.span;
166
167 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
168 using T = decltype(dummy);
169 MutableSpan<T> span_data = attribute_data.typed<T>();
170
171 /* Loop through backwards to not overwrite the data. */
172 for (int i = span_data.size() - 2; i >= 0; i--) {
173 span_data[i + 1] = span_data[i];
174 }
175 });
176 dst.finish();
177 });
178}
179
184static void extend_curve(bke::CurvesGeometry &curves, const bool on_back, const int new_points_num)
185{
186 if (!on_back) {
187 curves.resize(curves.points_num() + new_points_num, curves.curves_num());
188 curves.offsets_for_write().last() = curves.points_num();
189 return;
190 }
191
192 const int last_active_point = curves.points_by_curve()[0].last();
193
194 curves.resize(curves.points_num() + new_points_num, curves.curves_num());
195 MutableSpan<int> offsets = curves.offsets_for_write();
196
197 for (const int src_curve : curves.curves_range().drop_front(1)) {
198 offsets[src_curve] = offsets[src_curve] + new_points_num;
199 }
200 offsets.last() = curves.points_num();
201
202 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
203
204 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
205 if (iter.domain != bke::AttrDomain::Point) {
206 return;
207 }
208
210 GMutableSpan attribute_data = dst.span;
211
212 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
213 using T = decltype(dummy);
214 MutableSpan<T> span_data = attribute_data.typed<T>();
215
216 /* Loop through backwards to not overwrite the data. */
217 for (int i = (span_data.size() - 1) - new_points_num; i >= last_active_point; i--) {
218 span_data[i + new_points_num] = span_data[i];
219 }
220 });
221 dst.finish();
222 });
223
224 curves.tag_topology_changed();
225}
226
228 private:
229 /* Screen space coordinates from input samples. */
230 Vector<float2> screen_space_coords_orig_;
231
232 /* Temporary vector of curve fitted screen space coordinates per input sample from the active
233 * smoothing window. The length of this depends on `active_smooth_start_index_`. */
234 Vector<Vector<float2>> screen_space_curve_fitted_coords_;
235 /* Temporary vector of screen space offsets */
236 Vector<float2> screen_space_jitter_offsets_;
237
238 /* Screen space coordinates after smoothing. */
239 Vector<float2> screen_space_smoothed_coords_;
240 /* Screen space coordinates after smoothing and jittering. */
241 Vector<float2> screen_space_final_coords_;
242
243 /* The start index of the smoothing window. */
244 int active_smooth_start_index_ = 0;
245 blender::float4x2 texture_space_ = float4x2::identity();
246
247 /* Helper class to project screen space coordinates to 3d. */
249
250 /* Direction the pen is moving in smoothed over time. */
251 float2 smoothed_pen_direction_ = float2(0.0f);
252
253 /* Accumulated distance along the stroke. */
254 float accum_distance_ = 0.0f;
255
257
258 float stroke_random_radius_factor_;
259 float stroke_random_opacity_factor_;
260 float stroke_random_rotation_factor_;
261
262 float stroke_random_hue_factor_;
263 float stroke_random_sat_factor_;
264 float stroke_random_val_factor_;
265
266 /* The current time at which the paint operation begins. */
267 double start_time_;
268 /* Current delta time from #start_time_, updated after each extension sample. */
269 double delta_time_;
270
271 /* Whether the operation was temporarily called from tools other than draw tool. */
272 bool temp_draw_;
273
275
276 public:
277 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
278 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
279 void on_stroke_done(const bContext &C) override;
280
281 PaintOperation(const bool temp_draw = false)
282 {
283 temp_draw_ = temp_draw;
284 }
285};
286
294
296
299 ColorGeometry4f fill_color_ = ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f);
301
304
306
308 {
310 Object *object = CTX_data_active_object(&C);
311 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
312
313 Paint *paint = &scene_->toolsettings->gp_paint->paint;
315 settings_ = brush_->gpencil_settings;
316
318 use_vertex_color_ = brush_using_vertex_color(scene_->toolsettings->gp_paint, brush_);
319 if (use_vertex_color_) {
320 ColorGeometry4f color_base;
321 srgb_to_linearrgb_v3_v3(color_base, brush_->rgb);
322 color_base.a = settings_->vertex_factor;
324 vertex_color_ = color_base;
325 }
327 fill_color_ = color_base;
328 }
329 }
330 softness_ = 1.0f - settings_->hardness;
331
332 BLI_assert(grease_pencil->has_active_layer());
333 drawing_ = grease_pencil->get_editable_drawing_at(*grease_pencil->get_active_layer(),
334 scene_->r.cfra);
335 BLI_assert(drawing_ != nullptr);
336 }
337
339 const float distance,
340 const float radius,
341 const float pressure)
342 {
343 if (!use_settings_random_ || !(settings_->draw_random_press > 0.0f)) {
344 return radius;
345 }
346 float random_factor = 0.0f;
347 if ((settings_->flag2 & GP_BRUSH_USE_PRESS_AT_STROKE) == 0) {
348 /* TODO: This should be exposed as a setting to scale the noise along the stroke. */
349 constexpr float noise_scale = 1 / 20.0f;
350 random_factor = noise::perlin(
351 float2(distance * noise_scale, self.stroke_random_radius_factor_));
352 }
353 else {
354 random_factor = self.stroke_random_radius_factor_;
355 }
356
357 if ((settings_->flag2 & GP_BRUSH_USE_PRESSURE_RAND_PRESS) != 0) {
358 random_factor *= BKE_curvemapping_evaluateF(settings_->curve_rand_pressure, 0, pressure);
359 }
360
361 return math::interpolate(radius, radius * random_factor, settings_->draw_random_press);
362 }
363
365 const float distance,
366 const float opacity,
367 const float pressure)
368 {
369 if (!use_settings_random_ || !(settings_->draw_random_strength > 0.0f)) {
370 return opacity;
371 }
372 float random_factor = 0.0f;
373 if ((settings_->flag2 & GP_BRUSH_USE_STRENGTH_AT_STROKE) == 0) {
374 /* TODO: This should be exposed as a setting to scale the noise along the stroke. */
375 constexpr float noise_scale = 1 / 20.0f;
376 random_factor = noise::perlin(
377 float2(distance * noise_scale, self.stroke_random_opacity_factor_));
378 }
379 else {
380 random_factor = self.stroke_random_opacity_factor_;
381 }
382
383 if ((settings_->flag2 & GP_BRUSH_USE_STRENGTH_RAND_PRESS) != 0) {
384 random_factor *= BKE_curvemapping_evaluateF(settings_->curve_rand_strength, 0, pressure);
385 }
386
387 return math::interpolate(opacity, opacity * random_factor, settings_->draw_random_strength);
388 }
389
390 float randomize_rotation(PaintOperation &self, const float pressure)
391 {
392 if (!use_settings_random_ || !(settings_->uv_random > 0.0f)) {
393 return 0.0f;
394 }
395 float random_factor = 0.0f;
396 if ((settings_->flag2 & GP_BRUSH_USE_UV_AT_STROKE) == 0) {
397 random_factor = self.rng_.get_float();
398 }
399 else {
400 random_factor = self.stroke_random_rotation_factor_;
401 }
402
403 if ((settings_->flag2 & GP_BRUSH_USE_UV_RAND_PRESS) != 0) {
404 random_factor *= BKE_curvemapping_evaluateF(settings_->curve_rand_uv, 0, pressure);
405 }
406
407 const float random_rotation = (random_factor * 2.0f - 1.0f) * math::numbers::pi;
408 return math::interpolate(0.0f, random_rotation, settings_->uv_random);
409 }
410
412 const float distance,
414 const float pressure)
415 {
417 !(settings_->random_hue > 0.0f || settings_->random_saturation > 0.0f ||
418 settings_->random_value > 0.0f))
419 {
420 return color;
421 }
422 /* TODO: This should be exposed as a setting to scale the noise along the stroke. */
423 constexpr float noise_scale = 1 / 20.0f;
424
425 float random_hue = 0.0f;
426 if ((settings_->flag2 & GP_BRUSH_USE_HUE_AT_STROKE) == 0) {
427 random_hue = noise::perlin(float2(distance * noise_scale, self.stroke_random_hue_factor_));
428 }
429 else {
430 random_hue = self.stroke_random_hue_factor_;
431 }
432
433 float random_saturation = 0.0f;
434 if ((settings_->flag2 & GP_BRUSH_USE_SAT_AT_STROKE) == 0) {
435 random_saturation = noise::perlin(
436 float2(distance * noise_scale, self.stroke_random_sat_factor_));
437 }
438 else {
439 random_saturation = self.stroke_random_sat_factor_;
440 }
441
442 float random_value = 0.0f;
443 if ((settings_->flag2 & GP_BRUSH_USE_VAL_AT_STROKE) == 0) {
444 random_value = noise::perlin(float2(distance * noise_scale, self.stroke_random_val_factor_));
445 }
446 else {
447 random_value = self.stroke_random_val_factor_;
448 }
449
450 if ((settings_->flag2 & GP_BRUSH_USE_HUE_RAND_PRESS) != 0) {
451 random_hue *= BKE_curvemapping_evaluateF(settings_->curve_rand_hue, 0, pressure);
452 }
453 if ((settings_->flag2 & GP_BRUSH_USE_SAT_RAND_PRESS) != 0) {
454 random_saturation *= BKE_curvemapping_evaluateF(
455 settings_->curve_rand_saturation, 0, pressure);
456 }
457 if ((settings_->flag2 & GP_BRUSH_USE_VAL_RAND_PRESS) != 0) {
458 random_value *= BKE_curvemapping_evaluateF(settings_->curve_rand_value, 0, pressure);
459 }
460
461 float3 hsv;
462 rgb_to_hsv_v(color, hsv);
463
464 hsv[0] += math::interpolate(0.5f, random_hue, settings_->random_hue) - 0.5f;
465 /* Wrap hue. */
466 if (hsv[0] > 1.0f) {
467 hsv[0] -= 1.0f;
468 }
469 else if (hsv[0] < 0.0f) {
470 hsv[0] += 1.0f;
471 }
472 hsv[1] *= math::interpolate(1.0f, random_saturation * 2.0f, settings_->random_saturation);
473 hsv[2] *= math::interpolate(1.0f, random_value * 2.0f, settings_->random_value);
474
475 ColorGeometry4f random_color;
476 hsv_to_rgb_v(hsv, random_color);
477 random_color.a = color.a;
478 return random_color;
479 }
480
482 const bContext &C,
483 const InputSample &start_sample,
484 const int material_index,
485 const bool use_fill)
486 {
487 const float2 start_coords = start_sample.mouse_position;
488 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
489 const ARegion *region = CTX_wm_region(&C);
490
491 const float3 start_location = self.placement_.project(start_coords);
493 rv3d,
494 region,
495 brush_,
496 start_sample.pressure,
497 start_location,
498 self.placement_.to_world_space(),
499 settings_);
500 start_radius = randomize_radius(self, 0.0f, start_radius, start_sample.pressure);
501
503 start_sample.pressure, brush_, settings_);
504 start_opacity = randomize_opacity(self, 0.0f, start_opacity, start_sample.pressure);
505
506 /* Do not allow pressure opacity when drawing tool was invoked temporarily. */
507 const float fill_opacity = (!self.temp_draw_) ? start_opacity : 1.0f;
508
509 const float start_rotation = randomize_rotation(self, start_sample.pressure);
510 if (use_vertex_color_) {
512 }
513
514 Scene *scene = CTX_data_scene(&C);
515 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
516
517 self.screen_space_coords_orig_.append(start_coords);
518 self.screen_space_curve_fitted_coords_.append(Vector<float2>({start_coords}));
519 self.screen_space_jitter_offsets_.append(float2(0.0f));
520 self.screen_space_smoothed_coords_.append(start_coords);
521 self.screen_space_final_coords_.append(start_coords);
522
523 /* Resize the curves geometry so there is one more curve with a single point. */
524 bke::CurvesGeometry &curves = drawing_->strokes_for_write();
525 create_blank_curve(curves, on_back);
526
527 const int active_curve = on_back ? curves.curves_range().first() :
528 curves.curves_range().last();
529 const IndexRange curve_points = curves.points_by_curve()[active_curve];
530 const int last_active_point = curve_points.last();
531
532 Set<std::string> point_attributes_to_skip;
533 Set<std::string> curve_attributes_to_skip;
534 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
535 curves.positions_for_write()[last_active_point] = start_location;
536 drawing_->radii_for_write()[last_active_point] = start_radius;
537 drawing_->opacities_for_write()[last_active_point] = start_opacity;
538 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
539 if (use_vertex_color_ || attributes.contains("vertex_color")) {
540 drawing_->vertex_colors_for_write()[last_active_point] = vertex_color_;
541 point_attributes_to_skip.add("vertex_color");
542 }
543 if (use_fill || attributes.contains("fill_color")) {
544 drawing_->fill_colors_for_write()[active_curve] = fill_color_;
545 curve_attributes_to_skip.add("fill_color");
546 }
548 "delta_time", bke::AttrDomain::Point);
549 delta_times.span[last_active_point] = 0.0f;
550 point_attributes_to_skip.add("delta_time");
551 delta_times.finish();
552
554 "material_index", bke::AttrDomain::Curve);
556 "cyclic", bke::AttrDomain::Curve);
558 "softness", bke::AttrDomain::Curve);
560 "u_scale", bke::AttrDomain::Curve);
561 cyclic.span[active_curve] = false;
562 materials.span[active_curve] = material_index;
563 softness.span[active_curve] = softness_;
564 u_scale.span[active_curve] = 1.0f;
565 curve_attributes_to_skip.add_multiple({"material_index", "cyclic", "softness", "u_scale"});
566 cyclic.finish();
567 materials.finish();
568 softness.finish();
569 u_scale.finish();
570
571 if (settings_->uv_random > 0.0f || attributes.contains("rotation")) {
573 "rotation", bke::AttrDomain::Point);
574 rotations.span[last_active_point] = start_rotation;
575 point_attributes_to_skip.add("rotation");
576 rotations.finish();
577 }
578
579 /* Only set the attribute if the type is not the default or if it already exists. */
580 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
583 start_caps.span[active_curve] = settings_->caps_type;
584 curve_attributes_to_skip.add("start_cap");
585 start_caps.finish();
586 }
587
588 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
590 "end_cap", bke::AttrDomain::Curve);
591 end_caps.span[active_curve] = settings_->caps_type;
592 curve_attributes_to_skip.add("end_cap");
593 end_caps.finish();
594 }
595
596 if (use_fill && (start_opacity < 1.0f || attributes.contains("fill_opacity"))) {
597 bke::SpanAttributeWriter<float> fill_opacities =
598 attributes.lookup_or_add_for_write_span<float>(
599 "fill_opacity",
602 fill_opacities.span[active_curve] = fill_opacity;
603 curve_attributes_to_skip.add("fill_opacity");
604 fill_opacities.finish();
605 }
606
608 "init_time", bke::AttrDomain::Curve);
609 /* Truncating time in ms to uint32 then we don't lose precision in lower bits. */
610 init_times.span[active_curve] = float(uint64_t(self.start_time_ * double(1e3))) / float(1e3);
611 curve_attributes_to_skip.add("init_time");
612 init_times.finish();
613
614 curves.curve_types_for_write()[active_curve] = CURVE_TYPE_POLY;
615 curve_attributes_to_skip.add("curve_type");
616 curves.update_curve_types();
617
618 /* Initialize the rest of the attributes with default values. */
620 attributes,
622 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
623 IndexRange(last_active_point, 1));
625 attributes,
627 bke::attribute_filter_from_skip_ref(curve_attributes_to_skip),
628 IndexRange(active_curve, 1));
629
630 drawing_->tag_topology_changed();
631 }
632
633 void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
634 {
635 const Span<float2> coords_to_smooth = self.screen_space_coords_orig_.as_span().slice(
636 smooth_window);
637
638 /* Detect corners in the current slice of coordinates. */
639 const float corner_min_radius_px = 5.0f;
640 const float corner_max_radius_px = 30.0f;
641 const int64_t corner_max_samples = 64;
642 const float corner_angle_threshold = 0.6f;
643 IndexMaskMemory memory;
645 coords_to_smooth.drop_front(1).drop_back(1),
646 corner_min_radius_px,
647 corner_max_radius_px,
648 corner_max_samples,
649 corner_angle_threshold,
650 memory);
651
652 /* Pre-blur the coordinates for the curve fitting. This generally leads to a better (more
653 * stable) fit. */
654 Array<float2> coords_pre_blur(smooth_window.size());
655 const int pre_blur_iterations = 3;
657 coords_to_smooth,
658 pre_blur_iterations,
659 VArray<float>::ForSingle(settings_->active_smooth, smooth_window.size()),
660 true,
661 true,
662 false,
663 coords_pre_blur.as_mutable_span());
664
665 /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */
666 const float max_error_threshold_px = 5.0f;
668 coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask);
669
670 /* Sampling the curve at a fixed resolution. */
671 const int64_t sample_resolution = 32;
672 Array<float2> sampled_curve_points = sample_curve_2d(curve_points, sample_resolution);
673
674 /* Morphing the coordinates onto the curve. Result is stored in a temporary array. */
675 Array<float2> coords_smoothed(coords_to_smooth.size());
676 morph_points_to_curve(coords_to_smooth, sampled_curve_points, coords_smoothed);
677
678 MutableSpan<float2> window_coords = self.screen_space_smoothed_coords_.as_mutable_span().slice(
679 smooth_window);
680 const float converging_threshold_px = 0.1f;
681 bool stop_counting_converged = false;
682 int num_converged = 0;
683 for (const int64_t window_i : smooth_window.index_range()) {
684 /* Record the curve fitting of this point. */
685 self.screen_space_curve_fitted_coords_[window_i].append(coords_smoothed[window_i]);
686 Span<float2> fit_coords = self.screen_space_curve_fitted_coords_[window_i];
687
688 /* We compare the previous arithmetic mean to the current. Going from the back to the front,
689 * if a point hasn't moved by a minimum threshold, it counts as converged. */
690 float2 new_pos = arithmetic_mean(fit_coords);
691 if (!stop_counting_converged) {
692 float2 prev_pos = window_coords[window_i];
693 if (math::distance(new_pos, prev_pos) < converging_threshold_px) {
694 num_converged++;
695 }
696 else {
697 stop_counting_converged = true;
698 }
699 }
700
701 /* Update the positions in the current cache. */
702 window_coords[window_i] = new_pos;
703 }
704
705 /* Remove all the converged points from the active window and shrink the window accordingly. */
706 if (num_converged > 0) {
707 self.active_smooth_start_index_ += num_converged;
708 self.screen_space_curve_fitted_coords_.remove(0, num_converged);
709 }
710 }
711
713 const int new_points_num,
714 const float brush_radius_px,
715 const float pressure,
716 const IndexRange active_window,
717 MutableSpan<float3> curve_positions)
718 {
719 float jitter_factor = 1.0f;
721 jitter_factor = BKE_curvemapping_evaluateF(settings_->curve_jitter, 0, pressure);
722 }
723 const float2 tangent = math::normalize(self.smoothed_pen_direction_);
724 const float2 cotangent = float2(-tangent.y, tangent.x);
725 for ([[maybe_unused]] const int _ : IndexRange(new_points_num)) {
726 const float rand = self.rng_.get_float() * 2.0f - 1.0f;
727 const float factor = rand * settings_->draw_jitter * jitter_factor;
728 self.screen_space_jitter_offsets_.append(cotangent * factor * brush_radius_px);
729 }
730 const Span<float2> jitter_slice = self.screen_space_jitter_offsets_.as_mutable_span().slice(
731 active_window);
732 MutableSpan<float2> smoothed_coords =
733 self.screen_space_smoothed_coords_.as_mutable_span().slice(active_window);
734 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
735 active_window);
736 MutableSpan<float3> positions_slice = curve_positions.slice(active_window);
737 for (const int64_t window_i : active_window.index_range()) {
738 final_coords[window_i] = smoothed_coords[window_i] + jitter_slice[window_i];
739 positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
740 }
741 }
742
744 const bContext &C,
745 const InputSample &extension_sample)
746 {
747 Scene *scene = CTX_data_scene(&C);
748 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
749 const ARegion *region = CTX_wm_region(&C);
750 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
751
752 const float2 coords = extension_sample.mouse_position;
753 float3 position = self.placement_.project(coords);
755 region,
756 brush_,
757 extension_sample.pressure,
758 position,
759 self.placement_.to_world_space(),
760 settings_);
762 extension_sample.pressure, brush_, settings_);
763
764 const float brush_radius_px = brush_radius_to_pixel_radius(
765 rv3d, brush_, math::transform_point(self.placement_.to_world_space(), position));
766
767 bke::CurvesGeometry &curves = drawing_->strokes_for_write();
768 OffsetIndices<int> points_by_curve = curves.points_by_curve();
769 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
770
771 const int active_curve = on_back ? curves.curves_range().first() :
772 curves.curves_range().last();
773 const IndexRange curve_points = points_by_curve[active_curve];
774 const int last_active_point = curve_points.last();
775
776 const float2 prev_coords = self.screen_space_coords_orig_.last();
777 float prev_radius = drawing_->radii()[last_active_point];
778 const float prev_opacity = drawing_->opacities()[last_active_point];
779 const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors()[last_active_point];
780
781 const bool is_first_sample = (curve_points.size() == 1);
782
783 /* Use the vector from the previous to the next point. Set the direction based on the first two
784 * samples. For subsequent samples, interpolate with the previous direction to get a smoothed
785 * value over time. */
786 if (is_first_sample) {
787 self.smoothed_pen_direction_ = self.screen_space_coords_orig_.last() - coords;
788 }
789 else {
790 /* The smoothing rate is a factor from 0 to 1 that represents how quickly the
791 * `smoothed_pen_direction_` "reacts" to changes in direction.
792 * - 1.0f: Immediate reaction.
793 * - 0.0f: No reaction (value never changes). */
794 constexpr float smoothing_rate_factor = 0.3f;
795 self.smoothed_pen_direction_ = math::interpolate(self.smoothed_pen_direction_,
796 self.screen_space_coords_orig_.last() -
797 coords,
798 smoothing_rate_factor);
799 }
800
801 /* Approximate brush with non-circular shape by changing the radius based on the angle. */
802 float radius_factor = 1.0f;
803 if (settings_->draw_angle_factor > 0.0f) {
804 /* `angle` is the angle to the horizontal line in screen space. */
805 const float angle = settings_->draw_angle;
806 const float2 angle_vec = float2(math::cos(angle), math::sin(angle));
807
808 /* The angle factor is 1.0f when the direction is aligned with the angle vector and 0.0f when
809 * it is orthogonal to the angle vector. This is consistent with the behavior from GPv2. */
810 const float angle_factor = math::abs(
811 math::dot(angle_vec, math::normalize(self.smoothed_pen_direction_)));
812
813 /* Influence is controlled by `draw_angle_factor`. */
814 radius_factor = math::interpolate(1.0f, angle_factor, settings_->draw_angle_factor);
815 radius *= radius_factor;
816 }
817
818 /* Overwrite last point if it's very close. */
819 const float distance_px = math::distance(coords, prev_coords);
820 constexpr float point_override_threshold_px = 2.0f;
821 if (distance_px < point_override_threshold_px) {
822 self.accum_distance_ += distance_px;
823 /* Don't move the first point of the stroke. */
824 if (!is_first_sample) {
825 curves.positions_for_write()[last_active_point] = position;
826 }
827 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
828 radius = randomize_radius(self, self.accum_distance_, radius, extension_sample.pressure);
829 }
830 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
831 opacity = randomize_opacity(
832 self, self.accum_distance_, opacity, extension_sample.pressure);
833 }
834 drawing_->radii_for_write()[last_active_point] = math::max(radius, prev_radius);
835 drawing_->opacities_for_write()[last_active_point] = math::max(opacity, prev_opacity);
836 return;
837 }
838
839 /* Adjust the first points radius based on the computed angle. */
840 if (is_first_sample && settings_->draw_angle_factor > 0.0f) {
841 drawing_->radii_for_write()[last_active_point] *= radius_factor;
842 prev_radius = drawing_->radii()[last_active_point];
843 }
844
845 /* Clamp the number of points within a pixel in screen space. */
846 constexpr int max_points_per_pixel = 4;
847 /* The value `brush_->spacing` is a percentage of the brush radius in pixels. */
848 const float max_spacing_px = math::max((float(brush_->spacing) / 100.0f) *
849 float(brush_radius_px),
850 1.0f / float(max_points_per_pixel));
851 /* If the next sample is far away, we subdivide the segment to add more points. */
852 const int new_points_num = (distance_px > max_spacing_px) ?
853 int(math::floor(distance_px / max_spacing_px)) :
854 1;
855 /* Resize the curves geometry. */
856 extend_curve(curves, on_back, new_points_num);
857
858 Set<std::string> point_attributes_to_skip;
859 /* Subdivide new segment. */
860 const IndexRange new_points = curves.points_by_curve()[active_curve].take_back(new_points_num);
861 Array<float2> new_screen_space_coords(new_points_num);
862 MutableSpan<float3> positions = curves.positions_for_write();
863 MutableSpan<float3> new_positions = positions.slice(new_points);
864 MutableSpan<float> new_radii = drawing_->radii_for_write().slice(new_points);
865 MutableSpan<float> new_opacities = drawing_->opacities_for_write().slice(new_points);
866
867 /* Interpolate the screen space positions. */
868 linear_interpolation<float2>(prev_coords, coords, new_screen_space_coords, is_first_sample);
869 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
870
871 /* Randomize radii. */
872 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
873 for (const int i : IndexRange(new_points_num)) {
874 new_radii[i] = randomize_radius(
875 self, self.accum_distance_ + max_spacing_px * i, radius, extension_sample.pressure);
876 }
877 }
878 else {
879 linear_interpolation<float>(prev_radius, radius, new_radii, is_first_sample);
880 }
881
882 /* Randomize opacities. */
883 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
884 for (const int i : IndexRange(new_points_num)) {
885 new_opacities[i] = randomize_opacity(
886 self, self.accum_distance_ + max_spacing_px * i, opacity, extension_sample.pressure);
887 }
888 }
889 else {
890 linear_interpolation<float>(prev_opacity, opacity, new_opacities, is_first_sample);
891 }
892
893 /* Randomize rotations. */
894 if (use_settings_random_ && (settings_->uv_random > 0.0f || attributes.contains("rotation"))) {
896 "rotation", bke::AttrDomain::Point);
897 const MutableSpan<float> new_rotations = rotations.span.slice(new_points);
898 for (const int i : IndexRange(new_points_num)) {
899 new_rotations[i] = randomize_rotation(self, extension_sample.pressure);
900 }
901 point_attributes_to_skip.add("rotation");
902 rotations.finish();
903 }
904
905 /* Randomize vertex color. */
906 if (use_vertex_color_ || attributes.contains("vertex_color")) {
907 MutableSpan<ColorGeometry4f> new_vertex_colors = drawing_->vertex_colors_for_write().slice(
908 new_points);
909 if (use_settings_random_ || attributes.contains("vertex_color")) {
910 for (const int i : IndexRange(new_points_num)) {
911 new_vertex_colors[i] = randomize_color(self,
912 self.accum_distance_ + max_spacing_px * i,
914 extension_sample.pressure);
915 }
916 }
917 else {
919 prev_vertex_color, vertex_color_, new_vertex_colors, is_first_sample);
920 }
921 point_attributes_to_skip.add("vertex_color");
922 }
923
925 "delta_time", bke::AttrDomain::Point);
926 const double new_delta_time = BLI_time_now_seconds() - self.start_time_;
927 linear_interpolation<float>(float(self.delta_time_),
928 float(new_delta_time),
929 delta_times.span.slice(new_points),
930 is_first_sample);
931 point_attributes_to_skip.add("delta_time");
932 delta_times.finish();
933
934 /* Update the accumulated distance along the stroke in pixels. */
935 self.accum_distance_ += distance_px;
936
937 /* Update the current delta time. */
938 self.delta_time_ = new_delta_time;
939
940 /* Update screen space buffers with new points. */
941 self.screen_space_coords_orig_.extend(new_screen_space_coords);
942 self.screen_space_smoothed_coords_.extend(new_screen_space_coords);
943 self.screen_space_final_coords_.extend(new_screen_space_coords);
944 for (float2 new_position : new_screen_space_coords) {
945 self.screen_space_curve_fitted_coords_.append(Vector<float2>({new_position}));
946 }
947
948 /* Only start smoothing if there are enough points. */
949 constexpr int64_t min_active_smoothing_points_num = 8;
950 const IndexRange smooth_window = self.screen_space_coords_orig_.index_range().drop_front(
951 self.active_smooth_start_index_);
952 if (smooth_window.size() < min_active_smoothing_points_num) {
953 self.placement_.project(new_screen_space_coords, new_positions);
954 }
955 else {
956 /* Active smoothing is done in a window at the end of the new stroke. */
957 this->active_smoothing(self, smooth_window);
958 }
959
960 MutableSpan<float3> curve_positions = positions.slice(curves.points_by_curve()[active_curve]);
961 if (use_settings_random_ && settings_->draw_jitter > 0.0f) {
962 this->active_jitter(self,
963 new_points_num,
964 brush_radius_px,
965 extension_sample.pressure,
966 smooth_window,
967 curve_positions);
968 }
969 else {
970 MutableSpan<float2> smoothed_coords =
971 self.screen_space_smoothed_coords_.as_mutable_span().slice(smooth_window);
972 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
973 smooth_window);
974 /* Not jitter, so we just copy the positions over. */
975 final_coords.copy_from(smoothed_coords);
976 MutableSpan<float3> curve_positions_slice = curve_positions.slice(smooth_window);
977 for (const int64_t window_i : smooth_window.index_range()) {
978 curve_positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
979 }
980 }
981
982 /* Initialize the rest of the attributes with default values. */
984 attributes,
986 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
987 curves.points_range().take_back(1));
988
989 drawing_->set_texture_matrices({self.texture_space_}, IndexRange::from_single(active_curve));
990 }
991
992 void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
993 {
994 const Scene *scene = CTX_data_scene(&C);
995 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
996
997 this->process_extension_sample(self, C, extension_sample);
998
999 const bke::CurvesGeometry &curves = drawing_->strokes();
1000 const int active_curve = on_back ? curves.curves_range().first() :
1001 curves.curves_range().last();
1002 drawing_->tag_topology_changed(IndexRange::from_single(active_curve));
1003 }
1004};
1005
1007{
1008 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
1009 ARegion *region = CTX_wm_region(&C);
1011 Scene *scene = CTX_data_scene(&C);
1012 Object *object = CTX_data_active_object(&C);
1013 Object *eval_object = DEG_get_evaluated_object(depsgraph, object);
1014 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1015
1016 Paint *paint = &scene->toolsettings->gp_paint->paint;
1017 Brush *brush = BKE_paint_brush(paint);
1018
1019 if (brush->gpencil_settings == nullptr) {
1021 }
1022 BrushGpencilSettings *settings = brush->gpencil_settings;
1023
1033
1034 const bke::greasepencil::Layer &layer = *grease_pencil->get_active_layer();
1035 /* Initialize helper class for projecting screen space coordinates. */
1036 placement_ = ed::greasepencil::DrawingPlacement(*scene, *region, *view3d, *eval_object, &layer);
1037 if (placement_.use_project_to_surface()) {
1038 placement_.cache_viewport_depths(depsgraph, region, view3d);
1039 }
1040 else if (placement_.use_project_to_nearest_stroke()) {
1041 placement_.cache_viewport_depths(depsgraph, region, view3d);
1042 placement_.set_origin_to_nearest_stroke(start_sample.mouse_position);
1043 }
1044
1046 scene, region, start_sample.mouse_position, placement_);
1047
1048 /* `View` is already stored in object space but all others are in layer space. */
1050 texture_space_ = texture_space_ * layer.to_object_space(*object);
1051 }
1052
1054 if ((settings->flag & GP_BRUSH_GROUP_RANDOM) != 0) {
1055 stroke_random_radius_factor_ = rng_.get_float();
1056 stroke_random_opacity_factor_ = rng_.get_float();
1057 stroke_random_rotation_factor_ = rng_.get_float();
1058
1059 stroke_random_hue_factor_ = rng_.get_float();
1060 stroke_random_sat_factor_ = rng_.get_float();
1061 stroke_random_val_factor_ = rng_.get_float();
1062 }
1063
1065 CTX_data_main(&C), object, brush);
1066 const int material_index = BKE_object_material_index_get(object, material);
1067 const bool use_fill = (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
1068
1069 /* We're now starting to draw. */
1070 grease_pencil->runtime->is_drawing_stroke = true;
1071
1072 /* Initialize the start time to the current time. */
1073 start_time_ = BLI_time_now_seconds();
1074 /* Delta time starts at 0. */
1075 delta_time_ = 0.0f;
1076
1077 PaintOperationExecutor executor{C};
1078 executor.process_start_sample(*this, C, start_sample, material_index, use_fill);
1079
1080 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1081 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1082}
1083
1084void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
1085{
1086 Object *object = CTX_data_active_object(&C);
1087 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1088
1089 PaintOperationExecutor executor{C};
1090 executor.execute(*this, C, extension_sample);
1091
1092 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1093 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1094}
1095
1097 const float influence,
1098 const int iterations,
1099 const int active_curve)
1100{
1102 const IndexRange stroke = IndexRange::from_single(active_curve);
1103 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1104 const VArray<bool> cyclic = curves.cyclic();
1105 const VArray<bool> point_selection = VArray<bool>::ForSingle(true, curves.points_num());
1106
1107 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1108 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1110 points_by_curve,
1111 point_selection,
1112 cyclic,
1113 iterations,
1114 influence,
1115 false,
1116 true,
1117 positions.span);
1118 positions.finish();
1119 drawing.tag_positions_changed();
1120
1121 if (drawing.opacities().is_span()) {
1122 bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
1124 points_by_curve,
1125 point_selection,
1126 cyclic,
1127 iterations,
1128 influence,
1129 true,
1130 false,
1131 opacities.span);
1132 opacities.finish();
1133 }
1134 if (drawing.radii().is_span()) {
1135 bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
1137 points_by_curve,
1138 point_selection,
1139 cyclic,
1140 iterations,
1141 influence,
1142 true,
1143 false,
1144 radii.span);
1145 radii.finish();
1146 }
1147}
1148
1150 const float epsilon,
1151 const int active_curve)
1152{
1153 const bke::CurvesGeometry &curves = drawing.strokes();
1154 const bke::AttributeAccessor attributes = curves.attributes();
1155 const IndexRange points = curves.points_by_curve()[active_curve];
1156 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1157 ".draw_tool_screen_space_positions");
1158 BLI_assert(screen_space_positions_attribute.is_span());
1159
1160 const Span<float2> screen_space_positions =
1161 screen_space_positions_attribute.get_internal_span().slice(points);
1162
1163 Array<bool> points_to_delete_arr(drawing.strokes().points_num(), false);
1164 points_to_delete_arr.as_mutable_span().slice(points).fill(true);
1165 geometry::curve_simplify(curves.positions().slice(points),
1166 curves.cyclic()[active_curve],
1167 epsilon,
1168 screen_space_positions,
1169 points_to_delete_arr.as_mutable_span().slice(points));
1170
1171 IndexMaskMemory memory;
1172 const IndexMask points_to_delete = IndexMask::from_bools(points_to_delete_arr, memory);
1173 if (!points_to_delete.is_empty()) {
1174 drawing.strokes_for_write().remove_points(points_to_delete, {});
1175 drawing.tag_topology_changed();
1176 }
1177}
1178
1180 const int active_curve,
1181 const bool on_back)
1182{
1183 const bke::CurvesGeometry &curves = drawing.strokes();
1184 const IndexRange points = curves.points_by_curve()[active_curve];
1185 const bke::AttributeAccessor attributes = curves.attributes();
1186 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1187 ".draw_tool_screen_space_positions");
1188 BLI_assert(screen_space_positions_attribute.is_span());
1189 const Span<float2> screen_space_positions =
1190 screen_space_positions_attribute.get_internal_span().slice(points);
1191 /* Extract the drawn stroke into a separate geometry, so we can trim the ends for just this
1192 * stroke. */
1194 drawing.strokes(), IndexRange::from_single(active_curve), {});
1195 auto bounds = bounds::min_max(screen_space_positions);
1196 rcti screen_space_bounds;
1197 BLI_rcti_init(&screen_space_bounds,
1198 int(bounds->min.x),
1199 int(bounds->max.x),
1200 int(bounds->min.y),
1201 int(bounds->max.y));
1202 /* Use the first and last point. */
1203 const Vector<Vector<int>> point_selection = {{0, int(points.index_range().last())}};
1204 /* Trim the stroke ends by finding self intersections using the screen space positions. */
1206 stroke,
1207 screen_space_positions,
1208 {screen_space_bounds},
1210 point_selection,
1211 true);
1212
1213 /* No intersection found. */
1214 if (stroke_trimmed.points_num() == 0) {
1215 return;
1216 }
1217
1218 /* Remove the original stroke. */
1219 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1220
1221 /* Join the trimmed stroke into the drawing. */
1222 Curves *trimmed_curve = bke::curves_new_nomain(std::move(stroke_trimmed));
1223 Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
1224 std::array<bke::GeometrySet, 2> geometry_sets;
1225 if (on_back) {
1226 geometry_sets = {bke::GeometrySet::from_curves(trimmed_curve),
1227 bke::GeometrySet::from_curves(other_curves)};
1228 }
1229 else {
1230 geometry_sets = {bke::GeometrySet::from_curves(other_curves),
1231 bke::GeometrySet::from_curves(trimmed_curve)};
1232 }
1233 drawing.strokes_for_write() = std::move(
1234 geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
1235 drawing.tag_topology_changed();
1236}
1237
1239 const int active_curve,
1240 const float4x4 &viewmat,
1241 const ed::greasepencil::DrawingPlacement &placement,
1242 const float outline_radius,
1243 const int material_index,
1244 const bool on_back)
1245{
1246 /* Get the outline stroke (single curve). */
1248 drawing,
1249 IndexRange::from_single(active_curve),
1250 viewmat,
1251 3,
1252 outline_radius,
1253 0.0f,
1254 material_index);
1255
1256 /* Reproject the outline onto the drawing placement. */
1257 placement.reproject(outline.positions(), outline.positions_for_write());
1258
1259 /* Remove the original stroke. */
1260 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1261
1262 /* Join the outline stroke into the drawing. */
1263 Curves *outline_curve = bke::curves_new_nomain(std::move(outline));
1264 Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
1265 std::array<bke::GeometrySet, 2> geometry_sets;
1266 if (on_back) {
1267 geometry_sets = {bke::GeometrySet::from_curves(outline_curve),
1268 bke::GeometrySet::from_curves(other_curves)};
1269 }
1270 else {
1271 geometry_sets = {bke::GeometrySet::from_curves(other_curves),
1272 bke::GeometrySet::from_curves(outline_curve)};
1273 }
1274 drawing.strokes_for_write() = std::move(
1275 geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
1276 drawing.tag_topology_changed();
1277}
1278
1280 const float epsilon,
1281 const bool on_back,
1282 const int active_curve)
1283{
1284 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1286 const VArray<float> radii = drawing.radii();
1287
1288 /* Remove points at the end that have a radius close to 0. */
1289 int64_t num_points_to_remove = 0;
1290 for (int64_t index = points.last(); index >= points.first(); index--) {
1291 if (radii[index] < epsilon) {
1292 num_points_to_remove++;
1293 }
1294 else {
1295 break;
1296 }
1297 }
1298
1299 if (num_points_to_remove <= 0) {
1300 return 0;
1301 }
1302
1303 /* Don't remove the entire stroke. Leave at least one point. */
1304 if (points.size() - num_points_to_remove < 1) {
1305 num_points_to_remove = points.size() - 1;
1306 }
1307
1308 if (!on_back) {
1309 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1310 curves.offsets_for_write().last() = curves.points_num();
1311 return num_points_to_remove;
1312 }
1313
1314 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1315 const int last_active_point = curves.points_by_curve()[0].last();
1316
1317 /* Shift the data before resizing to not delete the data at the end. */
1318 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1319 if (iter.domain != bke::AttrDomain::Point) {
1320 return;
1321 }
1322
1324 GMutableSpan attribute_data = dst.span;
1325
1326 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1327 using T = decltype(dummy);
1328 MutableSpan<T> span_data = attribute_data.typed<T>();
1329
1330 for (int i = last_active_point - num_points_to_remove + 1;
1331 i < curves.points_num() - num_points_to_remove;
1332 i++)
1333 {
1334 span_data[i] = span_data[i + num_points_to_remove];
1335 }
1336 });
1337 dst.finish();
1338 });
1339
1340 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1341 MutableSpan<int> offsets = curves.offsets_for_write();
1342 for (const int src_curve : curves.curves_range().drop_front(1)) {
1343 offsets[src_curve] = offsets[src_curve] - num_points_to_remove;
1344 }
1345 offsets.last() = curves.points_num();
1346
1347 return num_points_to_remove;
1348}
1349
1350static void deselect_stroke(const bContext &C,
1352 const int active_curve)
1353{
1354 Scene *scene = CTX_data_scene(&C);
1355 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1356
1359 scene->toolsettings);
1360
1362 curves, selection_domain, CD_PROP_BOOL);
1363
1364 if (selection_domain == bke::AttrDomain::Curve) {
1366 }
1367 else if (selection_domain == bke::AttrDomain::Point) {
1368 ed::curves::fill_selection_false(selection.span.slice(points));
1369 }
1370
1371 selection.finish();
1372}
1373
1374static void process_stroke_weights(const Scene &scene,
1375 const Object &object,
1377 const int active_curve)
1378{
1380 const IndexRange points = curves.points_by_curve()[active_curve];
1381
1382 const int def_nr = BKE_object_defgroup_active_index_get(&object) - 1;
1383
1384 if (def_nr == -1) {
1385 return;
1386 }
1387
1388 const bDeformGroup *defgroup = static_cast<const bDeformGroup *>(
1389 BLI_findlink(BKE_object_defgroup_list(&object), def_nr));
1390
1391 const StringRef vertex_group_name = defgroup->name;
1392
1394 curves, IndexMask(points), vertex_group_name, scene.toolsettings->vgroup_weight);
1395
1396 if (scene.toolsettings->vgroup_weight == 0.0f) {
1397 return;
1398 }
1399
1400 /* Loop through all modifiers trying to find the pose channel for the vertex group name. */
1401 bPoseChannel *channel = nullptr;
1402 Object *ob_arm = nullptr;
1403 LISTBASE_FOREACH (ModifierData *, md, &(&object)->modifiers) {
1404 if (md->type != eModifierType_GreasePencilArmature) {
1405 continue;
1406 }
1407
1408 /* Skip not visible modifiers. */
1409 if (!(md->mode & eModifierMode_Realtime)) {
1410 continue;
1411 }
1412
1414 md);
1415 if (amd == nullptr) {
1416 continue;
1417 }
1418
1419 ob_arm = amd->object;
1420 /* Not an armature. */
1421 if (ob_arm->type != OB_ARMATURE || ob_arm->pose == nullptr) {
1422 continue;
1423 }
1424
1425 channel = BKE_pose_channel_find_name(ob_arm->pose, vertex_group_name.data());
1426 if (channel == nullptr) {
1427 continue;
1428 }
1429
1430 /* Found the channel. */
1431 break;
1432 }
1433
1434 /* Nothing valid was found. */
1435 if (channel == nullptr) {
1436 return;
1437 }
1438
1439 const float4x4 obinv = math::invert(object.object_to_world());
1440
1441 const float4x4 postmat = obinv * ob_arm->object_to_world();
1442 const float4x4 premat = math::invert(postmat);
1443
1444 const float4x4 matrix = postmat * math::invert(float4x4(channel->chan_mat)) * premat;
1445
1446 /* Update the position of the stroke to undo the movement caused by the modifier.*/
1447 MutableSpan<float3> positions = curves.positions_for_write().slice(points);
1448 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
1449 for (float3 &position : positions.slice(range)) {
1450 position = math::transform_point(matrix, position);
1451 }
1452 });
1453}
1454
1456{
1457 using namespace blender::bke;
1458 Scene *scene = CTX_data_scene(&C);
1459 Object *object = CTX_data_active_object(&C);
1461 const ARegion *region = CTX_wm_region(&C);
1462 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1463
1464 Paint *paint = &scene->toolsettings->gp_paint->paint;
1465 Brush *brush = BKE_paint_brush(paint);
1466 BrushGpencilSettings *settings = brush->gpencil_settings;
1467 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
1468 const bool do_post_processing = (settings->flag & GP_BRUSH_GROUP_SETTINGS) != 0;
1469 const bool do_automerge_endpoints = (scene->toolsettings->gpencil_flags &
1471
1472 /* Grease Pencil should have an active layer. */
1473 BLI_assert(grease_pencil.has_active_layer());
1474 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1475 /* Drawing should exist. */
1476 bke::greasepencil::Drawing &drawing = *grease_pencil.get_editable_drawing_at(active_layer,
1477 scene->r.cfra);
1478 const int active_curve = on_back ? drawing.strokes().curves_range().first() :
1479 drawing.strokes().curves_range().last();
1480 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1481 const IndexRange points = points_by_curve[active_curve];
1482
1483 /* Write the screen space positions of the new stroke as a temporary attribute, so all the
1484 * changes in topology with the operations below get propagated correctly. */
1486 bke::SpanAttributeWriter<float2> screen_space_positions =
1487 attributes.lookup_or_add_for_write_only_span<float2>(".draw_tool_screen_space_positions",
1489 screen_space_positions.span.slice(points).copy_from(this->screen_space_final_coords_);
1490 screen_space_positions.finish();
1491
1492 /* Remove trailing points with radii close to zero. */
1493 trim_end_points(drawing, 1e-5f, on_back, active_curve);
1494
1495 /* Set the selection of the newly drawn stroke to false. */
1496 deselect_stroke(C, drawing, active_curve);
1497
1498 if (do_post_processing) {
1499 if (settings->draw_smoothfac > 0.0f) {
1500 smooth_stroke(drawing, settings->draw_smoothfac, settings->draw_smoothlvl, active_curve);
1501 }
1502 if (settings->simplify_px > 0.0f) {
1503 simplify_stroke(drawing, settings->simplify_px, active_curve);
1504 }
1505 if ((settings->flag & GP_BRUSH_TRIM_STROKE) != 0) {
1506 trim_stroke_ends(drawing, active_curve, on_back);
1507 }
1509 process_stroke_weights(*scene, *object, drawing, active_curve);
1510 }
1511 if ((settings->flag & GP_BRUSH_OUTLINE_STROKE) != 0) {
1512 const float outline_radius = float(brush->unprojected_radius) * settings->outline_fac * 0.5f;
1513 const int material_index = [&]() {
1515 CTX_data_main(&C), object, brush);
1516 const int active_index = BKE_object_material_index_get(object, material);
1517 if (settings->material_alt == nullptr) {
1518 return active_index;
1519 }
1520 const int alt_index = BKE_object_material_slot_find_index(object, settings->material_alt);
1521 return (alt_index > -1) ? alt_index - 1 : active_index;
1522 }();
1523 outline_stroke(drawing,
1524 active_curve,
1525 float4x4(rv3d->viewmat),
1526 placement_,
1527 outline_radius,
1528 material_index,
1529 on_back);
1530 }
1531 }
1532 /* Remove the temporary attribute. */
1533 attributes.remove(".draw_tool_screen_space_positions");
1534
1535 drawing.set_texture_matrices({texture_space_}, IndexRange::from_single(active_curve));
1536
1537 if (do_automerge_endpoints) {
1538 constexpr float merge_distance = 20.0f;
1539 const float4x4 layer_to_world = active_layer.to_world_space(*object);
1540 const IndexMask selection = IndexRange::from_single(active_curve);
1542 *region, drawing.strokes(), layer_to_world, merge_distance, selection, {});
1543 }
1544
1545 drawing.tag_topology_changed();
1546
1547 /* Now we're done drawing. */
1548 grease_pencil.runtime->is_drawing_stroke = false;
1549
1550 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1551 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil.id);
1552}
1553
1554std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation(const bool temp_draw)
1555{
1556 return std::make_unique<PaintOperation>(temp_draw);
1557}
1558
1559} // namespace blender::ed::sculpt_paint::greasepencil
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:563
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
Low-level operations for curves.
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:601
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:579
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_ensure_from_active_input_brush(Main *bmain, Object *ob, Brush *brush)
Utility functions for vertex groups in grease pencil objects.
General operations, lookup, etc. for materials.
short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma)
int BKE_object_material_index_get(Object *ob, const Material *ma)
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ GP_BRUSH_USE_SAT_RAND_PRESS
@ GP_BRUSH_USE_STRENGTH_RAND_PRESS
@ GP_BRUSH_USE_VAL_RAND_PRESS
@ GP_BRUSH_USE_HUE_RAND_PRESS
@ GP_BRUSH_USE_STRENGTH_AT_STROKE
@ GP_BRUSH_USE_UV_RAND_PRESS
@ GP_BRUSH_USE_HUE_AT_STROKE
@ GP_BRUSH_USE_VAL_AT_STROKE
@ GP_BRUSH_USE_SAT_AT_STROKE
@ GP_BRUSH_USE_PRESS_AT_STROKE
@ GP_BRUSH_USE_UV_AT_STROKE
@ GP_BRUSH_USE_PRESSURE_RAND_PRESS
@ GP_BRUSH_GROUP_RANDOM
@ GP_BRUSH_OUTLINE_STROKE
@ GP_BRUSH_TRIM_STROKE
@ GP_BRUSH_GROUP_SETTINGS
@ GP_BRUSH_USE_JITTER_PRESSURE
@ BRUSH_LOCK_SIZE
@ GPPAINT_MODE_STROKE
@ GPPAINT_MODE_FILL
@ GPPAINT_MODE_BOTH
@ CURVE_TYPE_POLY
@ GP_STROKE_CAP_TYPE_ROUND
@ GP_MATERIAL_FILL_SHOW
@ eModifierMode_Realtime
@ eModifierType_GreasePencilArmature
@ OB_ARMATURE
@ GP_LOCKAXIS_VIEW
@ GP_TOOL_FLAG_PAINT_ONBACK
@ GP_TOOL_FLAG_AUTOMERGE_STROKE
@ GP_TOOL_FLAG_CREATE_WEIGHTS
float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
PyObject * self
BPy_StructRNA * depsgraph
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
static constexpr IndexRange from_single(const int64_t index)
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
static VArray ForSingle(T value, const int64_t size)
int64_t size() const
Definition BLI_array.hh:245
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
ChannelStorageType a
Definition BLI_color.hh:88
const CPPType & type() const
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
static constexpr IndexRange from_single(const int64_t index)
constexpr IndexRange index_range() const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr T & first() const
Definition BLI_span.hh:680
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
bool add(const Key &key)
Definition BLI_set.hh:248
void add_multiple(Span< Key > keys)
Definition BLI_set.hh:268
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:172
constexpr Span drop_back(int64_t n) const
Definition BLI_span.hh:183
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr const char * data() const
Span< T > get_internal_span() const
static VArray ForSingle(T value, const int64_t size)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
bool contains(const StringRef attribute_id) const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > positions() const
void remove_curves(const IndexMask &curves_to_delete, const AttributeFilter &attribute_filter)
void remove_points(const IndexMask &points_to_delete, const AttributeFilter &attribute_filter)
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
VArray< float > opacities() const
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
float4x4 to_world_space(const Object &object) const
float4x4 to_object_space(const Object &object) const
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
local_group_size(16, 16) .push_constant(Type b
VecBase< float, 2 > float2
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
blender::bke::AttrDomain ED_grease_pencil_edit_selection_domain_get(const ToolSettings *tool_settings)
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix2(float factor, const T &a, const T &b)
void evaluate_segment(const T &point_0, const T &point_1, const T &point_2, const T &point_3, MutableSpan< T > result)
void assign_to_vertex_group_from_mask(CurvesGeometry &curves, const IndexMask &mask, StringRef name, float weight)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
Curves * curves_new_nomain(int points_num, int curves_num)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:46
void fill_selection_false(GMutableSpan selection)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
bke::CurvesGeometry trim_curve_segments(const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, const Span< rcti > screen_space_curve_bounds, const IndexMask &curve_selection, const Vector< Vector< int > > &selected_points_in_curves, const bool keep_caps)
float opacity_from_input_sample(const float pressure, const Brush *brush, const BrushGpencilSettings *settings)
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 polyline_detect_corners(Span< float2 > points, const float radius_min, const float radius_max, const int samples_max, const float angle_threshold, IndexMaskMemory &memory)
bke::CurvesGeometry curves_merge_endpoints_by_distance(const ARegion &region, const bke::CurvesGeometry &src_curves, const float4x4 &layer_to_world, const float merge_distance, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
Array< float2 > polyline_fit_curve(Span< float2 > points, const float error_threshold, const IndexMask &corner_mask)
float4x2 calculate_texture_space(const Scene *scene, const ARegion *region, const float2 &mouse, const DrawingPlacement &placement)
bke::CurvesGeometry create_curves_outline(const bke::greasepencil::Drawing &drawing, const IndexMask &strokes, const float4x4 &transform, const int corner_subdivisions, const float outline_radius, const float outline_offset, const int material_index)
static void linear_interpolation(const T &a, const T &b, MutableSpan< T > dst, const bool include_first_point)
static void deselect_stroke(const bContext &C, bke::greasepencil::Drawing &drawing, const int active_curve)
static void create_blank_curve(bke::CurvesGeometry &curves, const bool on_back)
static void trim_stroke_ends(bke::greasepencil::Drawing &drawing, const int active_curve, const bool on_back)
static float2 arithmetic_mean(Span< float2 > values)
static void extend_curve(bke::CurvesGeometry &curves, const bool on_back, const int new_points_num)
static int trim_end_points(bke::greasepencil::Drawing &drawing, const float epsilon, const bool on_back, const int active_curve)
static float brush_radius_to_pixel_radius(const RegionView3D *rv3d, const Brush *brush, const float3 pos)
bool brush_using_vertex_color(const GpPaint *gp_paint, const Brush *brush)
static void morph_points_to_curve(Span< float2 > src, Span< float2 > target, MutableSpan< float2 > dst)
static void simplify_stroke(bke::greasepencil::Drawing &drawing, const float epsilon, const int active_curve)
static void process_stroke_weights(const Scene &scene, const Object &object, bke::greasepencil::Drawing &drawing, const int active_curve)
static void smooth_stroke(bke::greasepencil::Drawing &drawing, const float influence, const int iterations, const int active_curve)
static Array< float2 > sample_curve_2d(Span< float2 > positions, const int64_t resolution)
static void outline_stroke(bke::greasepencil::Drawing &drawing, const int active_curve, const float4x4 &viewmat, const ed::greasepencil::DrawingPlacement &placement, const float outline_radius, const int material_index, const bool on_back)
std::unique_ptr< GreasePencilStrokeOperation > new_paint_operation(bool temp_draw=false)
void smooth_curve_attribute(const IndexMask &curves_to_smooth, const OffsetIndices< int > points_by_curve, const VArray< bool > &point_selection, const VArray< bool > &cyclic, int iterations, float influence, bool smooth_ends, bool keep_shape, GMutableSpan attribute_data)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
void curve_simplify(const Span< float3 > positions, const bool cyclic, const float epsilon, const GSpan attribute_data, MutableSpan< bool > points_to_delete)
void gaussian_blur_1D(const GSpan src, int iterations, const VArray< float > &influence_by_point, const bool smooth_ends, const bool keep_shape, const bool is_cyclic, GMutableSpan dst)
void accumulate_lengths(const Span< T > values, const bool cyclic, MutableSpan< float > lengths)
void sample_at_lengths(Span< float > accumulated_segment_lengths, Span< float > sample_lengths, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factors)
void interpolate(const Span< T > src, const Span< int > indices, const Span< float > factors, MutableSpan< T > dst)
T cos(const AngleRadianBase< T > &a)
T safe_rcp(const T &a)
T floor(const T &a)
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
CartesianBasis invert(const CartesianBasis &basis)
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
T abs(const T &a)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
float perlin(float position)
Definition noise.cc:562
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, 4, 4 > float4x4
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
VecBase< float, 3 > float3
float distance(float a, float b)
MatBase< float, 4, 4 > float4x4
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
signed char int8_t
Definition stdint.h:75
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
struct CurveMapping * curve_jitter
struct CurveMapping * curve_rand_pressure
struct CurveMapping * curve_rand_strength
struct CurveMapping * curve_rand_saturation
struct CurveMapping * curve_rand_hue
struct CurveMapping * curve_rand_uv
struct Material * material_alt
struct CurveMapping * curve_rand_value
float unprojected_radius
struct BrushGpencilSettings * gpencil_settings
GreasePencilRuntimeHandle * runtime
struct MaterialGPencilStyle * gp_style
struct bPose * pose
float viewmat[4][4]
struct ToolSettings * toolsettings
struct RenderData r
struct GP_Sculpt_Settings gp_sculpt
float chan_mat[4][4]
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
float randomize_radius(PaintOperation &self, const float distance, const float radius, const float pressure)
void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
void process_extension_sample(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
float randomize_rotation(PaintOperation &self, const float pressure)
void active_jitter(PaintOperation &self, const int new_points_num, const float brush_radius_px, const float pressure, const IndexRange active_window, MutableSpan< float3 > curve_positions)
float randomize_opacity(PaintOperation &self, const float distance, const float opacity, const float pressure)
void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
ColorGeometry4f randomize_color(PaintOperation &self, const float distance, const ColorGeometry4f color, const float pressure)
void process_start_sample(PaintOperation &self, const bContext &C, const InputSample &start_sample, const int material_index, const bool use_fill)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)