diff options
-rw-r--r-- | source/blender/blenlib/BLI_even_spline.hh | 733 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_intern.h | 16 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.cc (renamed from source/blender/editors/sculpt_paint/paint_stroke.c) | 399 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.cc | 31 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_texture_types.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 1 |
8 files changed, 1167 insertions, 18 deletions
diff --git a/source/blender/blenlib/BLI_even_spline.hh b/source/blender/blenlib/BLI_even_spline.hh new file mode 100644 index 00000000000..4b80df0202c --- /dev/null +++ b/source/blender/blenlib/BLI_even_spline.hh @@ -0,0 +1,733 @@ +#pragma once + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" + +#include "BLI_math.h" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include <cstdio> +#include <utility> + +//#define FINITE_DIFF + +/* + * Arc length parameterized spline library. + */ +namespace blender { +/* + Abstract curve interface. + +template<typename Float> class Curve { + using Vector = vec_base<Float, 2>; + + public: + Float length; + + Vector evaluate(Float s); + Vector derivative(Float s); + Vector derivative2(Float s); + Float curvature(float s); + + void update(); +}; +*/ + +/* +comment: Reduce algebra script; + +on factor; +off period; + +procedure bez(a, b); + a + (b - a) * t; + +lin := bez(k1, k2); +quad := bez(lin, sub(k2=k3, k1=k2, lin)); + +cubic := bez(quad, sub(k3=k4, k2=k3, k1=k2, quad)); +dcubic := df(cubic, t); +icubic := int(cubic, t); + +x1 := 0; +y1 := 0; + +dx := sub(k1=x1, k2=x2, k3=x3, k4=x4, dcubic); +dy := sub(k1=y1, k2=y2, k3=y3, k4=y4, dcubic); +darc := sqrt(dx**2 + dy**2); + +arcstep := darc*dt + 0.5*df(darc, t)*dt*dt; + +d2x := df(dx / darc, t); +d2y := df(dy / darc, t); + +gentran +begin +declare << +x1,x2,x3,x4 : float; +y1,y2,y3,y4 : float; +dt,t : float; +>>; +return eval(arcstep) +end; + +on fort; +cubic; +dcubic; +icubic; +arcstep; +d2x; +d2y; +off fort; + +*/ +template<typename Float, int axes = 2, int table_size = 512> class CubicBezier { + using Vector = vec_base<Float, axes>; + + public: + Vector ps[4]; + + CubicBezier(Vector a, Vector b, Vector c, Vector d) + { + ps[0] = a; + ps[1] = b; + ps[2] = c; + ps[3] = d; + + deleted = false; + _arc_to_t = new Float[table_size]; + } + + ~CubicBezier() + { + deleted = true; + + if (_arc_to_t) { + delete[] _arc_to_t; + _arc_to_t = nullptr; + } + } + + CubicBezier() + { + deleted = false; + _arc_to_t = new Float[table_size]; + } + + CubicBezier(const CubicBezier &b) + { + _arc_to_t = new Float[table_size]; + *this = b; + deleted = false; + } + + CubicBezier &operator=(const CubicBezier &b) + { + ps[0] = b.ps[0]; + ps[1] = b.ps[1]; + ps[2] = b.ps[2]; + ps[3] = b.ps[3]; + + length = b.length; + + if (!_arc_to_t) { + _arc_to_t = new Float[table_size]; + } + + if (b._arc_to_t) { + for (int i = 0; i < table_size; i++) { + _arc_to_t[i] = b._arc_to_t[i]; + } + } + + return *this; + } + +#if 1 + CubicBezier(CubicBezier &&b) + { + *this = b; + } + + CubicBezier &operator=(CubicBezier &&b) + { + ps[0] = b.ps[0]; + ps[1] = b.ps[1]; + ps[2] = b.ps[2]; + ps[3] = b.ps[3]; + + length = b.length; + + if (b._arc_to_t) { + _arc_to_t = std::move(b._arc_to_t); + b._arc_to_t = nullptr; + } + else { + _arc_to_t = new Float[table_size]; + } + + return *this; + } +#endif + + Float length; + + void update() + { + Float t = 0.0, dt = 1.0 / (Float)table_size; + Float s = 0.0; + + if (!_arc_to_t) { + _arc_to_t = new Float[table_size]; + } + + auto table = _arc_to_t; + + for (int i = 0; i < table_size; i++) { + table[i] = -1.0; + } + + length = 0.0; + + for (int i = 0; i < table_size; i++, t += dt) { + Float dlen = 0.0; + for (int j = 0; j < axes; j++) { + float dv = dcubic(ps[0][j], ps[1][j], ps[2][j], ps[3][j], t); + + dlen += dv * dv; + } + + dlen = sqrt(dlen) * dt; + + length += dlen; + } + + const int samples = table_size; + dt = 1.0 / (Float)samples; + + t = 0.0; + s = 0.0; + + for (int i = 0; i < samples; i++, t += dt) { + Float dlen = 0.0; + for (int j = 0; j < axes; j++) { + float dv = dcubic(ps[0][j], ps[1][j], ps[2][j], ps[3][j], t); + + dlen += dv * dv; + } + + dlen = sqrt(dlen) * dt; + + int j = (int)((s / length) * (Float)table_size * 0.999999); + j = min_ii(j, table_size - 1); + + table[j] = t; + + s += dlen; + } + + table[0] = 0.0; + table[table_size - 1] = 1.0; + +#if 1 + /* Interpolate gaps in table. */ + for (int i = 0; i < table_size - 1; i++) { + if (table[i] == -1.0 || table[i + 1] != -1.0) { + continue; + } + + int i1 = i; + int i2 = i + 1; + + while (table[i2] == -1.0) { + i2++; + } + + int start = table[i1]; + int end = table[i2]; + Float dt2 = 1.0 / (i2 - i1); + + for (int j = i1 + 1; j < i2; j++) { + Float factor = (Float)(j - i1) * dt2; + table[j] = start + (end - start) * factor; + } + + i = i2 - 1; + } + +# if 0 + for (int i = 0; i < table_size; i++) { + printf("%.3f ", table[i]); + } + printf("\n\n"); +# endif +#endif + } + + inline Vector evaluate(Float s) + { + Float t = arc_to_t(s); + Vector r; + + for (int i = 0; i < axes; i++) { + r[i] = cubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t); + } + + return r; + } + + Vector derivative(Float s, bool exact = true) + { + Float t = arc_to_t(s); + Vector r; + + for (int i = 0; i < axes; i++) { + r[i] = dcubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t) * length; + } + + /* Real arc length parameterized tangent has unit length. */ + if (exact) { + Float len = sqrt(_dot(r, r)); + + if (len > 0.00001) { + r = r / len; + } + } + + return r; + } + + Vector derivative2(Float s) + { +#ifdef FINITE_DIFF + const Float df = 0.0005; + Float s1, s2; + + if (s >= 1.0 - df) { + s1 = s - df; + s2 = s; + } + else { + s1 = s; + s2 = s + df; + } + + Vector a = derivative(s1); + Vector b = derivative(s2); + + return (b - a) / df; +#else + Float t = arc_to_t(s); + Vector r; + + Float dx = dcubic(ps[0][0], ps[1][0], ps[2][0], ps[3][0], t); + Float d2x = d2cubic(ps[0][0], ps[1][0], ps[2][0], ps[3][0], t); + Float dy = dcubic(ps[0][1], ps[1][1], ps[2][1], ps[3][1], t); + Float d2y = d2cubic(ps[0][1], ps[1][1], ps[2][1], ps[3][1], t); + + /* + comment: arc length second derivative; + + operator x, y, z, dx, dy, dz, d2x, d2y, d2z; + forall t let df(x(t), t) = dx(t); + forall t let df(y(t), t) = dy(t); + forall t let df(z(t), t) = dz(t); + forall t let df(dx(t), t) = d2x(t); + forall t let df(dy(t), t) = d2y(t); + forall t let df(dz(t), t) = d2z(t); + + comment: arc length first derivative is just the normalized tangent; + + comment: 2d case; + + dlen := sqrt(df(x(t), t)**2 + df(y(t), t)**2); + + df(df(x(t), t) / dlen, t); + df(df(y(t), t) / dlen, t); + + comment: 3d case; + + dlen := sqrt(df(x(t), t)**2 + df(y(t), t)**2 + df(z(t), t)**2); + + comment: final derivatives; + + df(df(x(t), t) / dlen, t); + df(df(y(t), t) / dlen, t); + df(df(z(t), t) / dlen, t); + */ + if constexpr (axes == 2) { + /* Basically the 2d perpidicular normalized tangent multiplied by the curvature. */ + + Float div = sqrt(dx * dx + dy * dy) * (dx * dx + dy * dy); + + r[0] = ((d2x * dy - d2y * dx) * dy) / div; + r[1] = (-(d2x * dy - d2y * dx) * dx) / div; + } + else if constexpr (axes == 3) { + Float dz = dcubic(ps[0][2], ps[1][2], ps[2][2], ps[3][2], t); + Float d2z = d2cubic(ps[0][2], ps[1][2], ps[2][2], ps[3][2], t); + + Float div = sqrt(dx * dx + dy * dy + dz * dz) * (dy * dy + dz * dz + dx * dx); + + r[0] = (d2x * dy * dy + d2x * dz * dz - d2y * dx * dy - d2z * dx * dz) / div; + r[1] = (-(d2x * dx * dy - d2y * dx * dx - d2y * dz * dz + d2z * dy * dz)) / div; + r[2] = (-(d2x * dx * dz + d2y * dy * dz - d2z * dx * dx - d2z * dy * dy)) / div; + } + else { + for (int i = 0; i < axes; i++) { + r[i] = d2cubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t) * length; + } + } + + return r; +#endif + } + + Float curvature(Float s) + { + Vector dv2 = derivative2(s); + + if constexpr (axes == 2) { + Vector dv = derivative(s, true); + + /* Calculate signed curvature. Remember that dv is normalized. */ + return dv[0] * dv2[1] - dv[1] * dv2[0]; + } + + return sqrt(_dot(dv2, dv2)); + } + + private: + Float *_arc_to_t; + bool deleted = false; + + Float cubic(Float k1, Float k2, Float k3, Float k4, Float t) + { + return -(((3.0 * (t - 1.0) * k3 - k4 * t) * t - 3.0 * (t - 1.0) * (t - 1.0) * k2) * t + + (t - 1) * (t - 1) * (t - 1) * k1); + } + + Float dcubic(Float k1, Float k2, Float k3, Float k4, Float t) + { + return -3.0 * ((t - 1.0) * (t - 1.0) * k1 - k4 * t * t + (3.0 * t - 2.0) * k3 * t - + (3.0 * t - 1.0) * (t - 1.0) * k2); + } + + Float d2cubic(Float k1, Float k2, Float k3, Float k4, Float t) + { + return -6.0 * (k1 * t - k1 - 3.0 * k2 * t + 2.0 * k2 + 3.0 * k3 * t - k3 - k4 * t); + } + + Float _dot(Vector a, Vector b) + { + Float sum = 0.0; + + for (int i = 0; i < axes; i++) { + sum += a[i] * b[i]; + } + + return sum; + } + + Float clamp_s(Float s) + { + s = s < 0.0 ? 0.0 : s; + s = s >= length ? length * 0.999999 : s; + + return s; + } + + Float arc_to_t(Float s) + { + if (length == 0.0) { + return 0.0; + } + + s = clamp_s(s); + + Float t = s * (Float)(table_size - 1) / length; + + int i1 = floorf(t); + int i2 = min_ii(i1 + 1, table_size - 1); + + t -= (Float)i1; + + Float s1 = _arc_to_t[i1]; + Float s2 = _arc_to_t[i2]; + + return s1 + (s2 - s1) * t; + } +}; + +template<typename Float, int axes = 2> class BezierSpline { + using Vector = vec_base<Float, axes>; + struct Segment { + CubicBezier<Float, axes> bezier; + Float start = 0.0; + + Segment(const CubicBezier<Float> &bez) + { + bezier = bez; + } + + Segment(const Segment &b) + { + *this = b; + } + + Segment &operator=(const Segment &b) + { + bezier = b.bezier; + start = b.start; + + return *this; + } + + Segment() + { + } + }; + + public: + Float length = 0.0; + bool deleted = false; + blender::Vector<Segment> segments; + + void clear() + { + segments.clear(); + } + + BezierSpline() + { + } + + ~BezierSpline() + { + deleted = true; + } + + void add(CubicBezier<Float, axes> &bez) + { + need_update = true; + + Segment seg; + + seg.bezier = bez; + segments.append(seg); + + update(); + } + + void update() + { + need_update = false; + + length = 0.0; + for (Segment &seg : segments) { + seg.start = length; + length += seg.bezier.length; + } + } + + inline Vector evaluate(Float s) + { + if (s == 0.0) { + return segments[0].bezier.ps[0]; + } + + if (s >= length) { + return segments[segments.size() - 1].bezier.ps[3]; + } + + Segment *seg = get_segment(s); + + return seg->bezier.evaluate(s - seg->start); + } + + Vector derivative(Float s, bool exact = true) + { + if (segments.size() == 0) { + return Vector(); + } + + s = clamp_s(s); + Segment *seg = get_segment(s); + + return seg->bezier.derivative(s - seg->start, exact); + } + + Vector derivative2(Float s) + { + if (segments.size() == 0) { + return Vector(); + } + + s = clamp_s(s); + Segment *seg = get_segment(s); + + return seg->bezier.derivative2(s - seg->start); + } + + Float curvature(Float s) + { + if (segments.size() == 0) { + return 0.0; + } + + s = clamp_s(s); + Segment *seg = get_segment(s); + + return seg->bezier.curvature(s - seg->start); + } + + /* Find the closest point on the spline. Uses a bisecting root finding approach. + * Note: in thoery we could split the spline into quadratic segments and solve + * for the closest point directy. + */ + Vector closest_point(const Vector p, Float &r_s, Vector &r_tan, Float &r_dis) + { + if (segments.size() == 0) { + return Vector(); + } + + const int steps = 12; + Float s = 0.0, ds = length / steps; + Float mindis = FLT_MAX; + Vector minp; + Float mins = 0.0; + bool found = false; + + Vector lastdv, lastp; + Vector b, dvb; + + for (int i = 0; i < steps + 1; i++, s += ds, lastp = b, lastdv = dvb) { + b = evaluate(s); + dvb = derivative(s, false); /* We don't need real normalized derivative here. */ + + if (i == 0) { + continue; + } + + Vector dva = lastdv; + Vector a = lastp; + + Vector vec1 = a - p; + Vector vec2 = b - p; + + Float sign1 = _dot(vec1, dva); + Float sign2 = _dot(vec2, dvb); + + if ((sign1 < 0.0) == (sign2 < 0.0)) { + found = true; + + Float len = _dot(vec1, vec1); + + if (len < mindis) { + mindis = len; + mins = s; + minp = evaluate(s); + } + continue; + } + + found = true; + + Float start = s - ds; + Float end = s; + Float mid = (start + end) * 0.5; + const int binary_steps = 10; + + for (int j = 0; j < binary_steps; j++) { + Vector dvmid = derivative(mid, false); + Vector vecmid = evaluate(mid) - p; + Float sign_mid = _dot(vecmid, dvmid); + + if ((sign_mid < 0.0) == (sign1 < 0.0)) { + start = mid; + } + else { + end = mid; + } + mid = (start + end) * 0.5; + } + + Vector p2 = evaluate(mid); + Vector vec_mid = p2 - p; + Float len = _dot(vec_mid, vec_mid); + + if (len < mindis) { + mindis = len; + minp = p2; + mins = mid; + } + } + + if (!found) { + mins = 0.0; + minp = evaluate(mins); + Vector vec = minp - p; + mindis = _dot(vec, vec); + } + + r_tan = derivative(mins, true); + r_s = mins; + r_dis = sqrtf(mindis); + + return minp; + } + + void pop_front(int n = 1) + { + for (int i = 0; i < segments.size() - n; i++) { + segments[i] = segments[i + n]; + } + + segments.resize(segments.size() - n); + update(); + } + + private: + bool need_update; + + Float _dot(Vector a, Vector b) + { + Float sum = 0.0; + + for (int i = 0; i < axes; i++) { + sum += a[i] * b[i]; + } + + return sum; + } + + Float clamp_s(Float s) + { + s = s < 0.0 ? 0.0 : s; + s = s >= length ? length * 0.999999 : s; + + return s; + } + + Segment *get_segment(Float s) + { + // printf("\n"); + + for (Segment &seg : segments) { + // printf("s: %f %f\n", seg.start, seg.start + seg.bezier.length); + + if (s >= seg.start && s < seg.start + seg.bezier.length) { + return &seg; + } + } + + // printf("\n"); + + return nullptr; + } +}; + +using BezierSpline2f = BezierSpline<float, 2>; +using BezierSpline3f = BezierSpline<float, 3>; +} // namespace blender diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index b29fc0e9e7d..94a067845d9 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -53,7 +53,7 @@ set(SRC paint_image_proj.c paint_mask.c paint_ops.c - paint_stroke.c + paint_stroke.cc paint_utils.c paint_vertex.cc paint_vertex_color_ops.cc diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index c6fe7ed3072..caa6a456467 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -16,7 +16,16 @@ #include "DNA_scene_types.h" #ifdef __cplusplus +namespace blender { +template<typename Float, int axes> class BezierSpline; +} + +using BezierSpline2f = blender::BezierSpline<float, 2>; +using BezierSpline3f = blender::BezierSpline<float, 3>; extern "C" { +#else +typedef struct BezierSpline2f BezierSpline2f; +typedef struct BezierSpline3f BezierSpline3f; #endif struct ARegion; @@ -511,6 +520,13 @@ void paint_delete_blur_kernel(BlurKernel *); /* paint curve defines */ #define PAINT_CURVE_NUM_SEGMENTS 40 +void paint_stroke_spline_uv(struct PaintStroke *stroke, + struct StrokeCache *cache, + const float co[3], + float r_out[3], + float r_tan[3]); +float paint_stroke_spline_length(struct PaintStroke *stroke); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.cc index f1f864fdf82..85e40ace36e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.cc @@ -7,8 +7,10 @@ #include "MEM_guardedalloc.h" +#include "BLI_even_spline.hh" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_math_vec_types.hh" #include "BLI_rand.h" #include "BLI_utildefines.h" @@ -35,6 +37,7 @@ #include "GPU_state.h" #include "ED_screen.h" +#include "ED_space_api.h" #include "ED_view3d.h" #include "IMB_imbuf_types.h" @@ -46,6 +49,10 @@ #include <math.h> //#define DEBUG_TIME +#define DRAW_DEBUG_VIS + +using blender::float2; +using blender::float3; #ifdef DEBUG_TIME # include "PIL_time_utildefines.h" @@ -56,6 +63,14 @@ typedef struct PaintSample { float pressure; } 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 { void *mode_data; void *stroke_cursor; @@ -70,6 +85,9 @@ typedef struct PaintStroke { /* used for lines and curves */ ListBase line; + bool need_roll_mapping; + int stroke_sample_index; + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs * to smooth the stroke */ PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; @@ -77,6 +95,14 @@ typedef struct PaintStroke { int cur_sample; int tot_samples; + PaintStrokePoint points[PAINT_MAX_INPUT_SAMPLES]; + int num_points; + int cur_point; + int tot_points; + + BezierSpline2f *spline; + BezierSpline3f *world_spline; + float last_mouse_position[2]; float last_world_space_position[3]; float last_scene_spacing_delta[3]; @@ -84,6 +110,7 @@ typedef struct PaintStroke { bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; + float stroke_distance_world; /* Set whether any stroke step has yet occurred * e.g. in sculpt mode, stroke doesn't start until cursor @@ -104,6 +131,8 @@ typedef struct PaintStroke { float last_pressure; int stroke_mode; + float spacing_raw; + float last_tablet_event_pressure; float zoom_2d; @@ -124,14 +153,233 @@ typedef struct PaintStroke { StrokeDone done; bool original; /* Ray-cast original mesh at start of stroke. */ + void *debug_draw_handle; } 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; +} + +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]; + } + } + + for (int i = 0; i < 4; i++) { + if (!SCULPT_stroke_get_location(C, bezier3d.ps[i], mvals[i], true)) { + ED_view3d_win_to_3d(CTX_wm_view3d(C), + CTX_wm_region(C), + stroke->last_world_space_position, + mvals[i], + bezier3d.ps[i]); + } + } + + 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]; +#if 1 + 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); +#else + zero_v2(b); +#endif + + add_v2_v2(b, a); + +#if 1 + 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); +#else + zero_v2(c); +#endif + + add_v2_v2(c, d); +#if 0 + printf("\n"); + printf("a: %.2f: %.2f\n", a[0], a[1]); + printf("b: %.2f: %.2f\n", b[0], b[1]); + printf("c: %.2f: %.2f\n", c[0], c[1]); + printf("d: %.2f: %.2f\n", d[0], d[1]); +#endif + + 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(); + } +} + +#ifdef DRAW_DEBUG_VIS +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; + } + + 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 = stroke->world_spline->length / (steps - 1); + + for (int i = 0; i < steps; i++, s += ds) { + float3 co = stroke->world_spline->evaluate(s); + + mul_v3_m4v3(co, ob->object_to_world, co); + immVertex3fv(pos, co); + } + immEnd(); + + s = 0.0f; + ds = 0.1f; + steps = (int)floorf(stroke->world_spline->length / ds + 0.5f); + + immUniformColor4ub(255, 0, 0, 170); + immBegin(GPU_PRIM_POINTS, steps); + for (int i = 0; i < steps; i++, s += ds) { + float3 co = stroke->world_spline->evaluate(s); + mul_v3_m4v3(co, ob->object_to_world, co); + + immVertex3fv(pos, co); + } + + immEnd(); + + immUniformColor4ub(0, 255, 25, 55); + for (int is_points = 0; is_points < 2; is_points++) { + immBegin(is_points ? GPU_PRIM_POINTS : GPU_PRIM_LINE_STRIP, stroke->num_points); + for (int i = 0; i < stroke->num_points; i++) { + int idx = (i + stroke->cur_point) % stroke->num_points; + float3 co = stroke->points[idx].location; + mul_v3_m4v3(co, ob->object_to_world, co); + + immVertex3fv(pos, co); + } + immEnd(); + } + + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); +} +#endif + /*** Cursors ***/ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - PaintStroke *stroke = customdata; + PaintStroke *stroke = (PaintStroke *)customdata; if (stroke && brush) { GPU_line_smooth(true); @@ -161,7 +409,7 @@ static void paint_draw_smooth_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); - PaintStroke *stroke = customdata; + PaintStroke *stroke = (PaintStroke *)customdata; GPU_line_smooth(true); @@ -616,17 +864,65 @@ static void paint_brush_stroke_add_step( /* Add to stroke */ if (add_step) { + PaintStrokePoint *point; + PaintStrokePoint temp; + + int n = 1; + int max_points = paint_stroke_max_points(paint, stroke); + + if (stroke->num_points < max_points) { + // n = max_points - stroke->num_points; + } + for (int i = 0; i < n; i++) { + 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 (stroke->need_roll_mapping) { + if (stroke->spline->segments.size() < paint_stroke_max_points(paint, stroke)) { + 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_float_set(&itemptr, "size", ups->pixel_radius); - RNA_float_set_array(&itemptr, "location", location); + RNA_float_set(&itemptr, "size", point->size); + RNA_float_set_array(&itemptr, "location", point->location); /* 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. */ - RNA_float_set_array(&itemptr, "mouse_event", mval); - RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); - RNA_float_set(&itemptr, "pressure", pressure); - RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt); - RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt); + RNA_float_set_array(&itemptr, "mouse_event", point->mouse_in); + RNA_boolean_set(&itemptr, "pen_flip", point->pen_flip); + RNA_float_set(&itemptr, "pressure", point->pressure); + RNA_float_set(&itemptr, "x_tilt", point->x_tilt); + RNA_float_set(&itemptr, "y_tilt", point->y_tilt); stroke->update_step(C, op, stroke, &itemptr); @@ -703,6 +999,8 @@ static float paint_space_stroke_spacing(bContext *C, spacing = spacing * (1.5f - spacing_pressure); } + stroke->spacing_raw = spacing * 0.01; + 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 * avoid affecting the simulation update rate when changing the radius of the brush. @@ -897,7 +1195,7 @@ PaintStroke *paint_stroke_new(bContext *C, int event_type) { 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); UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; Paint *p = BKE_paint_get_active_from_context(C); @@ -907,6 +1205,13 @@ PaintStroke *paint_stroke_new(bContext *C, ED_view3d_viewcontext_init(C, &stroke->vc, depsgraph); +#ifdef DRAW_DEBUG_VIS + 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); +#endif + stroke->get_location = get_location; stroke->test_start = test_start; stroke->update_step = update_step; @@ -921,6 +1226,18 @@ PaintStroke *paint_stroke_new(bContext *C, get_imapaint_zoom(C, &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<blender::BezierSpline2f>("BezierSpline2f"); + stroke->world_spline = MEM_new<blender::BezierSpline3f>("BezierSpline3f"); + } + if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { if (br->flag & BRUSH_CURVE) { RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); @@ -948,7 +1265,7 @@ PaintStroke *paint_stroke_new(bContext *C, 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); @@ -962,7 +1279,7 @@ void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke) rv3d->rflag &= ~RV3D_PAINTING; } - BKE_paint_set_overlay_override(0); + BKE_paint_set_overlay_override((eOverlayFlags)0); if (stroke == NULL) { return; @@ -981,11 +1298,18 @@ void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke) } if (stroke->stroke_cursor) { - WM_paint_cursor_end(stroke->stroke_cursor); + WM_paint_cursor_end((wmPaintCursor *)stroke->stroke_cursor); } BLI_freelistN(&stroke->line); +#ifdef DRAW_DEBUG_VIS + ARegion *region = CTX_wm_region(C); + + ED_region_draw_cb_exit(region->type, stroke->debug_draw_handle); + ED_region_tag_redraw(region); +#endif + MEM_SAFE_FREE(stroke); } @@ -1035,7 +1359,7 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) } 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; } @@ -1451,8 +1775,22 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS stroke->last_tablet_event_pressure = pressure; } - paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); - paint_stroke_sample_average(stroke, &sample_average); + if (!stroke->need_roll_mapping) { + paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); + 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. */ if (WM_event_is_tablet(event)) { @@ -1698,3 +2036,30 @@ bool PAINT_brush_tool_poll(bContext *C) } 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; +} diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 684fcdbff9e..b27abda55e9 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2513,6 +2513,35 @@ float SCULPT_brush_strength_factor(SculptSession *ss, avg += br->texture_sample_bias; } + else if (mtex->brush_map_mode == MTEX_MAP_MODE_ROLL) { + float point_3d[3]; + point_3d[2] = 0.0f; + + float tan[3], curv[3]; + + paint_stroke_spline_uv(ss->cache->stroke, ss->cache, brush_point, point_3d, tan); + +#if 0 + if (SCULPT_has_colors(ss)) { + float color[4] = {point_3d[0], point_3d[1], 0.0f, 1.0f}; + mul_v3_fl(color, 0.25f / ss->cache->initial_radius); + color[0] -= floorf(color[0]); + color[1] -= floorf(color[1]); + color[2] -= floorf(color[2]); + + SCULPT_vertex_color_set(ss, vertex, color); + } + +// avg = 0.0f; +#endif + + float pixel_radius = br->size; + mul_v3_fl(point_3d, 1.0f / ss->cache->initial_radius); + + //avg = BKE_brush_sample_tex_3d(scene, br, mtex, point_3d, rgba, thread_id, ss->tex_pool); + avg = paint_get_tex_pixel(mtex, point_3d[0], point_3d[1], ss->tex_pool, thread_id); + avg += br->texture_sample_bias; + } else { const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; avg = BKE_brush_sample_tex_3d(scene, br, mtex, point_3d, rgba, 0, ss->tex_pool); @@ -5501,7 +5530,9 @@ static void sculpt_stroke_update_step(bContext *C, const Brush *brush = BKE_paint_brush(&sd->paint); ToolSettings *tool_settings = CTX_data_tool_settings(C); StrokeCache *cache = ss->cache; + cache->stroke_distance = paint_stroke_distance_get(stroke); + cache->stroke = stroke; SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 852b3c2719a..46ba5f6d3cc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -498,6 +498,8 @@ typedef struct StrokeCache { float true_last_location[3]; float location[3]; float last_location[3]; + + struct PaintStroke *stroke; float stroke_distance; /* Used for alternating between deformation in brushes that need to apply different ones to diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index c7d49db130e..5c4d2a30b60 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -437,6 +437,7 @@ typedef struct ColorMapping { #define MTEX_MAP_MODE_AREA 3 #define MTEX_MAP_MODE_RANDOM 4 #define MTEX_MAP_MODE_STENCIL 5 +#define MTEX_MAP_MODE_ROLL 6 /* brush_angle_mode */ #define MTEX_ANGLE_RANDOM 1 diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index ce51b52de39..0b2f73021ed 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -77,6 +77,7 @@ static const EnumPropertyItem rna_enum_brush_texture_slot_map_all_mode_items[] = {MTEX_MAP_MODE_3D, "3D", 0, "3D", ""}, {MTEX_MAP_MODE_RANDOM, "RANDOM", 0, "Random", ""}, {MTEX_MAP_MODE_STENCIL, "STENCIL", 0, "Stencil", ""}, + {MTEX_MAP_MODE_ROLL, "ROLL", 0, "Roll", ""}, {0, NULL, 0, NULL, NULL}, }; |