sculpt-dev: roll brush update

* Wrote a little C++ library for working with arc length
  parameterized cubic splines.
* Stroke samples are queued if a roll texture exists.
  That way we can build a stroke curve of sufficient
  length to cover all the verts in the brush radius.
This commit is contained in:
Joseph Eagar 2022-10-18 16:33:05 -07:00
parent cb22eae31b
commit 8786b5c0c0
6 changed files with 990 additions and 170 deletions

View File

@ -0,0 +1,642 @@
#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>
/*
* 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;
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;
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
}
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)
{
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;
}
return r;
}
Vector derivative2(Float s)
{
Float t = arc_to_t(s);
Vector r;
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;
}
Float curvature(Float s)
{
/* Get signed curvature if in 2d. */
if constexpr (axes == 2) {
Vector dv1 = derivative(s);
Vector dv2 = derivative2(s);
return (dv1[0] * dv2[1] - dv1[1] * dv2[0]) /
powf(dv1[0] * dv1[0] + dv1[1] * dv1[1], 3.0 / 2.0);
}
else { /* Otherwise use magnitude of second derivative (this works because we are arc-length
parameterized). */
Vector dv2 = derivative2(s);
Float len = 0.0;
for (int i = 0; i < axes; i++) {
len += dv2[i] * dv2[i];
}
return sqrt(len);
}
}
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 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;
}
}
Vector evaluate(Float s)
{
if (segments.size() == 0) {
return Vector();
}
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)
{
if (segments.size() == 0) {
return Vector();
}
s = clamp_s(s);
Segment *seg = get_segment(s);
return seg->bezier.derivative(s - seg->start);
}
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);
}
Vector closest_point(const Vector p, Float &r_s, Vector &r_tan, Float &r_dis)
{
const int steps = 5;
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);
if (i == 0) {
continue;
}
Vector dva = lastdv;
Vector a = lastp;
// Vector dva = derivative(s);
// Vector dvb = derivative(s + ds);
// Vector a = evaluate(s);
// Vector b = evaluate(s + ds);
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 = 4;
for (int j = 0; j < binary_steps; j++) {
Vector dvmid = derivative(mid);
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);
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

View File

@ -313,6 +313,7 @@ set(SRC
BLI_sort.h
BLI_sort.hh
BLI_sort_utils.h
BLI_arc_spline.hh
BLI_span.hh
BLI_stack.h
BLI_stack.hh

View File

@ -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

View File

@ -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;
@ -67,6 +76,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;
@ -84,6 +101,15 @@ typedef struct PaintStroke {
/* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
* to smooth the stroke */
PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
PaintStrokePoint points[PAINT_MAX_INPUT_SAMPLES];
BezierSpline2f *spline;
BezierSpline3f *world_spline;
int num_points;
int cur_point;
int tot_points;
int num_samples;
int cur_sample;
int tot_samples;
@ -623,6 +649,15 @@ void paint_delete_blur_kernel(BlurKernel *);
bool paint_stroke_has_cubic(const PaintStroke *stroke);
float bezier3_arclength_v2(const float control[4][2]);
float bezier3_arclength_v3(const float control[4][3]);
void evaluate_cubic_bezier(const float control[4][3], float t, float r_pos[3], float r_tangent[3]);
void paint_project_spline(struct bContext *C,
struct StrokeCache *cache,
struct PaintStroke *stroke);
;
void paint_calc_cubic_uv_v3(
PaintStroke *stroke, struct StrokeCache *cache, const float co[3], float r_out[3], float r_tan[3]);
float paint_stroke_spline_length(PaintStroke *stroke);
#ifdef __cplusplus
}

View File

@ -10,6 +10,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rand.h"
#include "BLI_arc_spline.hh"
#include "BLI_utildefines.h"
#include "PIL_time.h"
@ -47,6 +48,8 @@
#include <math.h>
//#define DEBUG_TIME
using blender::float2;
using blender::float3;
#ifdef DEBUG_TIME
# include "PIL_time_utildefines.h"
@ -55,77 +58,112 @@
static void paint_stroke_add_sample(
const Paint *paint, PaintStroke *stroke, float x, float y, float pressure);
//#define DRAW_DEBUG_VIS
#define DRAW_DEBUG_VIS
static int paint_stroke_max_points(const Paint *paint, PaintStroke *stroke)
{
float s = max_ff(stroke->spacing, 0.05);
return stroke->has_cubic_stroke ? 8.0 : 1;
return stroke->has_cubic_stroke ? (int)(1.0f / s + 4.5f) : 1;
}
static int paint_stroke_max_samples(const Paint *paint, PaintStroke *stroke)
{
return CLAMPIS(paint->num_input_samples, 1, PAINT_MAX_INPUT_SAMPLES);
}
#ifdef DRAW_DEBUG_VIS
static void paint_brush_cubic_vis(const bContext *C, ARegion *region, void *userdata)
{
PaintStroke *stroke = userdata;
PaintStroke *stroke = (PaintStroke *)userdata;
Object *ob = CTX_data_active_object(C);
if (!stroke->num_samples) {
if (!ob || !ob->sculpt || !ob->sculpt->cache) {
return;
}
GPU_line_smooth(true);
SculptSession *ss = ob->sculpt;
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, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4ub(0, 0, 0, 155);
GPU_point_size(12);
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);
immBegin(GPU_PRIM_LINES, 6);
immVertex2f(pos, stroke->mouse_cubic[0][0], stroke->mouse_cubic[0][1]);
immVertex2f(pos, stroke->mouse_cubic[1][0], stroke->mouse_cubic[1][1]);
immVertex3fv(pos, ss->cache->world_cubic[0]);
immVertex3fv(pos, ss->cache->world_cubic[1]);
immVertex2f(pos, stroke->mouse_cubic[1][0], stroke->mouse_cubic[1][1]);
immVertex2f(pos, stroke->mouse_cubic[2][0], stroke->mouse_cubic[2][1]);
immVertex2f(pos, stroke->mouse_cubic[2][0], stroke->mouse_cubic[2][1]);
immVertex2f(pos, stroke->mouse_cubic[3][0], stroke->mouse_cubic[3][1]);
immVertex3fv(pos, ss->cache->world_cubic[1]);
immVertex3fv(pos, ss->cache->world_cubic[2]);
immVertex3fv(pos, ss->cache->world_cubic[2]);
immVertex3fv(pos, ss->cache->world_cubic[3]);
immEnd();
immBegin(GPU_PRIM_POINTS, 2);
immVertex2f(pos, stroke->mouse_cubic[0][0], stroke->mouse_cubic[0][1]);
// immVertex2f(pos, stroke->mouse_cubic[1][0], stroke->mouse_cubic[1][1]);
// immVertex2f(pos, stroke->mouse_cubic[2][0], stroke->mouse_cubic[2][1]);
immVertex2f(pos, stroke->mouse_cubic[3][0], stroke->mouse_cubic[3][1]);
const int steps = 256;
float t = 0.0f, dt = 1.0f / (float)(steps - 1);
immUniformColor4ub(45, 75, 255, 255);
immBegin(GPU_PRIM_LINE_STRIP, steps);
for (int i = 0; i < steps; i++, t += dt) {
float co[3], tan[3];
evaluate_cubic_bezier(ss->cache->world_cubic, t, co, tan);
immVertex3fv(pos, co);
}
immEnd();
immUniformColor4ub(255, 0, 0, 155);
immBegin(GPU_PRIM_LINE_STRIP, steps);
GPU_point_size(8);
float s = 0.0f;
float ds = stroke->world_spline->length / (steps - 1);
for (int i = 0; i < 2; i++) {
if (i) {
if (stroke->num_samples < 2) {
break;
}
for (int i = 0; i < steps; i++, s += ds) {
float mval[3], tan[3];
# if 0
float co[3];
float2 p = stroke->spline->evaluate(s);
immBegin(GPU_PRIM_LINES, (stroke->num_samples - 1) * 2);
mval[0] = p[0];
mval[1] = p[1];
mval[2] = 0.0f;
float3 loc(0.0, 0.0, 0.0);
mul_v3_m4v3(loc, ob->obmat, loc);
ED_view3d_win_to_3d(CTX_wm_view3d(C), CTX_wm_region(C), loc, mval, co);
# else
float3 co = stroke->world_spline->evaluate(s);
# endif
immVertex3fv(pos, co);
}
immEnd();
immUniformColor4ub(0, 255, 25, 255);
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;
immVertex3fv(pos, stroke->points[idx].location);
}
else {
immBegin(GPU_PRIM_POINTS, stroke->num_samples);
}
for (int j = 0; j < stroke->num_samples; j++) {
int j2 = (j + stroke->cur_sample) % stroke->num_samples;
immVertex2f(pos, stroke->samples[j2].mouse[0], stroke->samples[j2].mouse[1]);
if (i && j < stroke->num_samples - 1) {
int j3 = (j + stroke->cur_sample + 1) % stroke->num_samples;
immVertex2f(pos, stroke->samples[j3].mouse[0], stroke->samples[j3].mouse[1]);
}
}
immEnd();
}
immUniformColor4ub(0, 0, 255, 155);
immBegin(GPU_PRIM_POINTS, 1);
immVertex2fv(pos, stroke->samples[stroke->cur_sample].mouse);
immUniformColor4ub(0, 0, 0, 155);
immBegin(GPU_PRIM_POINTS, 4);
immVertex3fv(pos, ss->cache->world_cubic[0]);
immVertex3fv(pos, ss->cache->world_cubic[1]);
immVertex3fv(pos, ss->cache->world_cubic[2]);
immVertex3fv(pos, ss->cache->world_cubic[3]);
immEnd();
immUnbindProgram();
@ -140,7 +178,7 @@ 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);
@ -170,7 +208,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);
@ -294,27 +332,70 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m
return true;
}
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_brush_make_cubic(PaintStroke *stroke)
{
float a[2], b[2], c[2], d[2];
int count = paint_stroke_max_points(NULL, stroke);
int ia = (stroke->cur_sample - 3 + stroke->num_samples) % stroke->num_samples;
int id = (stroke->cur_sample - 2 + stroke->num_samples) % stroke->num_samples;
if (stroke->num_points < 4) {
return;
}
int ib = (stroke->cur_sample - 4 + stroke->num_samples) % stroke->num_samples;
int ic = (stroke->cur_sample - 1 + stroke->num_samples) % stroke->num_samples;
int cur = (stroke->cur_point - 1 + stroke->num_points) % stroke->num_points;
copy_v2_v2(a, stroke->samples[ia].mouse);
copy_v2_v2(b, stroke->samples[ib].mouse);
copy_v2_v2(c, stroke->samples[ic].mouse);
copy_v2_v2(d, stroke->samples[id].mouse);
int ia = (cur - 2 + stroke->num_points) % stroke->num_points;
int id = (cur - 1 + stroke->num_points) % stroke->num_points;
float tmp[2];
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(tmp, d, a);
sub_v2_v2v2(b, a, b);
interp_v2_v2v2(b, b, tmp, 0.5f);
mul_v2_fl(b, 1.0f / 3.0f);
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
@ -322,10 +403,10 @@ static void paint_brush_make_cubic(PaintStroke *stroke)
add_v2_v2(b, a);
#if 1
sub_v2_v2v2(tmp, a, d);
sub_v2_v2v2(c, d, c);
interp_v2_v2v2(c, c, tmp, 0.5f);
mul_v2_fl(c, 1.0f / 3.0f);
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
@ -344,6 +425,72 @@ static void paint_brush_make_cubic(PaintStroke *stroke)
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);
while (stroke->spline->segments.size() > paint_stroke_max_points(nullptr, stroke) + 1) {
stroke->spline->pop_front();
}
}
void paint_project_spline(bContext *C, StrokeCache *cache, PaintStroke *stroke)
{
Object *ob = CTX_data_active_object(C);
stroke->world_spline->clear();
for (auto &seg : stroke->spline->segments) {
blender::CubicBezier<float, 3> bezier;
float2 mvals[4];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 2; j++) {
mvals[i][j] = seg.bezier.ps[i][j];
}
}
for (int i = 0; i < 4; i++) {
if (!SCULPT_stroke_get_location(C, bezier.ps[i], mvals[i], true)) {
float loc[3];
mul_v3_m4v3(loc, ob->obmat, cache->true_location);
ED_view3d_win_to_3d(
CTX_wm_view3d(C), CTX_wm_region(C), cache->true_location, mvals[i], bezier.ps[i]);
}
}
bezier.update();
stroke->world_spline->add(bezier);
}
}
void paint_calc_cubic_uv_v3(
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[0], tan, r_out[1]);
r_tan = tan;
r_out[1] = 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[1] = -r_out[1];
}
}
float paint_stroke_spline_length(struct PaintStroke *stroke)
{
return stroke->world_spline->length;
}
bool paint_stroke_apply_subspacing(struct PaintStroke *stroke,
@ -614,36 +761,7 @@ static void paint_brush_stroke_add_step(
PointerRNA itemptr;
float location[3];
const float *mouse_in;
if (stroke->has_cubic_stroke) {
paint_stroke_add_sample(paint, stroke, mval[0], mval[1], pressure);
paint_brush_make_cubic(stroke);
}
float mousetemp[2];
if (stroke->has_cubic_stroke) {
printf("\n");
for (int i = 0; i < stroke->num_samples; i++) {
PaintSample *sample = stroke->samples + ((stroke->cur_sample + i) % stroke->num_samples);
printf("%.2f %.2f\n", sample->mouse[0], sample->mouse[1]);
}
mouse_in = mousetemp;
PaintSample *sample1 = stroke->samples +
((stroke->cur_sample - 3 + stroke->num_samples) % stroke->num_samples);
PaintSample *sample2 = stroke->samples +
((stroke->cur_sample - 2 + stroke->num_samples) % stroke->num_samples);
interp_v2_v2v2(mousetemp, sample1->mouse, sample2->mouse, 0.5f);
// copy_v2_v2(mousetemp, sample1->mouse);
}
else {
mouse_in = mval;
}
const float *mouse_in = mval;
stroke->stroke_sample_index++;
@ -676,7 +794,7 @@ static void paint_brush_stroke_add_step(
/* copy last position -before- jittering, or space fill code
* will create too many dabs */
copy_v2_v2(stroke->last_mouse_position, mval);
copy_v2_v2(stroke->last_mouse_position, mouse_in);
stroke->last_pressure2 = stroke->last_pressure;
stroke->last_pressure = pressure;
@ -702,25 +820,25 @@ static void paint_brush_stroke_add_step(
factor *= pressure;
}
BKE_brush_jitter_pos(scene, brush, mval, mouse_out);
BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out);
/* XXX: meh, this is round about because
* BKE_brush_jitter_pos isn't written in the best way to
* be reused here */
if (factor != 1.0f) {
sub_v2_v2v2(delta, mouse_out, mval);
sub_v2_v2v2(delta, mouse_out, mouse_in);
mul_v2_fl(delta, factor);
add_v2_v2v2(mouse_out, mval, delta);
add_v2_v2v2(mouse_out, mouse_in, delta);
}
}
else {
copy_v2_v2(mouse_out, mval);
copy_v2_v2(mouse_out, mouse_in);
}
bool is_location_is_set;
ups->last_hit = paint_brush_update(
C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set);
C, brush, mode, stroke, mouse_in, mouse_out, pressure, location, &is_location_is_set);
if (is_location_is_set) {
copy_v3_v3(ups->last_location, location);
}
@ -740,17 +858,54 @@ static void paint_brush_stroke_add_step(
/* Add to stroke */
if (add_step) {
PaintStrokePoint *point;
PaintStrokePoint temp;
paint_stroke_add_point(paint,
stroke,
mouse_in,
mouse_out,
location,
ups->pixel_radius,
pressure,
stroke->pen_flip,
stroke->x_tilt,
stroke->y_tilt);
if (stroke->has_cubic_stroke) {
if (stroke->num_points < paint_stroke_max_points(paint, stroke)) {
return;
}
int cur = stroke->cur_point - (paint_stroke_max_points(paint, stroke) >> 1);
paint_brush_make_cubic(stroke);
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);
if (stroke->has_cubic_stroke) {
RNA_float_set_array(&itemptr, "mouse_cubic", (float *)stroke->mouse_cubic);
@ -1074,6 +1229,8 @@ static int paint_space_stroke(bContext *C,
C, scene, stroke, pressure, dpressure, length);
float mouse[2];
stroke->spacing = spacing;
if (length >= spacing) {
if (use_scene_spacing) {
float final_world_space_position[3];
@ -1140,7 +1297,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);
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
@ -1150,11 +1307,14 @@ PaintStroke *paint_stroke_new(bContext *C,
float zoomx, zoomy;
ARegion *region = CTX_wm_region(C);
stroke->spline = MEM_new<blender::BezierSpline2f>("BezierSpline2f");
stroke->world_spline = MEM_new<blender::BezierSpline3f>("BezierSpline3f");
ED_view3d_viewcontext_init(C, &stroke->vc, depsgraph);
#ifdef DRAW_DEBUG_VIS
stroke->debug_draw_handle = ED_region_draw_cb_activate(
region->type, paint_brush_cubic_vis, stroke, REGION_DRAW_POST_PIXEL);
region->type, paint_brush_cubic_vis, stroke, REGION_DRAW_POST_VIEW);
#endif
stroke->get_location = get_location;
@ -1202,7 +1362,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, mode == PAINT_MODE_SCULPT);
@ -1216,12 +1376,15 @@ 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;
}
MEM_delete<blender::BezierSpline2f>(stroke->spline);
MEM_delete<blender::BezierSpline3f>(stroke->world_spline);
UnifiedPaintSettings *ups = stroke->ups;
ups->draw_anchored = false;
ups->stroke_active = false;
@ -1235,7 +1398,7 @@ 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);
@ -1297,7 +1460,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;
}
@ -1420,17 +1583,6 @@ bool paint_stroke_has_cubic(const PaintStroke *stroke)
return stroke->has_cubic_stroke;
}
static int paint_stroke_max_samples(const Paint *paint, PaintStroke *stroke)
{
int max_samples = CLAMPIS(paint->num_input_samples, 1, PAINT_MAX_INPUT_SAMPLES);
if (stroke->has_cubic_stroke) {
max_samples = max_ii(max_samples, 6);
}
return max_samples;
}
static void paint_stroke_add_sample(
const Paint *paint, PaintStroke *stroke, float x, float y, float pressure)
{
@ -1463,8 +1615,6 @@ static void paint_stroke_sample_average(const PaintStroke *stroke, PaintSample *
mul_v2_fl(average->mouse, 1.0f / stroke->num_samples);
average->pressure /= stroke->num_samples;
// printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);
}
/**
@ -1753,12 +1903,13 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
if (stroke->stroke_sample_index == 0) {
stroke->last_mouse_position[0] = event->mval[0];
stroke->last_mouse_position[1] = event->mval[1];
#if 0
int max_samples = paint_stroke_max_samples(p, stroke);
for (int i = stroke->num_samples; i < max_samples; i++) {
paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure);
}
#endif
}
/* Tilt. */
@ -1898,6 +2049,9 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
/* we want the stroke to have the first daub at the start location
* instead of waiting till we have moved the space distance */
if (first_dab && paint_space_stroke_enabled(br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) {
stroke->spacing = paint_space_stroke_spacing(
C, CTX_data_scene(C), stroke, 1.0f, sample_average.pressure);
stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0);
paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure);
redraw = true;

View File

@ -1278,6 +1278,10 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSessi
int v1,
int v2)
{
if (!ss->face_sets) {
return true;
}
const MeshElemMap *vert_map = &ss->pmap->pmap[v1];
int p1 = -1, p2 = -1;
for (int i = 0; i < vert_map->count; i++) {
@ -3770,10 +3774,7 @@ float bezier3_arclength_v2(const float control[4][2])
/* Evaluate bezier position and tangent at a specific parameter value
* using the De Casteljau algorithm. */
static void evaluate_cubic_bezier(const float control[4][3],
float t,
float r_pos[3],
float r_tangent[3])
void evaluate_cubic_bezier(const float control[4][3], float t, float r_pos[3], float r_tangent[3])
{
float layer1[3][3];
interp_v3_v3v3(layer1[0], control[0], control[1], t);
@ -4126,49 +4127,31 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
float point_3d[3];
point_3d[2] = 0.0f;
calc_cubic_uv_v3(ss->cache->world_cubic, SCULPT_vertex_co_get(ss, vertex), point_3d);
// calc_cubic_uv_v3(ss->cache->world_cubic, SCULPT_vertex_co_get(ss, vertex), point_3d);
float tan[3], curv[3];
float eps = 0.001;
if (point_3d[0] < eps || point_3d[0] >= 1.0f - eps) {
return 0.0f;
}
paint_calc_cubic_uv_v3(
ss->cache->stroke, ss->cache, SCULPT_vertex_co_get(ss, vertex), point_3d, tan);
if (point_3d[1] >= ss->cache->radius) {
// return 0.0f;
}
float pos[3], tan[3];
evaluate_cubic_bezier(ss->cache->world_cubic, point_3d[0], pos, tan);
float vec[3], vec2[3];
normalize_v3(tan);
sub_v3_v3v3(vec, SCULPT_vertex_co_get(ss, vertex), pos);
normalize_v3(vec);
cross_v3_v3v3(vec2, vec, tan);
if (dot_v3v3(vec2, ss->cache->view_normal) < 0.0) {
point_3d[1] = (ss->cache->radius + point_3d[1]) * 0.5f;
}
else {
point_3d[1] = (ss->cache->radius - point_3d[1]) * 0.5f;
}
point_3d[1] = (ss->cache->radius + point_3d[1]) * 0.5f;
/* Calc global distance. */
float t1 = ss->cache->last_stroke_distance_t;
float t2 = point_3d[0] * ss->cache->world_cubic_arclength / ss->cache->radius;
float t2 = point_3d[0] / ss->cache->radius;
point_3d[0] = t1 + t2;
point_3d[0] *= ss->cache->radius;
#if 0
float color[4] = {point_3d[0], point_3d[0], point_3d[0], 1.0f};
mul_v3_fl(color, 0.25f / ss->cache->radius);
color[0] -= floorf(color[0]);
color[1] -= floorf(color[1]);
color[2] -= floorf(color[2]);
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->radius);
color[0] -= floorf(color[0]);
color[1] -= floorf(color[1]);
color[2] -= floorf(color[2]);
SCULPT_vertex_color_set(ss, vertex, color);
SCULPT_vertex_color_set(ss, vertex, color);
}
// avg = 0.0f;
#endif
@ -7378,6 +7361,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
if (cache->has_cubic) {
float mouse_cubic[4][2];
// paint_project_spline(C, cache,
RNA_float_get_array(ptr, "mouse_cubic", (float *)mouse_cubic);
// printf("\n");
@ -7407,7 +7391,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
#endif
}
cache->world_cubic_arclength = bezier3_arclength_v3(cache->world_cubic);
cache->world_cubic_arclength = paint_stroke_spline_length(cache->stroke);
cache->mouse_cubic_arclength = bezier3_arclength_v3(cache->mouse_cubic);
}
}
@ -8360,6 +8344,10 @@ static void sculpt_stroke_update_step(bContext *C,
cache->stroke_distance = paint_stroke_distance_get(stroke);
SCULPT_stroke_modifiers_check(C, ob, brush);
if (stroke) {
paint_project_spline(C, ss->cache, stroke);
}
if (itemptr) {
sculpt_update_cache_variants(C, sd, ob, itemptr);
}
@ -9471,7 +9459,7 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex
mv->valence = tot;
}
#if 0
#if 0
else {
int tot = 0;
@ -9485,7 +9473,7 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex
printf("%s: error: valence error!\n", __func__);
}
}
#endif
#endif
return mv->valence;
}