diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index b2794e9d9d6..dfc75e2fd54 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -259,6 +259,16 @@ typedef struct SculptPoseIKChain { /* Cloth Brush */ +typedef enum eSculptClothConstraintType { + /* Constraint that creates the structure of the cloth. */ + SCULPT_CLOTH_CONSTRAINT_STRUCTURAL = 0, + /* Constraint that references the position of a vertex and a position in deformation_pos which + can be deformed by the tools. */ + SCULPT_CLOTH_CONSTRAINT_DEFORMATION = 1, + /* Constarint that references the vertex position and its initial position. */ + SCULPT_CLOTH_CONSTRAINT_SOFTBODY = 2, +} eSculptClothConstraintType; + typedef struct SculptClothLengthConstraint { /* Elements that are affected by the constraint. */ /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always @@ -274,6 +284,8 @@ typedef struct SculptClothLengthConstraint { float length; float strength; + + eSculptClothConstraintType type; } SculptClothLengthConstraint; typedef struct SculptClothSimulation { @@ -287,6 +299,7 @@ typedef struct SculptClothSimulation { * final positions of the simulated vertices are updated with constraints that use these points * as targets. */ float (*deformation_pos)[3]; + float *deformation_strength; float mass; float damping; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e1c1b8ee5fb..f8cd7dfe262 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 cf2ed0943eb..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); } 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/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 3873763e3e0..033a69d230e 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -343,6 +343,7 @@ typedef enum eBrushClothDeformType { BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4, BRUSH_CLOTH_DEFORM_INFLATE = 5, BRUSH_CLOTH_DEFORM_EXPAND = 6, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, } eBrushClothDeformType; typedef enum eBrushSmoothDeformType { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f48a7e6715d..0b923eb5635 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2016,6 +2016,7 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_CLOTH_DEFORM_INFLATE, "INFLATE", 0, "Inflate", ""}, {BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""}, + {BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""}, {0, NULL, 0, NULL, NULL}, };