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:
authorPablo Dobarro <pablodp606@gmail.com>2019-09-09 16:42:51 +0300
committerYimingWu <xp8110@outlook.com>2019-09-12 04:13:41 +0300
commit20d86c8c6c92676d075aac60d66d7a2121bcac38 (patch)
tree7f01b1fc35923199a75ffc5e0f934438cf874898
parentd183e2ffc9a61b15772f4fa8c25825a1993d2e80 (diff)
Sculpt: Mesh Filter Tool
The mesh filter tool applies a deformation to all vertices in the mesh at the same time. It includes multiple deformation modes and the option to lock the deformation axis. This commit also includes the FilterCache, which is needed in some new operators and tools. Reviewed By: brecht Differential Revision: https://developer.blender.org/D5513
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py10
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py18
-rw-r--r--source/blender/blenkernel/BKE_paint.h1
-rw-r--r--source/blender/blenkernel/intern/paint.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c356
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h18
6 files changed, 401 insertions, 4 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 30ef5c5e561..6d4c3616ca4 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -5718,6 +5718,15 @@ def km_3d_view_tool_sculpt_lasso_mask(params):
]},
)
+def km_3d_view_tool_sculpt_mesh_filter(params):
+ return (
+ "3D View Tool: Sculpt, Mesh Filter",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("sculpt.mesh_filter", {"type": params.tool_tweak, "value": 'ANY'},
+ None)
+ ]},
+ )
def km_3d_view_tool_paint_weight_sample_weight(params):
return (
@@ -6159,6 +6168,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_sculpt_box_hide(params),
km_3d_view_tool_sculpt_box_mask(params),
km_3d_view_tool_sculpt_lasso_mask(params),
+ km_3d_view_tool_sculpt_mesh_filter(params),
km_3d_view_tool_paint_weight_sample_weight(params),
km_3d_view_tool_paint_weight_sample_vertex_group(params),
km_3d_view_tool_paint_weight_gradient(params),
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 1403c1fb532..f868c945512 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -985,6 +985,22 @@ class _defs_sculpt:
keymap=(),
)
+ @ToolDef.from_fn
+ def mesh_filter():
+ def draw_settings(context, layout, tool):
+ props = tool.operator_properties("sculpt.mesh_filter")
+ layout.prop(props, "type", expand=False)
+ layout.prop(props, "strength")
+ layout.prop(props, "deform_axis")
+
+ return dict(
+ idname="builtin.mesh_filter",
+ label="Mesh Filter",
+ icon="ops.sculpt.mesh_filter",
+ widget=None,
+ keymap= (),
+ draw_settings=draw_settings,
+ )
class _defs_vertex_paint:
@@ -1967,6 +1983,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
),
_defs_sculpt.hide_border,
None,
+ _defs_sculpt.mesh_filter,
+ None,
*_tools_annotate,
],
'PAINT_TEXTURE': [
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index ed02a34196f..ef798479b23 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -256,6 +256,7 @@ typedef struct SculptSession {
float (*layer_co)[3]; /* Copy of the mesh vertices' locations */
struct StrokeCache *cache;
+ struct FilterCache *filter_cache;
/* Cursor data and active vertex for tools */
int active_vertex_index;
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 86b3e347513..b386a0d4483 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1268,7 +1268,7 @@ void BKE_sculpt_update_object_before_eval(Object *ob)
SculptSession *ss = ob->sculpt;
if (ss && ss->building_vp_handle == false) {
- if (!ss->cache) {
+ if (!ss->cache && !ss->filter_cache) {
/* We free pbvh on changes, except in the middle of drawing a stroke
* since it can't deal with changing PVBH node organization, we hope
* topology does not change in the meantime .. weak. */
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index dce8ea105e4..9ffeeb5a232 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -27,6 +27,7 @@
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_dial_2d.h"
+#include "BLI_hash.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@@ -108,6 +109,13 @@ static float *sculpt_vertex_co_get(SculptSession *ss, int index)
}
}
+static void sculpt_vertex_random_access_init(SculptSession *ss)
+{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ }
+}
+
#if 0 /* UNUSED */
static int sculpt_active_vertex_get(SculptSession *ss)
@@ -134,6 +142,7 @@ static int sculpt_vertex_count_get(SculptSession *ss)
}
}
+
static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -4766,12 +4775,12 @@ static void sculpt_flush_stroke_deform_task_cb(void *__restrict userdata,
}
/* flush displacement from deformed PBVH to original layer */
-static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob)
+static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) {
+ if (is_proxy_used) {
/* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't
* propagate needed deformation to original base */
@@ -6109,7 +6118,7 @@ static void sculpt_stroke_update_step(bContext *C,
* sculpt_flush_update_step().
*/
if (ss->modifiers_active) {
- sculpt_flush_stroke_deform(sd, ob);
+ sculpt_flush_stroke_deform(sd, ob, sculpt_tool_is_proxy_used(brush->sculpt_tool));
}
else if (ss->kb) {
sculpt_update_keyblock(ob);
@@ -7256,6 +7265,346 @@ static void SCULPT_OT_set_detail_size(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+static void filter_cache_init_task_cb(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ PBVHNode *node = data->nodes[i];
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
+ {
+ if (!vd.mask || (vd.mask && *vd.mask < 1.0f)) {
+ data->node_mask[i] = 1;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ if (data->node_mask[i] == 1) {
+ sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
+ }
+}
+
+static void sculpt_filter_cache_init(Object *ob, Sculpt *sd)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVH *pbvh = ob->sculpt->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+
+ ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache");
+
+ ss->filter_cache->random_seed = rand();
+
+ SculptSearchSphereData search_data = {
+ .original = true,
+ };
+ BKE_pbvh_search_gather(pbvh, NULL, &search_data, &nodes, &totnode);
+
+ int *node_mask = MEM_callocN((unsigned int)totnode * sizeof(int), "node mask");
+
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_normals_update(nodes[i]);
+ }
+ BKE_pbvh_update_normals(ss->pbvh, NULL);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .node_mask = node_mask,
+ };
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT);
+ BLI_task_parallel_range(0, totnode, &data, filter_cache_init_task_cb, &settings);
+
+ int tot_active_nodes = 0;
+ int active_node_index = 0;
+ PBVHNode **active_nodes;
+
+ /* Count number of PBVH nodes that are not fully masked */
+ for (int i = 0; i < totnode; i++) {
+ if (node_mask[i] == 1) {
+ tot_active_nodes++;
+ }
+ }
+
+ /* Create the final list of nodes that is going to be processed in the filter */
+ active_nodes = MEM_callocN(tot_active_nodes * sizeof(PBVHNode *), "active nodes");
+
+ for (int i = 0; i < totnode; i++) {
+ if (node_mask[i] == 1) {
+ active_nodes[active_node_index] = nodes[i];
+ active_node_index++;
+ }
+ }
+
+ ss->filter_cache->nodes = active_nodes;
+ ss->filter_cache->totnode = tot_active_nodes;
+
+ if (nodes) {
+ MEM_freeN(nodes);
+ }
+ if (node_mask) {
+ MEM_freeN(node_mask);
+ }
+}
+
+static void sculpt_filter_cache_free(SculptSession *ss)
+{
+ if (ss->filter_cache->nodes) {
+ MEM_freeN(ss->filter_cache->nodes);
+ }
+ if (ss->filter_cache->mask_update_it) {
+ MEM_freeN(ss->filter_cache->mask_update_it);
+ }
+ MEM_freeN(ss->filter_cache);
+ ss->filter_cache = NULL;
+}
+
+typedef enum eSculptMeshFilterTypes {
+ MESH_FILTER_SMOOTH = 0,
+ MESH_FILTER_SCALE = 1,
+ MESH_FILTER_INFLATE = 2,
+ MESH_FILTER_SPHERE = 3,
+ MESH_FILTER_RANDOM = 4,
+} eSculptMeshFilterTypes;
+
+static EnumPropertyItem prop_mesh_filter_types[] = {
+ {MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"},
+ {MESH_FILTER_SCALE, "SCALE", 0, "Scale", "Scale mesh"},
+ {MESH_FILTER_INFLATE, "INFLATE", 0, "Inflate", "Inflate mesh"},
+ {MESH_FILTER_SPHERE, "SPHERE", 0, "Sphere", "Morph into sphere"},
+ {MESH_FILTER_RANDOM, "RANDOM", 0, "Random", "Randomize vertex positions"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+typedef enum eMeshFilterDeformAxis {
+ MESH_FILTER_DEFORM_X = 1 << 0,
+ MESH_FILTER_DEFORM_Y = 1 << 1,
+ MESH_FILTER_DEFORM_Z = 1 << 2,
+} eMeshFilterDeformAxis;
+
+static EnumPropertyItem prop_mesh_filter_deform_axis_items[] = {
+ {MESH_FILTER_DEFORM_X, "X", 0, "X", "Deform in the X axis"},
+ {MESH_FILTER_DEFORM_Y, "Y", 0, "Y", "Deform in the Y axis"},
+ {MESH_FILTER_DEFORM_Z, "Z", 0, "Z", "Deform in the Z axis"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void mesh_filter_task_cb(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ PBVHNode *node = data->nodes[i];
+
+ const int filter_type = data->filter_type;
+
+ SculptOrigVertData orig_data;
+ sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
+ {
+ sculpt_orig_vert_data_update(&orig_data, &vd);
+ float orig_co[3], val[3], avg[3], normal[3], disp[3], disp2[3], transform[3][3], final_pos[3];
+ float fade = vd.mask ? *vd.mask : 0.0f;
+ fade = 1 - fade;
+ fade *= data->filter_strength;
+ copy_v3_v3(orig_co, orig_data.co);
+ switch (filter_type) {
+ case MESH_FILTER_SMOOTH:
+ CLAMP(fade, -1.0f, 1.0f);
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
+ neighbor_average(ss, avg, vd.index);
+ }
+ else if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ bmesh_neighbor_average(avg, vd.bm_vert);
+ }
+ sub_v3_v3v3(val, avg, orig_co);
+ madd_v3_v3v3fl(val, orig_co, val, fade);
+ sub_v3_v3v3(disp, val, orig_co);
+ break;
+ case MESH_FILTER_INFLATE:
+ normal_short_to_float_v3(normal, orig_data.no);
+ mul_v3_v3fl(disp, normal, fade);
+ break;
+ case MESH_FILTER_SCALE:
+ unit_m3(transform);
+ scale_m3_fl(transform, 1 + fade);
+ copy_v3_v3(val, orig_co);
+ mul_m3_v3(transform, val);
+ sub_v3_v3v3(disp, val, orig_co);
+ break;
+ case MESH_FILTER_SPHERE:
+ normalize_v3_v3(disp, orig_co);
+ if (fade > 0) {
+ mul_v3_v3fl(disp, disp, fade);
+ }
+ else {
+ mul_v3_v3fl(disp, disp, -fade);
+ }
+
+ unit_m3(transform);
+ if (fade > 0) {
+ scale_m3_fl(transform, 1 - fade);
+ }
+ else {
+ scale_m3_fl(transform, 1 + fade);
+ }
+ copy_v3_v3(val, orig_co);
+ mul_m3_v3(transform, val);
+ sub_v3_v3v3(disp2, val, orig_co);
+
+ mid_v3_v3v3(disp, disp, disp2);
+ break;
+ case MESH_FILTER_RANDOM:
+ normal_short_to_float_v3(normal, orig_data.no);
+ mul_v3_fl(normal, BLI_hash_int_01(vd.index ^ ss->filter_cache->random_seed) - 0.5f);
+ mul_v3_v3fl(disp, normal, fade);
+ break;
+ }
+
+ for (int it = 0; it < 3; it++) {
+ if (!ss->filter_cache->enabled_axis[it]) {
+ disp[it] = 0.0f;
+ }
+ }
+
+ add_v3_v3v3(final_pos, orig_co, disp);
+ copy_v3_v3(vd.co, final_pos);
+ if (vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ BKE_pbvh_node_mark_redraw(node);
+ BKE_pbvh_node_mark_normals_update(node);
+}
+
+static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ 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");
+ float filter_strength = RNA_float_get(op->ptr, "strength");
+
+ if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
+ sculpt_filter_cache_free(ss);
+ sculpt_undo_push_end();
+ sculpt_flush_update_done(C, ob);
+ return OPERATOR_FINISHED;
+ }
+
+ if (event->type != MOUSEMOVE) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ float len = event->prevclickx - event->mval[0];
+ filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC;
+
+ sculpt_vertex_random_access_init(ss);
+
+ bool needs_pmap = (filter_type == MESH_FILTER_SMOOTH);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = ss->filter_cache->nodes,
+ .filter_type = filter_type,
+ .filter_strength = filter_strength,
+ };
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) &&
+ ss->filter_cache->totnode > SCULPT_THREADED_LIMIT);
+ BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings);
+
+ if (ss->modifiers_active || ss->kb) {
+ sculpt_flush_stroke_deform(sd, ob, true);
+ }
+
+ sculpt_flush_update_step(C);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ 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");
+ if (deform_axis == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ sculpt_vertex_random_access_init(ss);
+
+ bool needs_pmap = (filter_type == MESH_FILTER_SMOOTH);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+
+ if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
+ return OPERATOR_CANCELLED;
+ }
+
+ sculpt_undo_push_begin("Mesh filter");
+
+ sculpt_filter_cache_init(ob, sd);
+
+ ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X;
+ ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y;
+ ss->filter_cache->enabled_axis[2] = deform_axis & MESH_FILTER_DEFORM_Z;
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Filter mesh";
+ ot->idname = "SCULPT_OT_mesh_filter";
+ ot->description = "Applies a filter to modify the current mesh";
+
+ /* api callbacks */
+ ot->invoke = sculpt_mesh_filter_invoke;
+ ot->modal = sculpt_mesh_filter_modal;
+ ot->poll = sculpt_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* rna */
+ RNA_def_enum(ot->srna,
+ "type",
+ prop_mesh_filter_types,
+ MESH_FILTER_INFLATE,
+ "Filter type",
+ "Operation that is going to be applied to the mesh");
+ RNA_def_float(
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+ RNA_def_enum_flag(ot->srna,
+ "deform_axis",
+ prop_mesh_filter_deform_axis_items,
+ MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z,
+ "Deform axis",
+ "Apply the deformation in the selected axis");
+}
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -7267,4 +7616,5 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_detail_flood_fill);
WM_operatortype_append(SCULPT_OT_sample_detail_size);
WM_operatortype_append(SCULPT_OT_set_detail_size);
+ WM_operatortype_append(SCULPT_OT_mesh_filter);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 9f1cb7a53a4..a248476fd49 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -175,6 +175,10 @@ typedef struct SculptThreadedTaskData {
int *count;
bool any_vertex_sampled;
+ int filter_type;
+ float filter_strength;
+ int *node_mask;
+
ThreadMutex mutex;
} SculptThreadedTaskData;
@@ -341,6 +345,20 @@ typedef struct StrokeCache {
} StrokeCache;
+typedef struct FilterCache {
+ bool enabled_axis[3];
+ int random_seed;
+
+ /* unmasked nodes */
+ PBVHNode **nodes;
+ int totnode;
+
+ /* mask expand iteration caches */
+ int mask_update_current_it;
+ int mask_update_last_it;
+ int *mask_update_it;
+} FilterCache;
+
void sculpt_cache_calc_brushdata_symm(StrokeCache *cache,
const char symm,
const char axis,