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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py11
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py20
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c324
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c3
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h10
7 files changed, 342 insertions, 29 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 0db7b79d8c9..08e81d89c4f 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -6272,6 +6272,16 @@ def km_3d_view_tool_sculpt_mesh_filter(params):
]},
)
+def km_3d_view_tool_sculpt_cloth_filter(params):
+ return (
+ "3D View Tool: Sculpt, Cloth Filter",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("sculpt.cloth_filter", {"type": params.tool_tweak, "value": 'ANY'},
+ None)
+ ]},
+ )
+
def km_3d_view_tool_paint_weight_sample_weight(params):
return (
"3D View Tool: Paint Weight, Sample Weight",
@@ -6811,6 +6821,7 @@ def generate_keymaps(params=None):
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_sculpt_cloth_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 1aa5dde168b..b4c1ea5163f 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1228,6 +1228,25 @@ class _defs_sculpt:
draw_settings=draw_settings,
)
+ @ToolDef.from_fn
+ def cloth_filter():
+ def draw_settings(_context, layout, tool):
+ props = tool.operator_properties("sculpt.cloth_filter")
+ layout.prop(props, "type", expand=False)
+ layout.prop(props, "strength")
+ layout.prop(props, "cloth_mass")
+ layout.prop(props, "cloth_damping")
+ layout.prop(props, "use_face_sets")
+
+ return dict(
+ idname="builtin.cloth_filter",
+ label="Cloth Filter",
+ icon="ops.sculpt.cloth_filter",
+ widget=None,
+ keymap=(),
+ draw_settings=draw_settings,
+ )
+
class _defs_vertex_paint:
@@ -2402,6 +2421,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_sculpt.hide_border,
None,
_defs_sculpt.mesh_filter,
+ _defs_sculpt.cloth_filter,
None,
_defs_transform.translate,
_defs_transform.rotate,
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 4ad6bcc5d0f..6e56135ec83 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -7898,4 +7898,5 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_change_visibility);
WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors);
WM_operatortype_append(SCULPT_OT_face_sets_init);
+ WM_operatortype_append(SCULPT_OT_cloth_filter);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c
index f0f6478d3a6..bfa657147fd 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -91,7 +91,7 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
float SCULPT_automasking_factor_get(SculptSession *ss, int vert)
{
- if (ss->cache->automask) {
+ if (ss->cache && ss->cache->automask) {
return ss->cache->automask[vert];
}
else {
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 6dac2b51645..3203282c30c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -106,9 +106,11 @@
#define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024
#define CLOTH_SIMULATION_TIME_STEP 0.01f
-static void cloth_brush_add_length_constraint(SculptSession *ss, const int v1, const int v2)
+static void cloth_brush_add_length_constraint(SculptSession *ss,
+ SculptClothSimulation *cloth_sim,
+ const int v1,
+ const int v2)
{
- SculptClothSimulation *cloth_sim = ss->cache->cloth_sim;
cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v1 = v1;
cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v2 = v2;
cloth_sim->length_constraints[cloth_sim->tot_length_constraints].length = len_v3v3(
@@ -133,12 +135,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd;
- const float radius = ss->cache->initial_radius;
- const float limit = radius + (radius * data->brush->cloth_sim_limit);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (len_squared_v3v3(vd.co, ss->cache->initial_location) < limit * limit) {
+ if (len_squared_v3v3(vd.co, data->cloth_sim_initial_location) <
+ data->cloth_sim_radius * data->cloth_sim_radius) {
SculptVertexNeighborIter ni;
int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX];
@@ -159,7 +160,8 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
for (int c_i = 0; c_i < tot_indices; c_i++) {
for (int c_j = 0; c_j < tot_indices; c_j++) {
if (c_i != c_j) {
- cloth_brush_add_length_constraint(ss, build_indices[c_i], build_indices[c_j]);
+ cloth_brush_add_length_constraint(
+ ss, data->cloth_sim, build_indices[c_i], build_indices[c_j]);
}
}
}
@@ -192,11 +194,11 @@ static float cloth_brush_simulation_falloff_get(const Brush *brush,
}
}
-static void cloth_brush_apply_force_to_vertex(SculptSession *ss,
+static void cloth_brush_apply_force_to_vertex(SculptSession *UNUSED(ss),
+ SculptClothSimulation *cloth_sim,
const float force[3],
const int vertex_index)
{
- SculptClothSimulation *cloth_sim = ss->cache->cloth_sim;
madd_v3_v3fl(cloth_sim->acceleration[vertex_index], force, 1.0f / cloth_sim->mass);
}
@@ -290,7 +292,6 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
float brush_disp[3];
float normal[3];
-
if (vd.no) {
normal_short_to_float_v3(normal, vd.no);
}
@@ -348,13 +349,15 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
madd_v3_v3fl(force, gravity, fade);
- cloth_brush_apply_force_to_vertex(ss, force, vd.index);
+ cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, force, vd.index);
}
}
BKE_pbvh_vertex_iter_end;
}
-static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, Brush *brush)
+static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss,
+ const float cloth_mass,
+ const float cloth_damping)
{
const int totverts = SCULPT_vertex_count_get(ss);
SculptClothSimulation *cloth_sim;
@@ -373,8 +376,8 @@ static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, B
cloth_sim->length_constraint_tweak = MEM_callocN(sizeof(float) * totverts,
"cloth sim length tweak");
- cloth_sim->mass = brush->cloth_mass;
- cloth_sim->damping = brush->cloth_damping;
+ cloth_sim->mass = cloth_mass;
+ cloth_sim->damping = cloth_damping;
return cloth_sim;
}
@@ -386,12 +389,16 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
PBVHVertexIter vd;
- SculptClothSimulation *cloth_sim = ss->cache->cloth_sim;
+ SculptClothSimulation *cloth_sim = data->cloth_sim;
const float time_step = data->cloth_time_step;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- const float sim_factor = cloth_brush_simulation_falloff_get(
- brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]);
+ const float sim_factor = ss->cache ? cloth_brush_simulation_falloff_get(
+ brush,
+ ss->cache->radius,
+ ss->cache->initial_location,
+ cloth_sim->init_pos[vd.index]) :
+ 1.0f;
if (sim_factor > 0.0f) {
int i = vd.index;
float temp[3];
@@ -412,7 +419,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
copy_v3_fl(cloth_sim->acceleration[i], 0.0f);
- copy_v3_v3(vd.co, ss->cache->cloth_sim->pos[vd.index]);
+ copy_v3_v3(vd.co, cloth_sim->pos[vd.index]);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
@@ -424,7 +431,10 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
static void cloth_brush_build_nodes_constraints(Sculpt *sd,
Object *ob,
PBVHNode **nodes,
- int totnode)
+ int totnode,
+ SculptClothSimulation *cloth_sim,
+ float initial_location[3],
+ const float radius)
{
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -440,6 +450,9 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
+ .cloth_sim = cloth_sim,
+ .cloth_sim_initial_location = initial_location,
+ .cloth_sim_radius = radius,
};
BLI_task_parallel_range(
0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings);
@@ -480,10 +493,18 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
SCULPT_automasking_factor_get(ss, v2);
- const float sim_factor_v1 = cloth_brush_simulation_falloff_get(
- brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[v1]);
- const float sim_factor_v2 = cloth_brush_simulation_falloff_get(
- brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[v2]);
+ const float sim_factor_v1 = ss->cache ? cloth_brush_simulation_falloff_get(
+ brush,
+ ss->cache->radius,
+ ss->cache->initial_location,
+ cloth_sim->init_pos[v1]) :
+ 1.0f;
+ const float sim_factor_v2 = ss->cache ? cloth_brush_simulation_falloff_get(
+ brush,
+ ss->cache->radius,
+ ss->cache->initial_location,
+ cloth_sim->init_pos[v2]) :
+ 1.0f;
madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, 1.0f * mask_v1 * sim_factor_v1);
madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, -1.0f * mask_v2 * sim_factor_v2);
@@ -491,13 +512,12 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
}
}
-static void cloth_brush_do_simulation_step(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+static void cloth_brush_do_simulation_step(
+ Sculpt *sd, Object *ob, SculptClothSimulation *cloth_sim, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- SculptClothSimulation *cloth_sim = ss->cache->cloth_sim;
-
/* Update the constraints. */
cloth_brush_satisfy_constraints(ss, brush, cloth_sim);
@@ -508,6 +528,7 @@ static void cloth_brush_do_simulation_step(Sculpt *sd, Object *ob, PBVHNode **no
.brush = brush,
.nodes = nodes,
.cloth_time_step = CLOTH_SIMULATION_TIME_STEP,
+ .cloth_sim = cloth_sim,
};
TaskParallelSettings settings;
@@ -608,7 +629,8 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* The simulation structure only needs to be created on the first symmetry pass. */
if (ss->cache->mirror_symmetry_pass == 0) {
- ss->cache->cloth_sim = cloth_brush_simulation_create(ss, brush);
+ ss->cache->cloth_sim = cloth_brush_simulation_create(
+ ss, brush->cloth_mass, brush->cloth_damping);
for (int i = 0; i < totverts; i++) {
copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
copy_v3_v3(ss->cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
@@ -616,7 +638,10 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
}
/* Build the constraints. */
- cloth_brush_build_nodes_constraints(sd, ob, nodes, totnode);
+ const float radius = ss->cache->initial_radius;
+ const float limit = radius + (radius * brush->cloth_sim_limit);
+ cloth_brush_build_nodes_constraints(
+ sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit);
return;
}
@@ -630,7 +655,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
cloth_brush_apply_brush_foces(sd, ob, nodes, totnode);
/* Update and write the simulation to the nodes. */
- cloth_brush_do_simulation_step(sd, ob, nodes, totnode);
+ cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode);
return;
}
@@ -706,3 +731,246 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
immEnd();
}
+
+/* Cloth Filter. */
+
+typedef enum eSculpClothFilterType {
+ CLOTH_FILTER_GRAVITY,
+ CLOTH_FILTER_INFLATE,
+ CLOTH_FILTER_EXPAND,
+ CLOTH_FILTER_PINCH,
+} eSculptClothFilterType;
+
+static EnumPropertyItem prop_cloth_filter_type[] = {
+ {CLOTH_FILTER_GRAVITY, "GRAVITY", 0, "Gravity", "Applies gravity to the simulation"},
+ {CLOTH_FILTER_INFLATE, "INFLATE", 0, "Inflate", "Inflates the cloth"},
+ {CLOTH_FILTER_EXPAND, "EXPAND", 0, "Expand", "Expands the cloth's dimensions"},
+ {CLOTH_FILTER_PINCH,
+ "PINCH",
+ 0,
+ "Pinch",
+ "Pinches the cloth to the point were the cursor was when the filter started"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ Sculpt *sd = data->sd;
+ SculptSession *ss = data->ob->sculpt;
+ PBVHNode *node = data->nodes[i];
+
+ SculptClothSimulation *cloth_sim = ss->filter_cache->cloth_sim;
+ const int filter_type = data->filter_type;
+
+ float sculpt_gravity[3] = {0.0f};
+ if (sd->gravity_object) {
+ copy_v3_v3(sculpt_gravity, sd->gravity_object->obmat[2]);
+ }
+ else {
+ sculpt_gravity[2] = -1.0f;
+ }
+ mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
+ {
+ float fade = vd.mask ? *vd.mask : 0.0f;
+ fade = 1.0f - fade;
+ float force[3] = {0.0f, 0.0f, 0.0f};
+
+ if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) {
+ if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) {
+ continue;
+ }
+ }
+
+ switch (filter_type) {
+ case CLOTH_FILTER_GRAVITY:
+ force[2] = -data->filter_strength * fade;
+ break;
+ case CLOTH_FILTER_INFLATE: {
+ float normal[3];
+ SCULPT_vertex_normal_get(ss, vd.index, normal);
+ mul_v3_v3fl(force, normal, fade * data->filter_strength);
+ } break;
+ case CLOTH_FILTER_EXPAND:
+ cloth_sim->length_constraint_tweak[vd.index] += fade * data->filter_strength * 0.01f;
+ zero_v3(force);
+ break;
+ case CLOTH_FILTER_PINCH:
+ sub_v3_v3v3(force, ss->filter_cache->cloth_sim_pinch_point, vd.co);
+ normalize_v3(force);
+ mul_v3_fl(force, fade * data->filter_strength);
+ break;
+ }
+
+ add_v3_v3(force, sculpt_gravity);
+
+ cloth_brush_apply_force_to_vertex(ss, cloth_sim, force, vd.index);
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ BKE_pbvh_node_mark_update(node);
+}
+
+static int sculpt_cloth_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, SCULPT_UPDATE_COORDS);
+ 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);
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+
+ const int totverts = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totverts; i++) {
+ copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ }
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = ss->filter_cache->nodes,
+ .filter_type = filter_type,
+ .filter_strength = filter_strength,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(
+ &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BLI_task_parallel_range(
+ 0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings);
+
+ /* Update and write the simulation to the nodes. */
+ cloth_brush_do_simulation_step(
+ sd, ob, ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode);
+
+ if (ss->deform_modifiers_active || ss->shapekey_active) {
+ SCULPT_flush_stroke_deform(sd, ob, true);
+ }
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+
+ /* Update the active vertex */
+ float mouse[2];
+ SculptCursorGeometryInfo sgi;
+ mouse[0] = event->mval[0];
+ mouse[1] = event->mval[1];
+ SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+
+ SCULPT_vertex_random_access_init(ss);
+
+ /* Needs mask data to be available as it is used when solving the constraints. */
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+
+ SCULPT_undo_push_begin("Cloth filter");
+ SCULPT_filter_cache_init(ob, sd);
+
+ const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
+ const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
+ ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, cloth_mass, cloth_damping);
+ copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss));
+
+ const int totverts = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totverts; i++) {
+ copy_v3_v3(ss->filter_cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(ss->filter_cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
+ }
+
+ float origin[3] = {0.0f, 0.0f, 0.0f};
+ cloth_brush_build_nodes_constraints(sd,
+ ob,
+ ss->filter_cache->nodes,
+ ss->filter_cache->totnode,
+ ss->filter_cache->cloth_sim,
+ origin,
+ FLT_MAX);
+
+ const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
+ 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;
+ }
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void SCULPT_OT_cloth_filter(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Filter cloth";
+ ot->idname = "SCULPT_OT_cloth_filter";
+ ot->description = "Applies a cloth simulation deformation to the entire mesh";
+
+ /* API callbacks. */
+ ot->invoke = sculpt_cloth_filter_invoke;
+ ot->modal = sculpt_cloth_filter_modal;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* RNA. */
+ RNA_def_enum(ot->srna,
+ "type",
+ prop_cloth_filter_type,
+ CLOTH_FILTER_GRAVITY,
+ "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_float(ot->srna,
+ "cloth_mass",
+ 1.0f,
+ 0.0f,
+ 2.0f,
+ "Cloth Mass",
+ "Mass of each simulation particle",
+ 0.0f,
+ 1.0f);
+ RNA_def_float(ot->srna,
+ "cloth_damping",
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "Cloth Damping",
+ "How much the applied forces are propagated through the cloth",
+ 0.0f,
+ 1.0f);
+ ot->prop = RNA_def_boolean(ot->srna,
+ "use_face_sets",
+ false,
+ "Use Face Sets",
+ "Apply the filter only to the Face Set under the cursor");
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index fc3e130cc98..58c9e1a569e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -121,6 +121,9 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
void SCULPT_filter_cache_free(SculptSession *ss)
{
+ if (ss->filter_cache->cloth_sim) {
+ SCULPT_cloth_simulation_free(ss->filter_cache->cloth_sim);
+ }
MEM_SAFE_FREE(ss->filter_cache->nodes);
MEM_SAFE_FREE(ss->filter_cache->mask_update_it);
MEM_SAFE_FREE(ss->filter_cache->prev_mask);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 6c217f66940..d7b221ae947 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -592,6 +592,9 @@ typedef struct SculptThreadedTaskData {
float transform_mats[8][4][4];
float cloth_time_step;
+ SculptClothSimulation *cloth_sim;
+ float *cloth_sim_initial_location;
+ float cloth_sim_radius;
float dirty_mask_min;
float dirty_mask_max;
@@ -837,6 +840,10 @@ typedef struct FilterCache {
PBVHNode **nodes;
int totnode;
+ /* Cloth filter. */
+ SculptClothSimulation *cloth_sim;
+ float cloth_sim_pinch_point[3];
+
/* mask expand iteration caches */
int mask_update_current_it;
int mask_update_last_it;
@@ -891,6 +898,9 @@ void SCULPT_OT_set_pivot_position(struct wmOperatorType *ot);
/* Mesh Filter. */
void SCULPT_OT_mesh_filter(struct wmOperatorType *ot);
+/* Cloth Filter. */
+void SCULPT_OT_cloth_filter(struct wmOperatorType *ot);
+
/* Mask filter and Dirty Mask. */
void SCULPT_OT_mask_filter(struct wmOperatorType *ot);
void SCULPT_OT_dirty_mask(struct wmOperatorType *ot);