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:
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_cloth.c')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c484
1 files changed, 371 insertions, 113 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index a95e4f7008c..20b164fa80c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -103,11 +103,32 @@
#include <stdlib.h>
#include <string.h>
+static void cloth_brush_simulation_location_get(SculptSession *ss,
+ const Brush *brush,
+ float r_location[3])
+{
+ if (!ss->cache || !brush) {
+ zero_v3(r_location);
+ return;
+ }
+
+ if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) {
+ copy_v3_v3(r_location, ss->cache->initial_location);
+ return;
+ }
+ copy_v3_v3(r_location, ss->cache->location);
+}
+
static float cloth_brush_simulation_falloff_get(const Brush *brush,
const float radius,
const float location[3],
const float co[3])
{
+ if (brush->sculpt_tool != SCULPT_TOOL_CLOTH) {
+ /* All brushes that are not the cloth brush do not use simulation areas. */
+ return 1.0f;
+ }
+
/* Global simulation does not have any falloff as the entire mesh is being simulated. */
if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) {
return 1.0f;
@@ -118,23 +139,27 @@ static float cloth_brush_simulation_falloff_get(const Brush *brush,
const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff);
if (distance > limit) {
- /* Outiside the limits. */
+ /* Outside the limits. */
return 0.0f;
}
if (distance < falloff) {
/* Before the falloff area. */
return 1.0f;
}
- /* Do a smoothstep transition inside the falloff area. */
+ /* Do a smooth-step transition inside the falloff area. */
float p = 1.0f - ((distance - falloff) / (limit - falloff));
return 3.0f * p * p - 2.0f * p * p * p;
}
#define CLOTH_LENGTH_CONSTRAINTS_BLOCK 100000
#define CLOTH_SIMULATION_ITERATIONS 5
+
+#define CLOTH_SOLVER_DISPLACEMENT_FACTOR 0.6f
#define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024
#define CLOTH_SIMULATION_TIME_STEP 0.01f
-#define CLOTH_DEFORMATION_TARGET_STRENGTH 0.35f
+#define CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH 0.35f
+#define CLOTH_DEFORMATION_TARGET_STRENGTH 0.01f
+#define CLOTH_DEFORMATION_GRAB_STRENGTH 0.1f
static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim,
const int v1,
@@ -156,6 +181,7 @@ static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim)
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
+ const int node_index,
const int v1,
const int v2,
const bool use_persistent)
@@ -166,6 +192,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
length_constraint->elem_index_a = v1;
length_constraint->elem_index_b = v2;
+ length_constraint->node = node_index;
+
length_constraint->elem_position_a = cloth_sim->pos[v1];
length_constraint->elem_position_b = cloth_sim->pos[v2];
@@ -186,11 +214,12 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
/* Reallocation if the array capacity is exceeded. */
cloth_brush_reallocate_constraints(cloth_sim);
- /* Add the constraint to the GSet to avoid creating it again. */
+ /* 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 node_index,
const int v,
const float strength)
{
@@ -200,8 +229,10 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
+ length_constraint->node = node_index;
+
length_constraint->elem_position_a = cloth_sim->pos[v];
- length_constraint->elem_position_b = cloth_sim->init_pos[v];
+ length_constraint->elem_position_b = cloth_sim->softbody_pos[v];
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY;
@@ -214,7 +245,35 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
cloth_brush_reallocate_constraints(cloth_sim);
}
+static void cloth_brush_add_pin_constraint(SculptClothSimulation *cloth_sim,
+ const int node_index,
+ 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->node = node_index;
+
+ 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_PIN;
+
+ 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 cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim,
+ const int node_index,
const int v,
const float strength)
{
@@ -224,6 +283,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_
length_constraint->elem_index_a = v;
length_constraint->elem_index_b = v;
+ length_constraint->node = node_index;
+
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION;
length_constraint->elem_position_a = cloth_sim->pos[v];
@@ -244,11 +305,20 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
+ PBVHNode *node = data->nodes[n];
+
+ const int node_index = POINTER_AS_INT(BLI_ghash_lookup(data->cloth_sim->node_state_index, node));
+ if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_UNINITIALIZED) {
+ /* The simulation already contains constraints for this node. */
+ return;
+ }
PBVHVertexIter vd;
const bool pin_simulation_boundary = ss->cache != NULL && brush != NULL &&
- brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY;
+ brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY &&
+ brush->cloth_simulation_area_type !=
+ BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC;
const bool use_persistent = brush != NULL && brush->flag & BRUSH_PERSISTENT;
@@ -256,18 +326,21 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
* positions. */
const bool cloth_is_deform_brush = ss->cache != NULL && brush != NULL &&
SCULPT_is_cloth_deform_brush(brush);
+
+ const bool use_falloff_plane = brush->cloth_force_falloff_type ==
+ BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
float radius_squared = 0.0f;
if (cloth_is_deform_brush) {
radius_squared = ss->cache->initial_radius * ss->cache->initial_radius;
}
- /* Only limit the contraint creation to a radius when the simulation is local. */
+ /* Only limit the constraint creation to a radius when the simulation is local. */
const float cloth_sim_radius_squared = brush->cloth_simulation_area_type ==
BRUSH_CLOTH_SIMULATION_AREA_LOCAL ?
data->cloth_sim_radius * data->cloth_sim_radius :
FLT_MAX;
- BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location);
if (len_squared < cloth_sim_radius_squared) {
@@ -283,9 +356,8 @@ 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);
+ if (data->cloth_sim->softbody_strength > 0.0f) {
+ cloth_brush_add_softbody_constraint(data->cloth_sim, node_index, vd.index, 1.0f);
}
/* As we don't know the order of the neighbor vertices, we create all possible combinations
@@ -297,8 +369,12 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
for (int c_j = 0; c_j < tot_indices; c_j++) {
if (c_i != c_j && !cloth_brush_sim_has_length_constraint(
data->cloth_sim, 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], use_persistent);
+ cloth_brush_add_length_constraint(ss,
+ data->cloth_sim,
+ node_index,
+ build_indices[c_i],
+ build_indices[c_j],
+ use_persistent);
}
}
}
@@ -307,17 +383,27 @@ 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 (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);
+ if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
+ if (use_falloff_plane) {
+ /* With plane falloff the strength of the constraints is set when applying the
+ * deformation forces. */
+ cloth_brush_add_deformation_constraint(
+ data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_GRAB_STRENGTH);
+ }
+ else if (len_squared < radius_squared) {
+ /* With radial falloff 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, node_index, vd.index, fade * CLOTH_DEFORMATION_GRAB_STRENGTH);
+ }
}
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);
+ data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH);
}
}
else if (data->cloth_sim->deformation_pos) {
@@ -325,7 +411,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
* their own code when modifying the deformation coordinates of the simulation, so
* deformation constraints are created with a fixed strength for all vertices. */
cloth_brush_add_deformation_constraint(
- data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
+ data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
}
if (pin_simulation_boundary) {
@@ -335,7 +421,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
if (sim_falloff < 1.0f) {
/* Create constraints with more strength the closer the vertex is to the simulation
* boundary. */
- cloth_brush_add_softbody_constraint(data->cloth_sim, vd.index, 1.0f - sim_falloff);
+ cloth_brush_add_pin_constraint(data->cloth_sim, node_index, vd.index, 1.0f - sim_falloff);
}
}
}
@@ -362,9 +448,8 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
const float *grab_delta = data->grab_delta;
float(*imat)[4] = data->mat;
- const bool use_falloff_plane = !SCULPT_is_cloth_deform_brush(brush) &&
- brush->cloth_force_falloff_type ==
- BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
+ const bool use_falloff_plane = brush->cloth_force_falloff_type ==
+ BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
PBVHVertexIter vd;
const float bstrength = ss->cache->bstrength;
@@ -374,7 +459,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
- /* For Pich Perpendicular Deform Type. */
+ /* For Pinch Perpendicular Deform Type. */
float x_object_space[3];
float z_object_space[3];
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR) {
@@ -396,22 +481,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor);
}
- /* Original data for deform brushes. */
- SculptOrigVertData orig_data;
- if (SCULPT_is_cloth_deform_brush(brush)) {
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
- }
-
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
float force[3];
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
const float sim_factor = cloth_brush_simulation_falloff_get(
- brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]);
+ brush, ss->cache->radius, sim_location, cloth_sim->init_pos[vd.index]);
float current_vertex_location[3];
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);
+ copy_v3_v3(current_vertex_location, ss->cache->cloth_sim->init_pos[vd.index]);
}
else {
copy_v3_v3(current_vertex_location, vd.co);
@@ -428,7 +508,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
float dist = sqrtf(test.dist);
if (use_falloff_plane) {
- dist = dist_to_plane_v3(vd.co, deform_plane);
+ dist = dist_to_plane_v3(current_vertex_location, deform_plane);
}
const float fade = sim_factor * bstrength *
@@ -463,9 +543,15 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
break;
case BRUSH_CLOTH_DEFORM_GRAB:
madd_v3_v3v3fl(cloth_sim->deformation_pos[vd.index],
- orig_data.co,
+ cloth_sim->init_pos[vd.index],
ss->cache->grab_delta_symmetry,
fade);
+ if (use_falloff_plane) {
+ cloth_sim->deformation_strength[vd.index] = clamp_f(fade, 0.0f, 1.0f);
+ }
+ else {
+ cloth_sim->deformation_strength[vd.index] = 1.0f;
+ }
zero_v3(force);
break;
case BRUSH_CLOTH_DEFORM_SNAKE_HOOK:
@@ -640,17 +726,27 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
+
+ PBVHNode *node = data->nodes[n];
PBVHVertexIter vd;
SculptClothSimulation *cloth_sim = data->cloth_sim;
const float time_step = data->cloth_time_step;
+
+ const int node_index = POINTER_AS_INT(BLI_ghash_lookup(data->cloth_sim->node_state_index, node));
+ if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_ACTIVE) {
+ return;
+ }
+
+ AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
+
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- 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;
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
+ const float sim_factor =
+ ss->cache ? cloth_brush_simulation_falloff_get(
+ brush, ss->cache->radius, sim_location, cloth_sim->init_pos[vd.index]) :
+ 1.0f;
if (sim_factor > 0.0f) {
int i = vd.index;
float temp[3];
@@ -660,10 +756,10 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
float pos_diff[3];
sub_v3_v3v3(pos_diff, cloth_sim->pos[i], cloth_sim->prev_pos[i]);
- mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping));
+ mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor);
const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) *
- SCULPT_automasking_factor_get(ss, vd.index);
+ SCULPT_automasking_factor_get(automasking, ss, vd.index);
madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v);
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
@@ -686,16 +782,27 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
}
}
BKE_pbvh_vertex_iter_end;
+
+ /* Disable the simulation on this node, it needs to be enabled again to continue. */
+ cloth_sim->node_state[node_index] = SCULPT_CLOTH_NODE_INACTIVE;
}
static void cloth_brush_satisfy_constraints(SculptSession *ss,
Brush *brush,
SculptClothSimulation *cloth_sim)
{
+
+ AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
+
for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) {
for (int i = 0; i < cloth_sim->tot_length_constraints; i++) {
-
const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i];
+
+ if (cloth_sim->node_state[constraint->node] != SCULPT_CLOTH_NODE_ACTIVE) {
+ /* Skip all constraints that were created for inactive nodes. */
+ continue;
+ }
+
const int v1 = constraint->elem_index_a;
const int v2 = constraint->elem_index_b;
@@ -710,31 +817,37 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
(cloth_sim->length_constraint_tweak[v2] * 0.5f);
if (current_distance > 0.0f) {
- mul_v3_v3fl(correction_vector, v1_to_v2, 1.0f - (constraint_distance / current_distance));
+ mul_v3_v3fl(correction_vector,
+ v1_to_v2,
+ CLOTH_SOLVER_DISPLACEMENT_FACTOR *
+ (1.0f - (constraint_distance / current_distance)));
}
else {
- copy_v3_v3(correction_vector, v1_to_v2);
+ mul_v3_v3fl(correction_vector, v1_to_v2, CLOTH_SOLVER_DISPLACEMENT_FACTOR);
}
mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f);
const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) *
- SCULPT_automasking_factor_get(ss, v1);
+ SCULPT_automasking_factor_get(automasking, ss, v1);
const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
- SCULPT_automasking_factor_get(ss, 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;
+ SCULPT_automasking_factor_get(automasking, ss, v2);
+
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
+
+ const float sim_factor_v1 = ss->cache ?
+ cloth_brush_simulation_falloff_get(brush,
+ ss->cache->radius,
+ sim_location,
+ cloth_sim->init_pos[v1]) :
+ 1.0f;
+ const float sim_factor_v2 = ss->cache ?
+ cloth_brush_simulation_falloff_get(brush,
+ ss->cache->radius,
+ sim_location,
+ cloth_sim->init_pos[v2]) :
+ 1.0f;
float deformation_strength = 1.0f;
if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) {
@@ -743,14 +856,26 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
0.5f;
}
- madd_v3_v3fl(cloth_sim->pos[v1],
- correction_vector_half,
- 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * deformation_strength);
- if (v1 != v2) {
- madd_v3_v3fl(cloth_sim->pos[v2],
+ if (constraint->type == SCULPT_CLOTH_CONSTRAINT_SOFTBODY) {
+ const float softbody_plasticity = brush ? brush->cloth_constraint_softbody_strength : 0.0f;
+ madd_v3_v3fl(cloth_sim->pos[v1],
correction_vector_half,
- -1.0f * mask_v2 * sim_factor_v2 * constraint->strength *
- deformation_strength);
+ 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * softbody_plasticity);
+ madd_v3_v3fl(cloth_sim->softbody_pos[v1],
+ correction_vector_half,
+ -1.0f * mask_v1 * sim_factor_v1 * constraint->strength *
+ (1.0f - softbody_plasticity));
+ }
+ else {
+ madd_v3_v3fl(cloth_sim->pos[v1],
+ correction_vector_half,
+ 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 *
+ deformation_strength);
+ }
}
}
}
@@ -805,7 +930,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
BKE_curvemapping_init(brush->curve);
- /* Init the grab delta. */
+ /* Initialize the grab delta. */
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
normalize_v3(grab_delta);
@@ -815,7 +940,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
return;
}
- /* Calcuate push offset. */
+ /* Calculate push offset. */
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_PUSH) {
mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius);
@@ -829,7 +954,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
brush->cloth_force_falloff_type == BRUSH_CLOTH_FORCE_FALLOFF_PLANE) {
SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
- /* Init stroke local space matrix. */
+ /* Initialize stroke local space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
cross_v3_v3v3(mat[1], area_no, mat[0]);
@@ -850,8 +975,8 @@ 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
+ if (ELEM(brush->cloth_deform_type, BRUSH_CLOTH_DEFORM_SNAKE_HOOK, BRUSH_CLOTH_DEFORM_GRAB)) {
+ /* Set the deformation strength to 0. Brushes will initialize the strength in the required
* area. */
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
@@ -865,12 +990,32 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings);
}
+/* Allocates nodes state and initializes them to Uninitialized, so constraints can be created for
+ * them. */
+static void cloth_sim_initialize_default_node_state(SculptSession *ss,
+ SculptClothSimulation *cloth_sim)
+{
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ cloth_sim->node_state = MEM_malloc_arrayN(
+ totnode, sizeof(eSculptClothNodeSimState), "node sim state");
+ cloth_sim->node_state_index = BLI_ghash_ptr_new("node sim state indices");
+ for (int i = 0; i < totnode; i++) {
+ cloth_sim->node_state[i] = SCULPT_CLOTH_NODE_UNINITIALIZED;
+ BLI_ghash_insert(cloth_sim->node_state_index, nodes[i], POINTER_FROM_INT(i));
+ }
+ MEM_SAFE_FREE(nodes);
+}
+
/* Public functions. */
SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
- Brush *brush,
const float cloth_mass,
const float cloth_damping,
- const bool use_collisions)
+ const float cloth_softbody_strength,
+ const bool use_collisions,
+ const bool needs_deform_coords)
{
const int totverts = SCULPT_vertex_count_get(ss);
SculptClothSimulation *cloth_sim;
@@ -892,32 +1037,38 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim->length_constraint_tweak = MEM_calloc_arrayN(
totverts, sizeof(float), "cloth sim length tweak");
- /* Brush can be NULL for tools that need the solver but don't rely on constraint to deformation
- * positions. */
- if (brush && SCULPT_is_cloth_deform_brush(brush)) {
+ if (needs_deform_coords) {
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");
}
+ if (cloth_softbody_strength > 0.0f) {
+ cloth_sim->softbody_pos = MEM_calloc_arrayN(
+ totverts, sizeof(float[3]), "cloth sim softbody pos");
+ }
+
cloth_sim->mass = cloth_mass;
cloth_sim->damping = cloth_damping;
+ cloth_sim->softbody_strength = cloth_softbody_strength;
if (use_collisions) {
cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
}
+ cloth_sim_initialize_default_node_state(ss, cloth_sim);
+
return cloth_sim;
}
-void SCULPT_cloth_brush_build_nodes_constraints(
+void SCULPT_cloth_brush_ensure_nodes_constraints(
Sculpt *sd,
Object *ob,
PBVHNode **nodes,
int totnode,
SculptClothSimulation *cloth_sim,
- /* Cannot be const, because it is assigned to a non-const variable.
+ /* Cannot be `const`, because it is assigned to a `non-const` variable.
* NOLINTNEXTLINE: readability-non-const-parameter. */
float initial_location[3],
const float radius)
@@ -952,6 +1103,7 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation
{
const int totverts = SCULPT_vertex_count_get(ss);
const bool has_deformation_pos = cloth_sim->deformation_pos != NULL;
+ const bool has_softbody_pos = cloth_sim->softbody_pos != NULL;
for (int i = 0; i < totverts; i++) {
copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
@@ -960,6 +1112,9 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation
copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
cloth_sim->deformation_strength[i] = 1.0f;
}
+ if (has_softbody_pos) {
+ copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i));
+ }
}
}
@@ -971,14 +1126,38 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim
}
}
+void SCULPT_cloth_sim_activate_nodes(SculptClothSimulation *cloth_sim,
+ PBVHNode **nodes,
+ int totnode)
+{
+ /* Activate the nodes inside the simulation area. */
+ for (int n = 0; n < totnode; n++) {
+ const int node_index = POINTER_AS_INT(BLI_ghash_lookup(cloth_sim->node_state_index, nodes[n]));
+ cloth_sim->node_state[node_index] = SCULPT_CLOTH_NODE_ACTIVE;
+ }
+}
+
+static void sculpt_cloth_ensure_constraints_in_simulation_area(Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ const float radius = ss->cache->initial_radius;
+ const float limit = radius + (radius * brush->cloth_sim_limit);
+ float sim_location[3];
+ cloth_brush_simulation_location_get(ss, brush, sim_location);
+ SCULPT_cloth_brush_ensure_nodes_constraints(
+ sd, ob, nodes, totnode, ss->cache->cloth_sim, sim_location, limit);
+}
+
/* Main Brush Function. */
void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- /* In the first brush step of each symmetry pass, build the constraints for the vertices in all
- * nodes inside the simulation's limits. */
/* Brushes that use anchored strokes and restore the mesh can't rely on symmetry passes and steps
* count as it is always the first step, so the simulation needs to be created when it does not
* exist for this stroke. */
@@ -988,25 +1167,40 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
ss,
- brush,
brush->cloth_mass,
brush->cloth_damping,
- (brush->flag2 & BRUSH_CLOTH_USE_COLLISION));
+ brush->cloth_constraint_softbody_strength,
+ (brush->flag2 & BRUSH_CLOTH_USE_COLLISION),
+ SCULPT_is_cloth_deform_brush(brush));
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
- /* Build the constraints. */
- const float radius = ss->cache->initial_radius;
- const float limit = radius + (radius * brush->cloth_sim_limit);
- SCULPT_cloth_brush_build_nodes_constraints(
- sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit);
-
+ if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) {
+ /* When using simulation a fixed local simulation area, constraints are created only using
+ * the initial stroke position and initial radius (per symmetry pass) instead of per node.
+ * This allows to skip unnecessary constraints that will never be simulated, making the
+ * solver faster. When the simulation starts for a node, the node gets activated and all its
+ * constraints are considered final. As the same node can be included inside the brush radius
+ * from multiple symmetry passes, the cloth brush can't activate the node for simulation yet
+ * as this will cause the ensure constraints function to skip the node in the next symmetry
+ * passes. It needs to build the constraints here and skip simulating the first step, so all
+ * passes can add their constraints to all affected nodes. */
+ sculpt_cloth_ensure_constraints_in_simulation_area(sd, ob, nodes, totnode);
+ }
+ /* The first step of a symmetry pass is never simulated as deformation modes need valid delta
+ * for brush tip alignment. */
return;
}
+ /* Ensure the constraints for the nodes. */
+ sculpt_cloth_ensure_constraints_in_simulation_area(sd, ob, nodes, totnode);
+
/* Store the initial state in the simulation. */
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
+ /* Enable the nodes that should be simulated. */
+ SCULPT_cloth_sim_activate_nodes(ss->cache->cloth_sim, nodes, totnode);
+
/* Apply forces to the vertices. */
cloth_brush_apply_brush_foces(sd, ob, nodes, totnode);
@@ -1023,8 +1217,11 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
MEM_SAFE_FREE(cloth_sim->length_constraints);
MEM_SAFE_FREE(cloth_sim->length_constraint_tweak);
MEM_SAFE_FREE(cloth_sim->deformation_pos);
+ MEM_SAFE_FREE(cloth_sim->softbody_pos);
MEM_SAFE_FREE(cloth_sim->init_pos);
MEM_SAFE_FREE(cloth_sim->deformation_strength);
+ MEM_SAFE_FREE(cloth_sim->node_state);
+ BLI_ghash_free(cloth_sim->node_state_index, NULL, NULL);
if (cloth_sim->collider_list) {
BKE_collider_cache_free(&cloth_sim->collider_list);
}
@@ -1066,9 +1263,14 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
const float outline_col[3],
float outline_alpha)
{
- float local_mat_inv[4][4];
- invert_m4_m4(local_mat_inv, ss->cache->stroke_local_mat);
- GPU_matrix_mul(ss->cache->stroke_local_mat);
+ float local_mat[4][4];
+ copy_m4_m4(local_mat, ss->cache->stroke_local_mat);
+
+ if (ss->cache->brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) {
+ add_v3_v3v3(local_mat[3], ss->cache->true_location, ss->cache->grab_delta);
+ }
+
+ GPU_matrix_mul(local_mat);
const float dist = ss->cache->radius;
const float arrow_x = ss->cache->radius * 0.2f;
@@ -1100,6 +1302,7 @@ typedef enum eSculpClothFilterType {
CLOTH_FILTER_INFLATE,
CLOTH_FILTER_EXPAND,
CLOTH_FILTER_PINCH,
+ CLOTH_FILTER_SCALE,
} eSculptClothFilterType;
static EnumPropertyItem prop_cloth_filter_type[] = {
@@ -1107,6 +1310,11 @@ static EnumPropertyItem prop_cloth_filter_type[] = {
{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", "Pulls the cloth to the cursor's start position"},
+ {CLOTH_FILTER_SCALE,
+ "SCALE",
+ 0,
+ "Scale",
+ "Scales the mesh as a soft-body using the origin of the object as scale"},
{0, NULL, 0, NULL, NULL},
};
@@ -1142,6 +1350,35 @@ static EnumPropertyItem prop_cloth_filter_force_axis_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static bool cloth_filter_is_deformation_filter(eSculptClothFilterType filter_type)
+{
+ return ELEM(filter_type, CLOTH_FILTER_SCALE);
+}
+
+static void cloth_filter_apply_displacement_to_deform_co(const int v_index,
+ const float disp[3],
+ FilterCache *filter_cache)
+{
+ float final_disp[3];
+ copy_v3_v3(final_disp, disp);
+ SCULPT_filter_zero_disabled_axis_components(final_disp, filter_cache);
+ add_v3_v3v3(filter_cache->cloth_sim->deformation_pos[v_index],
+ filter_cache->cloth_sim->init_pos[v_index],
+ final_disp);
+}
+
+static void cloth_filter_apply_forces_to_vertices(const int v_index,
+ const float force[3],
+ const float gravity[3],
+ FilterCache *filter_cache)
+{
+ float final_force[3];
+ copy_v3_v3(final_force, force);
+ SCULPT_filter_zero_disabled_axis_components(final_force, filter_cache);
+ add_v3_v3(final_force, gravity);
+ cloth_brush_apply_force_to_vertex(NULL, filter_cache->cloth_sim, final_force, v_index);
+}
+
static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
@@ -1152,7 +1389,9 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
PBVHNode *node = data->nodes[i];
SculptClothSimulation *cloth_sim = ss->filter_cache->cloth_sim;
- const int filter_type = data->filter_type;
+
+ const eSculptClothFilterType filter_type = data->filter_type;
+ const bool is_deformation_filter = cloth_filter_is_deformation_filter(filter_type);
float sculpt_gravity[3] = {0.0f};
if (sd->gravity_object) {
@@ -1167,8 +1406,10 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
float fade = vd.mask ? *vd.mask : 0.0f;
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
fade = 1.0f - fade;
float force[3] = {0.0f, 0.0f, 0.0f};
+ float disp[3], temp[3], transform[3][3];
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)) {
@@ -1202,19 +1443,23 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
normalize_v3(force);
mul_v3_fl(force, fade * data->filter_strength);
break;
- }
+ case CLOTH_FILTER_SCALE:
+ unit_m3(transform);
+ scale_m3_fl(transform, 1.0f + (fade * data->filter_strength));
+ copy_v3_v3(temp, cloth_sim->init_pos[vd.index]);
+ mul_m3_v3(transform, temp);
+ sub_v3_v3v3(disp, temp, cloth_sim->init_pos[vd.index]);
+ zero_v3(force);
- SCULPT_filter_to_orientation_space(force, ss->filter_cache);
- for (int axis = 0; axis < 3; axis++) {
- if (!ss->filter_cache->enabled_force_axis[axis]) {
- force[axis] = 0.0f;
- }
+ break;
}
- SCULPT_filter_to_object_space(force, ss->filter_cache);
- add_v3_v3(force, sculpt_gravity);
-
- cloth_brush_apply_force_to_vertex(ss, cloth_sim, force, vd.index);
+ if (is_deformation_filter) {
+ cloth_filter_apply_displacement_to_deform_co(vd.index, disp, ss->filter_cache);
+ }
+ else {
+ cloth_filter_apply_forces_to_vertices(vd.index, force, sculpt_gravity, ss->filter_cache);
+ }
}
BKE_pbvh_vertex_iter_end;
@@ -1267,6 +1512,10 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings);
+ /* Activate all nodes. */
+ SCULPT_cloth_sim_activate_nodes(
+ ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode);
+
/* Update and write the simulation to the nodes. */
SCULPT_cloth_brush_do_simulation_step(
sd, ob, ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode);
@@ -1285,6 +1534,8 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
+ const eSculptClothFilterType filter_type = RNA_enum_get(op->ptr, "type");
+
/* Update the active vertex */
float mouse[2];
SculptCursorGeometryInfo sgi;
@@ -1300,24 +1551,31 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_undo_push_begin("Cloth filter");
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
+ ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
+
const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");
ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss, NULL, cloth_mass, cloth_damping, use_collisions);
+ ss,
+ cloth_mass,
+ cloth_damping,
+ 0.0f,
+ use_collisions,
+ cloth_filter_is_deformation_filter(filter_type));
copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss));
SCULPT_cloth_brush_simulation_init(ss, ss->filter_cache->cloth_sim);
float origin[3] = {0.0f, 0.0f, 0.0f};
- SCULPT_cloth_brush_build_nodes_constraints(sd,
- ob,
- ss->filter_cache->nodes,
- ss->filter_cache->totnode,
- ss->filter_cache->cloth_sim,
- origin,
- FLT_MAX);
+ SCULPT_cloth_brush_ensure_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) {