diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_cloth.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_cloth.c | 232 |
1 files changed, 178 insertions, 54 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 4232be91034..51b2d50e73b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -85,7 +85,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" @@ -101,6 +100,28 @@ #include <stdlib.h> #include <string.h> +static float cloth_brush_simulation_falloff_get(const Brush *brush, + const float radius, + const float location[3], + const float co[3]) +{ + const float distance = len_v3v3(location, co); + const float limit = radius + (radius * brush->cloth_sim_limit); + const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff); + + if (distance > limit) { + /* Outiside the limits. */ + return 0.0f; + } + if (distance < falloff) { + /* Before the falloff area. */ + return 1.0f; + } + /* Do a smoothstep 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_MAX_CONSTRAINTS_PER_VERTEX 1024 @@ -113,19 +134,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,23 +143,115 @@ 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, + const bool use_persistent) +{ + 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]; + + if (use_persistent) { + length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), + SCULPT_vertex_persistent_co_get(ss, v2)); + } + else { + 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 cloth_brush_add_deformation_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->deformation_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; + const Brush *brush = data->brush; PBVHVertexIter vd; + const bool pin_simulation_boundary = ss->cache != NULL && brush != NULL && + brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY; + + const bool use_persistent = brush != NULL && brush->flag & BRUSH_PERSISTENT; + + /* Brush can be NULL in tools that use the solver without relying of constraints with deformation + * positions. */ + const bool cloth_is_deform_brush = ss->cache != NULL && brush != NULL && + SCULPT_is_cloth_deform_brush(brush); + float radius_squared = 0.0f; + if (cloth_is_deform_brush) { + radius_squared = ss->cache->initial_radius * ss->cache->initial_radius; + } + + const float cloth_sim_radius_squared = data->cloth_sim_radius * data->cloth_sim_radius; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (len_squared_v3v3(vd.co, data->cloth_sim_initial_location) < - data->cloth_sim_radius * data->cloth_sim_radius) { + const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location); + if (len_squared < cloth_sim_radius_squared) { SculptVertexNeighborIter ni; int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX]; @@ -162,6 +264,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 @@ -172,35 +279,29 @@ static void do_cloth_brush_build_constraints_task_cb_ex( 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]); + ss, data->cloth_sim, build_indices[c_i], build_indices[c_j], use_persistent); } } } } - } - BKE_pbvh_vertex_iter_end; -} -static float cloth_brush_simulation_falloff_get(const Brush *brush, - const float radius, - const float location[3], - const float co[3]) -{ - const float distance = len_v3v3(location, co); - const float limit = radius + (radius * brush->cloth_sim_limit); - const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff); + if (cloth_is_deform_brush && len_squared < radius_squared) { + 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 (distance > limit) { - /* Outiside the limits. */ - return 0.0f; - } - if (distance < falloff) { - /* Before the falloff area. */ - return 1.0f; + if (pin_simulation_boundary) { + const float sim_falloff = cloth_brush_simulation_falloff_get( + brush, ss->cache->initial_radius, ss->cache->location, vd.co); + /* Vertex is inside the area of the simulation without any falloff aplied. */ + 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); + } + } } - /* Do a smoothstep transition inside the falloff area. */ - float p = 1.0f - ((distance - falloff) / (limit - falloff)); - return 3.0f * p * p - 2.0f * p * p * p; + BKE_pbvh_vertex_iter_end; } static void cloth_brush_apply_force_to_vertex(SculptSession *UNUSED(ss), @@ -323,9 +424,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(force, offset, -fade); break; case BRUSH_CLOTH_DEFORM_GRAB: - /* Grab writes the positions in the simulation directly without applying forces. */ - madd_v3_v3v3fl( - cloth_sim->pos[vd.index], orig_data.co, ss->cache->grab_delta_symmetry, fade); + madd_v3_v3v3fl(cloth_sim->deformation_pos[vd.index], + orig_data.co, + ss->cache->grab_delta_symmetry, + fade); zero_v3(force); break; case BRUSH_CLOTH_DEFORM_PINCH_POINT: @@ -367,6 +469,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, } static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, + Brush *brush, const float cloth_mass, const float cloth_damping) { @@ -380,12 +483,20 @@ static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, "cloth length constraints"); cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; - cloth_sim->acceleration = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim acceleration"); - cloth_sim->pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim pos"); - cloth_sim->prev_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim prev pos"); - cloth_sim->init_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim init pos"); - cloth_sim->length_constraint_tweak = MEM_callocN(sizeof(float) * totverts, - "cloth sim length tweak"); + cloth_sim->acceleration = MEM_calloc_arrayN( + totverts, 3 * sizeof(float), "cloth sim acceleration"); + cloth_sim->pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim pos"); + cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim prev pos"); + cloth_sim->init_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim init pos"); + 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)) { + cloth_sim->deformation_pos = MEM_calloc_arrayN( + totverts, 3 * sizeof(float), "cloth sim deformation positions"); + } cloth_sim->mass = cloth_mass; cloth_sim->damping = cloth_damping; @@ -484,11 +595,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 +635,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); + } } } } @@ -577,7 +694,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod .mat = imat, }; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Init the grab delta. */ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); @@ -649,10 +766,16 @@ 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 (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) { ss->cache->cloth_sim = cloth_brush_simulation_create( - ss, brush->cloth_mass, brush->cloth_damping); + ss, brush, brush->cloth_mass, brush->cloth_damping); + + const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush); + 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)); + if (is_cloth_deform_brush) { + copy_v3_v3(ss->cache->cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + } } } @@ -684,6 +807,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim->acceleration); 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->init_pos); MEM_SAFE_FREE(cloth_sim); } @@ -913,7 +1037,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent 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); + ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, NULL, 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); |