Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/paint_stroke.cc
- This file was moved from source/blender/editors/sculpt_paint/paint_stroke.c.
| /* SPDX-License-Identifier: GPL-2.0-or-later | /* SPDX-License-Identifier: GPL-2.0-or-later | ||||
| * Copyright 2009 by Nicholas Bishop. All rights reserved. */ | * Copyright 2009 by Nicholas Bishop. All rights reserved. */ | ||||
| /** \file | /** \file | ||||
| * \ingroup edsculpt | * \ingroup edsculpt | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_even_spline.hh" | |||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_math_vector_types.hh" | |||||
| #include "BLI_rand.h" | #include "BLI_rand.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "PIL_time.h" | #include "PIL_time.h" | ||||
| #include "DNA_brush_types.h" | #include "DNA_brush_types.h" | ||||
| #include "DNA_curve_types.h" | #include "DNA_curve_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| Show All 10 Lines | |||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "GPU_immediate.h" | #include "GPU_immediate.h" | ||||
| #include "GPU_state.h" | #include "GPU_state.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_space_api.h" | |||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "IMB_imbuf_types.h" | #include "IMB_imbuf_types.h" | ||||
| #include "paint_intern.h" | #include "paint_intern.h" | ||||
| #include "sculpt_intern.h" | #include "sculpt_intern.h" | ||||
| #include <float.h> | #include <float.h> | ||||
| #include <math.h> | #include <math.h> | ||||
| //#define DEBUG_TIME | //#define DEBUG_TIME | ||||
| //#define DRAW_DEBUG_VIS | |||||
| using blender::float2; | |||||
| using blender::float3; | |||||
| #ifdef DEBUG_TIME | #ifdef DEBUG_TIME | ||||
| # include "PIL_time_utildefines.h" | # include "PIL_time_utildefines.h" | ||||
| #endif | #endif | ||||
| typedef struct PaintSample { | typedef struct PaintSample { | ||||
| float mouse[2]; | float mouse[2]; | ||||
| float pressure; | float pressure; | ||||
| } PaintSample; | } PaintSample; | ||||
| typedef struct PaintStrokePoint { | |||||
| float mouse_in[2], mouse_out[2]; | |||||
| float location[3]; | |||||
| float pressure, x_tilt, y_tilt; | |||||
| bool pen_flip; | |||||
| float size; | |||||
| } PaintStrokePoint; | |||||
| typedef struct PaintStroke { | typedef struct PaintStroke { | ||||
| void *mode_data; | void *mode_data; | ||||
| void *stroke_cursor; | void *stroke_cursor; | ||||
| wmTimer *timer; | wmTimer *timer; | ||||
| struct RNG *rng; | struct RNG *rng; | ||||
| /* Cached values */ | /* Cached values */ | ||||
| ViewContext vc; | ViewContext vc; | ||||
| Brush *brush; | Brush *brush; | ||||
| UnifiedPaintSettings *ups; | UnifiedPaintSettings *ups; | ||||
| /* used for lines and curves */ | /* used for lines and curves */ | ||||
| ListBase line; | ListBase line; | ||||
| bool need_roll_mapping; | |||||
| int stroke_sample_index; | |||||
| /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs | /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs | ||||
| * to smooth the stroke */ | * to smooth the stroke */ | ||||
| PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; | PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; | ||||
| int num_samples; | int num_samples; | ||||
| int cur_sample; | int cur_sample; | ||||
| int tot_samples; | int tot_samples; | ||||
| PaintStrokePoint points[PAINT_MAX_INPUT_SAMPLES]; | |||||
brecht: Can you add a comment explaining the difference between samples and points? | |||||
| int num_points; | |||||
| int cur_point; | |||||
| int tot_points; | |||||
| BezierSpline2f *spline; | |||||
| BezierSpline3f *world_spline; | |||||
| float last_mouse_position[2]; | float last_mouse_position[2]; | ||||
| float last_world_space_position[3]; | float last_world_space_position[3]; | ||||
| float last_scene_spacing_delta[3]; | float last_scene_spacing_delta[3]; | ||||
| bool stroke_over_mesh; | bool stroke_over_mesh; | ||||
| /* space distance covered so far */ | /* space distance covered so far */ | ||||
| float stroke_distance; | float stroke_distance; | ||||
| float stroke_distance_world; | |||||
| /* Set whether any stroke step has yet occurred | /* Set whether any stroke step has yet occurred | ||||
| * e.g. in sculpt mode, stroke doesn't start until cursor | * e.g. in sculpt mode, stroke doesn't start until cursor | ||||
| * passes over the mesh */ | * passes over the mesh */ | ||||
| bool stroke_started; | bool stroke_started; | ||||
| /* Set when enough motion was found for rake rotation */ | /* Set when enough motion was found for rake rotation */ | ||||
| bool rake_started; | bool rake_started; | ||||
| /* event that started stroke, for modal() return */ | /* event that started stroke, for modal() return */ | ||||
| int event_type; | int event_type; | ||||
| /* check if stroke variables have been initialized */ | /* check if stroke variables have been initialized */ | ||||
| bool stroke_init; | bool stroke_init; | ||||
| /* check if various brush mapping variables have been initialized */ | /* check if various brush mapping variables have been initialized */ | ||||
| bool brush_init; | bool brush_init; | ||||
| float initial_mouse[2]; | float initial_mouse[2]; | ||||
| /* cached_pressure stores initial pressure for size pressure influence mainly */ | /* cached_pressure stores initial pressure for size pressure influence mainly */ | ||||
| float cached_size_pressure; | float cached_size_pressure; | ||||
| /* last pressure will store last pressure value for use in interpolation for space strokes */ | /* last pressure will store last pressure value for use in interpolation for space strokes */ | ||||
| float last_pressure; | float last_pressure; | ||||
| int stroke_mode; | int stroke_mode; | ||||
| float spacing_raw; | |||||
| float last_tablet_event_pressure; | float last_tablet_event_pressure; | ||||
| float zoom_2d; | float zoom_2d; | ||||
| int pen_flip; | int pen_flip; | ||||
| /* Tilt, as read from the event. */ | /* Tilt, as read from the event. */ | ||||
| float x_tilt; | float x_tilt; | ||||
| float y_tilt; | float y_tilt; | ||||
| /* line constraint */ | /* line constraint */ | ||||
| bool constrain_line; | bool constrain_line; | ||||
| float constrained_pos[2]; | float constrained_pos[2]; | ||||
| StrokeGetLocation get_location; | StrokeGetLocation get_location; | ||||
| StrokeTestStart test_start; | StrokeTestStart test_start; | ||||
| StrokeUpdateStep update_step; | StrokeUpdateStep update_step; | ||||
| StrokeRedraw redraw; | StrokeRedraw redraw; | ||||
| StrokeDone done; | StrokeDone done; | ||||
| bool original; /* Ray-cast original mesh at start of stroke. */ | bool original; /* Ray-cast original mesh at start of stroke. */ | ||||
| void *debug_draw_handle; | |||||
| } PaintStroke; | } PaintStroke; | ||||
| static int paint_stroke_max_points(const Paint *paint, PaintStroke *stroke) | |||||
| { | |||||
| if (!stroke->need_roll_mapping) { | |||||
| return 1; | |||||
| } | |||||
| float s = max_ff(stroke->spacing_raw, 0.05); | |||||
| int tot = (int)ceilf(1.0f / s) + 2; | |||||
| tot = max_ii(tot, 5); | |||||
| return tot; | |||||
Not Done Inline ActionsThere is no clear relation between this logic and PAINT_MAX_INPUT_SAMPLES. I think things should be structured in such a way that it's obvious there is enough space in the array. brecht: There is no clear relation between this logic and `PAINT_MAX_INPUT_SAMPLES`. I think things… | |||||
| } | |||||
| static void paint_stroke_add_point(const Paint *paint, | |||||
| PaintStroke *stroke, | |||||
| const float mouse_in[2], | |||||
| const float mouse_out[2], | |||||
| const float loc[3], | |||||
| float size, | |||||
| float pressure, | |||||
| bool pen_flip, | |||||
| float x_tilt, | |||||
| float y_tilt) | |||||
| { | |||||
| PaintStrokePoint *point = &stroke->points[stroke->cur_point]; | |||||
| int max_points = paint_stroke_max_points(paint, stroke); | |||||
| point->size = size; | |||||
| copy_v2_v2(point->mouse_in, mouse_in); | |||||
| copy_v2_v2(point->mouse_out, mouse_out); | |||||
| point->x_tilt = x_tilt; | |||||
| point->y_tilt = y_tilt; | |||||
| point->pen_flip = pen_flip; | |||||
| copy_v3_v3(point->location, loc); | |||||
| point->pressure = pressure; | |||||
| stroke->cur_point++; | |||||
| if (stroke->cur_point >= max_points) { | |||||
| stroke->cur_point = 0; | |||||
| } | |||||
| if (stroke->num_points < max_points) { | |||||
| stroke->num_points++; | |||||
| } | |||||
| } | |||||
| static void paint_project_cubic(bContext *C, | |||||
| PaintStroke *stroke, | |||||
| blender::CubicBezier<float, 2> &bezier2d, | |||||
| blender::CubicBezier<float, 3> &bezier3d) | |||||
| { | |||||
| float2 mvals[4]; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| for (int j = 0; j < 2; j++) { | |||||
| mvals[i][j] = bezier2d.ps[i][j]; | |||||
| } | |||||
| } | |||||
| float last_z_pos[3]; | |||||
| bool have_last_z = false; | |||||
| for (int i = 0; i < stroke->world_spline->order(); i++) { | |||||
| if (!SCULPT_stroke_get_location(C, bezier3d.ps[i], mvals[i], true)) { | |||||
| if (!have_last_z) { | |||||
| if (stroke->world_spline->segments.size() > 0) { | |||||
| copy_v3_v3(last_z_pos, | |||||
| stroke->world_spline->segments[stroke->world_spline->segments.size() - 1] | |||||
| .bezier.ps[3]); | |||||
| } | |||||
| else { | |||||
| copy_v3_v3(last_z_pos, stroke->last_world_space_position); | |||||
| } | |||||
| have_last_z = true; | |||||
| } | |||||
| ED_view3d_win_to_3d( | |||||
| CTX_wm_view3d(C), CTX_wm_region(C), last_z_pos, mvals[i], bezier3d.ps[i]); | |||||
| } | |||||
| else { | |||||
| copy_v3_v3(last_z_pos, bezier3d.ps[i]); | |||||
| have_last_z = true; | |||||
| } | |||||
| } | |||||
| bezier3d.update(); | |||||
| } | |||||
| static void paint_brush_make_spline(bContext *C, PaintStroke *stroke) | |||||
| { | |||||
| float a[2], b[2], c[2], d[2]; | |||||
| if (stroke->num_points < 4) { | |||||
| return; | |||||
| } | |||||
| int cur = (stroke->cur_point - 1 + stroke->num_points) % stroke->num_points; | |||||
| int ia = (cur - 2 + stroke->num_points) % stroke->num_points; | |||||
| int id = (cur - 1 + stroke->num_points) % stroke->num_points; | |||||
| int ib = (cur - 3 + stroke->num_points) % stroke->num_points; | |||||
| int ic = (cur - 0 + stroke->num_points) % stroke->num_points; | |||||
| copy_v2_v2(a, stroke->points[ia].mouse_out); | |||||
| copy_v2_v2(b, stroke->points[ib].mouse_out); | |||||
| copy_v2_v2(c, stroke->points[ic].mouse_out); | |||||
| copy_v2_v2(d, stroke->points[id].mouse_out); | |||||
| float scale = 1.0; | |||||
| scale /= 3.0f; | |||||
| float tmp1[2]; | |||||
| float tmp2[2]; | |||||
| sub_v2_v2v2(tmp1, d, a); | |||||
| sub_v2_v2v2(tmp2, a, b); | |||||
| interp_v2_v2v2(b, tmp1, tmp2, 0.5f); | |||||
| mul_v2_fl(b, scale); | |||||
| add_v2_v2(b, a); | |||||
| sub_v2_v2v2(tmp1, a, d); | |||||
| sub_v2_v2v2(tmp2, d, c); | |||||
| interp_v2_v2v2(c, tmp1, tmp2, 0.5f); | |||||
| mul_v2_fl(c, scale); | |||||
| add_v2_v2(c, d); | |||||
| blender::CubicBezier<float, 2> bez(a, b, c, d); | |||||
| bez.update(); | |||||
| stroke->spline->add(bez); | |||||
| blender::CubicBezier<float, 3> bez3d; | |||||
| paint_project_cubic(C, stroke, bez, bez3d); | |||||
| bez3d.update(); | |||||
| stroke->world_spline->add(bez3d); | |||||
| while (stroke->spline->segments.size() > paint_stroke_max_points(nullptr, stroke) + 1) { | |||||
| stroke->spline->pop_front(); | |||||
| } | |||||
| while (stroke->world_spline->segments.size() > paint_stroke_max_points(nullptr, stroke) + 1) { | |||||
| stroke->stroke_distance_world += stroke->world_spline->segments[0].bezier.length; | |||||
| stroke->world_spline->pop_front(); | |||||
| } | |||||
| } | |||||
| static void paint_brush_cubic_vis(const bContext *C, ARegion *region, void *userdata) | |||||
| { | |||||
| PaintStroke *stroke = (PaintStroke *)userdata; | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| if (!ob || !ob->sculpt || !ob->sculpt->cache || !stroke->world_spline) { | |||||
| return; | |||||
| } | |||||
| auto *spline = stroke->world_spline; | |||||
| if (spline->segments.size() == 0) { | |||||
| return; | |||||
| } | |||||
| #ifndef DRAW_DEBUG_VIS | |||||
| if (spline->segments.size() == paint_stroke_max_points(nullptr, stroke)) { | |||||
| return; | |||||
| } | |||||
| #endif | |||||
| GPU_line_smooth(false); | |||||
| GPU_depth_test(GPU_DEPTH_NONE); | |||||
| GPU_depth_mask(false); | |||||
| GPU_blend(GPU_BLEND_ALPHA); | |||||
| uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); | |||||
| immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); // GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); | |||||
| immUniformColor4ub(255, 150, 0, 185); | |||||
| int steps = 256; | |||||
| immUniformColor4ub(45, 75, 255, 255); | |||||
| immBegin(GPU_PRIM_LINE_STRIP, steps); | |||||
| float s = 0.0f; | |||||
| float ds = 0.99999f * spline->length / (steps - 1); | |||||
| for (int i = 0; i < steps; i++, s += ds) { | |||||
| float3 co = spline->evaluate(s); | |||||
| mul_v3_m4v3(co, ob->object_to_world, co); | |||||
| immVertex3fv(pos, co); | |||||
| } | |||||
| immEnd(); | |||||
| /* Control points. */ | |||||
| #if defined(DRAW_DEBUG_VIS) && 0 | |||||
| immUniformColor4ub(255, 0, 0, 170); | |||||
| int components = spline->order(); | |||||
| immBegin(GPU_PRIM_POINTS, spline->segments.size() * components); | |||||
| for (auto &segment : spline->segments) { | |||||
| for (size_t i = 0; i < components; i++) { | |||||
| float3 co = segment.bezier.ps[i]; | |||||
| mul_v3_m4v3(co, ob->object_to_world, co); | |||||
| immVertex3fv(pos, co); | |||||
| } | |||||
| } | |||||
| immEnd(); | |||||
| #endif | |||||
| #if defined(DRAW_DEBUG_VIS) && 1 /* Inflection points. */ | |||||
| immUniformColor4ub(0, 255, 0, 100); | |||||
| immBegin(GPU_PRIM_POINTS, spline->inflection_points.size()); | |||||
| for (float t : spline->inflection_points) { | |||||
| float3 co = spline->evaluate(t); | |||||
| mul_v3_m4v3(co, ob->object_to_world, co); | |||||
| immVertex3fv(pos, co); | |||||
| } | |||||
| immEnd(); | |||||
| #endif | |||||
| immUnbindProgram(); | |||||
| GPU_blend(GPU_BLEND_NONE); | |||||
| GPU_line_smooth(false); | |||||
| } | |||||
| /*** Cursors ***/ | /*** Cursors ***/ | ||||
| static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) | static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) | ||||
| { | { | ||||
| Paint *paint = BKE_paint_get_active_from_context(C); | Paint *paint = BKE_paint_get_active_from_context(C); | ||||
| Brush *brush = BKE_paint_brush(paint); | Brush *brush = BKE_paint_brush(paint); | ||||
| PaintStroke *stroke = customdata; | PaintStroke *stroke = (PaintStroke *)customdata; | ||||
| if (stroke && brush) { | if (stroke && brush) { | ||||
| GPU_line_smooth(true); | GPU_line_smooth(true); | ||||
| GPU_blend(GPU_BLEND_ALPHA); | GPU_blend(GPU_BLEND_ALPHA); | ||||
| ARegion *region = stroke->vc.region; | ARegion *region = stroke->vc.region; | ||||
| uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); | uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); | ||||
| Show All 13 Lines | if (stroke && brush) { | ||||
| GPU_blend(GPU_BLEND_NONE); | GPU_blend(GPU_BLEND_NONE); | ||||
| GPU_line_smooth(false); | GPU_line_smooth(false); | ||||
| } | } | ||||
| } | } | ||||
| static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) | static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) | ||||
| { | { | ||||
| Paint *paint = BKE_paint_get_active_from_context(C); | Paint *paint = BKE_paint_get_active_from_context(C); | ||||
| PaintStroke *stroke = customdata; | PaintStroke *stroke = (PaintStroke *)customdata; | ||||
| GPU_line_smooth(true); | GPU_line_smooth(true); | ||||
| uint shdr_pos = GPU_vertformat_attr_add( | uint shdr_pos = GPU_vertformat_attr_add( | ||||
| immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); | immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); | ||||
| immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); | immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); | ||||
| ▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | switch (mode) { | ||||
| case PAINT_MODE_SCULPT: | case PAINT_MODE_SCULPT: | ||||
| return brush->flag & BRUSH_SCENE_SPACING; | return brush->flag & BRUSH_SCENE_SPACING; | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode)) | static bool paint_tool_raycast_original(Brush *brush, ePaintMode /*mode*/) | ||||
| { | { | ||||
| return brush->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT); | return brush->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT); | ||||
| } | } | ||||
| static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) | static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) | ||||
| { | { | ||||
| if (brush->flag & BRUSH_ANCHORED) { | if (brush->flag & BRUSH_ANCHORED) { | ||||
| return false; | return false; | ||||
| ▲ Show 20 Lines • Show All 350 Lines • ▼ Show 20 Lines | #endif | ||||
| if (paint_stroke_use_dash(brush)) { | if (paint_stroke_use_dash(brush)) { | ||||
| int dash_samples = stroke->tot_samples % brush->dash_samples; | int dash_samples = stroke->tot_samples % brush->dash_samples; | ||||
| float dash = (float)dash_samples / (float)brush->dash_samples; | float dash = (float)dash_samples / (float)brush->dash_samples; | ||||
| if (dash > brush->dash_ratio) { | if (dash > brush->dash_ratio) { | ||||
| add_step = false; | add_step = false; | ||||
| } | } | ||||
| } | } | ||||
| paint_stroke_add_point(paint, | |||||
| stroke, | |||||
| mval, | |||||
| mouse_out, | |||||
| location, | |||||
| ups->pixel_radius, | |||||
| pressure, | |||||
| stroke->pen_flip, | |||||
| stroke->x_tilt, | |||||
| stroke->y_tilt); | |||||
| if (stroke->need_roll_mapping) { | |||||
| paint_brush_make_spline(C, stroke); | |||||
| } | |||||
| if (!add_step) { | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| if (region) { | |||||
| ED_region_tag_redraw(region); | |||||
| } | |||||
| stroke->tot_samples++; | |||||
| return; | |||||
| } | |||||
| /* Add to stroke */ | /* Add to stroke */ | ||||
| if (add_step) { | PaintStrokePoint *point; | ||||
| PaintStrokePoint temp; | |||||
| if (stroke->need_roll_mapping) { | |||||
| if (stroke->spline->segments.size() < paint_stroke_max_points(paint, stroke)) { | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| if (region) { | |||||
| ED_region_tag_redraw(region); | |||||
| } | |||||
| stroke->tot_samples++; | |||||
| return; | |||||
| } | |||||
| int cur = stroke->cur_point - (paint_stroke_max_points(paint, stroke) >> 1) - 2; | |||||
| cur = (cur + stroke->num_points) % stroke->num_points; | |||||
| PaintStrokePoint *p1 = stroke->points + ((cur + stroke->num_points) % stroke->num_points); | |||||
| PaintStrokePoint *p2 = stroke->points + ((cur - 1 + stroke->num_points) % stroke->num_points); | |||||
| point = &temp; | |||||
| temp = *p1; | |||||
| interp_v3_v3v3(temp.location, p1->location, p2->location, 0.5f); | |||||
| interp_v2_v2v2(temp.mouse_in, p1->mouse_in, p2->mouse_in, 0.5f); | |||||
| interp_v2_v2v2(temp.mouse_out, p1->mouse_out, p2->mouse_out, 0.5f); | |||||
| } | |||||
| else { | |||||
| point = stroke->points + ((stroke->cur_point - 1 + stroke->num_points) % stroke->num_points); | |||||
| } | |||||
| RNA_collection_add(op->ptr, "stroke", &itemptr); | RNA_collection_add(op->ptr, "stroke", &itemptr); | ||||
| RNA_float_set(&itemptr, "size", ups->pixel_radius); | RNA_float_set(&itemptr, "size", point->size); | ||||
| RNA_float_set_array(&itemptr, "location", location); | RNA_float_set_array(&itemptr, "location", point->location); | ||||
| /* Mouse coordinates modified by the stroke type options. */ | /* Mouse coordinates modified by the stroke type options. */ | ||||
| RNA_float_set_array(&itemptr, "mouse", mouse_out); | RNA_float_set_array(&itemptr, "mouse", point->mouse_out); | ||||
| /* Original mouse coordinates. */ | /* Original mouse coordinates. */ | ||||
| RNA_float_set_array(&itemptr, "mouse_event", mval); | RNA_float_set_array(&itemptr, "mouse_event", point->mouse_in); | ||||
| RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); | RNA_boolean_set(&itemptr, "pen_flip", point->pen_flip); | ||||
| RNA_float_set(&itemptr, "pressure", pressure); | RNA_float_set(&itemptr, "pressure", point->pressure); | ||||
| RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt); | RNA_float_set(&itemptr, "x_tilt", point->x_tilt); | ||||
| RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt); | RNA_float_set(&itemptr, "y_tilt", point->y_tilt); | ||||
| stroke->update_step(C, op, stroke, &itemptr); | stroke->update_step(C, op, stroke, &itemptr); | ||||
| /* don't record this for now, it takes up a lot of memory when doing long | /* don't record this for now, it takes up a lot of memory when doing long | ||||
| * strokes with small brush size, and operators have register disabled */ | * strokes with small brush size, and operators have register disabled */ | ||||
| RNA_collection_clear(op->ptr, "stroke"); | RNA_collection_clear(op->ptr, "stroke"); | ||||
| } | |||||
| stroke->tot_samples++; | stroke->tot_samples++; | ||||
| } | } | ||||
| /* Returns zero if no sculpt changes should be made, non-zero otherwise */ | /* Returns zero if no sculpt changes should be made, non-zero otherwise */ | ||||
| static bool paint_smooth_stroke(PaintStroke *stroke, | static bool paint_smooth_stroke(PaintStroke *stroke, | ||||
| const PaintSample *sample, | const PaintSample *sample, | ||||
| ePaintMode mode, | ePaintMode mode, | ||||
| ▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | static float paint_space_stroke_spacing(bContext *C, | ||||
| float spacing = stroke->brush->spacing; | float spacing = stroke->brush->spacing; | ||||
| /* apply spacing pressure */ | /* apply spacing pressure */ | ||||
| if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) { | if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) { | ||||
| spacing = spacing * (1.5f - spacing_pressure); | spacing = spacing * (1.5f - spacing_pressure); | ||||
| } | } | ||||
| stroke->spacing_raw = spacing * 0.01; | |||||
| if (SCULPT_is_cloth_deform_brush(brush)) { | if (SCULPT_is_cloth_deform_brush(brush)) { | ||||
| /* The spacing in tools that use the cloth solver should not be affected by the brush radius to | /* The spacing in tools that use the cloth solver should not be affected by the brush radius to | ||||
| * avoid affecting the simulation update rate when changing the radius of the brush. | * avoid affecting the simulation update rate when changing the radius of the brush. | ||||
| * With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2 | * With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2 | ||||
| * pixels movement of the cursor. */ | * pixels movement of the cursor. */ | ||||
| size_clamp = 100.0f; | size_clamp = 100.0f; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 178 Lines • ▼ Show 20 Lines | PaintStroke *paint_stroke_new(bContext *C, | ||||
| StrokeGetLocation get_location, | StrokeGetLocation get_location, | ||||
| StrokeTestStart test_start, | StrokeTestStart test_start, | ||||
| StrokeUpdateStep update_step, | StrokeUpdateStep update_step, | ||||
| StrokeRedraw redraw, | StrokeRedraw redraw, | ||||
| StrokeDone done, | StrokeDone done, | ||||
| int event_type) | int event_type) | ||||
| { | { | ||||
| struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | ||||
| PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); | PaintStroke *stroke = (PaintStroke *)MEM_callocN(sizeof(PaintStroke), "PaintStroke"); | ||||
| ToolSettings *toolsettings = CTX_data_tool_settings(C); | ToolSettings *toolsettings = CTX_data_tool_settings(C); | ||||
| UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; | UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; | ||||
| Paint *p = BKE_paint_get_active_from_context(C); | Paint *p = BKE_paint_get_active_from_context(C); | ||||
| Brush *br = stroke->brush = BKE_paint_brush(p); | Brush *br = stroke->brush = BKE_paint_brush(p); | ||||
| RegionView3D *rv3d = CTX_wm_region_view3d(C); | RegionView3D *rv3d = CTX_wm_region_view3d(C); | ||||
| float zoomx, zoomy; | float zoomx, zoomy; | ||||
| ED_view3d_viewcontext_init(C, &stroke->vc, depsgraph); | ED_view3d_viewcontext_init(C, &stroke->vc, depsgraph); | ||||
| ARegion *region = CTX_wm_region(C); | |||||
| stroke->debug_draw_handle = ED_region_draw_cb_activate( | |||||
| region->type, paint_brush_cubic_vis, stroke, REGION_DRAW_POST_VIEW); | |||||
| stroke->get_location = get_location; | stroke->get_location = get_location; | ||||
| stroke->test_start = test_start; | stroke->test_start = test_start; | ||||
| stroke->update_step = update_step; | stroke->update_step = update_step; | ||||
| stroke->redraw = redraw; | stroke->redraw = redraw; | ||||
| stroke->done = done; | stroke->done = done; | ||||
| stroke->event_type = event_type; /* for modal, return event */ | stroke->event_type = event_type; /* for modal, return event */ | ||||
| stroke->ups = ups; | stroke->ups = ups; | ||||
| stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); | stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); | ||||
| stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C)); | stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C)); | ||||
| get_imapaint_zoom(C, &zoomx, &zoomy); | get_imapaint_zoom(C, &zoomx, &zoomy); | ||||
| stroke->zoom_2d = max_ff(zoomx, zoomy); | stroke->zoom_2d = max_ff(zoomx, zoomy); | ||||
| if (br->mtex.tex && br->mtex.brush_map_mode == MTEX_MAP_MODE_ROLL) { | |||||
| stroke->need_roll_mapping = true; | |||||
| } | |||||
| if (br->mask_mtex.tex && br->mask_mtex.brush_map_mode == MTEX_MAP_MODE_ROLL) { | |||||
| stroke->need_roll_mapping = true; | |||||
| } | |||||
| if (stroke->need_roll_mapping) { | |||||
| stroke->spline = MEM_new<BezierSpline2f>("BezierSpline2f"); | |||||
| stroke->world_spline = MEM_new<BezierSpline3f>("BezierSpline3f"); | |||||
| } | |||||
| if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { | if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { | ||||
| if (br->flag & BRUSH_CURVE) { | if (br->flag & BRUSH_CURVE) { | ||||
| RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); | RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); | ||||
| } | } | ||||
| } | } | ||||
| /* initialize here */ | /* initialize here */ | ||||
| ups->overlap_factor = 1.0; | ups->overlap_factor = 1.0; | ||||
| ups->stroke_active = true; | ups->stroke_active = true; | ||||
| Show All 11 Lines | PaintStroke *paint_stroke_new(bContext *C, | ||||
| } | } | ||||
| /* initialize here to avoid initialization conflict with threaded strokes */ | /* initialize here to avoid initialization conflict with threaded strokes */ | ||||
| BKE_curvemapping_init(br->curve); | BKE_curvemapping_init(br->curve); | ||||
| if (p->flags & PAINT_USE_CAVITY_MASK) { | if (p->flags & PAINT_USE_CAVITY_MASK) { | ||||
| BKE_curvemapping_init(p->cavity_curve); | BKE_curvemapping_init(p->cavity_curve); | ||||
| } | } | ||||
| BKE_paint_set_overlay_override(br->overlay_flags); | BKE_paint_set_overlay_override((eOverlayFlags)br->overlay_flags); | ||||
| ups->start_pixel_radius = BKE_brush_size_get(CTX_data_scene(C), br); | ups->start_pixel_radius = BKE_brush_size_get(CTX_data_scene(C), br); | ||||
| return stroke; | return stroke; | ||||
| } | } | ||||
| void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke) | void paint_stroke_free(bContext *C, wmOperator * /*op*/, PaintStroke *stroke) | ||||
| { | { | ||||
| RegionView3D *rv3d = CTX_wm_region_view3d(C); | RegionView3D *rv3d = CTX_wm_region_view3d(C); | ||||
| if (rv3d) { | if (rv3d) { | ||||
| rv3d->rflag &= ~RV3D_PAINTING; | rv3d->rflag &= ~RV3D_PAINTING; | ||||
| } | } | ||||
| BKE_paint_set_overlay_override(0); | BKE_paint_set_overlay_override((eOverlayFlags)0); | ||||
| if (stroke == NULL) { | if (stroke == NULL) { | ||||
| return; | return; | ||||
| } | } | ||||
| UnifiedPaintSettings *ups = stroke->ups; | UnifiedPaintSettings *ups = stroke->ups; | ||||
| ups->draw_anchored = false; | ups->draw_anchored = false; | ||||
| ups->stroke_active = false; | ups->stroke_active = false; | ||||
| if (stroke->timer) { | if (stroke->timer) { | ||||
| WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); | WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); | ||||
| } | } | ||||
| if (stroke->rng) { | if (stroke->rng) { | ||||
| BLI_rng_free(stroke->rng); | BLI_rng_free(stroke->rng); | ||||
| } | } | ||||
| if (stroke->stroke_cursor) { | if (stroke->stroke_cursor) { | ||||
| WM_paint_cursor_end(stroke->stroke_cursor); | WM_paint_cursor_end((wmPaintCursor *)stroke->stroke_cursor); | ||||
| } | } | ||||
| BLI_freelistN(&stroke->line); | BLI_freelistN(&stroke->line); | ||||
| ARegion *region = CTX_wm_region(C); | |||||
| ED_region_draw_cb_exit(region->type, stroke->debug_draw_handle); | |||||
| ED_region_tag_redraw(region); | |||||
| MEM_delete<BezierSpline3f>(stroke->world_spline); | |||||
| MEM_delete<BezierSpline2f>(stroke->spline); | |||||
| MEM_SAFE_FREE(stroke); | MEM_SAFE_FREE(stroke); | ||||
| } | } | ||||
| static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) | static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) | ||||
| { | { | ||||
| UnifiedPaintSettings *ups = stroke->ups; | UnifiedPaintSettings *ups = stroke->ups; | ||||
| /* reset rotation here to avoid doing so in cursor display */ | /* reset rotation here to avoid doing so in cursor display */ | ||||
| Show All 33 Lines | if (br->sculpt_tool == SCULPT_TOOL_CLOTH || SCULPT_is_cloth_deform_brush(br)) { | ||||
| /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do | /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do | ||||
| * not support dynamic size, stroke spacing needs to be enabled so it is possible to control | * not support dynamic size, stroke spacing needs to be enabled so it is possible to control | ||||
| * whether the simulation runs constantly or only when the brush moves when using the cloth | * whether the simulation runs constantly or only when the brush moves when using the cloth | ||||
| * grab brushes. */ | * grab brushes. */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (mode == PAINT_MODE_SCULPT_CURVES && | if (mode == PAINT_MODE_SCULPT_CURVES && | ||||
| !curves_sculpt_brush_uses_spacing(br->curves_sculpt_tool)) { | !curves_sculpt_brush_uses_spacing((eBrushCurvesSculptTool)br->curves_sculpt_tool)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| return paint_supports_dynamic_size(br, mode); | return paint_supports_dynamic_size(br, mode); | ||||
| } | } | ||||
| static bool sculpt_is_grab_tool(Brush *br) | static bool sculpt_is_grab_tool(Brush *br) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 399 Lines • ▼ Show 20 Lines | int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p) | ||||
| * pressure. */ | * pressure. */ | ||||
| if (event->type == TIMER) { | if (event->type == TIMER) { | ||||
| pressure = stroke->last_tablet_event_pressure; | pressure = stroke->last_tablet_event_pressure; | ||||
| } | } | ||||
| else { | else { | ||||
| stroke->last_tablet_event_pressure = pressure; | stroke->last_tablet_event_pressure = pressure; | ||||
| } | } | ||||
| if (!stroke->need_roll_mapping) { | |||||
| paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); | paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); | ||||
| paint_stroke_sample_average(stroke, &sample_average); | paint_stroke_sample_average(stroke, &sample_average); | ||||
| } | |||||
| else { | |||||
| sample_average.mouse[0] = (float)event->mval[0]; | |||||
| sample_average.mouse[1] = (float)event->mval[1]; | |||||
| sample_average.pressure = pressure; | |||||
| } | |||||
| if (stroke->stroke_sample_index == 0) { | |||||
| stroke->last_mouse_position[0] = event->mval[0]; | |||||
| stroke->last_mouse_position[1] = event->mval[1]; | |||||
| } | |||||
| stroke->stroke_sample_index++; | |||||
| /* Tilt. */ | /* Tilt. */ | ||||
| if (WM_event_is_tablet(event)) { | if (WM_event_is_tablet(event)) { | ||||
| stroke->x_tilt = event->tablet.x_tilt; | stroke->x_tilt = event->tablet.x_tilt; | ||||
| stroke->y_tilt = event->tablet.y_tilt; | stroke->y_tilt = event->tablet.y_tilt; | ||||
| } | } | ||||
| #ifdef WITH_INPUT_NDOF | #ifdef WITH_INPUT_NDOF | ||||
| ▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | if (p && ob && BKE_paint_brush(p) && | ||||
| /* Check the current tool is a brush. */ | /* Check the current tool is a brush. */ | ||||
| bToolRef *tref = area->runtime.tool; | bToolRef *tref = area->runtime.tool; | ||||
| if (tref && tref->runtime && tref->runtime->data_block[0]) { | if (tref && tref->runtime && tref->runtime->data_block[0]) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| void paint_stroke_spline_uv( | |||||
| PaintStroke *stroke, StrokeCache *cache, const float co[3], float r_out[3], float r_tan[3]) | |||||
| { | |||||
| float3 tan; | |||||
| float3 p = stroke->world_spline->closest_point(co, r_out[1], tan, r_out[0]); | |||||
| r_tan = tan; | |||||
| r_out[0] = len_v3v3(p, co); | |||||
| r_out[2] = 0.0f; | |||||
| float3 vec = p - float3(co); | |||||
| float3 vec2; | |||||
| cross_v3_v3v3(vec2, vec, tan); | |||||
| if (dot_v3v3(vec2, cache->view_normal) < 0.0f) { | |||||
| r_out[0] = -r_out[0]; | |||||
| } | |||||
| r_out[1] += stroke->stroke_distance_world; | |||||
| } | |||||
| float paint_stroke_spline_length(PaintStroke *stroke) | |||||
| { | |||||
| return stroke->world_spline->length; | |||||
| } | |||||
Can you add a comment explaining the difference between samples and points?