Sculpt: Cloth Snake Hook Brush

This implements Snake Hook as a deform type for the cloth brush. This
brush changes the strength of the deformation constraints per brush step
to avoid affecting the results of the simulation as much as possible. It
allows to grab the cloth without producing any artifacts in the surface
and create more natural looking folds than any of the other deformation
modes.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D8621
This commit is contained in:
Pablo Dobarro 2020-08-24 23:26:37 +02:00
parent 5a634735e6
commit 46eca3366e
6 changed files with 81 additions and 18 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 &&

View File

@ -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 {

View File

@ -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},
};