Sculpt: Cloth Brush Soft Body Influence property

This property adds constraints to the simulation using the initial
location of the vertices, making it behave like a soft body. The
strength of these constraints can be modified with the brush parameter.
This makes some deformation modes more subtle and predictable, making it
possible to use the cloth brush to add surface detail in a more
controllable way without loosing completely the original shape of the
mesh.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D7845
This commit is contained in:
Pablo Dobarro 2020-07-29 19:12:35 +02:00
parent ebda95953c
commit 8c10e56331
5 changed files with 88 additions and 20 deletions

View File

@ -662,6 +662,7 @@ def brush_settings(layout, context, brush, popover=False):
layout.separator()
layout.prop(brush, "cloth_mass")
layout.prop(brush, "cloth_damping")
layout.prop(brush, "cloth_constraint_softbody_strength")
layout.separator()
elif sculpt_tool == 'SCRAPE':

View File

@ -260,10 +260,20 @@ typedef struct SculptPoseIKChain {
/* Cloth Brush */
typedef struct SculptClothLengthConstraint {
int v1;
int v2;
/* 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
* deformed. Element b cound be another vertex of the same mesh or any other position (arbitrary
* point, position for a previous state). In that case, elem_index_a and elem_index_b should be
* the same to avoid affecting two different vertices when solving the constraints.
* *elem_position points to the position which is owned by the element. */
int elem_index_a;
float *elem_position_a;
int elem_index_b;
float *elem_position_b;
float length;
float strength;
} SculptClothLengthConstraint;
typedef struct SculptClothSimulation {

View File

@ -113,19 +113,8 @@ static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_s
return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2);
}
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
const int v1,
const int v2)
static void cloth_brush_reallocate_constraints(SculptClothSimulation *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(
SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2));
cloth_sim->tot_length_constraints++;
/* Reallocation if the array capacity is exceeded. */
if (cloth_sim->tot_length_constraints >= cloth_sim->capacity_length_constraints) {
cloth_sim->capacity_length_constraints += CLOTH_LENGTH_CONSTRAINTS_BLOCK;
cloth_sim->length_constraints = MEM_reallocN_id(cloth_sim->length_constraints,
@ -133,16 +122,62 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
sizeof(SculptClothLengthConstraint),
"length constraints");
}
}
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
const int v1,
const int v2)
{
SculptClothLengthConstraint *length_constraint =
&cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
length_constraint->elem_index_a = v1;
length_constraint->elem_index_b = v2;
length_constraint->elem_position_a = cloth_sim->pos[v1];
length_constraint->elem_position_b = cloth_sim->pos[v2];
length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2));
length_constraint->strength = 1.0f;
cloth_sim->tot_length_constraints++;
/* Reallocation if the array capacity is exceeded. */
cloth_brush_reallocate_constraints(cloth_sim);
/* Add the constraint to the GSet to avoid creating it again. */
BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2);
}
static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim,
const int v,
const float strength)
{
SculptClothLengthConstraint *length_constraint =
&cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
length_constraint->elem_position_a = cloth_sim->pos[v];
length_constraint->elem_position_b = cloth_sim->init_pos[v];
length_constraint->length = 0.0f;
length_constraint->strength = strength;
cloth_sim->tot_length_constraints++;
/* Reallocation if the array capacity is exceeded. */
cloth_brush_reallocate_constraints(cloth_sim);
}
static void do_cloth_brush_build_constraints_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
Brush *brush = data->brush;
PBVHVertexIter vd;
@ -162,6 +197,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (brush->cloth_constraint_softbody_strength > 0.0f) {
cloth_brush_add_softbody_constraint(
data->cloth_sim, vd.index, brush->cloth_constraint_softbody_strength);
}
/* As we don't know the order of the neighbor vertices, we create all possible combinations
* between the neighbor and the original vertex as length constraints. */
/* This results on a pattern that contains structural, shear and bending constraints for all
@ -484,11 +524,11 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
for (int i = 0; i < cloth_sim->tot_length_constraints; i++) {
const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i];
const int v1 = constraint->v1;
const int v2 = constraint->v2;
const int v1 = constraint->elem_index_a;
const int v2 = constraint->elem_index_b;
float v1_to_v2[3];
sub_v3_v3v3(v1_to_v2, cloth_sim->pos[v2], cloth_sim->pos[v1]);
sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a);
const float current_distance = len_v3(v1_to_v2);
float correction_vector[3];
float correction_vector_half[3];
@ -524,8 +564,14 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
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);
madd_v3_v3fl(cloth_sim->pos[v1],
correction_vector_half,
1.0f * mask_v1 * sim_factor_v1 * constraint->strength);
if (v1 != v2) {
madd_v3_v3fl(cloth_sim->pos[v2],
correction_vector_half,
-1.0f * mask_v2 * sim_factor_v2 * constraint->strength);
}
}
}
}

View File

@ -508,7 +508,7 @@ typedef struct Brush {
/** Source for fill tool color gradient application. */
char gradient_fill_mode;
char _pad0[1];
char _pad0[5];
/** Projection shape (sphere, circle). */
char falloff_shape;
@ -580,6 +580,8 @@ typedef struct Brush {
float cloth_sim_limit;
float cloth_sim_falloff;
float cloth_constraint_softbody_strength;
/* smooth */
int smooth_deform_type;
float surface_smooth_shape_preservation;

View File

@ -2568,6 +2568,15 @@ static void rna_def_brush(BlenderRNA *brna)
"Area to apply deformation falloff to the effects of the simulation");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "cloth_constraint_softbody_strength", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "cloth_constraint_softbody_strength");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(
prop,
"Soft Body Influence",
"How much the simulation preserves the original shape, acting as a soft body");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "hardness", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "hardness");
RNA_def_property_range(prop, 0.0f, 1.0f);