diff options
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); |