Blender V4.3
paint_stroke.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 by Nicholas Bishop. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10#include <cfloat>
11#include <cmath>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_math_matrix.h"
16#include "BLI_rand.hh"
17#include "BLI_utildefines.h"
18
19#include "DNA_brush_types.h"
20#include "DNA_curve_types.h"
21#include "DNA_object_types.h"
22#include "DNA_scene_types.h"
23
24#include "RNA_access.hh"
25
26#include "BKE_brush.hh"
27#include "BKE_colortools.hh"
28#include "BKE_context.hh"
29#include "BKE_curve.hh"
30#include "BKE_image.hh"
31#include "BKE_paint.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#include "GPU_immediate.hh"
37#include "GPU_state.hh"
38
39#include "ED_screen.hh"
40#include "ED_view3d.hh"
41
42#include "IMB_imbuf_types.hh"
43
44#include "paint_intern.hh"
45#include "sculpt_cloth.hh"
46#include "sculpt_intern.hh"
47
48// #define DEBUG_TIME
49
50#ifdef DEBUG_TIME
51# include "BLI_time_utildefines.h"
52#endif
53
55
58 float pressure;
59};
60
62 std::unique_ptr<PaintModeData> mode_data;
65 std::optional<RandomNumberGenerator> rng;
66
67 /* Cached values */
71
72 /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
73 * to smooth the stroke */
78
82
84 /* space distance covered so far */
86
87 /* Set whether any stroke step has yet occurred
88 * e.g. in sculpt mode, stroke doesn't start until cursor
89 * passes over the mesh */
91 /* Set when enough motion was found for rake rotation */
93 /* event that started stroke, for modal() return */
95 /* check if stroke variables have been initialized */
97 /* check if various brush mapping variables have been initialized */
100 /* cached_pressure stores initial pressure for size pressure influence mainly */
102 /* last pressure will store last pressure value for use in interpolation for space strokes */
105
107
108 float zoom_2d;
110
111 /* Tilt, as read from the event. */
112 float x_tilt;
113 float y_tilt;
114
115 /* line constraint */
118
124
125 bool original; /* Ray-cast original mesh at start of stroke. */
126};
127
128/*** Cursors ***/
129static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata)
130{
132 Brush *brush = BKE_paint_brush(paint);
133 PaintStroke *stroke = static_cast<PaintStroke *>(customdata);
134
135 if (stroke && brush) {
136 GPU_line_smooth(true);
138
139 ARegion *region = stroke->vc.region;
140
144
146 immVertex2f(pos, x, y);
148 stroke->last_mouse_position[0] + region->winrct.xmin,
149 stroke->last_mouse_position[1] + region->winrct.ymin);
150
151 immEnd();
152
154
156 GPU_line_smooth(false);
157 }
158}
159
160static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata)
161{
163 PaintStroke *stroke = static_cast<PaintStroke *>(customdata);
164
165 GPU_line_smooth(true);
166
167 uint shdr_pos = GPU_vertformat_attr_add(
169
171
172 float viewport_size[4];
173 GPU_viewport_size_get_f(viewport_size);
174 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
175
176 immUniform1i("colors_len", 2); /* "advanced" mode */
177 const float alpha = float(paint->paint_cursor_col[3]) / 255.0f;
178 immUniform4f("color", 0.0f, 0.0f, 0.0f, alpha);
179 immUniform4f("color2", 1.0f, 1.0f, 1.0f, alpha);
180 immUniform1f("dash_width", 6.0f);
181 immUniform1f("udash_factor", 0.5f);
182
184
185 ARegion *region = stroke->vc.region;
186
187 if (stroke->constrain_line) {
188 immVertex2f(shdr_pos,
189 stroke->last_mouse_position[0] + region->winrct.xmin,
190 stroke->last_mouse_position[1] + region->winrct.ymin);
191
192 immVertex2f(shdr_pos,
193 stroke->constrained_pos[0] + region->winrct.xmin,
194 stroke->constrained_pos[1] + region->winrct.ymin);
195 }
196 else {
197 immVertex2f(shdr_pos,
198 stroke->last_mouse_position[0] + region->winrct.xmin,
199 stroke->last_mouse_position[1] + region->winrct.ymin);
200
201 immVertex2f(shdr_pos, x, y);
202 }
203
204 immEnd();
205
207
208 GPU_line_smooth(false);
209}
210
211static bool image_paint_brush_type_require_location(const Brush &brush, const PaintMode mode)
212{
213 switch (mode) {
215 if (ELEM(brush.sculpt_brush_type,
223 {
224 return false;
225 }
226 else if (cloth::is_cloth_deform_brush(brush)) {
227 return false;
228 }
229 else {
230 return true;
231 }
232 default:
233 break;
234 }
235
236 return true;
237}
238
239static bool paint_stroke_use_scene_spacing(const Brush &brush, const PaintMode mode)
240{
241 switch (mode) {
243 return brush.flag & BRUSH_SCENE_SPACING;
244 default:
245 break;
246 }
247 return false;
248}
249
250static bool image_paint_brush_type_raycast_original(const Brush &brush, PaintMode /*mode*/)
251{
252 return brush.flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT);
253}
254
256 const PaintMode mode)
257{
258 if (brush.flag & BRUSH_ANCHORED) {
259 return false;
260 }
261
262 switch (mode) {
264 if (ELEM(brush.sculpt_brush_type,
273 {
274 return false;
275 }
276 else {
277 return true;
278 }
279 default:
280 break;
281 }
282
283 return true;
284}
285
287 const Brush &brush,
288 PaintMode mode,
289 PaintStroke *stroke,
290 const float mouse_init[2],
291 float mouse[2],
292 float pressure,
293 float r_location[3],
294 bool *r_location_is_set)
295{
296 Scene *scene = CTX_data_scene(C);
297 UnifiedPaintSettings &ups = *stroke->ups;
298 bool location_sampled = false;
299 bool location_success = false;
300 /* Use to perform all operations except applying the stroke,
301 * needed for operations that require cursor motion (rake). */
302 bool is_dry_run = false;
303 bool do_random = false;
304 bool do_random_mask = false;
305 *r_location_is_set = false;
306 /* XXX: Use pressure value from first brush step for brushes which don't
307 * support strokes (grab, thumb). They depends on initial state and
308 * brush coord/pressure/etc.
309 * It's more an events design issue, which doesn't split coordinate/pressure/angle
310 * changing events. We should avoid this after events system re-design */
311 if (!stroke->brush_init) {
312 copy_v2_v2(stroke->initial_mouse, mouse);
313 copy_v2_v2(ups.last_rake, mouse);
314 copy_v2_v2(ups.tex_mouse, mouse);
315 copy_v2_v2(ups.mask_tex_mouse, mouse);
316 stroke->cached_size_pressure = pressure;
317
318 stroke->brush_init = true;
319 }
320
321 if (paint_supports_dynamic_size(brush, mode)) {
322 copy_v2_v2(ups.tex_mouse, mouse);
323 copy_v2_v2(ups.mask_tex_mouse, mouse);
324 stroke->cached_size_pressure = pressure;
325 }
326
327 /* Truly temporary data that isn't stored in properties */
328
329 ups.stroke_active = true;
331
332 ups.pixel_radius = BKE_brush_size_get(scene, &brush);
333 ups.initial_pixel_radius = BKE_brush_size_get(scene, &brush);
334
335 if (BKE_brush_use_size_pressure(&brush) && paint_supports_dynamic_size(brush, mode)) {
336 ups.pixel_radius *= stroke->cached_size_pressure;
337 }
338
339 if (paint_supports_dynamic_tex_coords(brush, mode)) {
340
341 if (ELEM(brush.mtex.brush_map_mode,
345 {
346 do_random = true;
347 }
348
351 }
352 else {
353 copy_v2_v2(ups.tex_mouse, mouse);
354 }
355
356 /* take care of mask texture, if any */
357 if (brush.mask_mtex.tex) {
358
359 if (ELEM(brush.mask_mtex.brush_map_mode,
363 {
364 do_random_mask = true;
365 }
366
369 }
370 else {
371 copy_v2_v2(ups.mask_tex_mouse, mouse);
372 }
373 }
374 }
375
376 if (brush.flag & BRUSH_ANCHORED) {
377 bool hit = false;
378 float halfway[2];
379
380 const float dx = mouse[0] - stroke->initial_mouse[0];
381 const float dy = mouse[1] - stroke->initial_mouse[1];
382
383 ups.anchored_size = ups.pixel_radius = sqrtf(dx * dx + dy * dy);
384
385 ups.brush_rotation = ups.brush_rotation_sec = atan2f(dy, dx) + float(0.5f * M_PI);
386
387 if (brush.flag & BRUSH_EDGE_TO_EDGE) {
388 halfway[0] = dx * 0.5f + stroke->initial_mouse[0];
389 halfway[1] = dy * 0.5f + stroke->initial_mouse[1];
390
391 if (stroke->get_location) {
392 if (stroke->get_location(C, r_location, halfway, stroke->original)) {
393 hit = true;
394 location_sampled = true;
395 location_success = true;
396 *r_location_is_set = true;
397 }
398 else if (!image_paint_brush_type_require_location(brush, mode)) {
399 hit = true;
400 }
401 }
402 else {
403 hit = true;
404 }
405 }
406 if (hit) {
408 copy_v2_v2(ups.tex_mouse, halfway);
409 copy_v2_v2(ups.mask_tex_mouse, halfway);
410 copy_v2_v2(mouse, halfway);
411 ups.anchored_size /= 2.0f;
412 ups.pixel_radius /= 2.0f;
413 stroke->stroke_distance = ups.pixel_radius;
414 }
415 else {
417 copy_v2_v2(mouse, stroke->initial_mouse);
418 stroke->stroke_distance = ups.pixel_radius;
419 }
420 ups.pixel_radius /= stroke->zoom_2d;
421 ups.draw_anchored = true;
422 }
423 else {
424 /* here we are using the initial mouse coordinate because we do not want the rake
425 * result to depend on jittering */
426 if (!stroke->brush_init) {
427 copy_v2_v2(ups.last_rake, mouse_init);
428 }
429 /* curve strokes do their own rake calculation */
430 else if (!(brush.flag & BRUSH_CURVE)) {
431 if (!paint_calculate_rake_rotation(ups, brush, mouse_init, mode, stroke->rake_started)) {
432 /* Not enough motion to define an angle. */
433 if (!stroke->rake_started) {
434 is_dry_run = true;
435 }
436 }
437 else {
438 stroke->rake_started = true;
439 }
440 }
441 }
442
443 if ((do_random || do_random_mask) && !stroke->rng) {
444 /* Lazy initialization. */
446 }
447
448 if (do_random) {
450 ups.brush_rotation += -brush.mtex.random_angle / 2.0f +
451 brush.mtex.random_angle * stroke->rng->get_float();
452 }
453 }
454
455 if (do_random_mask) {
457 ups.brush_rotation_sec += -brush.mask_mtex.random_angle / 2.0f +
458 brush.mask_mtex.random_angle * stroke->rng->get_float();
459 }
460 }
461
462 if (!location_sampled) {
463 if (stroke->get_location) {
464 if (stroke->get_location(C, r_location, mouse, stroke->original)) {
465 location_success = true;
466 *r_location_is_set = true;
467 }
468 else if (!image_paint_brush_type_require_location(brush, mode)) {
469 location_success = true;
470 }
471 }
472 else {
473 zero_v3(r_location);
474 location_success = true;
475 /* don't set 'r_location_is_set', since we don't want to use the value. */
476 }
477 }
478
479 return location_success && (is_dry_run == false);
480}
481
482static bool paint_stroke_use_dash(const Brush &brush)
483{
484 /* Only these stroke modes support dash lines */
485 return brush.flag & BRUSH_SPACE || brush.flag & BRUSH_LINE || brush.flag & BRUSH_CURVE;
486}
487
488static bool paint_stroke_use_jitter(PaintMode mode, const Brush &brush, bool invert)
489{
490 bool use_jitter = (brush.flag & BRUSH_ABSOLUTE_JITTER) ? (brush.jitter_absolute != 0) :
491 (brush.jitter != 0);
492
493 /* jitter-ed brush gives weird and unpredictable result for this
494 * kinds of stroke, so manually disable jitter usage (sergey) */
495 use_jitter &= (brush.flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0;
496 use_jitter &= (!ELEM(mode, PaintMode::Texture2D, PaintMode::Texture3D) ||
498
499 return use_jitter;
500}
501
503 const PaintStroke &stroke,
504 const PaintMode mode,
505 const Brush &brush,
506 const float pressure,
507 const float mval[2],
508 float r_mouse_out[2])
509{
510 if (paint_stroke_use_jitter(mode, brush, stroke.stroke_mode == BRUSH_STROKE_INVERT)) {
511 float delta[2];
512 float factor = stroke.zoom_2d;
513
514 if (brush.flag & BRUSH_JITTER_PRESSURE) {
515 factor *= pressure;
516 }
517
518 BKE_brush_jitter_pos(scene, brush, mval, r_mouse_out);
519
520 /* XXX: meh, this is round about because
521 * BKE_brush_jitter_pos isn't written in the best way to
522 * be reused here */
523 if (factor != 1.0f) {
524 sub_v2_v2v2(delta, r_mouse_out, mval);
525 mul_v2_fl(delta, factor);
526 add_v2_v2v2(r_mouse_out, mval, delta);
527 }
528 }
529 else {
530 copy_v2_v2(r_mouse_out, mval);
531 }
532}
533
534/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
536 bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure)
537{
538 Scene &scene = *CTX_data_scene(C);
541 const Brush &brush = *BKE_paint_brush_for_read(&paint);
542 UnifiedPaintSettings *ups = stroke->ups;
543 float mouse_out[2];
544 PointerRNA itemptr;
545 float location[3];
546
547/* the following code is adapted from texture paint. It may not be needed but leaving here
548 * just in case for reference (code in texpaint removed as part of refactoring).
549 * It's strange that only texpaint had these guards. */
550#if 0
551 /* special exception here for too high pressure values on first touch in
552 * windows for some tablets, then we just skip first touch. */
553 if (tablet && (pressure >= 0.99f) &&
554 ((pop->s.brush.flag & BRUSH_SPACING_PRESSURE) ||
555 BKE_brush_use_alpha_pressure(pop->s.brush) || BKE_brush_use_size_pressure(pop->s.brush)))
556 {
557 return;
558 }
559
560 /* This can be removed once fixed properly in
561 * BKE_brush_painter_paint(
562 * BrushPainter *painter, BrushFunc func,
563 * float *pos, double time, float pressure, void *user);
564 * at zero pressure we should do nothing 1/2^12 is 0.0002
565 * which is the sensitivity of the most sensitive pen tablet available */
566 if (tablet && (pressure < 0.0002f) &&
567 ((pop->s.brush.flag & BRUSH_SPACING_PRESSURE) ||
568 BKE_brush_use_alpha_pressure(pop->s.brush) || BKE_brush_use_size_pressure(pop->s.brush)))
569 {
570 return;
571 }
572#endif
573
574 /* copy last position -before- jittering, or space fill code
575 * will create too many dabs */
576 copy_v2_v2(stroke->last_mouse_position, mval);
577 stroke->last_pressure = pressure;
578
579 if (paint_stroke_use_scene_spacing(brush, mode)) {
580 float world_space_position[3];
581
583 C, world_space_position, stroke->last_mouse_position, stroke->original))
584 {
585 copy_v3_v3(stroke->last_world_space_position, world_space_position);
586 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
587 }
588 else {
590 }
591 }
592
593 /* Get jitter position (same as mval if no jitter is used). */
594 paint_stroke_jitter_pos(scene, *stroke, mode, brush, pressure, mval, mouse_out);
595
596 bool is_location_is_set;
598 C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set);
599 if (is_location_is_set) {
600 copy_v3_v3(ups->last_location, location);
601 }
602 if (!ups->last_hit) {
603 return;
604 }
605
606 /* Dash */
607 bool add_step = true;
608 if (paint_stroke_use_dash(brush)) {
609 int dash_samples = stroke->tot_samples % brush.dash_samples;
610 float dash = float(dash_samples) / float(brush.dash_samples);
611 if (dash > brush.dash_ratio) {
612 add_step = false;
613 }
614 }
615
616 /* Add to stroke */
617 if (add_step) {
618 RNA_collection_add(op->ptr, "stroke", &itemptr);
619 RNA_float_set(&itemptr, "size", ups->pixel_radius);
620 RNA_float_set_array(&itemptr, "location", location);
621 /* Mouse coordinates modified by the stroke type options. */
622 RNA_float_set_array(&itemptr, "mouse", mouse_out);
623 /* Original mouse coordinates. */
624 RNA_float_set_array(&itemptr, "mouse_event", mval);
625 RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip);
626 RNA_float_set(&itemptr, "pressure", pressure);
627 RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt);
628 RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt);
629
630 stroke->update_step(C, op, stroke, &itemptr);
631
632 /* don't record this for now, it takes up a lot of memory when doing long
633 * strokes with small brush size, and operators have register disabled */
634 RNA_collection_clear(op->ptr, "stroke");
635 }
636
637 stroke->tot_samples++;
638}
639
640/* Returns zero if no sculpt changes should be made, non-zero otherwise */
642 const PaintSample *sample,
643 PaintMode mode,
644 float r_mouse[2],
645 float *r_pressure)
646{
647 if (paint_supports_smooth_stroke(stroke, *stroke->brush, mode)) {
648 float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d;
649 float u = stroke->brush->smooth_stroke_factor;
650
651 /* If the mouse is moving within the radius of the last move,
652 * don't update the mouse position. This allows sharp turns. */
653 if (len_squared_v2v2(stroke->last_mouse_position, sample->mouse) < square_f(radius)) {
654 return false;
655 }
656
657 interp_v2_v2v2(r_mouse, sample->mouse, stroke->last_mouse_position, u);
658 *r_pressure = interpf(sample->pressure, stroke->last_pressure, u);
659 }
660 else {
661 r_mouse[0] = sample->mouse[0];
662 r_mouse[1] = sample->mouse[1];
663 *r_pressure = sample->pressure;
664 }
665
666 return true;
667}
668
670 const Scene *scene,
671 PaintStroke *stroke,
672 float size_pressure,
673 float spacing_pressure)
674{
677 const Brush &brush = *BKE_paint_brush_for_read(paint);
678 float size_clamp = 0.0f;
679 float size = BKE_brush_size_get(scene, stroke->brush) * size_pressure;
680 if (paint_stroke_use_scene_spacing(brush, mode)) {
681 if (!BKE_brush_use_locked_size(scene, &brush)) {
682 float last_object_space_position[3];
683 mul_v3_m4v3(last_object_space_position,
684 stroke->vc.obact->world_to_object().ptr(),
686 size_clamp = paint_calc_object_space_radius(stroke->vc, last_object_space_position, size);
687 }
688 else {
689 size_clamp = BKE_brush_unprojected_radius_get(scene, &brush) * size_pressure;
690 }
691 }
692 else {
693 /* brushes can have a minimum size of 1.0 but with pressure it can be smaller than a pixel
694 * causing very high step sizes, hanging blender #32381. */
695 size_clamp = max_ff(1.0f, size);
696 }
697
698 float spacing = stroke->brush->spacing;
699
700 /* apply spacing pressure */
701 if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) {
702 spacing = spacing * (1.5f - spacing_pressure);
703 }
704
705 if (cloth::is_cloth_deform_brush(brush)) {
706 /* The spacing in tools that use the cloth solver should not be affected by the brush radius to
707 * avoid affecting the simulation update rate when changing the radius of the brush.
708 * With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2
709 * pixels movement of the cursor. */
710 size_clamp = 100.0f;
711 }
712
713 /* stroke system is used for 2d paint too, so we need to account for
714 * the fact that brush can be scaled there. */
715 spacing *= stroke->zoom_2d;
716
717 if (paint_stroke_use_scene_spacing(brush, mode)) {
718 /* Low pressure on size (with tablets) can cause infinite recursion in paint_space_stroke(),
719 * see #129853. */
720 return max_ff(FLT_EPSILON, size_clamp * spacing / 50.0f);
721 }
722 return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
723}
724
725static float paint_stroke_overlapped_curve(const Brush &br, float x, float spacing)
726{
727 /* Avoid division by small numbers, can happen
728 * on some pen setups. See #105341.
729 */
730
731 spacing = max_ff(spacing, 0.1f);
732
733 const int n = 100 / spacing;
734 const float h = spacing / 50.0f;
735 const float x0 = x - 1;
736
737 float sum = 0;
738 for (int i = 0; i < n; i++) {
739 float xx;
740
741 xx = fabsf(x0 + i * h);
742
743 if (xx < 1.0f) {
744 sum += BKE_brush_curve_strength(&br, xx, 1);
745 }
746 }
747
748 return sum;
749}
750
751static float paint_stroke_integrate_overlap(const Brush &br, float factor)
752{
753 float spacing = br.spacing * factor;
754
755 if (!(br.flag & BRUSH_SPACE_ATTEN && (br.spacing < 100))) {
756 return 1.0;
757 }
758
759 int m = 10;
760 float g = 1.0f / m;
761 float max = 0;
762 for (int i = 0; i < m; i++) {
763 float overlap = fabs(paint_stroke_overlapped_curve(br, i * g, spacing));
764
765 if (overlap > max) {
766 max = overlap;
767 }
768 }
769
770 if (max == 0.0f) {
771 return 1.0f;
772 }
773 return 1.0f / max;
774}
775
777 const Scene *scene,
778 PaintStroke *stroke,
779 float pressure,
780 float dpressure,
781 float length)
782{
783 if (BKE_brush_use_size_pressure(stroke->brush)) {
784 /* use pressure to modify size. set spacing so that at 100%, the circles
785 * are aligned nicely with no overlap. for this the spacing needs to be
786 * the average of the previous and next size. */
787 float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
788 float q = s * dpressure / (2.0f * length);
789 float pressure_fac = (1.0f + q) / (1.0f - q);
790
791 float last_size_pressure = stroke->last_pressure;
792 float new_size_pressure = stroke->last_pressure * pressure_fac;
793
794 /* average spacing */
795 float last_spacing = paint_space_stroke_spacing(
796 C, scene, stroke, last_size_pressure, pressure);
797 float new_spacing = paint_space_stroke_spacing(C, scene, stroke, new_size_pressure, pressure);
798
799 return 0.5f * (last_spacing + new_spacing);
800 }
801
802 /* no size pressure */
803 return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
804}
805
806/* For brushes with stroke spacing enabled, moves mouse in steps
807 * towards the final mouse location. */
809 wmOperator *op,
810 PaintStroke *stroke,
811 const float final_mouse[2],
812 float final_pressure)
813{
814 const Scene *scene = CTX_data_scene(C);
815 ARegion *region = CTX_wm_region(C);
816 UnifiedPaintSettings *ups = stroke->ups;
817 const Paint &paint = *BKE_paint_get_active_from_context(C);
819 const Brush &brush = *BKE_paint_brush_for_read(&paint);
820 int count = 0;
821
822 const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
823 float d_world_space_position[3] = {0.0f};
824
825 float no_pressure_spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
826 float pressure = stroke->last_pressure;
827 float dpressure = final_pressure - stroke->last_pressure;
828
829 float dmouse[2];
830 sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position);
831 float length = normalize_v2(dmouse);
832
833 if (use_scene_spacing) {
834 float world_space_position[3];
835 bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original);
836 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), world_space_position);
837 if (hit && stroke->stroke_over_mesh) {
838 sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position);
839 length = len_v3(d_world_space_position);
840 stroke->stroke_over_mesh = true;
841 }
842 else {
843 length = 0.0f;
844 zero_v3(d_world_space_position);
845 stroke->stroke_over_mesh = hit;
846 if (stroke->stroke_over_mesh) {
847 copy_v3_v3(stroke->last_world_space_position, world_space_position);
848 }
849 }
850 }
851
852 while (length > 0.0f) {
854 C, scene, stroke, pressure, dpressure, length);
855 BLI_assert(spacing >= 0.0f);
856
857 float mouse[2];
858
859 if (length >= spacing) {
860 if (use_scene_spacing) {
861 float final_world_space_position[3];
862 normalize_v3(d_world_space_position);
863 mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing);
864 add_v3_v3v3(final_world_space_position,
866 final_world_space_position);
867 ED_view3d_project_v2(region, final_world_space_position, mouse);
868
869 mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing);
870 }
871 else {
872 mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
873 mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing;
874 }
875 pressure = stroke->last_pressure + (spacing / length) * dpressure;
876
878 spacing / no_pressure_spacing);
879
880 stroke->stroke_distance += spacing / stroke->zoom_2d;
881 paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
882
883 length -= spacing;
884 pressure = stroke->last_pressure;
885 dpressure = final_pressure - stroke->last_pressure;
886
887 count++;
888 }
889 else {
890 break;
891 }
892 }
893
894 return count;
895}
896
897/**** Public API ****/
898
900 wmOperator *op,
901 StrokeGetLocation get_location,
902 StrokeTestStart test_start,
903 StrokeUpdateStep update_step,
904 StrokeRedraw redraw,
905 StrokeDone done,
906 int event_type)
907{
909 PaintStroke *stroke = MEM_new<PaintStroke>(__func__);
910 ToolSettings *toolsettings = CTX_data_tool_settings(C);
911 UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
913 Brush *br = stroke->brush = BKE_paint_brush(paint);
915 float zoomx, zoomy;
916
918
919 stroke->get_location = get_location;
920 stroke->test_start = test_start;
921 stroke->update_step = update_step;
922 stroke->redraw = redraw;
923 stroke->done = done;
924 stroke->event_type = event_type; /* for modal, return event */
925 stroke->ups = ups;
926 stroke->stroke_mode = RNA_enum_get(op->ptr, "mode");
927
930
931 get_imapaint_zoom(C, &zoomx, &zoomy);
932 stroke->zoom_2d = max_ff(zoomx, zoomy);
933
934 /* Check here if color sampling the main brush should do color conversion. This is done here
935 * to avoid locking up to get the image buffer during sampling. */
936 ups->do_linear_conversion = false;
937 ups->colorspace = nullptr;
938
939 if (br->mtex.tex && br->mtex.tex->type == TEX_IMAGE && br->mtex.tex->ima) {
941 br->mtex.tex->ima, &br->mtex.tex->iuser, nullptr);
942 if (tex_ibuf && tex_ibuf->float_buffer.data == nullptr) {
943 ups->do_linear_conversion = true;
944 ups->colorspace = tex_ibuf->byte_buffer.colorspace;
945 }
946 BKE_image_pool_release_ibuf(br->mtex.tex->ima, tex_ibuf, nullptr);
947 }
948
949 if (stroke->stroke_mode == BRUSH_STROKE_INVERT) {
950 if (br->flag & BRUSH_CURVE) {
952 }
953 }
954 /* initialize here */
955 ups->overlap_factor = 1.0;
956 ups->stroke_active = true;
957
958 if (rv3d) {
959 rv3d->rflag |= RV3D_PAINTING;
960 }
961
962 /* Preserve location from last stroke while applying and resetting
963 * ups->average_stroke_counter to 1.
964 */
965 if (ups->average_stroke_counter) {
966 mul_v3_fl(ups->average_stroke_accum, 1.0f / float(ups->average_stroke_counter));
967 ups->average_stroke_counter = 1;
968 }
969
970 /* initialize here to avoid initialization conflict with threaded strokes */
972 if (paint->flags & PAINT_USE_CAVITY_MASK) {
974 }
975
977
979
980 return stroke;
981}
982
984{
986 if (rv3d) {
987 rv3d->rflag &= ~RV3D_PAINTING;
988 }
989
991
992 if (stroke == nullptr) {
993 return;
994 }
995
996 UnifiedPaintSettings *ups = stroke->ups;
997 ups->draw_anchored = false;
998 ups->stroke_active = false;
999
1000 if (stroke->timer) {
1002 }
1003
1004 if (stroke->stroke_cursor) {
1005 WM_paint_cursor_end(static_cast<wmPaintCursor *>(stroke->stroke_cursor));
1006 }
1007
1008 MEM_delete(stroke);
1009}
1010
1011static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke)
1012{
1013 UnifiedPaintSettings *ups = stroke->ups;
1014
1015 /* reset rotation here to avoid doing so in cursor display */
1016 if (!(stroke->brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
1017 ups->brush_rotation = 0.0f;
1018 }
1019
1020 if (!(stroke->brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
1021 ups->brush_rotation_sec = 0.0f;
1022 }
1023
1024 if (stroke->stroke_started) {
1025 if (stroke->redraw) {
1026 stroke->redraw(C, stroke, true);
1027 }
1028
1029 if (stroke->done) {
1030 stroke->done(C, stroke);
1031 }
1032 }
1033
1034 paint_stroke_free(C, op, stroke);
1035}
1036
1041
1043{
1044 if ((br.flag & BRUSH_SPACE) == 0) {
1045 return false;
1046 }
1047
1049 /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do
1050 * not support dynamic size, stroke spacing needs to be enabled so it is possible to control
1051 * whether the simulation runs constantly or only when the brush moves when using the cloth
1052 * grab brushes. */
1053 return true;
1054 }
1055
1056 if (mode == PaintMode::SculptCurves &&
1058 {
1059 return false;
1060 }
1061
1063 /* No spacing needed for now. */
1064 return false;
1065 }
1066
1067 return paint_supports_dynamic_size(br, mode);
1068}
1069
1086
1088{
1089 if (br.flag & BRUSH_ANCHORED) {
1090 return false;
1091 }
1092
1093 switch (mode) {
1094 case PaintMode::Sculpt:
1095 if (sculpt_is_grab_tool(br)) {
1096 return false;
1097 }
1098 break;
1099
1100 case PaintMode::Texture2D: /* fall through */
1103 return false;
1104 }
1105 break;
1106
1107 default:
1108 break;
1109 }
1110 return true;
1111}
1112
1114{
1115 /* The grease pencil draw tool needs to enable this when the `stroke_mode` is set to
1116 * `BRUSH_STROKE_SMOOTH`. */
1117 if (mode == PaintMode::GPencil &&
1120 {
1121 return true;
1122 }
1123 if (!(brush.flag & BRUSH_SMOOTH_STROKE) ||
1125 {
1126 return false;
1127 }
1128
1129 switch (mode) {
1130 case PaintMode::Sculpt:
1131 if (sculpt_is_grab_tool(brush)) {
1132 return false;
1133 }
1134 break;
1135 default:
1136 break;
1137 }
1138 return true;
1139}
1140
1142{
1143 /* omit: PAINT_WEIGHT, PAINT_SCULPT_UV, PAINT_INVALID */
1144 return ELEM(
1146}
1147
1149{
1150 if (br.flag & BRUSH_ANCHORED) {
1151 return false;
1152 }
1153
1154 switch (mode) {
1155 case PaintMode::Sculpt:
1156 if (sculpt_is_grab_tool(br)) {
1157 return false;
1158 }
1159 break;
1160 default:
1161 break;
1162 }
1163 return true;
1164}
1165
1166#define PAINT_STROKE_MODAL_CANCEL 1
1167
1169{
1170 static EnumPropertyItem modal_items[] = {
1171 {PAINT_STROKE_MODAL_CANCEL, "CANCEL", 0, "Cancel", "Cancel and undo a stroke in progress"},
1172 {0}};
1173
1174 static const char *name = "Paint Stroke Modal";
1175
1176 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name);
1177
1178 /* This function is called for each space-type, only needs to add map once. */
1179 if (!keymap) {
1180 keymap = WM_modalkeymap_ensure(keyconf, name, modal_items);
1181 }
1182
1183 return keymap;
1184}
1185
1187 PaintStroke *stroke, int input_samples, float x, float y, float pressure)
1188{
1189 PaintSample *sample = &stroke->samples[stroke->cur_sample];
1190 int max_samples = std::clamp(input_samples, 1, PAINT_MAX_INPUT_SAMPLES);
1191
1192 sample->mouse[0] = x;
1193 sample->mouse[1] = y;
1194 sample->pressure = pressure;
1195
1196 stroke->cur_sample++;
1197 if (stroke->cur_sample >= max_samples) {
1198 stroke->cur_sample = 0;
1199 }
1200 if (stroke->num_samples < max_samples) {
1201 stroke->num_samples++;
1202 }
1203}
1204
1205static void paint_stroke_sample_average(const PaintStroke *stroke, PaintSample *average)
1206{
1207 memset(average, 0, sizeof(*average));
1208
1209 BLI_assert(stroke->num_samples > 0);
1210
1211 for (int i = 0; i < stroke->num_samples; i++) {
1212 add_v2_v2(average->mouse, stroke->samples[i].mouse);
1213 average->pressure += stroke->samples[i].pressure;
1214 }
1215
1216 mul_v2_fl(average->mouse, 1.0f / stroke->num_samples);
1217 average->pressure /= stroke->num_samples;
1218
1219 // printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);
1220}
1221
1227 wmOperator *op,
1228 PaintStroke *stroke,
1229 float spacing,
1230 float *length_residue,
1231 const float old_pos[2],
1232 const float new_pos[2])
1233{
1234 UnifiedPaintSettings *ups = stroke->ups;
1236 Brush &brush = *BKE_paint_brush(paint);
1238 ARegion *region = CTX_wm_region(C);
1239
1240 const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
1241
1242 float mouse[2], dmouse[2];
1243 float length;
1244 float d_world_space_position[3] = {0.0f};
1245 float world_space_position_old[3], world_space_position_new[3];
1246
1247 copy_v2_v2(stroke->last_mouse_position, old_pos);
1248
1249 if (use_scene_spacing) {
1250 bool hit_old = SCULPT_stroke_get_location(
1251 C, world_space_position_old, old_pos, stroke->original);
1252 bool hit_new = SCULPT_stroke_get_location(
1253 C, world_space_position_new, new_pos, stroke->original);
1254 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), world_space_position_old);
1255 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), world_space_position_new);
1256 if (hit_old && hit_new && stroke->stroke_over_mesh) {
1257 sub_v3_v3v3(d_world_space_position, world_space_position_new, world_space_position_old);
1258 length = len_v3(d_world_space_position);
1259 stroke->stroke_over_mesh = true;
1260 }
1261 else {
1262 length = 0.0f;
1263 zero_v3(d_world_space_position);
1264 stroke->stroke_over_mesh = hit_new;
1265 if (stroke->stroke_over_mesh) {
1266 copy_v3_v3(stroke->last_world_space_position, world_space_position_old);
1267 }
1268 }
1269 }
1270 else {
1271 sub_v2_v2v2(dmouse, new_pos, old_pos);
1272 length = normalize_v2(dmouse);
1273 }
1274
1275 BLI_assert(length >= 0.0f);
1276
1277 if (length == 0.0f) {
1278 return;
1279 }
1280
1281 while (length > 0.0f) {
1282 float spacing_final = spacing - *length_residue;
1283 length += *length_residue;
1284 *length_residue = 0.0;
1285
1286 if (length >= spacing) {
1287 if (use_scene_spacing) {
1288 float final_world_space_position[3];
1289 normalize_v3(d_world_space_position);
1290 mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final);
1292 final_world_space_position, world_space_position_old, final_world_space_position);
1293 ED_view3d_project_v2(region, final_world_space_position, mouse);
1294 }
1295 else {
1296 mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final;
1297 mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final;
1298 }
1299
1301
1302 stroke->stroke_distance += spacing / stroke->zoom_2d;
1303 paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0);
1304
1305 length -= spacing;
1306 spacing_final = spacing;
1307 }
1308 else {
1309 break;
1310 }
1311 }
1312
1313 *length_residue = length;
1314}
1315
1317 wmOperator *op,
1318 PaintStroke *stroke,
1319 const float mouse[2])
1320{
1321 Brush *br = stroke->brush;
1322 if (stroke->stroke_started && (br->flag & BRUSH_LINE)) {
1324
1325 paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0);
1326 paint_space_stroke(C, op, stroke, mouse, 1.0);
1327 }
1328}
1329
1331{
1332 const Brush &br = *stroke->brush;
1333 if (!(br.flag & BRUSH_CURVE)) {
1334 return false;
1335 }
1336
1338 const Scene *scene = CTX_data_scene(C);
1339 const float spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
1340 const PaintCurve *pc = br.paint_curve;
1341 const PaintCurvePoint *pcp;
1342 float length_residue = 0.0f;
1343 int i;
1344
1345 if (!pc) {
1346 return true;
1347 }
1348
1349#ifdef DEBUG_TIME
1350 TIMEIT_START_AVERAGED(whole_stroke);
1351#endif
1352
1353 pcp = pc->points;
1355
1356 for (i = 0; i < pc->tot_points - 1; i++, pcp++) {
1357 int j;
1358 float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
1359 float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
1360 const PaintCurvePoint *pcp_next = pcp + 1;
1361 bool do_rake = false;
1362
1363 for (j = 0; j < 2; j++) {
1365 pcp->bez.vec[2][j],
1366 pcp_next->bez.vec[0][j],
1367 pcp_next->bez.vec[1][j],
1368 data + j,
1370 sizeof(float[2]));
1371 }
1372
1375 {
1376 do_rake = true;
1377 for (j = 0; j < 2; j++) {
1379 pcp->bez.vec[2][j],
1380 pcp_next->bez.vec[0][j],
1381 pcp_next->bez.vec[1][j],
1382 tangents + j,
1384 sizeof(float[2]));
1385 }
1386 }
1387
1388 for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) {
1389 if (do_rake) {
1390 float rotation = atan2f(tangents[2 * j + 1], tangents[2 * j]) + float(0.5f * M_PI);
1391 paint_update_brush_rake_rotation(ups, br, rotation);
1392 }
1393
1394 if (!stroke->stroke_started) {
1395 stroke->last_pressure = 1.0;
1396 copy_v2_v2(stroke->last_mouse_position, data + 2 * j);
1397
1400 C, stroke->last_world_space_position, data + 2 * j, stroke->original);
1401 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
1402 }
1403
1404 stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
1405
1406 if (stroke->stroke_started) {
1407 paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0);
1409 C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
1410 }
1411 }
1412 else {
1414 C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
1415 }
1416 }
1417 }
1418
1419 stroke_done(C, op, stroke);
1420
1421#ifdef DEBUG_TIME
1422 TIMEIT_END_AVERAGED(whole_stroke);
1423#endif
1424
1425 return true;
1426}
1427
1428static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2])
1429{
1430 if (stroke->constrain_line) {
1431 float line[2];
1432 float angle, len, res;
1433
1434 sub_v2_v2v2(line, mouse, stroke->last_mouse_position);
1435 angle = atan2f(line[1], line[0]);
1436 len = len_v2(line);
1437
1438 /* divide angle by PI/4 */
1439 angle = 4.0f * angle / float(M_PI);
1440
1441 /* now take residue */
1442 res = angle - floorf(angle);
1443
1444 /* residue decides how close we are at a certain angle */
1445 if (res <= 0.5f) {
1446 angle = floorf(angle) * float(M_PI_4);
1447 }
1448 else {
1449 angle = (floorf(angle) + 1.0f) * float(M_PI_4);
1450 }
1451
1452 mouse[0] = stroke->constrained_pos[0] = len * cosf(angle) + stroke->last_mouse_position[0];
1453 mouse[1] = stroke->constrained_pos[1] = len * sinf(angle) + stroke->last_mouse_position[1];
1454 }
1455}
1456
1457int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
1458{
1459 Scene *scene = CTX_data_scene(C);
1462 PaintStroke *stroke = *stroke_p;
1463 Brush *br = stroke->brush = BKE_paint_brush(paint);
1464 PaintSample sample_average;
1465 float mouse[2];
1466 bool first_dab = false;
1467 bool first_modal = false;
1468 bool redraw = false;
1469
1470 if (event->type == INBETWEEN_MOUSEMOVE &&
1472 {
1474 }
1475
1476 /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */
1477 const float tablet_pressure = WM_event_tablet_data(event, &stroke->pen_flip, nullptr);
1478 float pressure = ((br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? 1.0f :
1479 tablet_pressure);
1480
1481 /* When processing a timer event the pressure from the event is 0, so use the last valid
1482 * pressure. */
1483 if (event->type == TIMER) {
1484 pressure = stroke->last_tablet_event_pressure;
1485 }
1486 else {
1487 stroke->last_tablet_event_pressure = pressure;
1488 }
1489
1490 int input_samples = BKE_brush_input_samples_get(scene, br);
1491 paint_stroke_add_sample(stroke, input_samples, event->mval[0], event->mval[1], pressure);
1492 paint_stroke_sample_average(stroke, &sample_average);
1493
1494 /* Tilt. */
1495 if (WM_event_is_tablet(event)) {
1496 stroke->x_tilt = event->tablet.x_tilt;
1497 stroke->y_tilt = event->tablet.y_tilt;
1498 }
1499
1500#ifdef WITH_INPUT_NDOF
1501 /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
1502 * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
1503 * since the 2D deltas are zero -- code in this file needs to be updated to use the
1504 * post-NDOF_MOTION MOUSEMOVE */
1505 if (event->type == NDOF_MOTION) {
1506 return OPERATOR_PASS_THROUGH;
1507 }
1508#endif
1509
1510 /* one time initialization */
1511 if (!stroke->stroke_init) {
1512 if (paint_stroke_curve_end(C, op, stroke)) {
1513 *stroke_p = nullptr;
1514 return OPERATOR_FINISHED;
1515 }
1516
1517 stroke->stroke_init = true;
1518 first_modal = true;
1519 }
1520
1521 /* one time stroke initialization */
1522 if (!stroke->stroke_started) {
1523 stroke->last_pressure = sample_average.pressure;
1524 copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
1525 if (paint_stroke_use_scene_spacing(*br, mode)) {
1527 C, stroke->last_world_space_position, sample_average.mouse, stroke->original);
1528 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
1529 }
1530 stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
1531
1532 if (stroke->stroke_started) {
1533 /* StrokeTestStart often updates the currently active brush so we need to re-retrieve it
1534 * here. */
1535 br = BKE_paint_brush(paint);
1536
1537 if (paint_supports_smooth_stroke(stroke, *br, mode)) {
1542 stroke);
1543 }
1544
1545 if (br->flag & BRUSH_AIRBRUSH) {
1546 stroke->timer = WM_event_timer_add(
1547 CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate);
1548 }
1549
1550 if (br->flag & BRUSH_LINE) {
1553 }
1554
1555 first_dab = true;
1556 }
1557 }
1558
1559 /* Cancel */
1560 if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) {
1561 if (op->type->cancel) {
1562 op->type->cancel(C, op);
1563 }
1564 else {
1565 paint_stroke_cancel(C, op, stroke);
1566 }
1567 return OPERATOR_CANCELLED;
1568 }
1569
1570 if (event->type == stroke->event_type && !first_modal) {
1571 if (event->val == KM_RELEASE) {
1572 copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
1573 paint_stroke_line_constrain(stroke, mouse);
1574 paint_stroke_line_end(C, op, stroke, mouse);
1575 stroke_done(C, op, stroke);
1576 *stroke_p = nullptr;
1577 return OPERATOR_FINISHED;
1578 }
1579 }
1580 else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) {
1581 paint_stroke_line_end(C, op, stroke, sample_average.mouse);
1582 stroke_done(C, op, stroke);
1583 *stroke_p = nullptr;
1584 return OPERATOR_FINISHED;
1585 }
1586 else if (br->flag & BRUSH_LINE) {
1587 if (event->modifier & KM_ALT) {
1588 stroke->constrain_line = true;
1589 }
1590 else {
1591 stroke->constrain_line = false;
1592 }
1593
1594 copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
1595 paint_stroke_line_constrain(stroke, mouse);
1596
1597 if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
1600 {
1601 copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
1602 }
1603 paint_calculate_rake_rotation(*stroke->ups, *br, mouse, mode, true);
1604 }
1605 }
1606 else if (first_modal ||
1607 /* regular dabs */
1608 (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
1609 /* airbrush */
1610 ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
1611 event->customdata == stroke->timer))
1612 {
1613 if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) {
1614 if (stroke->stroke_started) {
1615 if (paint_space_stroke_enabled(*br, mode)) {
1616 if (paint_space_stroke(C, op, stroke, mouse, pressure)) {
1617 redraw = true;
1618 }
1619 }
1620 else {
1621 float dmouse[2];
1622 sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position);
1623 stroke->stroke_distance += len_v2(dmouse);
1624 paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
1625 redraw = true;
1626 }
1627 }
1628 }
1629 }
1630
1631 /* we want the stroke to have the first daub at the start location
1632 * instead of waiting till we have moved the space distance */
1633 if (first_dab && paint_space_stroke_enabled(*br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) {
1635 paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure);
1636 redraw = true;
1637 }
1638
1639 /* Don't update the paint cursor in #INBETWEEN_MOUSEMOVE events. */
1640 if (event->type != INBETWEEN_MOUSEMOVE) {
1641 wmWindow *window = CTX_wm_window(C);
1642 ARegion *region = CTX_wm_region(C);
1643
1644 if (region && (paint->flags & PAINT_SHOW_BRUSH)) {
1645 WM_paint_cursor_tag_redraw(window, region);
1646 }
1647 }
1648
1649 /* Draw for all events (even in between) otherwise updating the brush
1650 * display is noticeably delayed.
1651 */
1652 if (redraw && stroke->redraw) {
1653 stroke->redraw(C, stroke, false);
1654 }
1655
1657}
1658
1660{
1661 /* only when executed for the first time */
1662 if (stroke->stroke_started == 0) {
1663 PropertyRNA *strokeprop;
1664 PointerRNA firstpoint;
1665 float mouse[2];
1666
1667 strokeprop = RNA_struct_find_property(op->ptr, "stroke");
1668
1669 if (RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
1670 RNA_float_get_array(&firstpoint, "mouse", mouse);
1671 stroke->stroke_started = stroke->test_start(C, op, mouse);
1672 }
1673 }
1674
1675 if (stroke->stroke_started) {
1676 RNA_BEGIN (op->ptr, itemptr, "stroke") {
1677 stroke->update_step(C, op, stroke, &itemptr);
1678 }
1679 RNA_END;
1680 }
1681
1682 bool ok = (stroke->stroke_started != 0);
1683
1684 stroke_done(C, op, stroke);
1685
1687}
1688
1690{
1691 stroke_done(C, op, stroke);
1692}
1693
1695{
1696 return &stroke->vc;
1697}
1698
1700{
1701 return stroke->mode_data.get();
1702}
1703
1705{
1706 return stroke->pen_flip;
1707}
1708
1710{
1711 return stroke->stroke_mode == BRUSH_STROKE_INVERT;
1712}
1713
1715{
1716 return stroke->stroke_distance;
1717}
1718
1719void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr<PaintModeData> mode_data)
1720{
1721 stroke->mode_data = std::move(mode_data);
1722}
1723
1725{
1726 return stroke->stroke_started;
1727}
1728
1729static const bToolRef *brush_tool_get(const bContext *C)
1730{
1733 ScrArea *area = CTX_wm_area(C);
1734 ARegion *region = CTX_wm_region(C);
1735
1736 if (paint && ob && BKE_paint_brush(paint) &&
1737 (area && ELEM(area->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) &&
1738 (region && region->regiontype == RGN_TYPE_WINDOW))
1739 {
1740 if (area->runtime.tool && area->runtime.tool->runtime &&
1741 (area->runtime.tool->runtime->flag & TOOLREF_FLAG_USE_BRUSHES))
1742 {
1743 return area->runtime.tool;
1744 }
1745 }
1746 return nullptr;
1747}
1748
1750{
1751 /* Check the current tool is a brush. */
1752 return brush_tool_get(C) != nullptr;
1753}
1754
1756{
1757 const bToolRef *tref = brush_tool_get(C);
1758 if (!tref) {
1759 return false;
1760 }
1761
1762 /* Don't use brush cursor when the tool sets its own cursor. */
1763 if (tref->runtime->cursor != WM_CURSOR_DEFAULT) {
1764 return false;
1765 }
1766
1767 return true;
1768}
1769
1770} // namespace blender::ed::sculpt_paint
void BKE_brush_jitter_pos(const Scene &scene, const Brush &brush, const float pos[2], float jitterpos[2])
Definition brush.cc:1224
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1096
void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
Definition brush.cc:1251
float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1133
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1075
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1388
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1091
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush)
Definition brush.cc:1083
int BKE_brush_input_samples_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1180
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1663
void BKE_curve_forward_diff_tangent_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1691
ImBuf * BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool)
void BKE_image_pool_release_ibuf(Image *ima, ImBuf *ibuf, ImagePool *pool)
PaintMode
Definition BKE_paint.hh:99
@ SculptGreasePencil
void BKE_paint_set_overlay_override(enum eOverlayFlags flag)
Definition paint.cc:297
void paint_update_brush_rake_rotation(UnifiedPaintSettings &ups, const Brush &brush, float rotation)
Definition paint.cc:1980
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
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
bool paint_calculate_rake_rotation(UnifiedPaintSettings &ups, const Brush &brush, const float mouse_pos[2], PaintMode paint_mode, bool stroke_has_started)
Definition paint.cc:2005
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:506
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float max_ff(float a, float b)
MINLINE float square_f(float a)
MINLINE float interpf(float target, float origin, float t)
#define M_PI
#define M_PI_4
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_fl2(float v[2], float x, float y)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition math_vector.c:21
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v2(float n[2])
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
Utility defines for timing/benchmarks.
#define TIMEIT_START_AVERAGED(var)
#define TIMEIT_END_AVERAGED(var)
#define ELEM(...)
eBrushGPaintType
@ GPAINT_BRUSH_TYPE_DRAW
@ SCULPT_BRUSH_TYPE_THUMB
@ SCULPT_BRUSH_TYPE_GRAB
@ SCULPT_BRUSH_TYPE_BOUNDARY
@ SCULPT_BRUSH_TYPE_CLOTH
@ SCULPT_BRUSH_TYPE_POSE
@ SCULPT_BRUSH_TYPE_SNAKE_HOOK
@ SCULPT_BRUSH_TYPE_ELASTIC_DEFORM
@ SCULPT_BRUSH_TYPE_ROTATE
@ BRUSH_SPACE_ATTEN
@ BRUSH_DRAG_DOT
@ BRUSH_SPACING_PRESSURE
@ BRUSH_LINE
@ BRUSH_CURVE
@ BRUSH_EDGE_TO_EDGE
@ BRUSH_SMOOTH_STROKE
@ BRUSH_ANCHORED
@ BRUSH_JITTER_PRESSURE
@ BRUSH_ABSOLUTE_JITTER
@ BRUSH_USE_GRADIENT
@ BRUSH_SCENE_SPACING
@ BRUSH_AIRBRUSH
@ BRUSH_SPACE
@ IMAGE_PAINT_BRUSH_TYPE_FILL
@ IMAGE_PAINT_BRUSH_TYPE_CLONE
@ BRUSH_CLOTH_DEFORM_GRAB
eOverlayFlags
eBrushCurvesSculptType
@ CURVES_SCULPT_BRUSH_TYPE_ADD
@ CURVES_SCULPT_BRUSH_TYPE_DENSITY
Object is a sort of wrapper for general info.
@ PAINT_USE_CAVITY_MASK
@ PAINT_SHOW_BRUSH
#define PAINT_MAX_INPUT_SAMPLES
@ RGN_TYPE_WINDOW
#define RGN_TYPE_ANY
@ SPACE_IMAGE
@ SPACE_VIEW3D
#define SPACE_TYPE_ANY
@ TEX_IMAGE
@ MTEX_MAP_MODE_AREA
@ MTEX_MAP_MODE_RANDOM
@ MTEX_MAP_MODE_VIEW
@ MTEX_ANGLE_RANDOM
@ MTEX_ANGLE_RAKE
@ RV3D_PAINTING
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ TOOLREF_FLAG_USE_BRUSHES
void ED_view3d_project_v2(const ARegion *region, const float world[3], float r_region_co[2])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void immUniformColor4ubv(const unsigned char rgba[4])
void immUniform4f(const char *name, float x, float y, float z, float w)
void immEnd()
void immUnbindProgram()
void immUniform2f(const char *name, float x, float y)
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1i(const char *name, int x)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immBegin(GPUPrimType, uint vertex_len)
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:262
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ KM_RELEASE
Definition WM_types.hh:285
@ KM_ALT
Definition WM_types.hh:257
static T sum(const btAlignedObjectArray< T > &items)
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
const Depsgraph * depsgraph
#define sinf(x)
#define cosf(x)
#define atan2f(x, y)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
int len
draw_view in_light_buf[] float
int count
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
ccl_device_inline float2 fabs(const float2 a)
bool is_cloth_deform_brush(const Brush &brush)
static void paint_stroke_add_sample(PaintStroke *stroke, int input_samples, float x, float y, float pressure)
int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure)
static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2])
bool paint_supports_texture(PaintMode mode)
static const bToolRef * brush_tool_get(const bContext *C)
void(*)(const bContext *C, PaintStroke *stroke, bool final) StrokeRedraw
static bool paint_smooth_stroke(PaintStroke *stroke, const PaintSample *sample, PaintMode mode, float r_mouse[2], float *r_pressure)
bool paint_stroke_started(PaintStroke *stroke)
static void paint_line_strokes_spacing(bContext *C, wmOperator *op, PaintStroke *stroke, float spacing, float *length_residue, const float old_pos[2], const float new_pos[2])
static bool sculpt_is_grab_tool(const Brush &br)
void paint_stroke_jitter_pos(Scene &scene, const PaintStroke &stroke, const PaintMode mode, const Brush &brush, const float pressure, const float mval[2], float r_mouse_out[2])
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
bool paint_supports_dynamic_tex_coords(const Brush &br, PaintMode mode)
bool paint_supports_dynamic_size(const Brush &br, PaintMode mode)
static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata)
void(*)(bContext *C, wmOperator *op, PaintStroke *stroke, PointerRNA *itemptr) StrokeUpdateStep
static float paint_stroke_integrate_overlap(const Brush &br, float factor)
static void stroke_done(const bContext *C, PaintStroke *stroke)
static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *stroke)
bool paint_stroke_flipped(PaintStroke *stroke)
static void paint_stroke_sample_average(const PaintStroke *stroke, PaintSample *average)
static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata)
static float paint_space_stroke_spacing_variable(bContext *C, const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length)
static void paint_stroke_line_end(bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse[2])
static int paint_space_stroke(bContext *C, wmOperator *op, PaintStroke *stroke, const float final_mouse[2], float final_pressure)
static bool paint_stroke_use_jitter(PaintMode mode, const Brush &brush, bool invert)
static bool paint_stroke_use_dash(const Brush &brush)
ViewContext * paint_stroke_view_context(PaintStroke *stroke)
bool paint_brush_cursor_poll(bContext *C)
static bool image_paint_brush_type_require_inbetween_mouse_events(const Brush &brush, const PaintMode mode)
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
bool(*)(bContext *C, float location[3], const float mouse[2], bool force_original) StrokeGetLocation
static bool image_paint_brush_type_require_location(const Brush &brush, const PaintMode mode)
static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptType tool)
wmKeyMap * paint_stroke_modal_keymap(wmKeyConfig *keyconf)
bool(*)(bContext *C, wmOperator *op, const float mouse[2]) StrokeTestStart
bool paint_brush_update(bContext *C, const Brush &brush, PaintMode mode, PaintStroke *stroke, const float mouse_init[2], float mouse[2], float pressure, float r_location[3], bool *r_location_is_set)
void * paint_stroke_mode_data(PaintStroke *stroke)
float paint_stroke_distance_get(PaintStroke *stroke)
bool paint_space_stroke_enabled(const Brush &br, PaintMode mode)
void paint_stroke_free(bContext *C, wmOperator *op, PaintStroke *stroke)
PaintStroke * paint_stroke_new(bContext *C, wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeRedraw redraw, StrokeDone done, int event_type)
static bool paint_stroke_use_scene_spacing(const Brush &brush, const PaintMode mode)
static float paint_space_stroke_spacing(bContext *C, const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure)
static float paint_stroke_overlapped_curve(const Brush &br, float x, float spacing)
void(*)(const bContext *C, PaintStroke *stroke) StrokeDone
static bool image_paint_brush_type_raycast_original(const Brush &brush, PaintMode)
bool paint_stroke_inverted(PaintStroke *stroke)
bool paint_brush_tool_poll(bContext *C)
void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr< PaintModeData > mode_data)
bool paint_supports_smooth_stroke(PaintStroke *stroke, const Brush &br, PaintMode mode)
bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
@ BRUSH_STROKE_SMOOTH
@ BRUSH_STROKE_NORMAL
@ BRUSH_STROKE_INVERT
float paint_calc_object_space_radius(const ViewContext &vc, const blender::float3 &center, float pixel_radius)
#define PAINT_CURVE_NUM_SEGMENTS
#define PAINT_STROKE_MODAL_CANCEL
void RNA_collection_clear(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
bool RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, PointerRNA *r_ptr)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2], bool force_original)
Definition sculpt.cc:4732
float vec[3][3]
char sculpt_brush_type
float jitter
int cloth_deform_type
struct MTex mtex
float smooth_stroke_factor
int smooth_stroke_radius
char image_brush_type
struct CurveMapping * curve
int dash_samples
int jitter_absolute
float dash_ratio
char curves_sculpt_brush_type
struct MTex mask_mtex
char gpencil_brush_type
struct PaintCurve * paint_curve
int overlay_flags
ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
char brush_angle_mode
char brush_map_mode
float random_angle
struct Tex * tex
PaintCurvePoint * points
unsigned char paint_cursor_col[4]
struct CurveMapping * cavity_curve
struct ImageUser iuser
struct Image * ima
struct UnifiedPaintSettings unified_paint_settings
struct ColorSpace * colorspace
ARegion * region
Definition ED_view3d.hh:73
Object * obact
Definition ED_view3d.hh:71
bToolRef_Runtime * runtime
std::unique_ptr< PaintModeData > mode_data
std::optional< RandomNumberGenerator > rng
PaintSample samples[PAINT_MAX_INPUT_SAMPLES]
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
uint8_t modifier
Definition WM_types.hh:739
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct wmOperatorType * type
struct PointerRNA * ptr
float max
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *)
Definition wm_draw.cc:1533
float WM_event_tablet_data(const wmEvent *event, bool *r_pen_flip, float r_tilt[2])
bool WM_event_is_tablet(const wmEvent *event)
#define ISMOUSE_MOTION(event_type)
@ TIMER
@ EVT_MODAL_MAP
@ EVT_SPACEKEY
@ NDOF_MOTION
@ INBETWEEN_MOUSEMOVE
@ EVT_RETKEY
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
bool WM_paint_cursor_end(wmPaintCursor *handle)
wmPaintCursor * WM_paint_cursor_activate(short space_type, short region_type, bool(*poll)(bContext *C), wmPaintCursorDraw draw, void *customdata)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)