diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_cloth.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_cloth.c | 252 |
1 files changed, 189 insertions, 63 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 3203282c30c..39cab883100 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -25,7 +25,7 @@ #include "BLI_blenlib.h" #include "BLI_dial_2d.h" -#include "BLI_ghash.h" +#include "BLI_edgehash.h" #include "BLI_gsqueue.h" #include "BLI_hash.h" #include "BLI_math.h" @@ -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" @@ -106,19 +105,15 @@ #define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024 #define CLOTH_SIMULATION_TIME_STEP 0.01f -static void cloth_brush_add_length_constraint(SculptSession *ss, - SculptClothSimulation *cloth_sim, - const int v1, - const int v2) +static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim, + const int v1, + const int v2) { - 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++; + return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2); +} - /* Reallocation if the array capacity is exceeded. */ +static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim) +{ 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, @@ -128,18 +123,110 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, } } +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 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]; @@ -152,6 +239,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 @@ -159,13 +251,19 @@ static void do_cloth_brush_build_constraints_task_cb_ex( for (int c_i = 0; c_i < tot_indices; c_i++) { for (int c_j = 0; c_j < tot_indices; c_j++) { - if (c_i != 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]); + ss, data->cloth_sim, build_indices[c_i], build_indices[c_j], use_persistent); } } } } + + 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); + } } BKE_pbvh_vertex_iter_end; } @@ -183,15 +281,13 @@ static float cloth_brush_simulation_falloff_get(const Brush *brush, /* Outiside the limits. */ return 0.0f; } - else if (distance < falloff) { + if (distance < falloff) { /* Before the falloff area. */ return 1.0f; } - else { - /* 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; - } + /* 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; } static void cloth_brush_apply_force_to_vertex(SculptSession *UNUSED(ss), @@ -245,8 +341,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, /* Gravity */ float gravity[3] = {0.0f}; if (ss->cache->supports_gravity) { - madd_v3_v3fl( - gravity, ss->cache->gravity_direction, -ss->cache->radius * data->sd->gravity_factor); + madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor); } /* Original data for deform brushes. */ @@ -270,6 +365,11 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, copy_v3_v3(current_vertex_location, vd.co); } + /* Apply gravity in the entire simulation area. */ + float vertex_gravity[3]; + mul_v3_v3fl(vertex_gravity, gravity, sim_factor); + cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, vertex_gravity, vd.index); + /* When using the plane falloff mode the falloff is not constrained by the brush radius. */ if (sculpt_brush_test_sq_fn(&test, current_vertex_location) || use_falloff_plane) { @@ -310,9 +410,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: @@ -347,8 +448,6 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, break; } - madd_v3_v3fl(force, gravity, fade); - cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, force, vd.index); } } @@ -356,6 +455,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) { @@ -369,12 +469,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 neeed 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; @@ -428,13 +536,16 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( BKE_pbvh_vertex_iter_end; } -static void cloth_brush_build_nodes_constraints(Sculpt *sd, - Object *ob, - PBVHNode **nodes, - int totnode, - SculptClothSimulation *cloth_sim, - float initial_location[3], - const float radius) +static void cloth_brush_build_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. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + float initial_location[3], + const float radius) { Brush *brush = BKE_paint_brush(&sd->paint); @@ -445,6 +556,8 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd, TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, false, totnode); + cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints"); + SculptThreadedTaskData build_constraints_data = { .sd = sd, .ob = ob, @@ -456,6 +569,8 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd, }; BLI_task_parallel_range( 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings); + + BLI_edgeset_free(cloth_sim->created_length_constraints); } static void cloth_brush_satisfy_constraints(SculptSession *ss, @@ -466,11 +581,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]; @@ -506,8 +621,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); + } } } } @@ -532,7 +653,7 @@ static void cloth_brush_do_simulation_step( }; TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range( 0, totnode, &solve_simulation_data, do_cloth_brush_solve_simulation_task_cb_ex, &settings); } @@ -607,7 +728,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod } TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range( 0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings); } @@ -623,17 +744,24 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* In the first brush step of each symmetry pass, build the constraints for the vertices in all * nodes inside the simulation's limits. */ - /* Brush stroke types that restore the mesh on each brush step also need the cloth sim data to be - * created on each step. */ - if (ss->cache->first_time || !ss->cache->cloth_sim) { + /* 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. */ + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) || !ss->cache->cloth_sim) { /* The simulation structure only needs to be created on the first symmetry pass. */ - if (ss->cache->mirror_symmetry_pass == 0) { + 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)); + } } } @@ -656,8 +784,6 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* Update and write the simulation to the nodes. */ cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode); - - return; } void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) @@ -667,6 +793,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); } @@ -841,7 +968,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent SCULPT_vertex_random_access_init(ss); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { @@ -857,8 +984,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent }; TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings( - &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); + BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode); BLI_task_parallel_range( 0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings); @@ -890,14 +1016,14 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_vertex_random_access_init(ss); /* Needs mask data to be available as it is used when solving the constraints. */ - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); SCULPT_undo_push_begin("Cloth filter"); - SCULPT_filter_cache_init(ob, sd); + SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS); 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); |