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:
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c12
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c604
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c28
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c91
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c170
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c37
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c6
9 files changed, 529 insertions, 434 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 0e38340d3bc..ee514fa745c 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -566,7 +566,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
if (load_tex(brush, vc, zoom, col, primary)) {
GPU_color_mask(true, true, true, true);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
GPU_matrix_push();
@@ -693,7 +693,7 @@ static bool paint_draw_cursor_overlay(
float center[2];
GPU_color_mask(true, true, true, true);
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
if (ups->draw_anchored) {
copy_v2_v2(center, ups->anchored_initial_mouse);
@@ -776,7 +776,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags();
eGPUBlend blend_state = GPU_blend_get();
- bool depth_test = GPU_depth_test_enabled();
+ eGPUDepthTest depth_test = GPU_depth_test_get();
/* Translate to region. */
GPU_matrix_push();
@@ -1147,9 +1147,9 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr,
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f);
/* Cursor normally draws on top, but for this part we need depth tests. */
- const bool depth_test = GPU_depth_test_enabled();
+ const eGPUDepthTest depth_test = GPU_depth_test_get();
if (!depth_test) {
- GPU_depth_test(true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
GPU_line_width(1.0f);
@@ -1163,7 +1163,7 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr,
/* Restore depth test value. */
if (!depth_test) {
- GPU_depth_test(false);
+ GPU_depth_test(GPU_DEPTH_NONE);
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 05ffb80d8a1..ab8b81a8155 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -31,6 +31,7 @@
#include "BLI_lasso_2d.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
+#include "BLI_rect.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@@ -221,392 +222,383 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot)
1.0f);
}
-/* Box select, operator is VIEW3D_OT_select_box, defined in view3d_select.c. */
+/* Sculpt Gesture Operators. */
-static bool is_effected(float planes[4][4], const float co[3])
-{
- return isect_point_planes_v3(planes, 4, co);
-}
+typedef enum eSculptGestureShapeType {
+ SCULPT_GESTURE_SHAPE_BOX,
+ SCULPT_GESTURE_SHAPE_LASSO,
+} eMaskGesturesShapeType;
-static void flip_plane(float out[4], const float in[4], const char symm)
-{
- if (symm & PAINT_SYMM_X) {
- out[0] = -in[0];
- }
- else {
- out[0] = in[0];
- }
- if (symm & PAINT_SYMM_Y) {
- out[1] = -in[1];
- }
- else {
- out[1] = in[1];
- }
- if (symm & PAINT_SYMM_Z) {
- out[2] = -in[2];
- }
- else {
- out[2] = in[2];
- }
+typedef struct LassoGestureData {
+ float projviewobjmat[4][4];
- out[3] = in[3];
-}
+ rcti boundbox;
+ int width;
-static void mask_box_select_task_cb(void *__restrict userdata,
- const int i,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- MaskTaskData *data = userdata;
+ /* 2D bitmap to test if a vertex is affected by the lasso shape. */
+ BLI_bitmap *mask_px;
+} LassoGestureData;
- PBVHNode *node = data->nodes[i];
+typedef struct SculptGestureContext {
+ SculptSession *ss;
+ ViewContext vc;
- const PaintMaskFloodMode mode = data->mode;
- const float value = data->value;
- float(*clip_planes_final)[4] = data->clip_planes_final;
+ /* Enabled and currently active symmetry. */
+ ePaintSymmetryFlags symm;
+ ePaintSymmetryFlags symmpass;
- PBVHVertexIter vi;
- bool any_masked = false;
- bool redraw = false;
+ /* Operation parameters. */
+ eMaskGesturesShapeType shape_type;
+ bool front_faces_only;
- float vertex_normal[3];
+ /* Mask operation parameters. */
+ PaintMaskFloodMode mask_mode;
+ float mask_value;
- BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE)
- {
- SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal);
- float dot = dot_v3v3(data->view_normal, vertex_normal);
- const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f);
+ /* View parameters. */
+ float true_view_normal[3];
+ float view_normal[3];
- if (is_effected_front_face && is_effected(clip_planes_final, vi.co)) {
- float prevmask = *vi.mask;
- if (!any_masked) {
- any_masked = true;
+ float true_clip_planes[4][4];
+ float clip_planes[4][4];
- SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
+ /* Lasso Gesture. */
+ LassoGestureData lasso;
- if (data->multires) {
- BKE_pbvh_node_mark_normals_update(node);
- }
- }
- mask_flood_fill_set_elem(vi.mask, mode, value);
- if (prevmask != *vi.mask) {
- redraw = true;
- }
- }
- }
- BKE_pbvh_vertex_iter_end;
+ /* Task Callback Data. */
+ PBVHNode **nodes;
+ int totnode;
+} SculptGestureContext;
- if (redraw) {
- BKE_pbvh_node_mark_update_mask(node);
- }
+static void sculpt_gesture_operator_properties(wmOperatorType *ot)
+{
+ RNA_def_boolean(ot->srna,
+ "use_front_faces_only",
+ false,
+ "Front Faces Only",
+ "Affect only faces facing towards the view");
}
-static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op)
+static void sculpt_gesture_context_init_common(bContext *C,
+ wmOperator *op,
+ SculptGestureContext *sgcontext)
{
- ViewContext vc;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
-
- Sculpt *sd = vc.scene->toolsettings->sculpt;
- BoundBox bb;
- float clip_planes[4][4];
- float clip_planes_final[4][4];
- ARegion *region = vc.region;
- Object *ob = vc.obact;
- bool multires;
- PBVH *pbvh;
- PBVHNode **nodes;
- int totnode;
- int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
-
- const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode");
- const float value = RNA_float_get(op->ptr, "value");
- const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
+ ED_view3d_viewcontext_init(C, &sgcontext->vc, depsgraph);
- rcti rect;
- WM_operator_properties_border_to_rcti(op, &rect);
+ Sculpt *sd = sgcontext->vc.scene->toolsettings->sculpt;
+ Object *ob = sgcontext->vc.obact;
- /* Transform the clip planes in object space. */
- ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect);
+ /* Operator properties. */
+ sgcontext->front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
- pbvh = ob->sculpt->pbvh;
- multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
+ /* SculptSession */
+ sgcontext->ss = ob->sculpt;
- SCULPT_undo_push_begin("Mask box fill");
+ /* Symmetry. */
+ sgcontext->symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
- /* Calculate the view normal in object space. */
+ /* View Normal. */
float mat[3][3];
float view_dir[3] = {0.0f, 0.0f, 1.0f};
- float true_view_normal[3];
- copy_m3_m4(mat, vc.rv3d->viewinv);
+ copy_m3_m4(mat, sgcontext->vc.rv3d->viewinv);
mul_m3_v3(mat, view_dir);
copy_m3_m4(mat, ob->imat);
mul_m3_v3(mat, view_dir);
- normalize_v3_v3(true_view_normal, view_dir);
+ normalize_v3_v3(sgcontext->true_view_normal, view_dir);
+}
- for (int symmpass = 0; symmpass <= symm; symmpass++) {
- if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) &&
- (symm != 6 || (symmpass != 3 && symmpass != 5)))) {
+static void sculpt_gesture_lasso_px_cb(int x, int x_end, int y, void *user_data)
+{
+ SculptGestureContext *mcontext = user_data;
+ LassoGestureData *lasso = &mcontext->lasso;
+ int index = (y * lasso->width) + x;
+ int index_end = (y * lasso->width) + x_end;
+ do {
+ BLI_BITMAP_ENABLE(lasso->mask_px, index);
+ } while (++index != index_end);
+}
- /* Flip the planes symmetrically as needed. */
- for (int j = 0; j < 4; j++) {
- flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
- }
+static SculptGestureContext *sculpt_gesture_init_from_lasso(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext),
+ "sculpt gesture context lasso");
+ sgcontext->shape_type = SCULPT_GESTURE_SHAPE_LASSO;
+
+ sculpt_gesture_context_init_common(C, op, sgcontext);
- PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4};
- BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode);
+ int mcoords_len;
+ const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
- negate_m4(clip_planes_final);
+ if (!mcoords) {
+ return NULL;
+ }
- MaskTaskData data = {
- .ob = ob,
- .pbvh = pbvh,
- .nodes = nodes,
- .multires = multires,
- .mode = mode,
- .value = value,
- .clip_planes_final = clip_planes_final,
- .front_faces_only = front_faces_only,
- };
+ ED_view3d_ob_project_mat_get(
+ sgcontext->vc.rv3d, sgcontext->vc.obact, sgcontext->lasso.projviewobjmat);
+ BLI_lasso_boundbox(&sgcontext->lasso.boundbox, mcoords, mcoords_len);
+ sgcontext->lasso.width = sgcontext->lasso.boundbox.xmax - sgcontext->lasso.boundbox.xmin;
+ sgcontext->lasso.mask_px = BLI_BITMAP_NEW(
+ sgcontext->lasso.width * (sgcontext->lasso.boundbox.ymax - sgcontext->lasso.boundbox.ymin),
+ __func__);
+
+ BLI_bitmap_draw_2d_poly_v2i_n(sgcontext->lasso.boundbox.xmin,
+ sgcontext->lasso.boundbox.ymin,
+ sgcontext->lasso.boundbox.xmax,
+ sgcontext->lasso.boundbox.ymax,
+ mcoords,
+ mcoords_len,
+ sculpt_gesture_lasso_px_cb,
+ sgcontext);
- flip_v3_v3(data.view_normal, true_view_normal, symmpass);
+ BoundBox bb;
+ ED_view3d_clipping_calc(&bb,
+ sgcontext->true_clip_planes,
+ sgcontext->vc.region,
+ sgcontext->vc.obact,
+ &sgcontext->lasso.boundbox);
+ MEM_freeN((void *)mcoords);
+
+ return sgcontext;
+}
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings);
+static SculptGestureContext *sculpt_gesture_init_from_box(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext),
+ "sculpt gesture context box");
+ sgcontext->shape_type = SCULPT_GESTURE_SHAPE_BOX;
- if (nodes) {
- MEM_freeN(nodes);
- }
- }
- }
+ sculpt_gesture_context_init_common(C, op, sgcontext);
- if (multires) {
- multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);
- }
+ rcti rect;
+ WM_operator_properties_border_to_rcti(op, &rect);
- BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask);
+ BoundBox bb;
+ ED_view3d_clipping_calc(
+ &bb, sgcontext->true_clip_planes, sgcontext->vc.region, sgcontext->vc.obact, &rect);
- SCULPT_undo_push_end();
+ return sgcontext;
+}
- ED_region_tag_redraw(region);
+static void sculpt_gesture_context_free(SculptGestureContext *sgcontext)
+{
+ MEM_SAFE_FREE(sgcontext->lasso.mask_px);
+ MEM_SAFE_FREE(sgcontext->nodes);
+ MEM_SAFE_FREE(sgcontext);
+}
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+static void flip_plane(float out[4], const float in[4], const char symm)
+{
+ if (symm & PAINT_SYMM_X) {
+ out[0] = -in[0];
+ }
+ else {
+ out[0] = in[0];
+ }
+ if (symm & PAINT_SYMM_Y) {
+ out[1] = -in[1];
+ }
+ else {
+ out[1] = in[1];
+ }
+ if (symm & PAINT_SYMM_Z) {
+ out[2] = -in[2];
+ }
+ else {
+ out[2] = in[2];
+ }
- return true;
+ out[3] = in[3];
}
-typedef struct LassoMaskData {
- struct ViewContext *vc;
- float projviewobjmat[4][4];
- BLI_bitmap *px;
- int width;
- /* Bounding box for scanfilling. */
- rcti rect;
- int symmpass;
+static void sculpt_gesture_flip_for_symmetry_pass(SculptGestureContext *sgcontext,
+ const ePaintSymmetryFlags symmpass)
+{
+ sgcontext->symmpass = symmpass;
+ for (int j = 0; j < 4; j++) {
+ flip_plane(sgcontext->clip_planes[j], sgcontext->true_clip_planes[j], symmpass);
+ }
+ negate_m4(sgcontext->clip_planes);
+ flip_v3_v3(sgcontext->view_normal, sgcontext->true_view_normal, symmpass);
+}
- MaskTaskData task_data;
-} LassoMaskData;
+static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext)
+{
+ SculptSession *ss = sgcontext->ss;
+ float clip_planes[4][4];
+ copy_m4_m4(clip_planes, sgcontext->clip_planes);
+ negate_m4(clip_planes);
+ PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4};
+ BKE_pbvh_search_gather(ss->pbvh,
+ BKE_pbvh_node_frustum_contain_AABB,
+ &frustum,
+ &sgcontext->nodes,
+ &sgcontext->totnode);
+}
-/**
- * Lasso select. This could be defined as part of #VIEW3D_OT_select_lasso,
- * still the shortcuts conflict, so we will use a separate operator.
- */
-static bool is_effected_lasso(LassoMaskData *data, const float co[3])
+static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, const float co[3])
{
float scr_co_f[2];
int scr_co_s[2];
float co_final[3];
- flip_v3_v3(co_final, co, data->symmpass);
+ flip_v3_v3(co_final, co, sgcontext->symmpass);
+
/* First project point to 2d space. */
- ED_view3d_project_float_v2_m4(data->vc->region, co_final, scr_co_f, data->projviewobjmat);
+ ED_view3d_project_float_v2_m4(
+ sgcontext->vc.region, co_final, scr_co_f, sgcontext->lasso.projviewobjmat);
scr_co_s[0] = scr_co_f[0];
scr_co_s[1] = scr_co_f[1];
- /* Clip against screen, because lasso is limited to screen only. */
- if ((scr_co_s[0] < data->rect.xmin) || (scr_co_s[1] < data->rect.ymin) ||
- (scr_co_s[0] >= data->rect.xmax) || (scr_co_s[1] >= data->rect.ymax)) {
+ /* Clip against lasso boundbox. */
+ LassoGestureData *lasso = &sgcontext->lasso;
+ if (!BLI_rcti_isect_pt(&lasso->boundbox, scr_co_s[0], scr_co_s[1])) {
return false;
}
- scr_co_s[0] -= data->rect.xmin;
- scr_co_s[1] -= data->rect.ymin;
+ scr_co_s[0] -= lasso->boundbox.xmin;
+ scr_co_s[1] -= lasso->boundbox.ymin;
- return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]);
+ return BLI_BITMAP_TEST_BOOL(lasso->mask_px, scr_co_s[1] * lasso->width + scr_co_s[0]);
}
-static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data)
+static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd)
{
- LassoMaskData *data = user_data;
- int index = (y * data->width) + x;
- int index_end = (y * data->width) + x_end;
- do {
- BLI_BITMAP_ENABLE(data->px, index);
- } while (++index != index_end);
+ float vertex_normal[3];
+ SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal);
+ float dot = dot_v3v3(sgcontext->view_normal, vertex_normal);
+ const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f);
+
+ if (!is_effected_front_face) {
+ return false;
+ }
+
+ switch (sgcontext->shape_type) {
+ case SCULPT_GESTURE_SHAPE_BOX:
+ return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co);
+ case SCULPT_GESTURE_SHAPE_LASSO:
+ return sculpt_gesture_is_effected_lasso(sgcontext, vd->co);
+ }
+ return false;
}
-static void mask_gesture_lasso_task_cb(void *__restrict userdata,
+static void mask_gesture_apply_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
- LassoMaskData *lasso_data = userdata;
- MaskTaskData *data = &lasso_data->task_data;
-
- PBVHNode *node = data->nodes[i];
+ SculptGestureContext *sgcontext = userdata;
+ Object *ob = sgcontext->vc.obact;
+ PBVHNode *node = sgcontext->nodes[i];
- const PaintMaskFloodMode mode = data->mode;
- const float value = data->value;
+ const bool is_multires = BKE_pbvh_type(sgcontext->ss->pbvh) == PBVH_GRIDS;
- PBVHVertexIter vi;
+ PBVHVertexIter vd;
bool any_masked = false;
+ bool redraw = false;
- float vertex_normal[3];
-
- BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE)
+ BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
- SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal);
- float dot = dot_v3v3(lasso_data->task_data.view_normal, vertex_normal);
- const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f);
-
- if (is_effected_front_face && is_effected_lasso(lasso_data, vi.co)) {
+ if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
+ float prevmask = *vd.mask;
if (!any_masked) {
any_masked = true;
- SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
+ SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
- BKE_pbvh_node_mark_redraw(node);
- if (data->multires) {
+ if (is_multires) {
BKE_pbvh_node_mark_normals_update(node);
}
}
-
- mask_flood_fill_set_elem(vi.mask, mode, value);
+ mask_flood_fill_set_elem(vd.mask, sgcontext->mask_mode, sgcontext->mask_value);
+ if (prevmask != *vd.mask) {
+ redraw = true;
+ }
}
}
BKE_pbvh_vertex_iter_end;
+
+ if (redraw) {
+ BKE_pbvh_node_mark_update_mask(node);
+ }
}
-static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
+static void sculpt_gesture_apply(bContext *C, SculptGestureContext *mcontext)
{
- int mcoords_len;
- const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ BKE_sculpt_update_object_for_edit(depsgraph, mcontext->vc.obact, false, true, false);
- if (mcoords) {
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- float clip_planes[4][4], clip_planes_final[4][4];
- BoundBox bb;
- Object *ob;
- ViewContext vc;
- LassoMaskData data;
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
- PBVH *pbvh;
- PBVHNode **nodes;
- int totnode;
- bool multires;
- PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode");
- float value = RNA_float_get(op->ptr, "value");
- const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only");
-
- /* Calculations of individual vertices are done in 2D screen space to diminish the amount of
- * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle
- * of lasso. */
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
-
- /* Lasso data calculations. */
- data.vc = &vc;
- ob = vc.obact;
- ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat);
-
- BLI_lasso_boundbox(&data.rect, mcoords, mcoords_len);
- data.width = data.rect.xmax - data.rect.xmin;
- data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__);
-
- BLI_bitmap_draw_2d_poly_v2i_n(data.rect.xmin,
- data.rect.ymin,
- data.rect.xmax,
- data.rect.ymax,
- mcoords,
- mcoords_len,
- mask_lasso_px_cb,
- &data);
-
- ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect);
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
- pbvh = ob->sculpt->pbvh;
- multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
-
- SCULPT_undo_push_begin("Mask lasso fill");
-
- /* Calculate the view normal in object space. */
- float mat[3][3];
- float view_dir[3] = {0.0f, 0.0f, 1.0f};
- float true_view_normal[3];
- copy_m3_m4(mat, vc.rv3d->viewinv);
- mul_m3_v3(mat, view_dir);
- copy_m3_m4(mat, ob->imat);
- mul_m3_v3(mat, view_dir);
- normalize_v3_v3(true_view_normal, view_dir);
-
- for (int symmpass = 0; symmpass <= symm; symmpass++) {
- if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) &&
- (symm != 6 || (symmpass != 3 && symmpass != 5)))) {
-
- /* Flip the planes symmetrically as needed. */
- for (int j = 0; j < 4; j++) {
- flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
- }
+ SCULPT_undo_push_begin("Sculpt Gesture Apply");
- flip_v3_v3(data.task_data.view_normal, true_view_normal, symmpass);
+ for (ePaintSymmetryFlags symmpass = 0; symmpass <= mcontext->symm; symmpass++) {
+ if (SCULPT_is_symmetry_iteration_valid(symmpass, mcontext->symm)) {
+ sculpt_gesture_flip_for_symmetry_pass(mcontext, symmpass);
+ sculpt_gesture_update_effected_nodes(mcontext);
- data.symmpass = symmpass;
-
- /* Gather nodes inside lasso's enclosing rectangle
- * (should greatly help with bigger meshes). */
- PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4};
- BKE_pbvh_search_gather(
- pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode);
-
- negate_m4(clip_planes_final);
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, mcontext->totnode);
+ BLI_task_parallel_range(
+ 0, mcontext->totnode, mcontext, mask_gesture_apply_task_cb, &settings);
- data.task_data.ob = ob;
- data.task_data.pbvh = pbvh;
- data.task_data.nodes = nodes;
- data.task_data.multires = multires;
- data.task_data.mode = mode;
- data.task_data.value = value;
- data.task_data.front_faces_only = front_faces_only;
+ MEM_SAFE_FREE(mcontext->nodes);
+ }
+ }
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings);
+ if (BKE_pbvh_type(mcontext->ss->pbvh) == PBVH_GRIDS) {
+ multires_mark_as_modified(depsgraph, mcontext->vc.obact, MULTIRES_COORDS_MODIFIED);
+ }
- if (nodes) {
- MEM_freeN(nodes);
- }
- }
- }
+ BKE_pbvh_update_vertex_data(mcontext->ss->pbvh, PBVH_UpdateMask);
- if (multires) {
- multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);
- }
+ SCULPT_undo_push_end();
- BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask);
+ ED_region_tag_redraw(mcontext->vc.region);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, mcontext->vc.obact);
+}
- SCULPT_undo_push_end();
+static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op)
+{
+ sgcontext->mask_mode = RNA_enum_get(op->ptr, "mode");
+ sgcontext->mask_value = RNA_float_get(op->ptr, "value");
+}
- ED_region_tag_redraw(vc.region);
- MEM_freeN((void *)mcoords);
- MEM_freeN(data.px);
+static void paint_mask_gesture_operator_properties(wmOperatorType *ot)
+{
+ RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
+ RNA_def_float(
+ ot->srna,
+ "value",
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ "Value",
+ "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
+ 0.0f,
+ 1.0f);
+}
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op);
+ if (!sgcontext) {
+ return OPERATOR_CANCELLED;
+ }
+ sculpt_gesture_init_mask_properties(sgcontext, op);
+ sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_context_free(sgcontext);
+ return OPERATOR_FINISHED;
+}
- return OPERATOR_FINISHED;
+static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
+{
+ SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op);
+ if (!sgcontext) {
+ return OPERATOR_CANCELLED;
}
- return OPERATOR_PASS_THROUGH;
+ sculpt_gesture_init_mask_properties(sgcontext, op);
+ sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_context_free(sgcontext);
+ return OPERATOR_FINISHED;
}
void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
@@ -625,23 +617,9 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
+ sculpt_gesture_operator_properties(ot);
- RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
- RNA_def_float(
- ot->srna,
- "value",
- 1.0f,
- 0.0f,
- 1.0f,
- "Value",
- "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
- 0.0f,
- 1.0f);
- RNA_def_boolean(ot->srna,
- "use_front_faces_only",
- false,
- "Front Faces Only",
- "Affect only faces facing towards the view");
+ paint_mask_gesture_operator_properties(ot);
}
void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
@@ -660,21 +638,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_border(ot);
+ sculpt_gesture_operator_properties(ot);
- RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
- RNA_def_float(
- ot->srna,
- "value",
- 1.0f,
- 0.0f,
- 1.0f,
- "Value",
- "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
- 0.0f,
- 1.0f);
- RNA_def_boolean(ot->srna,
- "use_front_faces_only",
- false,
- "Front Faces Only",
- "Affect only faces facing towards the view");
+ paint_mask_gesture_operator_properties(ot);
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 52cdebf3fd5..e709224f370 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -691,6 +691,14 @@ static float paint_space_stroke_spacing(bContext *C,
spacing = spacing * (1.5f - spacing_pressure);
}
+ 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.
+ With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2
+ pixels movement of the cursor. */
+ size_clamp = 100.0f;
+ }
+
/* stroke system is used for 2d paint too, so we need to account for
* the fact that brush can be scaled there. */
spacing *= stroke->zoom_2d;
@@ -1001,7 +1009,7 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
return false;
}
- if (br->sculpt_tool == SCULPT_TOOL_CLOTH) {
+ if (br->sculpt_tool == SCULPT_TOOL_CLOTH || SCULPT_is_cloth_deform_brush(br)) {
/* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do
* not support dynamic size, stroke spacing needs to be enabled so it is possible to control
* whether the simulation runs constantly or only when the brush moves when using the cloth
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index e1c1b8ee5fb..7a066f35f23 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -2273,7 +2273,7 @@ static float brush_strength(const Sculpt *sd,
case SCULPT_TOOL_DISPLACEMENT_ERASER:
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_CLOTH:
- if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
+ if (ELEM(brush->cloth_deform_type, BRUSH_CLOTH_DEFORM_GRAB, BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) {
/* Grab deform uses the same falloff as a regular grab brush. */
return root_alpha * feather;
}
@@ -6634,13 +6634,19 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
* generally used to create grab deformations. */
static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
{
- return ELEM(brush->sculpt_tool,
- SCULPT_TOOL_GRAB,
- SCULPT_TOOL_POSE,
- SCULPT_TOOL_BOUNDARY,
- SCULPT_TOOL_THUMB,
- SCULPT_TOOL_ELASTIC_DEFORM) ||
- SCULPT_is_cloth_deform_brush(brush);
+ if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_GRAB,
+ SCULPT_TOOL_POSE,
+ SCULPT_TOOL_BOUNDARY,
+ SCULPT_TOOL_THUMB,
+ SCULPT_TOOL_ELASTIC_DEFORM)) {
+ return true;
+ }
+ if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
+ brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
+ return true;
+ }
+ return false;
}
/* In these brushes the grab delta is calculated from the previous stroke location, which is used
@@ -6648,7 +6654,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
static bool sculpt_needs_delta_for_tip_orientation(Brush *brush)
{
if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
- return !SCULPT_is_cloth_deform_brush(brush);
+ return brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK;
}
return ELEM(brush->sculpt_tool,
SCULPT_TOOL_CLAY_STRIPS,
@@ -6694,7 +6700,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
copy_v3_v3(cache->orig_grab_location, cache->true_location);
}
}
- else if (tool == SCULPT_TOOL_SNAKE_HOOK) {
+ else if (tool == SCULPT_TOOL_SNAKE_HOOK ||
+ (tool == SCULPT_TOOL_CLOTH &&
+ brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) {
add_v3_v3(cache->true_location, cache->grab_delta);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 9a3fbe474b8..c3666c8aaad 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -169,6 +169,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
length_constraint->elem_position_a = cloth_sim->pos[v1];
length_constraint->elem_position_b = cloth_sim->pos[v2];
+ length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL;
+
if (use_persistent) {
length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1),
SCULPT_vertex_persistent_co_get(ss, v2));
@@ -201,6 +203,8 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
length_constraint->elem_position_a = cloth_sim->pos[v];
length_constraint->elem_position_b = cloth_sim->init_pos[v];
+ length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY;
+
length_constraint->length = 0.0f;
length_constraint->strength = strength;
@@ -220,6 +224,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
+ length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION;
+
length_constraint->elem_position_a = cloth_sim->pos[v];
length_constraint->elem_position_b = cloth_sim->deformation_pos[v];
@@ -301,12 +307,18 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
if (brush && brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
/* The cloth brush works by applying forces in most of its modes, but some of them require
* deformation coordinates to make the simulation stable. */
- if (cloth_is_deform_brush && len_squared < radius_squared) {
- /* When a deform brush is used as part of the cloth brush, deformation constraints are
- * created with different strengths and only inside the radius of the brush. */
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB && len_squared < radius_squared) {
+ /* When the grab brush brush is used as part of the cloth brush, deformation constraints
+ * are created with different strengths and only inside the radius of the brush. */
const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius);
cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade);
}
+ else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
+ /* Cloth Snake Hook creates deformation constraint with fixed strength because the strength
+ * is controlled per iteration using cloth_sim->deformation_strength. */
+ cloth_brush_add_deformation_constraint(
+ data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
+ }
}
else if (data->cloth_sim->deformation_pos) {
/* Any other tool that target the cloth simulation handle the falloff in
@@ -397,7 +409,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]);
float current_vertex_location[3];
- if (SCULPT_is_cloth_deform_brush(brush)) {
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
copy_v3_v3(current_vertex_location, orig_data.co);
}
@@ -456,6 +468,12 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
fade);
zero_v3(force);
break;
+ case BRUSH_CLOTH_DEFORM_SNAKE_HOOK:
+ copy_v3_v3(cloth_sim->deformation_pos[vd.index], cloth_sim->pos[vd.index]);
+ madd_v3_v3fl(cloth_sim->deformation_pos[vd.index], ss->cache->grab_delta_symmetry, fade);
+ cloth_sim->deformation_strength[vd.index] = fade;
+ zero_v3(force);
+ break;
case BRUSH_CLOTH_DEFORM_PINCH_POINT:
if (use_falloff_plane) {
float distance = dist_signed_to_plane_v3(vd.co, deform_plane);
@@ -718,13 +736,21 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
cloth_sim->init_pos[v2]) :
1.0f;
+ float deformation_strength = 1.0f;
+ if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) {
+ deformation_strength = (cloth_sim->deformation_strength[v1] +
+ cloth_sim->deformation_strength[v2]) *
+ 0.5f;
+ }
+
madd_v3_v3fl(cloth_sim->pos[v1],
correction_vector_half,
- 1.0f * mask_v1 * sim_factor_v1 * constraint->strength);
+ 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * deformation_strength);
if (v1 != v2) {
madd_v3_v3fl(cloth_sim->pos[v2],
correction_vector_half,
- -1.0f * mask_v2 * sim_factor_v2 * constraint->strength);
+ -1.0f * mask_v2 * sim_factor_v2 * constraint->strength *
+ deformation_strength);
}
}
}
@@ -824,6 +850,15 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
}
}
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
+ /* Set the deformation strength to 0. Snake hook will initialize the strength in the required
+ * area. */
+ const int totverts = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totverts; i++) {
+ ss->cache->cloth_sim->deformation_strength[i] = 0.0f;
+ }
+ }
+
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
@@ -862,6 +897,8 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
if (brush && SCULPT_is_cloth_deform_brush(brush)) {
cloth_sim->deformation_pos = MEM_calloc_arrayN(
totverts, sizeof(float[3]), "cloth sim deformation positions");
+ cloth_sim->deformation_strength = MEM_calloc_arrayN(
+ totverts, sizeof(float), "cloth sim deformation strength");
}
cloth_sim->mass = cloth_mass;
@@ -921,6 +958,7 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation
copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
if (has_deformation_pos) {
copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
+ cloth_sim->deformation_strength[i] = 1.0f;
}
}
}
@@ -986,6 +1024,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
MEM_SAFE_FREE(cloth_sim->length_constraint_tweak);
MEM_SAFE_FREE(cloth_sim->deformation_pos);
MEM_SAFE_FREE(cloth_sim->init_pos);
+ MEM_SAFE_FREE(cloth_sim->deformation_strength);
if (cloth_sim->collider_list) {
BKE_collider_cache_free(&cloth_sim->collider_list);
}
@@ -1071,6 +1110,25 @@ static EnumPropertyItem prop_cloth_filter_type[] = {
{0, NULL, 0, NULL, NULL},
};
+static EnumPropertyItem prop_cloth_filter_orientation_items[] = {
+ {SCULPT_FILTER_ORIENTATION_LOCAL,
+ "LOCAL",
+ 0,
+ "Local",
+ "Use the local axis to limit the force and set the gravity direction"},
+ {SCULPT_FILTER_ORIENTATION_WORLD,
+ "WORLD",
+ 0,
+ "World",
+ "Use the global axis to limit the force and set the gravity direction"},
+ {SCULPT_FILTER_ORIENTATION_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Use the view axis to limit the force and set the gravity direction"},
+ {0, NULL, 0, NULL, NULL},
+};
+
typedef enum eClothFilterForceAxis {
CLOTH_FILTER_FORCE_X = 1 << 0,
CLOTH_FILTER_FORCE_Y = 1 << 1,
@@ -1120,7 +1178,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
switch (filter_type) {
case CLOTH_FILTER_GRAVITY:
- force[2] = -data->filter_strength * fade;
+ if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) {
+ /* When using the view orientation apply gravity in the -Y axis, this way objects will
+ * fall down instead of backwards. */
+ force[1] = -data->filter_strength * fade;
+ }
+ else {
+ force[2] = -data->filter_strength * fade;
+ }
+ SCULPT_filter_to_object_space(force, ss->filter_cache);
break;
case CLOTH_FILTER_INFLATE: {
float normal[3];
@@ -1138,11 +1204,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
break;
}
+ SCULPT_filter_to_orientation_space(force, ss->filter_cache);
for (int axis = 0; axis < 3; axis++) {
if (!ss->filter_cache->enabled_force_axis[axis]) {
force[axis] = 0.0f;
}
}
+ SCULPT_filter_to_object_space(force, ss->filter_cache);
add_v3_v3(force, sculpt_gravity);
@@ -1264,6 +1332,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
ss->filter_cache->enabled_force_axis[1] = force_axis & CLOTH_FILTER_FORCE_Y;
ss->filter_cache->enabled_force_axis[2] = force_axis & CLOTH_FILTER_FORCE_Z;
+ SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation");
+ ss->filter_cache->orientation = orientation;
+
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
@@ -1297,6 +1368,12 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot)
CLOTH_FILTER_FORCE_X | CLOTH_FILTER_FORCE_Y | CLOTH_FILTER_FORCE_Z,
"Force axis",
"Apply the force in the selected axis");
+ RNA_def_enum(ot->srna,
+ "orientation",
+ prop_cloth_filter_orientation_items,
+ SCULPT_FILTER_ORIENTATION_LOCAL,
+ "Orientation",
+ "Orientation of the axis to limit the filter force");
RNA_def_float(ot->srna,
"cloth_mass",
1.0f,
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 56e70a8d47a..619a1b975b6 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -181,7 +181,7 @@ void SCULPT_filter_cache_free(SculptSession *ss)
MEM_SAFE_FREE(ss->filter_cache);
}
-typedef enum eSculptMeshFilterTypes {
+typedef enum eSculptMeshFilterType {
MESH_FILTER_SMOOTH = 0,
MESH_FILTER_SCALE = 1,
MESH_FILTER_INFLATE = 2,
@@ -193,7 +193,7 @@ typedef enum eSculptMeshFilterTypes {
MESH_FILTER_SHARPEN = 8,
MESH_FILTER_ENHANCE_DETAILS = 9,
MESH_FILTER_ERASE_DISPLACEMENT = 10,
-} eSculptMeshFilterTypes;
+} eSculptMeshFilterType;
static EnumPropertyItem prop_mesh_filter_types[] = {
{MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"},
@@ -258,7 +258,7 @@ static EnumPropertyItem prop_mesh_filter_orientation_items[] = {
{0, NULL, 0, NULL, NULL},
};
-static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets)
+static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets)
{
return use_face_sets || ELEM(filter_type,
MESH_FILTER_SMOOTH,
@@ -277,7 +277,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
- const int filter_type = data->filter_type;
+ const eSculptMeshFilterType filter_type = data->filter_type;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
@@ -486,49 +486,80 @@ static void mesh_filter_task_cb(void *__restrict userdata,
static void mesh_filter_enhance_details_init_directions(SculptSession *ss)
{
const int totvert = SCULPT_vertex_count_get(ss);
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->detail_directions = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "detail directions");
for (int i = 0; i < totvert; i++) {
float avg[3];
SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
}
}
+static void mesh_filter_surface_smooth_init(SculptSession *ss,
+ const float shape_preservation,
+ const float current_vertex_displacement)
+{
+ const int totvert = SCULPT_vertex_count_get(ss);
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->surface_smooth_laplacian_disp = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "surface smooth displacement");
+ filter_cache->surface_smooth_shape_preservation = shape_preservation;
+ filter_cache->surface_smooth_current_vertex = current_vertex_displacement;
+}
+
static void mesh_filter_init_limit_surface_co(SculptSession *ss)
{
const int totvert = SCULPT_vertex_count_get(ss);
- ss->filter_cache->limit_surface_co = MEM_malloc_arrayN(
- 3 * sizeof(float), totvert, "limit surface co");
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->limit_surface_co = MEM_malloc_arrayN(
+ sizeof(float[3]), totvert, "limit surface co");
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, ss->filter_cache->limit_surface_co[i]);
+ SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]);
}
}
-static void mesh_filter_sharpen_init_factors(SculptSession *ss)
+static void mesh_filter_sharpen_init(SculptSession *ss,
+ const float smooth_ratio,
+ const float intensify_detail_strength,
+ const int curvature_smooth_iterations)
{
const int totvert = SCULPT_vertex_count_get(ss);
+ FilterCache *filter_cache = ss->filter_cache;
+
+ filter_cache->sharpen_smooth_ratio = smooth_ratio;
+ filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength;
+ filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations;
+ filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor");
+ filter_cache->detail_directions = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "sharpen detail direction");
+
for (int i = 0; i < totvert; i++) {
float avg[3];
SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
- ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->detail_directions[i]);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]);
}
float max_factor = 0.0f;
for (int i = 0; i < totvert; i++) {
- if (ss->filter_cache->sharpen_factor[i] > max_factor) {
- max_factor = ss->filter_cache->sharpen_factor[i];
+ if (filter_cache->sharpen_factor[i] > max_factor) {
+ max_factor = filter_cache->sharpen_factor[i];
}
}
max_factor = 1.0f / max_factor;
for (int i = 0; i < totvert; i++) {
- ss->filter_cache->sharpen_factor[i] *= max_factor;
- ss->filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - ss->filter_cache->sharpen_factor[i]);
+ filter_cache->sharpen_factor[i] *= max_factor;
+ filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - filter_cache->sharpen_factor[i]);
}
/* Smooth the calculated factors and directions to remove high frecuency detail. */
for (int smooth_iterations = 0;
- smooth_iterations < ss->filter_cache->sharpen_curvature_smooth_iterations;
+ smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations;
smooth_iterations++) {
for (int i = 0; i < totvert; i++) {
float direction_avg[3] = {0.0f, 0.0f, 0.0f};
@@ -537,15 +568,15 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss)
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
- add_v3_v3(direction_avg, ss->filter_cache->detail_directions[ni.index]);
- sharpen_avg += ss->filter_cache->sharpen_factor[ni.index];
+ add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]);
+ sharpen_avg += filter_cache->sharpen_factor[ni.index];
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
- mul_v3_v3fl(ss->filter_cache->detail_directions[i], direction_avg, 1.0f / total);
- ss->filter_cache->sharpen_factor[i] = sharpen_avg / total;
+ mul_v3_v3fl(filter_cache->detail_directions[i], direction_avg, 1.0f / total);
+ filter_cache->sharpen_factor[i] = sharpen_avg / total;
}
}
}
@@ -590,7 +621,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- int filter_type = RNA_enum_get(op->ptr, "type");
+ eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type");
float filter_strength = RNA_float_get(op->ptr, "strength");
const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
@@ -654,17 +685,21 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- int filter_type = RNA_enum_get(op->ptr, "type");
SculptSession *ss = ob->sculpt;
- PBVH *pbvh = ob->sculpt->pbvh;
- int deform_axis = RNA_enum_get(op->ptr, "deform_axis");
+ const eMeshFilterDeformAxis deform_axis = RNA_enum_get(op->ptr, "deform_axis");
+ const eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type");
+ const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
+ const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
+
if (deform_axis == 0) {
+ /* All axis are disabled, so the filter is not going to produce any deformation. */
return OPERATOR_CANCELLED;
}
- if (RNA_boolean_get(op->ptr, "use_face_sets")) {
- /* Update the active vertex */
+ if (use_face_sets) {
+ /* Update the active face set manually as the paint cursor is not enabled when using the Mesh
+ * Filter Tool. */
float mouse[2];
SculptCursorGeometryInfo sgi;
mouse[0] = event->mval[0];
@@ -672,67 +707,48 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
}
- const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
-
SCULPT_vertex_random_access_ensure(ss);
-
- const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
if (needs_topology_info) {
SCULPT_boundary_info_ensure(ob);
}
- const int totvert = SCULPT_vertex_count_get(ss);
- if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) {
- return OPERATOR_CANCELLED;
- }
-
- SCULPT_undo_push_begin("Mesh filter");
-
- if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) {
- SCULPT_boundary_info_ensure(ob);
- }
+ SCULPT_undo_push_begin("Mesh Filter");
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
- if (use_face_sets) {
- ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss);
- }
- else {
- ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
- }
-
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SURFACE_SMOOTH) {
- ss->filter_cache->surface_smooth_laplacian_disp = MEM_mallocN(sizeof(float[3]) * totvert,
- "surface smooth disp");
- ss->filter_cache->surface_smooth_shape_preservation = RNA_float_get(
- op->ptr, "surface_smooth_shape_preservation");
- ss->filter_cache->surface_smooth_current_vertex = RNA_float_get(
- op->ptr, "surface_smooth_current_vertex");
- }
-
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SHARPEN) {
- ss->filter_cache->sharpen_smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio");
- ss->filter_cache->sharpen_intensify_detail_strength = RNA_float_get(
- op->ptr, "sharpen_intensify_detail_strength");
- ss->filter_cache->sharpen_curvature_smooth_iterations = RNA_int_get(
- op->ptr, "sharpen_curvature_smooth_iterations");
-
- ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor");
- ss->filter_cache->detail_directions = MEM_malloc_arrayN(
- totvert, sizeof(float[3]), "sharpen detail direction");
-
- mesh_filter_sharpen_init_factors(ss);
- }
-
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ENHANCE_DETAILS) {
- ss->filter_cache->detail_directions = MEM_malloc_arrayN(
- totvert, sizeof(float[3]), "detail direction");
- mesh_filter_enhance_details_init_directions(ss);
- }
+ FilterCache *filter_cache = ss->filter_cache;
+ filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) :
+ SCULPT_FACE_SET_NONE;
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ERASE_DISPLACEMENT) {
- mesh_filter_init_limit_surface_co(ss);
+ switch (filter_type) {
+ case MESH_FILTER_SURFACE_SMOOTH: {
+ const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation");
+ const float current_vertex_displacement = RNA_float_get(op->ptr,
+ "surface_smooth_current_vertex");
+ mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement);
+ break;
+ }
+ case MESH_FILTER_SHARPEN: {
+ const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio");
+ const float intensify_detail_strength = RNA_float_get(op->ptr,
+ "sharpen_intensify_detail_strength");
+ const int curvature_smooth_iterations = RNA_int_get(op->ptr,
+ "sharpen_curvature_smooth_iterations");
+ mesh_filter_sharpen_init(
+ ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations);
+ break;
+ }
+ case MESH_FILTER_ENHANCE_DETAILS: {
+ mesh_filter_enhance_details_init_directions(ss);
+ break;
+ }
+ case MESH_FILTER_ERASE_DISPLACEMENT: {
+ mesh_filter_init_limit_surface_co(ss);
+ break;
+ }
+ default:
+ break;
}
ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 548ef00ad87..47a375a2318 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -398,8 +398,9 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush)
{
- return (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
- brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) ||
+ return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type,
+ BRUSH_CLOTH_DEFORM_GRAB,
+ BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) ||
/* All brushes that are not the cloth brush deform the simulation using softbody
constriants instead of applying forces. */
(brush->sculpt_tool != SCULPT_TOOL_CLOTH &&
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 63fe8643628..87ee7480c92 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -66,25 +66,40 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3],
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
-
- if (SCULPT_vertex_is_boundary(ss, index)) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
- return;
- }
+ int neighbor_count = 0;
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, index);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ neighbor_count++;
+ if (is_boundary) {
+ /* Boundary vertices use only other boundary vertices. */
+ if (SCULPT_vertex_is_boundary(ss, ni.index)) {
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ total++;
+ }
+ }
+ else {
+ /* Interior vertices use all neighbors. */
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ total++;
+ }
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (total > 0) {
- mul_v3_v3fl(result, avg, 1.0f / total);
+ /* Do not modify corner vertices. */
+ if (neighbor_count <= 2) {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ return;
}
- else {
+
+ /* Avoid division by 0 when there are no neighbors. */
+ if (total == 0) {
copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ return;
}
+
+ mul_v3_v3fl(result, avg, 1.0f / total);
}
/* For bmesh: Average surrounding verts based on an orthogonality measure.
@@ -316,7 +331,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
}
else {
float avg[3], val[3];
- SCULPT_neighbor_coords_average(ss, avg, vd.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
sub_v3_v3v3(val, avg, vd.co);
madd_v3_v3v3fl(val, vd.co, val, fade);
SCULPT_clip(sd, ss, vd.co, val);
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index bdada4d2565..b52b04eba3a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -326,6 +326,12 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(nodes);
}
+ /* Update the viewport navigation rotation origin. */
+ UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
+ copy_v3_v3(ups->average_stroke_accum, ss->pivot_pos);
+ ups->average_stroke_counter = 1;
+ ups->last_stroke_valid = true;
+
ED_region_tag_redraw(region);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);