Blender V4.5
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
8
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_math_matrix.hh"
17#include "BLI_math_vector.h"
18#include "BLI_rand.hh"
19#include "BLI_utildefines.h"
20
21#include "DNA_brush_types.h"
22#include "DNA_curve_types.h"
23#include "DNA_object_types.h"
24#include "DNA_scene_types.h"
25
26#include "RNA_access.hh"
27
28#include "BKE_brush.hh"
29#include "BKE_colortools.hh"
30#include "BKE_context.hh"
31#include "BKE_curve.hh"
32#include "BKE_image.hh"
33#include "BKE_paint.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "GPU_immediate.hh"
39#include "GPU_state.hh"
40
41#include "ED_screen.hh"
42#include "ED_view3d.hh"
43
44#include "IMB_imbuf_types.hh"
45
46#include "paint_intern.hh"
47#include "sculpt_cloth.hh"
48#include "sculpt_intern.hh"
49
50// #define DEBUG_TIME
51
52#ifdef DEBUG_TIME
53# include "BLI_time_utildefines.h"
54#endif
55
57
60 float pressure;
61};
62
72 std::unique_ptr<PaintModeData> mode_data;
75 std::optional<RandomNumberGenerator> rng;
76
77 /* Cached values */
81
82 /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
83 * to smooth the stroke */
88
92
94 /* space distance covered so far */
96
97 /* Set whether any stroke step has yet occurred
98 * e.g. in sculpt mode, stroke doesn't start until cursor
99 * passes over the mesh */
101 /* Set when enough motion was found for rake rotation */
103 /* event that started stroke, for modal() return */
105 /* check if stroke variables have been initialized */
107 /* check if various brush mapping variables have been initialized */
110 /* cached_pressure stores initial pressure for size pressure influence mainly */
112 /* last pressure will store last pressure value for use in interpolation for space strokes */
115
117
118 float zoom_2d;
120
121 /* Tilt, as read from the event. */
123
124 /* line constraint */
127
133
134 bool original; /* Ray-cast original mesh at start of stroke. */
135};
136
137/*** Cursors ***/
139 const blender::int2 &xy,
140 const blender::float2 & /*tilt*/,
141 void *customdata)
142{
144 const Brush *brush = BKE_paint_brush_for_read(paint);
145 PaintStroke *stroke = static_cast<PaintStroke *>(customdata);
147
148 if ((mode == PaintMode::GPencil) && (paint->flags & PAINT_SHOW_BRUSH) == 0) {
149 return;
150 }
151
152 if (stroke && brush) {
153 GPU_line_smooth(true);
155
156 const ARegion *region = stroke->vc.region;
157
161 immUniformColor4ubv(paint->paint_cursor_col);
162
166 stroke->last_mouse_position[0] + region->winrct.xmin,
167 stroke->last_mouse_position[1] + region->winrct.ymin);
168
169 immEnd();
170
172
174 GPU_line_smooth(false);
175 }
176}
177
179 const blender::int2 &xy,
180 const blender::float2 & /*tilt*/,
181 void *customdata)
182{
184 PaintStroke *stroke = static_cast<PaintStroke *>(customdata);
185
186 GPU_line_smooth(true);
187
188 const uint shdr_pos = GPU_vertformat_attr_add(
190
192
193 float4 viewport_size;
194 GPU_viewport_size_get_f(viewport_size);
195 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
196
197 immUniform1i("colors_len", 2); /* "advanced" mode */
198 const float alpha = float(paint->paint_cursor_col[3]) / 255.0f;
199 immUniform4f("color", 0.0f, 0.0f, 0.0f, alpha);
200 immUniform4f("color2", 1.0f, 1.0f, 1.0f, alpha);
201 immUniform1f("dash_width", 6.0f);
202 immUniform1f("udash_factor", 0.5f);
203
205
206 const ARegion *region = stroke->vc.region;
207
208 if (stroke->constrain_line) {
209 immVertex2f(shdr_pos,
210 stroke->last_mouse_position[0] + region->winrct.xmin,
211 stroke->last_mouse_position[1] + region->winrct.ymin);
212
213 immVertex2f(shdr_pos,
214 stroke->constrained_pos[0] + region->winrct.xmin,
215 stroke->constrained_pos[1] + region->winrct.ymin);
216 }
217 else {
218 immVertex2f(shdr_pos,
219 stroke->last_mouse_position[0] + region->winrct.xmin,
220 stroke->last_mouse_position[1] + region->winrct.ymin);
221
222 immVertex2fv(shdr_pos, float2(xy));
223 }
224
225 immEnd();
226
228
229 GPU_line_smooth(false);
230}
231
232static bool image_paint_brush_type_require_location(const Brush &brush, const PaintMode mode)
233{
234 switch (mode) {
236 if (ELEM(brush.sculpt_brush_type,
244 {
245 return false;
246 }
247 else if (cloth::is_cloth_deform_brush(brush)) {
248 return false;
249 }
250 else {
251 return true;
252 }
253 default:
254 break;
255 }
256
257 return true;
258}
259
260static bool paint_stroke_use_scene_spacing(const Brush &brush, const PaintMode mode)
261{
262 switch (mode) {
264 return brush.flag & BRUSH_SCENE_SPACING;
265 default:
266 break;
267 }
268 return false;
269}
270
271static bool image_paint_brush_type_raycast_original(const Brush &brush, PaintMode /*mode*/)
272{
273 return brush.flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT);
274}
275
277 const PaintMode mode)
278{
279 if (brush.flag & BRUSH_ANCHORED) {
280 return false;
281 }
282
283 switch (mode) {
285 if (ELEM(brush.sculpt_brush_type,
294 {
295 return false;
296 }
297 else {
298 return true;
299 }
300 default:
301 break;
302 }
303
304 return true;
305}
306
308 const Brush &brush,
309 const PaintMode mode,
310 PaintStroke *stroke,
311 const float mouse_init[2],
312 float mouse[2],
313 const float pressure,
314 float r_location[3],
315 bool *r_location_is_set)
316{
317 Scene *scene = CTX_data_scene(C);
318 UnifiedPaintSettings &ups = *stroke->ups;
319 bool location_sampled = false;
320 bool location_success = false;
321 /* Use to perform all operations except applying the stroke,
322 * needed for operations that require cursor motion (rake). */
323 bool is_dry_run = false;
324 bool do_random = false;
325 bool do_random_mask = false;
326 *r_location_is_set = false;
327 /* XXX: Use pressure value from first brush step for brushes which don't
328 * support strokes (grab, thumb). They depends on initial state and
329 * brush coord/pressure/etc.
330 * It's more an events design issue, which doesn't split coordinate/pressure/angle
331 * changing events. We should avoid this after events system re-design */
332 if (!stroke->brush_init) {
333 copy_v2_v2(stroke->initial_mouse, mouse);
334 copy_v2_v2(ups.last_rake, mouse);
335 copy_v2_v2(ups.tex_mouse, mouse);
336 copy_v2_v2(ups.mask_tex_mouse, mouse);
337 stroke->cached_size_pressure = pressure;
338
339 stroke->brush_init = true;
340 }
341
342 if (paint_supports_dynamic_size(brush, mode)) {
343 copy_v2_v2(ups.tex_mouse, mouse);
344 copy_v2_v2(ups.mask_tex_mouse, mouse);
345 stroke->cached_size_pressure = pressure;
346 }
347
348 /* Truly temporary data that isn't stored in properties */
349
350 ups.stroke_active = true;
352
353 ups.pixel_radius = BKE_brush_size_get(scene, &brush);
354 ups.initial_pixel_radius = BKE_brush_size_get(scene, &brush);
355
356 if (BKE_brush_use_size_pressure(&brush) && paint_supports_dynamic_size(brush, mode)) {
357 ups.pixel_radius *= stroke->cached_size_pressure;
358 }
359
360 if (paint_supports_dynamic_tex_coords(brush, mode)) {
361
362 if (ELEM(brush.mtex.brush_map_mode,
366 {
367 do_random = true;
368 }
369
372 }
373 else {
374 copy_v2_v2(ups.tex_mouse, mouse);
375 }
376
377 /* take care of mask texture, if any */
378 if (brush.mask_mtex.tex) {
379
380 if (ELEM(brush.mask_mtex.brush_map_mode,
384 {
385 do_random_mask = true;
386 }
387
390 }
391 else {
392 copy_v2_v2(ups.mask_tex_mouse, mouse);
393 }
394 }
395 }
396
397 if (brush.flag & BRUSH_ANCHORED) {
398 bool hit = false;
399 float2 halfway;
400
401 const float dx = mouse[0] - stroke->initial_mouse[0];
402 const float dy = mouse[1] - stroke->initial_mouse[1];
403
404 ups.anchored_size = ups.pixel_radius = sqrtf(dx * dx + dy * dy);
405
406 ups.brush_rotation = ups.brush_rotation_sec = atan2f(dy, dx) + float(0.5f * M_PI);
407
408 if (brush.flag & BRUSH_EDGE_TO_EDGE) {
409 halfway[0] = dx * 0.5f + stroke->initial_mouse[0];
410 halfway[1] = dy * 0.5f + stroke->initial_mouse[1];
411
412 if (stroke->get_location) {
413 if (stroke->get_location(C, r_location, halfway, stroke->original)) {
414 hit = true;
415 location_sampled = true;
416 location_success = true;
417 *r_location_is_set = true;
418 }
419 else if (!image_paint_brush_type_require_location(brush, mode)) {
420 hit = true;
421 }
422 }
423 else {
424 hit = true;
425 }
426 }
427 if (hit) {
429 copy_v2_v2(ups.tex_mouse, halfway);
430 copy_v2_v2(ups.mask_tex_mouse, halfway);
431 copy_v2_v2(mouse, halfway);
432 ups.anchored_size /= 2.0f;
433 ups.pixel_radius /= 2.0f;
434 stroke->stroke_distance = ups.pixel_radius;
435 }
436 else {
438 copy_v2_v2(mouse, stroke->initial_mouse);
439 stroke->stroke_distance = ups.pixel_radius;
440 }
441 ups.pixel_radius /= stroke->zoom_2d;
442 ups.draw_anchored = true;
443 }
444 else {
445 /* curve strokes do their own rake calculation */
446 if (!(brush.flag & BRUSH_CURVE)) {
447 if (!paint_calculate_rake_rotation(ups, brush, mouse_init, mode, stroke->rake_started)) {
448 /* Not enough motion to define an angle. */
449 if (!stroke->rake_started) {
450 is_dry_run = true;
451 }
452 }
453 else {
454 stroke->rake_started = true;
455 }
456 }
457 }
458
459 if ((do_random || do_random_mask) && !stroke->rng) {
460 /* Lazy initialization. */
462 }
463
464 if (do_random) {
466 ups.brush_rotation += -brush.mtex.random_angle / 2.0f +
467 brush.mtex.random_angle * stroke->rng->get_float();
468 }
469 }
470
471 if (do_random_mask) {
473 ups.brush_rotation_sec += -brush.mask_mtex.random_angle / 2.0f +
474 brush.mask_mtex.random_angle * stroke->rng->get_float();
475 }
476 }
477
478 if (!location_sampled) {
479 if (stroke->get_location) {
480 if (stroke->get_location(C, r_location, mouse, stroke->original)) {
481 location_success = true;
482 *r_location_is_set = true;
483 }
484 else if (!image_paint_brush_type_require_location(brush, mode)) {
485 location_success = true;
486 }
487 }
488 else {
489 zero_v3(r_location);
490 location_success = true;
491 /* don't set 'r_location_is_set', since we don't want to use the value. */
492 }
493 }
494
495 return location_success && !is_dry_run;
496}
497
498static bool paint_stroke_use_dash(const Brush &brush)
499{
500 /* Only these stroke modes support dash lines */
501 return brush.flag & BRUSH_SPACE || brush.flag & BRUSH_LINE || brush.flag & BRUSH_CURVE;
502}
503
504static bool paint_stroke_use_jitter(const PaintMode mode, const Brush &brush, const bool invert)
505{
506 bool use_jitter = brush.flag & BRUSH_ABSOLUTE_JITTER ? brush.jitter_absolute != 0 :
507 brush.jitter != 0;
508
509 /* jitter-ed brush gives weird and unpredictable result for this
510 * kinds of stroke, so manually disable jitter usage (sergey) */
511 use_jitter &= (brush.flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0;
512 use_jitter &= !ELEM(mode, PaintMode::Texture2D, PaintMode::Texture3D) ||
514
515 return use_jitter;
516}
517
519 const PaintStroke &stroke,
520 const PaintMode mode,
521 const Brush &brush,
522 const float pressure,
523 const float mval[2],
524 float r_mouse_out[2])
525{
526 if (paint_stroke_use_jitter(mode, brush, stroke.stroke_mode == BRUSH_STROKE_INVERT)) {
527 float factor = stroke.zoom_2d;
528
529 if (brush.flag & BRUSH_JITTER_PRESSURE) {
530 factor *= pressure;
531 }
532
533 BKE_brush_jitter_pos(scene, brush, mval, r_mouse_out);
534
535 /* XXX: meh, this is round about because
536 * BKE_brush_jitter_pos isn't written in the best way to
537 * be reused here */
538 if (factor != 1.0f) {
539 float2 delta;
540 sub_v2_v2v2(delta, r_mouse_out, mval);
541 mul_v2_fl(delta, factor);
542 add_v2_v2v2(r_mouse_out, mval, delta);
543 }
544 }
545 else {
546 copy_v2_v2(r_mouse_out, mval);
547 }
548}
549
550/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
552 bContext *C, wmOperator *op, PaintStroke *stroke, const float2 mval, float pressure)
553{
554 Scene &scene = *CTX_data_scene(C);
557 const Brush &brush = *BKE_paint_brush_for_read(&paint);
558 UnifiedPaintSettings *ups = stroke->ups;
559
560/* the following code is adapted from texture paint. It may not be needed but leaving here
561 * just in case for reference (code in texpaint removed as part of refactoring).
562 * It's strange that only texpaint had these guards. */
563#if 0
564 /* special exception here for too high pressure values on first touch in
565 * windows for some tablets, then we just skip first touch. */
566 if (tablet && (pressure >= 0.99f) &&
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
573 /* This can be removed once fixed properly in
574 * BKE_brush_painter_paint(
575 * BrushPainter *painter, BrushFunc func,
576 * float *pos, double time, float pressure, void *user);
577 * at zero pressure we should do nothing 1/2^12 is 0.0002
578 * which is the sensitivity of the most sensitive pen tablet available */
579 if (tablet && (pressure < 0.0002f) &&
580 ((pop->s.brush.flag & BRUSH_SPACING_PRESSURE) ||
581 BKE_brush_use_alpha_pressure(pop->s.brush) || BKE_brush_use_size_pressure(pop->s.brush)))
582 {
583 return;
584 }
585#endif
586
587 /* copy last position -before- jittering, or space fill code
588 * will create too many dabs */
589 stroke->last_mouse_position = mval;
590 stroke->last_pressure = pressure;
591
592 if (paint_stroke_use_scene_spacing(brush, mode)) {
593 float3 world_space_position;
594
596 C, world_space_position, stroke->last_mouse_position, stroke->original))
597 {
599 stroke->vc.obact->object_to_world(), world_space_position);
600 }
601 else {
603 }
604 }
605
606 float2 mouse_out;
607 /* Get jitter position (same as mval if no jitter is used). */
608 paint_stroke_jitter_pos(scene, *stroke, mode, brush, pressure, mval, mouse_out);
609
610 float3 location;
611 bool is_location_is_set;
613 C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set);
614 if (is_location_is_set) {
615 copy_v3_v3(ups->last_location, location);
616 }
617 if (!ups->last_hit) {
618 return;
619 }
620
621 /* Dash */
622 bool add_step = true;
623 if (paint_stroke_use_dash(brush)) {
624 const int dash_samples = stroke->tot_samples % brush.dash_samples;
625 const float dash = float(dash_samples) / float(brush.dash_samples);
626 if (dash > brush.dash_ratio) {
627 add_step = false;
628 }
629 }
630
631 /* Add to stroke */
632 if (add_step) {
633 PointerRNA itemptr;
634 RNA_collection_add(op->ptr, "stroke", &itemptr);
635 RNA_float_set(&itemptr, "size", ups->pixel_radius);
636 RNA_float_set_array(&itemptr, "location", location);
637 /* Mouse coordinates modified by the stroke type options. */
638 RNA_float_set_array(&itemptr, "mouse", mouse_out);
639 /* Original mouse coordinates. */
640 RNA_float_set_array(&itemptr, "mouse_event", mval);
641 RNA_float_set(&itemptr, "pressure", pressure);
642 RNA_float_set(&itemptr, "x_tilt", stroke->tilt.x);
643 RNA_float_set(&itemptr, "y_tilt", stroke->tilt.y);
644
645 stroke->update_step(C, op, stroke, &itemptr);
646
647 /* don't record this for now, it takes up a lot of memory when doing long
648 * strokes with small brush size, and operators have register disabled */
649 RNA_collection_clear(op->ptr, "stroke");
650 }
651
652 stroke->tot_samples++;
653}
654
655/* Returns zero if no sculpt changes should be made, non-zero otherwise */
657 const PaintSample *sample,
658 const PaintMode mode,
659 float2 &r_mouse,
660 float &r_pressure)
661{
662 if (paint_supports_smooth_stroke(stroke, *stroke->brush, mode)) {
663 const float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d;
664 const float u = stroke->brush->smooth_stroke_factor;
665
666 /* If the mouse is moving within the radius of the last move,
667 * don't update the mouse position. This allows sharp turns. */
668 if (math::distance_squared(stroke->last_mouse_position, sample->mouse) < square_f(radius)) {
669 return false;
670 }
671
672 r_mouse = math::interpolate(sample->mouse, stroke->last_mouse_position, u);
673 r_pressure = math::interpolate(sample->pressure, stroke->last_pressure, u);
674 }
675 else {
676 r_mouse = sample->mouse;
677 r_pressure = sample->pressure;
678 }
679
680 return true;
681}
682
684 const Scene *scene,
685 PaintStroke *stroke,
686 const float size_pressure,
687 const float spacing_pressure)
688{
691 const Brush &brush = *BKE_paint_brush_for_read(paint);
692
693 float size_clamp = 0.0f;
694 if (paint_stroke_use_scene_spacing(brush, mode)) {
695 const float3 last_object_space_position = math::transform_point(
696 stroke->vc.obact->world_to_object(), stroke->last_world_space_position);
697 size_clamp = object_space_radius_get(
698 stroke->vc, *scene, brush, last_object_space_position, size_pressure);
699 }
700 else {
701 /* brushes can have a minimum size of 1.0 but with pressure it can be smaller than a pixel
702 * causing very high step sizes, hanging blender #32381. */
703 size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure);
704 }
705
706 float spacing = stroke->brush->spacing;
707
708 /* apply spacing pressure */
709 if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) {
710 spacing = spacing * (1.5f - spacing_pressure);
711 }
712
713 if (cloth::is_cloth_deform_brush(brush)) {
714 /* The spacing in tools that use the cloth solver should not be affected by the brush radius to
715 * avoid affecting the simulation update rate when changing the radius of the brush.
716 * With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2
717 * pixels movement of the cursor. */
718 size_clamp = 100.0f;
719 }
720
721 /* stroke system is used for 2d paint too, so we need to account for
722 * the fact that brush can be scaled there. */
723 spacing *= stroke->zoom_2d;
724
725 if (paint_stroke_use_scene_spacing(brush, mode)) {
726 /* Low pressure on size (with tablets) can cause infinite recursion in paint_space_stroke(),
727 * see #129853. */
728 return max_ff(FLT_EPSILON, size_clamp * spacing / 50.0f);
729 }
730 return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
731}
732
733static float paint_stroke_overlapped_curve(const Brush &br, const float x, const float spacing)
734{
735 /* Avoid division by small numbers, can happen
736 * on some pen setups. See #105341.
737 */
738
739 const float clamped_spacing = max_ff(spacing, 0.1f);
740
741 const int n = 100 / clamped_spacing;
742 const float h = clamped_spacing / 50.0f;
743 const float x0 = x - 1;
744
745 float sum = 0;
746 for (int i = 0; i < n; i++) {
747 float xx = fabsf(x0 + i * h);
748
749 if (xx < 1.0f) {
750 sum += BKE_brush_curve_strength(&br, xx, 1);
751 }
752 }
753
754 return sum;
755}
756
757static float paint_stroke_integrate_overlap(const Brush &br, const float factor)
758{
759 const float spacing = br.spacing * factor;
760
761 if (!(br.flag & BRUSH_SPACE_ATTEN && (br.spacing < 100))) {
762 return 1.0;
763 }
764
765 constexpr int m = 10;
766 float g = 1.0f / m;
767 float max = 0;
768 for (int i = 0; i < m; i++) {
769 const float overlap = fabs(paint_stroke_overlapped_curve(br, i * g, spacing));
770
771 max = std::max(overlap, max);
772 }
773
774 if (max == 0.0f) {
775 return 1.0f;
776 }
777 return 1.0f / max;
778}
779
781 const Scene *scene,
782 PaintStroke *stroke,
783 const float pressure,
784 const float pressure_delta,
785 const float length)
786{
787 if (BKE_brush_use_size_pressure(stroke->brush)) {
788 /* use pressure to modify size. set spacing so that at 100%, the circles
789 * are aligned nicely with no overlap. for this the spacing needs to be
790 * the average of the previous and next size. */
791 const float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
792 const float q = s * pressure_delta / (2.0f * length);
793 const float pressure_fac = (1.0f + q) / (1.0f - q);
794
795 const float last_size_pressure = stroke->last_pressure;
796 const float new_size_pressure = stroke->last_pressure * pressure_fac;
797
798 /* average spacing */
799 const float last_spacing = paint_space_stroke_spacing(
800 C, scene, stroke, last_size_pressure, pressure);
801 const float new_spacing = paint_space_stroke_spacing(
802 C, scene, stroke, new_size_pressure, pressure);
803
804 return 0.5f * (last_spacing + new_spacing);
805 }
806
807 /* no size pressure */
808 return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
809}
810
811/* For brushes with stroke spacing enabled, moves mouse in steps
812 * towards the final mouse location. */
814 wmOperator *op,
815 PaintStroke *stroke,
816 const float2 final_mouse,
817 const float final_pressure)
818{
819 const Scene *scene = CTX_data_scene(C);
820 const ARegion *region = CTX_wm_region(C);
821 UnifiedPaintSettings *ups = stroke->ups;
824 const Brush &brush = *BKE_paint_brush_for_read(&paint);
825
826 float2 mouse_delta = final_mouse - stroke->last_mouse_position;
827 float length = normalize_v2(mouse_delta);
828
829 float3 world_space_position_delta;
830 const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
831 if (use_scene_spacing) {
832 float3 world_space_position;
833 const bool hit = stroke_get_location_bvh(
834 C, world_space_position, final_mouse, stroke->original);
835 world_space_position = math::transform_point(stroke->vc.obact->object_to_world(),
836 world_space_position);
837 if (hit && stroke->stroke_over_mesh) {
838 world_space_position_delta = world_space_position - stroke->last_world_space_position;
839 length = math::length(world_space_position_delta);
840 stroke->stroke_over_mesh = true;
841 }
842 else {
843 length = 0.0f;
844 world_space_position_delta = {0.0f, 0.0f, 0.0f};
845 stroke->stroke_over_mesh = hit;
846 if (stroke->stroke_over_mesh) {
847 stroke->last_world_space_position = world_space_position;
848 }
849 }
850 }
851
852 float pressure = stroke->last_pressure;
853 float pressure_delta = final_pressure - stroke->last_pressure;
854 const float no_pressure_spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
855 int count = 0;
856 while (length > 0.0f) {
857 const float spacing = paint_space_stroke_spacing_variable(
858 C, scene, stroke, pressure, pressure_delta, length);
859 BLI_assert(spacing >= 0.0f);
860
861 if (length >= spacing) {
862 float2 mouse;
863 if (use_scene_spacing) {
864 float3 final_world_space_position;
865 world_space_position_delta = math::normalize(world_space_position_delta);
866 final_world_space_position = world_space_position_delta * spacing +
868 ED_view3d_project_v2(region, final_world_space_position, mouse);
869
870 stroke->last_scene_spacing_delta = world_space_position_delta * spacing;
871 }
872 else {
873 mouse = stroke->last_mouse_position + mouse_delta * spacing;
874 }
875 pressure = stroke->last_pressure + (spacing / length) * pressure_delta;
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 pressure_delta = 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 const StrokeGetLocation get_location,
902 const StrokeTestStart test_start,
903 const StrokeUpdateStep update_step,
904 const StrokeRedraw redraw,
905 const StrokeDone done,
906 const 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
917
918 stroke->get_location = get_location;
919 stroke->test_start = test_start;
920 stroke->update_step = update_step;
921 stroke->redraw = redraw;
922 stroke->done = done;
923 stroke->event_type = event_type; /* for modal, return event */
924 stroke->ups = ups;
925 stroke->stroke_mode = RNA_enum_get(op->ptr, "mode");
926
929
930 float zoomx;
931 float zoomy;
932 get_imapaint_zoom(C, &zoomx, &zoomy);
933 stroke->zoom_2d = std::max(zoomx, zoomy);
934
935 /* Check here if color sampling the main brush should do color conversion. This is done here
936 * to avoid locking up to get the image buffer during sampling. */
937 ups->do_linear_conversion = false;
938 ups->colorspace = nullptr;
939
940 if (br->mtex.tex && br->mtex.tex->type == TEX_IMAGE && br->mtex.tex->ima) {
942 br->mtex.tex->ima, &br->mtex.tex->iuser, nullptr);
943 if (tex_ibuf && tex_ibuf->float_buffer.data == nullptr) {
944 ups->do_linear_conversion = true;
945 ups->colorspace = tex_ibuf->byte_buffer.colorspace;
946 }
947 BKE_image_pool_release_ibuf(br->mtex.tex->ima, tex_ibuf, nullptr);
948 }
949
950 if (stroke->stroke_mode == BRUSH_STROKE_INVERT) {
951 if (br->flag & BRUSH_CURVE) {
953 }
954 }
955 /* initialize here */
956 ups->overlap_factor = 1.0;
957 ups->stroke_active = true;
958
959 if (rv3d) {
960 rv3d->rflag |= RV3D_PAINTING;
961 }
962
963 /* Preserve location from last stroke while applying and resetting
964 * ups->average_stroke_counter to 1.
965 */
966 if (ups->average_stroke_counter) {
967 mul_v3_fl(ups->average_stroke_accum, 1.0f / float(ups->average_stroke_counter));
968 ups->average_stroke_counter = 1;
969 }
970
971 /* initialize here to avoid initialization conflict with threaded strokes */
973 if (paint->flags & PAINT_USE_CAVITY_MASK) {
974 BKE_curvemapping_init(paint->cavity_curve);
975 }
976
978
980
981 return stroke;
982}
983
985{
986 if (RegionView3D *rv3d = CTX_wm_region_view3d(C)) {
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
1042bool paint_space_stroke_enabled(const Brush &br, const PaintMode mode)
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
1113bool paint_supports_smooth_stroke(PaintStroke *stroke, const Brush &brush, const PaintMode mode)
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 const 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 const int input_samples,
1188 const float x,
1189 const float y,
1190 const float pressure)
1191{
1192 PaintSample *sample = &stroke->samples[stroke->cur_sample];
1193 const int max_samples = std::clamp(input_samples, 1, PAINT_MAX_INPUT_SAMPLES);
1194
1195 sample->mouse[0] = x;
1196 sample->mouse[1] = y;
1197 sample->pressure = pressure;
1198
1199 stroke->cur_sample++;
1200 if (stroke->cur_sample >= max_samples) {
1201 stroke->cur_sample = 0;
1202 }
1203 if (stroke->num_samples < max_samples) {
1204 stroke->num_samples++;
1205 }
1206}
1207
1209{
1210 memset(average, 0, sizeof(*average));
1211
1212 BLI_assert(stroke->num_samples > 0);
1213
1214 for (int i = 0; i < stroke->num_samples; i++) {
1215 average->mouse += stroke->samples[i].mouse;
1216 average->pressure += stroke->samples[i].pressure;
1217 }
1218
1219 average->mouse /= stroke->num_samples;
1220 average->pressure /= stroke->num_samples;
1221
1222 // printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);
1223}
1224
1230 wmOperator *op,
1231 PaintStroke *stroke,
1232 const float spacing,
1233 float *length_residue,
1234 const float2 old_pos,
1235 const float2 new_pos)
1236{
1237 UnifiedPaintSettings *ups = stroke->ups;
1239 const Brush &brush = *BKE_paint_brush(paint);
1241 const ARegion *region = CTX_wm_region(C);
1242
1243 const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
1244
1245 float2 mouse_delta;
1246 float length;
1247 float3 world_space_position_delta;
1248 float3 world_space_position_old;
1249
1250 stroke->last_mouse_position = old_pos;
1251
1252 if (use_scene_spacing) {
1253 const bool hit_old = stroke_get_location_bvh(
1254 C, world_space_position_old, old_pos, stroke->original);
1255
1256 float3 world_space_position_new;
1257 const bool hit_new = stroke_get_location_bvh(
1258 C, world_space_position_new, new_pos, stroke->original);
1259
1260 world_space_position_old = math::transform_point(stroke->vc.obact->object_to_world(),
1261 world_space_position_old);
1262 world_space_position_new = math::transform_point(stroke->vc.obact->object_to_world(),
1263 world_space_position_new);
1264 if (hit_old && hit_new && stroke->stroke_over_mesh) {
1265 world_space_position_delta = world_space_position_new - world_space_position_old;
1266 length = math::length(world_space_position_delta);
1267 stroke->stroke_over_mesh = true;
1268 }
1269 else {
1270 length = 0.0f;
1271 world_space_position_delta = {0.0f, 0.0f, 0.0f};
1272 stroke->stroke_over_mesh = hit_new;
1273 if (stroke->stroke_over_mesh) {
1274 stroke->last_world_space_position = world_space_position_old;
1275 }
1276 }
1277 }
1278 else {
1279 mouse_delta = new_pos - old_pos;
1280 mouse_delta = math::normalize_and_get_length(mouse_delta, length);
1281 }
1282
1283 BLI_assert(length >= 0.0f);
1284
1285 if (length == 0.0f) {
1286 return;
1287 }
1288
1289 float2 mouse;
1290 while (length > 0.0f) {
1291 float spacing_final = spacing - *length_residue;
1292 length += *length_residue;
1293 *length_residue = 0.0;
1294
1295 if (length >= spacing) {
1296 if (use_scene_spacing) {
1297 world_space_position_delta = math::normalize(world_space_position_delta);
1298 const float3 final_world_space_position = world_space_position_delta * spacing_final +
1299 world_space_position_old;
1300 ED_view3d_project_v2(region, final_world_space_position, mouse);
1301 }
1302 else {
1303 mouse = stroke->last_mouse_position + mouse_delta * spacing_final;
1304 }
1305
1307
1308 stroke->stroke_distance += spacing / stroke->zoom_2d;
1309 paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0);
1310
1311 length -= spacing;
1312 spacing_final = spacing;
1313 }
1314 else {
1315 break;
1316 }
1317 }
1318
1319 *length_residue = length;
1320}
1321
1323 wmOperator *op,
1324 PaintStroke *stroke,
1325 const float2 mouse)
1326{
1327 Brush *br = stroke->brush;
1328 if (stroke->stroke_started && (br->flag & BRUSH_LINE)) {
1330
1331 paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0);
1332 paint_space_stroke(C, op, stroke, mouse, 1.0);
1333 }
1334}
1335
1337{
1338 const Brush &br = *stroke->brush;
1339 if (!(br.flag & BRUSH_CURVE)) {
1340 return false;
1341 }
1342
1344 const Scene *scene = CTX_data_scene(C);
1345 const float spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
1346 const PaintCurve *pc = br.paint_curve;
1347
1348 if (!pc) {
1349 return true;
1350 }
1351
1352#ifdef DEBUG_TIME
1353 TIMEIT_START_AVERAGED(whole_stroke);
1354#endif
1355
1356 const PaintCurvePoint *pcp = pc->points;
1358
1359 float length_residue = 0.0f;
1360 for (int i = 0; i < pc->tot_points - 1; i++, pcp++) {
1361 float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
1362 float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
1363 const PaintCurvePoint *pcp_next = pcp + 1;
1364 bool do_rake = false;
1365
1366 for (int j = 0; j < 2; j++) {
1368 pcp->bez.vec[2][j],
1369 pcp_next->bez.vec[0][j],
1370 pcp_next->bez.vec[1][j],
1371 data + j,
1373 sizeof(float[2]));
1374 }
1375
1378 {
1379 do_rake = true;
1380 for (int j = 0; j < 2; j++) {
1382 pcp->bez.vec[2][j],
1383 pcp_next->bez.vec[0][j],
1384 pcp_next->bez.vec[1][j],
1385 tangents + j,
1387 sizeof(float[2]));
1388 }
1389 }
1390
1391 for (int j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) {
1392 if (do_rake) {
1393 const float rotation = atan2f(tangents[2 * j + 1], tangents[2 * j]) + float(0.5f * M_PI);
1394 paint_update_brush_rake_rotation(ups, br, rotation);
1395 }
1396
1397 if (!stroke->stroke_started) {
1398 stroke->last_pressure = 1.0;
1399 copy_v2_v2(stroke->last_mouse_position, data + 2 * j);
1400
1403 C, stroke->last_world_space_position, data + 2 * j, stroke->original);
1404 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
1405 }
1406
1407 stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
1408
1409 if (stroke->stroke_started) {
1410 paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0);
1412 C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
1413 }
1414 }
1415 else {
1417 C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
1418 }
1419 }
1420 }
1421
1422 stroke_done(C, op, stroke);
1423
1424#ifdef DEBUG_TIME
1425 TIMEIT_END_AVERAGED(whole_stroke);
1426#endif
1427
1428 return true;
1429}
1430
1432{
1433 if (stroke->constrain_line) {
1434 float2 line = mouse - stroke->last_mouse_position;
1435 float angle = atan2f(line[1], line[0]);
1436 const float len = math::length(line);
1437
1438 /* divide angle by PI/4 */
1439 angle = 4.0f * angle / float(M_PI);
1440
1441 /* now take residue */
1442 const float 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
1458 wmOperator *op,
1459 const wmEvent *event,
1460 PaintStroke **stroke_p)
1461{
1462 const Scene *scene = CTX_data_scene(C);
1465 PaintStroke *stroke = *stroke_p;
1466 const Brush *br = stroke->brush = BKE_paint_brush(paint);
1467 bool first_dab = false;
1468 bool first_modal = false;
1469 bool redraw = false;
1470
1471 if (event->type == INBETWEEN_MOUSEMOVE &&
1473 {
1475 }
1476
1477 /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */
1478 const float tablet_pressure = WM_event_tablet_data(event, &stroke->pen_flip, nullptr);
1479 float pressure = ((br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? 1.0f :
1480 tablet_pressure);
1481
1482 /* When processing a timer event the pressure from the event is 0, so use the last valid
1483 * pressure. */
1484 if (event->type == TIMER) {
1485 pressure = stroke->last_tablet_event_pressure;
1486 }
1487 else {
1488 stroke->last_tablet_event_pressure = pressure;
1489 }
1490
1491 const int input_samples = BKE_brush_input_samples_get(scene, br);
1492 paint_stroke_add_sample(stroke, input_samples, event->mval[0], event->mval[1], pressure);
1493
1494 PaintSample sample_average;
1495 paint_stroke_sample_average(stroke, &sample_average);
1496
1497 /* Tilt. */
1498 if (WM_event_is_tablet(event)) {
1499 stroke->tilt = event->tablet.tilt;
1500 }
1501
1502#ifdef WITH_INPUT_NDOF
1503 /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
1504 * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
1505 * since the 2D deltas are zero -- code in this file needs to be updated to use the
1506 * post-NDOF_MOTION MOUSEMOVE */
1507 if (event->type == NDOF_MOTION) {
1508 return OPERATOR_PASS_THROUGH;
1509 }
1510#endif
1511
1512 /* one time initialization */
1513 if (!stroke->stroke_init) {
1514 if (paint_stroke_curve_end(C, op, stroke)) {
1515 *stroke_p = nullptr;
1516 return OPERATOR_FINISHED;
1517 }
1518
1519 stroke->stroke_init = true;
1520 first_modal = true;
1521 }
1522
1523 /* one time stroke initialization */
1524 if (!stroke->stroke_started) {
1525 RNA_boolean_set(op->ptr, "pen_flip", stroke->pen_flip);
1526
1527 stroke->last_pressure = sample_average.pressure;
1528 stroke->last_mouse_position = sample_average.mouse;
1529 if (paint_stroke_use_scene_spacing(*br, mode)) {
1531 C, stroke->last_world_space_position, sample_average.mouse, stroke->original);
1533 stroke->vc.obact->object_to_world(), stroke->last_world_space_position);
1534 }
1535 stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
1536
1537 if (stroke->stroke_started) {
1538 /* StrokeTestStart often updates the currently active brush so we need to re-retrieve it
1539 * here. */
1540 br = BKE_paint_brush(paint);
1541
1542 if (paint_supports_smooth_stroke(stroke, *br, mode)) {
1547 stroke);
1548 }
1549
1550 if (br->flag & BRUSH_AIRBRUSH) {
1551 stroke->timer = WM_event_timer_add(
1553 }
1554
1555 if (br->flag & BRUSH_LINE) {
1558 }
1559
1560 first_dab = true;
1561 }
1562 }
1563
1564 /* Cancel */
1565 if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) {
1566 if (op->type->cancel) {
1567 op->type->cancel(C, op);
1568 }
1569 else {
1570 paint_stroke_cancel(C, op, stroke);
1571 }
1572 return OPERATOR_CANCELLED;
1573 }
1574
1575 /* Handles shift-key active smooth toggling during a grease pencil stroke. */
1576 if (mode == PaintMode::GPencil) {
1577 if (event->modifier & KM_SHIFT) {
1579 if (!stroke->stroke_cursor) {
1584 stroke);
1585 }
1586 }
1587 else {
1589 if (stroke->stroke_cursor != nullptr) {
1590 WM_paint_cursor_end(static_cast<wmPaintCursor *>(stroke->stroke_cursor));
1591 stroke->stroke_cursor = nullptr;
1592 }
1593 }
1594 }
1595
1596 float2 mouse;
1597 if (event->type == stroke->event_type && !first_modal) {
1598 if (event->val == KM_RELEASE) {
1599 mouse = {float(event->mval[0]), float(event->mval[1])};
1600 paint_stroke_line_constrain(stroke, mouse);
1601 paint_stroke_line_end(C, op, stroke, mouse);
1602 stroke_done(C, op, stroke);
1603 *stroke_p = nullptr;
1604 return OPERATOR_FINISHED;
1605 }
1606 }
1607 else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) {
1608 paint_stroke_line_end(C, op, stroke, sample_average.mouse);
1609 stroke_done(C, op, stroke);
1610 *stroke_p = nullptr;
1611 return OPERATOR_FINISHED;
1612 }
1613 else if (br->flag & BRUSH_LINE) {
1614 if (event->modifier & KM_ALT) {
1615 stroke->constrain_line = true;
1616 }
1617 else {
1618 stroke->constrain_line = false;
1619 }
1620
1621 mouse = {float(event->mval[0]), float(event->mval[1])};
1622 paint_stroke_line_constrain(stroke, mouse);
1623
1624 if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
1627 {
1628 copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
1629 }
1630 paint_calculate_rake_rotation(*stroke->ups, *br, mouse, mode, true);
1631 }
1632 }
1633 else if (first_modal ||
1634 /* regular dabs */
1635 (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
1636 /* airbrush */
1637 ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
1638 event->customdata == stroke->timer))
1639 {
1640 if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, pressure)) {
1641 if (stroke->stroke_started) {
1642 if (paint_space_stroke_enabled(*br, mode)) {
1643 if (paint_space_stroke(C, op, stroke, mouse, pressure)) {
1644 redraw = true;
1645 }
1646 }
1647 else {
1648 const float2 mouse_delta = mouse - stroke->last_mouse_position;
1649 stroke->stroke_distance += math::length(mouse_delta);
1650 paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
1651 redraw = true;
1652 }
1653 }
1654 }
1655 }
1656
1657 /* we want the stroke to have the first daub at the start location
1658 * instead of waiting till we have moved the space distance */
1659 if (first_dab && paint_space_stroke_enabled(*br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) {
1661 paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure);
1662 redraw = true;
1663 }
1664
1665 /* Don't update the paint cursor in #INBETWEEN_MOUSEMOVE events. */
1666 if (event->type != INBETWEEN_MOUSEMOVE) {
1667 wmWindow *window = CTX_wm_window(C);
1668 ARegion *region = CTX_wm_region(C);
1669
1670 if (region && (paint->flags & PAINT_SHOW_BRUSH)) {
1671 WM_paint_cursor_tag_redraw(window, region);
1672 }
1673 }
1674
1675 /* Draw for all events (even in between) otherwise updating the brush
1676 * display is noticeably delayed.
1677 */
1678 if (redraw && stroke->redraw) {
1679 stroke->redraw(C, stroke, false);
1680 }
1681
1683}
1684
1686{
1687 /* only when executed for the first time */
1688 if (!stroke->stroke_started) {
1689 PointerRNA firstpoint;
1690 PropertyRNA *strokeprop = RNA_struct_find_property(op->ptr, "stroke");
1691
1692 if (RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
1693 float2 mouse;
1694 RNA_float_get_array(&firstpoint, "mouse", mouse);
1695 stroke->stroke_started = stroke->test_start(C, op, mouse);
1696 }
1697 }
1698
1699 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "override_location");
1700 const bool override_location = prop && RNA_property_boolean_get(op->ptr, prop) &&
1701 stroke->get_location;
1702 if (stroke->stroke_started) {
1703 RNA_BEGIN (op->ptr, itemptr, "stroke") {
1704 if (override_location) {
1705 float2 mval;
1706 RNA_float_get_array(&itemptr, "mouse_event", mval);
1707
1708 float3 location;
1709 if (stroke->get_location(C, location, mval, false)) {
1710 RNA_float_set_array(&itemptr, "location", location);
1711 stroke->update_step(C, op, stroke, &itemptr);
1712 }
1713 }
1714 else {
1715 stroke->update_step(C, op, stroke, &itemptr);
1716 }
1717 }
1718 RNA_END;
1719 }
1720
1721 const bool ok = stroke->stroke_started;
1722
1723 stroke_done(C, op, stroke);
1724
1726}
1727
1729{
1730 stroke_done(C, op, stroke);
1731}
1732
1734{
1735 return &stroke->vc;
1736}
1737
1739{
1740 return stroke->mode_data.get();
1741}
1742
1744{
1745 return stroke->pen_flip;
1746}
1747
1749{
1750 return stroke->stroke_mode == BRUSH_STROKE_INVERT;
1751}
1752
1754{
1755 return stroke->stroke_distance;
1756}
1757
1758void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr<PaintModeData> mode_data)
1759{
1760 stroke->mode_data = std::move(mode_data);
1761}
1762
1764{
1765 return stroke->stroke_started;
1766}
1767
1768static const bToolRef *brush_tool_get(const bContext *C)
1769{
1771 const Object *ob = CTX_data_active_object(C);
1772 const ScrArea *area = CTX_wm_area(C);
1773 const ARegion *region = CTX_wm_region(C);
1774
1775 if (paint && ob && BKE_paint_brush(paint) &&
1776 (area && ELEM(area->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) &&
1777 (region && region->regiontype == RGN_TYPE_WINDOW))
1778 {
1779 if (area->runtime.tool && area->runtime.tool->runtime &&
1781 {
1782 return area->runtime.tool;
1783 }
1784 }
1785 return nullptr;
1786}
1787
1789{
1790 /* Check the current tool is a brush. */
1791 return brush_tool_get(C) != nullptr;
1792}
1793
1795{
1796 const bToolRef *tref = brush_tool_get(C);
1797 if (!tref) {
1798 return false;
1799 }
1800
1801 /* Don't use brush cursor when the tool sets its own cursor. */
1802 if (tref->runtime->cursor != WM_CURSOR_DEFAULT) {
1803 return false;
1804 }
1805
1806 return true;
1807}
1808
1809} // 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:1340
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1231
void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
Definition brush.cc:1367
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1210
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1510
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1226
int BKE_brush_input_samples_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1296
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:1673
void BKE_curve_forward_diff_tangent_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1701
ImBuf * BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool)
void BKE_image_pool_release_ibuf(Image *ima, ImBuf *ibuf, ImagePool *pool)
void BKE_paint_set_overlay_override(eOverlayFlags flag)
Definition paint.cc:294
PaintMode
Definition BKE_paint.hh:93
void paint_update_brush_rake_rotation(UnifiedPaintSettings &ups, const Brush &brush, float rotation)
Definition paint.cc:2027
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:641
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
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:2052
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:496
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float max_ff(float a, float b)
MINLINE float square_f(float a)
#define M_PI
#define M_PI_4
void mul_m4_v3(const float M[4][4], float r[3])
MINLINE void mul_v2_fl(float r[2], float f)
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_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])
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
@ MTEX_MAP_MODE_AREA
@ MTEX_MAP_MODE_RANDOM
@ MTEX_MAP_MODE_VIEW
@ TEX_IMAGE
@ MTEX_ANGLE_RANDOM
@ MTEX_ANGLE_RAKE
@ RV3D_PAINTING
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ 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 immVertex2fv(uint attr_id, const float data[2])
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:273
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
#define C
Definition RandGen.cpp:29
@ KM_ALT
Definition WM_types.hh:277
@ KM_SHIFT
Definition WM_types.hh:275
@ KM_RELEASE
Definition WM_types.hh:309
BMesh const char void * data
BPy_StructRNA * depsgraph
static T sum(const btAlignedObjectArray< T > &items)
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
#define sinf(x)
#define cosf(x)
#define atan2f(x, y)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
uint pos
float length(VecOp< float, D >) RET
int count
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
ccl_device_inline float average(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
bool is_cloth_deform_brush(const Brush &brush)
bool stroke_get_location_bvh(bContext *C, float out[3], const float mval[2], const bool force_original)
Definition sculpt.cc:4921
float object_space_radius_get(const ViewContext &vc, const Scene &scene, const Brush &brush, const float3 &location, const float scale_factor)
Definition sculpt.cc:115
static void paint_draw_smooth_cursor(bContext *C, const blender::int2 &xy, const blender::float2 &, void *customdata)
bool paint_supports_texture(PaintMode mode)
static const bToolRef * brush_tool_get(const bContext *C)
void(*)(const bContext *C, PaintStroke *stroke, bool final) StrokeRedraw
wmOperatorStatus paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
static int paint_space_stroke(bContext *C, wmOperator *op, PaintStroke *stroke, const float2 final_mouse, const float final_pressure)
bool paint_stroke_started(PaintStroke *stroke)
static bool sculpt_is_grab_tool(const Brush &br)
static float paint_stroke_overlapped_curve(const Brush &br, const float x, const float spacing)
static bool paint_stroke_use_jitter(const PaintMode mode, const Brush &brush, const bool invert)
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 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 float paint_stroke_integrate_overlap(const Brush &br, const float factor)
wmOperatorStatus 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 paint_stroke_use_dash(const Brush &brush)
ViewContext * paint_stroke_view_context(PaintStroke *stroke)
static void paint_draw_line_cursor(bContext *C, const blender::int2 &xy, const blender::float2 &, void *customdata)
static float paint_space_stroke_spacing_variable(bContext *C, const Scene *scene, PaintStroke *stroke, const float pressure, const float pressure_delta, const float length)
bool paint_brush_cursor_poll(bContext *C)
static bool image_paint_brush_type_require_inbetween_mouse_events(const Brush &brush, const PaintMode mode)
static void paint_stroke_line_end(bContext *C, wmOperator *op, PaintStroke *stroke, const float2 mouse)
static bool image_paint_brush_type_require_location(const Brush &brush, const PaintMode mode)
static void paint_stroke_line_constrain(PaintStroke *stroke, float2 &mouse)
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)
void(*)(bContext *C, wmOperator *op, PaintStroke *stroke, PointerRNA *itemptr) StrokeUpdateStep
float paint_stroke_distance_get(PaintStroke *stroke)
static void paint_stroke_add_sample(PaintStroke *stroke, const int input_samples, const float x, const float y, const float pressure)
bool paint_space_stroke_enabled(const Brush &br, PaintMode mode)
static bool paint_smooth_stroke(PaintStroke *stroke, const PaintSample *sample, const PaintMode mode, float2 &r_mouse, float &r_pressure)
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(const bContext *C, const Scene *scene, PaintStroke *stroke, const float size_pressure, const float spacing_pressure)
static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, PaintStroke *stroke, const float2 mval, float pressure)
static void paint_line_strokes_spacing(bContext *C, wmOperator *op, PaintStroke *stroke, const float spacing, float *length_residue, const float2 old_pos, const float2 new_pos)
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)
T length(const VecBase< T, Size > &a)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
@ BRUSH_STROKE_SMOOTH
@ BRUSH_STROKE_NORMAL
@ BRUSH_STROKE_INVERT
#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)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
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)
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
const ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
char brush_angle_mode
char brush_map_mode
float random_angle
struct Tex * tex
PaintCurvePoint * points
struct bToolRef * tool
ScrArea_Runtime runtime
struct ImageUser iuser
struct Image * ima
struct UnifiedPaintSettings unified_paint_settings
const ColorSpaceHandle * colorspace
ARegion * region
Definition ED_view3d.hh:77
Object * obact
Definition ED_view3d.hh:75
bToolRef_Runtime * runtime
std::unique_ptr< PaintModeData > mode_data
std::optional< RandomNumberGenerator > rng
PaintSample samples[PAINT_MAX_INPUT_SAMPLES]
int ymin
int xmin
wmEventModifierFlag modifier
Definition WM_types.hh:771
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
void * customdata
Definition WM_types.hh:804
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1070
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
uint len
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
int xy[2]
Definition wm_draw.cc:174
void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *)
Definition wm_draw.cc:1573
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:929
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
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)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)