Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenlib/BLI_even_spline.hh733
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h16
-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.cc31
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h2
-rw-r--r--source/blender/makesdna/DNA_texture_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_brush.c1
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},
};