Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_cloth.c
| Show All 19 Lines | |||||
| /** \file | /** \file | ||||
| * \ingroup edsculpt | * \ingroup edsculpt | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_dial_2d.h" | #include "BLI_dial_2d.h" | ||||
| #include "BLI_edgehash.h" | |||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_gsqueue.h" | #include "BLI_gsqueue.h" | ||||
| #include "BLI_hash.h" | #include "BLI_hash.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_task.h" | #include "BLI_task.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
| #include "bmesh.h" | #include "bmesh.h" | ||||
| #include "bmesh_tools.h" | #include "bmesh_tools.h" | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #define CLOTH_LENGTH_CONSTRAINTS_BLOCK 100000 | #define CLOTH_CONSTRAINTS_BLOCK 100000 | ||||
| #define CLOTH_SIMULATION_ITERATIONS 5 | #define CLOTH_SIMULATION_ITERATIONS 10 | ||||
| #define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024 | #define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024 | ||||
| #define CLOTH_SIMULATION_TIME_STEP 0.01f | #define CLOTH_SIMULATION_TIME_STEP 0.01f | ||||
| static void cloth_brush_constraint_key_get(int r_key[2], const int v1, const int v2) | static void cloth_brush_constraint_key_get(int r_key[2], const int v1, const int v2) | ||||
| { | { | ||||
| if (v1 < v2) { | if (v1 < v2) { | ||||
| r_key[0] = v1; | r_key[0] = v1; | ||||
| r_key[1] = v2; | r_key[1] = v2; | ||||
| } | } | ||||
| else { | else { | ||||
| r_key[0] = v2; | r_key[0] = v2; | ||||
| r_key[1] = v1; | r_key[1] = v1; | ||||
| } | } | ||||
| } | } | ||||
| static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim, | static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim, | ||||
| const int v1, | const int v1, | ||||
| const int v2) | const int v2) | ||||
| { | { | ||||
| int constraint[2]; | int constraint[2]; | ||||
| cloth_brush_constraint_key_get(constraint, v1, v2); | cloth_brush_constraint_key_get(constraint, v1, v2); | ||||
| return BLI_gset_haskey(cloth_sim->created_length_constraints, constraint); | return BLI_gset_haskey(cloth_sim->created_length_constraints, constraint); | ||||
| } | } | ||||
| static bool cloth_brush_sim_has_angular_constraint(SculptClothSimulation *cloth_sim, | |||||
| const int v1, | |||||
| const int v2) | |||||
| { | |||||
| return BLI_edgeset_haskey(cloth_sim->created_angular_constraints, v1, v2); | |||||
| } | |||||
| static void cloth_brush_add_angular_constraint(SculptSession *ss, | |||||
| SculptClothSimulation *cloth_sim, | |||||
| const int origin, | |||||
| const int v1, | |||||
| const int v2, | |||||
| const SculptClothConstraintType type) | |||||
| { | |||||
| float v1_co[3], v2_co[3], origin_co[3]; | |||||
| copy_v3_v3(v1_co, SCULPT_vertex_co_get(ss, v1)); | |||||
| copy_v3_v3(v2_co, SCULPT_vertex_co_get(ss, v2)); | |||||
| copy_v3_v3(origin_co, SCULPT_vertex_co_get(ss, origin)); | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].v1 = v1; | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].v2 = v2; | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].origin = origin; | |||||
| int v1_tri = -1; | |||||
| int v2_tri = -1; | |||||
| SculptVertexNeighborIter ni; | |||||
| SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, origin, ni) { | |||||
| if (v1_tri == -1 && ni.index != v1 && SCULPT_vertices_share_face(ss, v1, ni.index)) { | |||||
| v1_tri = ni.index; | |||||
| } | |||||
| if (v2_tri == -1 && ni.index != v2 && SCULPT_vertices_share_face(ss, v2, ni.index)) { | |||||
| v2_tri = ni.index; | |||||
| } | |||||
| } | |||||
| SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | |||||
| printf("TRIANGLE POINT %d\n", v1_tri); | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].v1_tri = v1_tri; | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].v2_tri = v2_tri; | |||||
| float normal_t1[3]; | |||||
| float normal_t2[3]; | |||||
| normal_tri_v3(normal_t1, origin_co, v1_co, SCULPT_vertex_co_get(ss, v1_tri)); | |||||
| normal_tri_v3(normal_t2, origin_co, SCULPT_vertex_co_get(ss, v1_tri), v2_co); | |||||
| float normal[3]; | |||||
| SCULPT_vertex_normal_get(ss, origin, normal); | |||||
| float origin_normal_co[3]; | |||||
| add_v3_v3v3(origin_normal_co, origin_co, normal); | |||||
| const float angle_v1 = angle_v3v3v3(v1_co, origin_co, origin_normal_co); | |||||
| const float angle_v2 = angle_v3v3v3(v2_co, origin_co, origin_normal_co); | |||||
| //const float current_angle = angle_v1 + angle_v2; | |||||
| bool flip_rotation = false; | |||||
| float rotation_axis[3]; | |||||
| float d_v1[3]; | |||||
| sub_v3_v3v3(d_v1, v1_co, origin_co); | |||||
| normalize_v3(d_v1); | |||||
| cross_v3_v3v3_hi_prec(rotation_axis, normal, d_v1); | |||||
| normalize_v3(rotation_axis); | |||||
| float current_pos_v1[3], current_pos_v2[3]; | |||||
| float new_pos_v1[3], new_pos_v2[3]; | |||||
| copy_v3_v3(current_pos_v1, v1_co); | |||||
| sub_v3_v3(current_pos_v1, origin_co); | |||||
| rotate_v3_v3v3fl(new_pos_v1, current_pos_v1, rotation_axis, -0.1f); | |||||
| const float prev_distance = len_v3v3(current_pos_v1, normal); | |||||
| const float new_distance = len_v3v3(new_pos_v1, normal); | |||||
| flip_rotation = prev_distance > new_distance; | |||||
| const float current_angle = angle_normalized_v3v3(normal_t1, normal_t2); | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].angle = 0.0f; | |||||
| cloth_sim->angular_constraints[cloth_sim->tot_angular_constraints].type = type; | |||||
| cloth_sim->tot_angular_constraints++; | |||||
| /* Reallocation if the array capacity is exceeded. */ | |||||
| if (cloth_sim->tot_angular_constraints >= cloth_sim->capacity_angular_constraints) { | |||||
| cloth_sim->capacity_angular_constraints += CLOTH_CONSTRAINTS_BLOCK; | |||||
| cloth_sim->angular_constraints = MEM_reallocN_id(cloth_sim->angular_constraints, | |||||
| cloth_sim->capacity_angular_constraints * | |||||
| sizeof(SculptClothAngularConstraint), | |||||
| "angular constraints"); | |||||
| } | |||||
| /* Add the constraint to the GSet to avoid creating it again. */ | |||||
| BLI_edgeset_add(cloth_sim->created_angular_constraints, v1, v2); | |||||
| } | |||||
| static void cloth_brush_add_length_constraint(SculptSession *ss, | static void cloth_brush_add_length_constraint(SculptSession *ss, | ||||
| SculptClothSimulation *cloth_sim, | SculptClothSimulation *cloth_sim, | ||||
| const int v1, | const int v1, | ||||
| const int v2) | const int v2, | ||||
| const SculptClothConstraintType type) | |||||
| { | { | ||||
| cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v1 = v1; | 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].v2 = v2; | ||||
| cloth_sim->length_constraints[cloth_sim->tot_length_constraints].length = len_v3v3( | cloth_sim->length_constraints[cloth_sim->tot_length_constraints].length = len_v3v3( | ||||
| SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2)); | SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2)); | ||||
| cloth_sim->length_constraints[cloth_sim->tot_length_constraints].type = type; | |||||
| cloth_sim->tot_length_constraints++; | cloth_sim->tot_length_constraints++; | ||||
| /* Reallocation if the array capacity is exceeded. */ | /* Reallocation if the array capacity is exceeded. */ | ||||
| if (cloth_sim->tot_length_constraints >= cloth_sim->capacity_length_constraints) { | if (cloth_sim->tot_length_constraints >= cloth_sim->capacity_length_constraints) { | ||||
| cloth_sim->capacity_length_constraints += CLOTH_LENGTH_CONSTRAINTS_BLOCK; | cloth_sim->capacity_length_constraints += CLOTH_CONSTRAINTS_BLOCK; | ||||
| cloth_sim->length_constraints = MEM_reallocN_id(cloth_sim->length_constraints, | cloth_sim->length_constraints = MEM_reallocN_id(cloth_sim->length_constraints, | ||||
| cloth_sim->capacity_length_constraints * | cloth_sim->capacity_length_constraints * | ||||
| sizeof(SculptClothLengthConstraint), | sizeof(SculptClothLengthConstraint), | ||||
| "length constraints"); | "length constraints"); | ||||
| } | } | ||||
| /* Add the constraint to the GSet to avoid creating it again. */ | /* Add the constraint to the GSet to avoid creating it again. */ | ||||
| int constraint[2]; | int constraint[2]; | ||||
| cloth_brush_constraint_key_get(constraint, v1, v2); | cloth_brush_constraint_key_get(constraint, v1, v2); | ||||
| BLI_gset_add(cloth_sim->created_length_constraints, constraint); | BLI_gset_insert(cloth_sim->created_length_constraints, constraint); | ||||
| } | } | ||||
| static void do_cloth_brush_build_constraints_task_cb_ex( | static void do_cloth_brush_build_constraints_task_cb_ex( | ||||
| void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) | void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = userdata; | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) | 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) < | if (len_squared_v3v3(vd.co, data->cloth_sim_initial_location) < | ||||
| data->cloth_sim_radius * data->cloth_sim_radius) { | data->cloth_sim_radius * data->cloth_sim_radius) { | ||||
| SculptVertexNeighborIter ni; | |||||
| int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX]; | int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX]; | ||||
| int tot_indices = 0; | int tot_indices = 0; | ||||
| build_indices[tot_indices] = vd.index; | SculptVertexNeighborIter ni; | ||||
| tot_indices++; | |||||
| SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { | SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { | ||||
| build_indices[tot_indices] = ni.index; | build_indices[tot_indices] = ni.index; | ||||
| tot_indices++; | tot_indices++; | ||||
| /* Tension Constraints. */ | |||||
| if (!cloth_brush_sim_has_length_constraint(data->cloth_sim, vd.index, ni.index)) { | |||||
| cloth_brush_add_length_constraint( | |||||
| ss, data->cloth_sim, vd.index, ni.index, SCULPT_CLOTH_CONSTRAINT_TENSION); | |||||
| } | |||||
| } | } | ||||
| SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | ||||
| /* As we don't know the order of the neighbor vertices, we create all possible combinations | //printf("CHECKING VERTED %d\n", vd.index); | ||||
| * between the neighbor and the original vertex as length constraints. */ | |||||
| /* This results on a pattern that contains structural, shear and bending constraints for all | |||||
| * vertices, but constraints are repeated taking more memory than necessary. */ | |||||
| for (int c_i = 0; c_i < tot_indices; c_i++) { | for (int c_i = 0; c_i < tot_indices; c_i++) { | ||||
| for (int c_j = 0; c_j < tot_indices; c_j++) { | for (int c_j = 0; c_j < tot_indices; c_j++) { | ||||
| /* | |||||
| if (c_i != c_j && !cloth_brush_sim_has_length_constraint( | if (c_i != c_j && !cloth_brush_sim_has_length_constraint( | ||||
| data->cloth_sim, build_indices[c_i], build_indices[c_j])) { | 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]); | |||||
| */ | |||||
| if (c_i != c_j) { | |||||
| if (SCULPT_vertices_share_face(ss, 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], | |||||
| SCULPT_CLOTH_CONSTRAINT_SHEARING); | |||||
| } | |||||
| else { | |||||
| /* Bending Constraints. */ | |||||
| /* | |||||
| cloth_brush_add_length_constraint(ss, | |||||
| data->cloth_sim, | |||||
| build_indices[c_i], | |||||
| build_indices[c_j], | |||||
| SCULPT_CLOTH_CONSTRAINT_BENDING); | |||||
| */ | |||||
| if (!cloth_brush_sim_has_angular_constraint( | |||||
| data->cloth_sim, build_indices[c_i], build_indices[c_j])) { | |||||
| printf("ADDING %d %d %d\n", vd.index, build_indices[c_i], build_indices[c_j]); | |||||
| cloth_brush_add_angular_constraint(ss, | |||||
| data->cloth_sim, | |||||
| vd.index, | |||||
| build_indices[c_i], | |||||
| build_indices[c_j], | |||||
| SCULPT_CLOTH_CONSTRAINT_BENDING); | |||||
| } | |||||
| else { | |||||
| printf("SKIPPING %d %d %d\n", vd.index, build_indices[c_i], build_indices[c_j]); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | if (sculpt_brush_test_sq_fn(&test, current_vertex_location) || use_falloff_plane) { | ||||
| cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, force, vd.index); | cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, force, vd.index); | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, | static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, | ||||
| const float cloth_mass, | const float cloth_mass, | ||||
| const float cloth_damping) | const float cloth_damping, | ||||
| const float cloth_tension, | |||||
| const float cloth_shearing, | |||||
| const float cloth_bending) | |||||
| { | { | ||||
| const int totverts = SCULPT_vertex_count_get(ss); | const int totverts = SCULPT_vertex_count_get(ss); | ||||
| SculptClothSimulation *cloth_sim; | SculptClothSimulation *cloth_sim; | ||||
| cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); | cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); | ||||
| cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) * | cloth_sim->length_constraints = MEM_callocN( | ||||
| CLOTH_LENGTH_CONSTRAINTS_BLOCK, | sizeof(SculptClothLengthConstraint) * CLOTH_CONSTRAINTS_BLOCK, "cloth length constraints"); | ||||
| "cloth length constraints"); | cloth_sim->capacity_length_constraints = CLOTH_CONSTRAINTS_BLOCK; | ||||
| cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; | |||||
| cloth_sim->angular_constraints = MEM_callocN( | |||||
| sizeof(SculptClothLengthConstraint) * CLOTH_CONSTRAINTS_BLOCK, "cloth length constraints"); | |||||
| cloth_sim->capacity_angular_constraints = CLOTH_CONSTRAINTS_BLOCK; | |||||
| cloth_sim->acceleration = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim acceleration"); | 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->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->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->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_constraint_tweak = MEM_callocN(sizeof(float) * totverts, | ||||
| "cloth sim length tweak"); | "cloth sim length tweak"); | ||||
| cloth_sim->mass = cloth_mass; | cloth_sim->mass = cloth_mass; | ||||
| cloth_sim->damping = cloth_damping; | cloth_sim->damping = cloth_damping; | ||||
| cloth_sim->tension = cloth_tension; | |||||
| cloth_sim->shearing = cloth_shearing; | |||||
| cloth_sim->bending = cloth_bending; | |||||
| return cloth_sim; | return cloth_sim; | ||||
| } | } | ||||
| static void do_cloth_brush_solve_simulation_task_cb_ex( | static void do_cloth_brush_solve_simulation_task_cb_ex( | ||||
| void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) | void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = userdata; | ||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | static void cloth_brush_build_nodes_constraints(Sculpt *sd, | ||||
| /* Currently all constrains are added to the same global array which can't be accessed from | /* Currently all constrains are added to the same global array which can't be accessed from | ||||
| * different threads. */ | * different threads. */ | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, false, totnode); | BKE_pbvh_parallel_range_settings(&settings, false, totnode); | ||||
| cloth_sim->created_length_constraints = BLI_gset_new( | cloth_sim->created_length_constraints = BLI_gset_new( | ||||
| BLI_ghashutil_inthash_v2_p, BLI_ghashutil_inthash_v2_cmp, "created length constraints"); | BLI_ghashutil_inthash_v2_p, BLI_ghashutil_inthash_v2_cmp, "created length constraints"); | ||||
| cloth_sim->created_angular_constraints = BLI_edgeset_new("created angular constraints"); | |||||
| SculptThreadedTaskData build_constraints_data = { | SculptThreadedTaskData build_constraints_data = { | ||||
| .sd = sd, | .sd = sd, | ||||
| .ob = ob, | .ob = ob, | ||||
| .brush = brush, | .brush = brush, | ||||
| .nodes = nodes, | .nodes = nodes, | ||||
| .cloth_sim = cloth_sim, | .cloth_sim = cloth_sim, | ||||
| .cloth_sim_initial_location = initial_location, | .cloth_sim_initial_location = initial_location, | ||||
| .cloth_sim_radius = radius, | .cloth_sim_radius = radius, | ||||
| }; | }; | ||||
| BLI_task_parallel_range( | BLI_task_parallel_range( | ||||
| 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings); | 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings); | ||||
| BLI_gset_free(cloth_sim->created_length_constraints, NULL); | BLI_gset_free(cloth_sim->created_length_constraints, NULL); | ||||
| BLI_edgeset_free(cloth_sim->created_angular_constraints); | |||||
| } | } | ||||
| static void cloth_brush_satisfy_constraints(SculptSession *ss, | static void cloth_brush_satisfy_length_constraint(SculptSession *ss, | ||||
| Brush *brush, | Brush *brush, | ||||
| SculptClothSimulation *cloth_sim) | SculptClothSimulation *cloth_sim, | ||||
| SculptClothLengthConstraint *constraint) | |||||
| { | { | ||||
| 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]; | |||||
| const int v1 = constraint->v1; | const int v1 = constraint->v1; | ||||
| const int v2 = constraint->v2; | const int v2 = constraint->v2; | ||||
| float v1_to_v2[3]; | 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, cloth_sim->pos[v2], cloth_sim->pos[v1]); | ||||
| const float current_distance = len_v3(v1_to_v2); | const float current_distance = len_v3(v1_to_v2); | ||||
| float correction_vector[3]; | float correction_vector[3]; | ||||
| float correction_vector_half[3]; | float correction_vector_half[3]; | ||||
| const float constraint_distance = constraint->length + | const float constraint_distance = constraint->length + | ||||
| (cloth_sim->length_constraint_tweak[v1] * 0.5f) + | (cloth_sim->length_constraint_tweak[v1] * 0.5f) + | ||||
| (cloth_sim->length_constraint_tweak[v2] * 0.5f); | (cloth_sim->length_constraint_tweak[v2] * 0.5f); | ||||
| if (current_distance > 0.0f) { | 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, 1.0f - (constraint_distance / current_distance)); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v3_v3(correction_vector, v1_to_v2); | copy_v3_v3(correction_vector, v1_to_v2); | ||||
| } | } | ||||
| mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f); | mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f); | ||||
| const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) * | const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) * | ||||
| SCULPT_automasking_factor_get(ss, v1); | SCULPT_automasking_factor_get(ss, v1); | ||||
| const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) * | const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) * | ||||
| SCULPT_automasking_factor_get(ss, v2); | SCULPT_automasking_factor_get(ss, v2); | ||||
| const float sim_factor_v1 = ss->cache ? cloth_brush_simulation_falloff_get( | const float sim_factor_v1 = ss->cache ? | ||||
| brush, | cloth_brush_simulation_falloff_get(brush, | ||||
| ss->cache->radius, | ss->cache->radius, | ||||
| ss->cache->initial_location, | ss->cache->initial_location, | ||||
| cloth_sim->init_pos[v1]) : | cloth_sim->init_pos[v1]) : | ||||
| 1.0f; | 1.0f; | ||||
| const float sim_factor_v2 = ss->cache ? cloth_brush_simulation_falloff_get( | const float sim_factor_v2 = ss->cache ? | ||||
| brush, | cloth_brush_simulation_falloff_get(brush, | ||||
| ss->cache->radius, | |||||
| ss->cache->initial_location, | |||||
| cloth_sim->init_pos[v2]) : | |||||
| 1.0f; | |||||
| float constraint_strength = 1.0f; | |||||
| switch (constraint->type) { | |||||
| case SCULPT_CLOTH_CONSTRAINT_TENSION: | |||||
| constraint_strength = cloth_sim->tension; | |||||
| break; | |||||
| case SCULPT_CLOTH_CONSTRAINT_SHEARING: | |||||
| constraint_strength = cloth_sim->shearing; | |||||
| break; | |||||
| case SCULPT_CLOTH_CONSTRAINT_BENDING: | |||||
| constraint_strength = cloth_sim->bending; | |||||
| break; | |||||
| } | |||||
| madd_v3_v3fl(cloth_sim->pos[v1], | |||||
| correction_vector_half, | |||||
| 1.0f * mask_v1 * sim_factor_v1 * constraint_strength); | |||||
| madd_v3_v3fl(cloth_sim->pos[v2], | |||||
| correction_vector_half, | |||||
| -1.0f * mask_v2 * sim_factor_v2 * constraint_strength); | |||||
| } | |||||
| static void cloth_brush_satisfy_angular_constraint(SculptSession *ss, | |||||
| Brush *brush, | |||||
| SculptClothSimulation *cloth_sim, | |||||
| SculptClothAngularConstraint *constraint) | |||||
| { | |||||
| const int v1 = constraint->v1; | |||||
| const int v2 = constraint->v2; | |||||
| const int origin = constraint->origin; | |||||
| float *v1_co = cloth_sim->pos[v1]; | |||||
| float *v2_co = cloth_sim->pos[v2]; | |||||
| float *origin_co = cloth_sim->pos[origin]; | |||||
| float normal[3]; | |||||
| float normal_t1[3]; | |||||
| float normal_t2[3]; | |||||
| normal_tri_v3(normal_t1, origin_co, v1_co, cloth_sim->pos[constraint->v1_tri]); | |||||
| normal_tri_v3(normal_t2, origin_co, cloth_sim->pos[constraint->v1_tri], v2_co); | |||||
| mid_v3_v3v3(normal, normal_t1, normal_t2); | |||||
| normalize_v3(normal); | |||||
| /* | |||||
| printf("normal tindex %d\n", constraint->v1_tri); | |||||
| print_v3("normal t1", normal_t1); | |||||
| print_v3("normal t2", normal_t2); | |||||
| print_v3("normal", normal); | |||||
| */ | |||||
| float plane[4]; | |||||
| plane_from_point_normal_v3(plane, origin_co, normal); | |||||
| float current_angle = angle_normalized_v3v3(normal_t1, normal_t2); | |||||
| const float correction_angle = current_angle - constraint->angle; | |||||
| float correction_angle_half = correction_angle * 0.5f; | |||||
| //if (dot_v3v3(normal_t1, normal_t2) < 0.0f) { | |||||
| if (current_angle > 0.0f || true) { | |||||
| float rotation_axis_v1[3]; | |||||
| float rotation_axis_v2[3]; | |||||
| float d_v1[3], d_v2[3]; | |||||
| sub_v3_v3v3(d_v1, v1_co, origin_co); | |||||
| sub_v3_v3v3(d_v2, v2_co, origin_co); | |||||
| normalize_v3(d_v1); | |||||
| normalize_v3(d_v2); | |||||
| cross_v3_v3v3_hi_prec(rotation_axis_v1, normal, d_v1); | |||||
| normalize_v3(rotation_axis_v1); | |||||
| cross_v3_v3v3_hi_prec(rotation_axis_v2, normal, d_v2); | |||||
| normalize_v3(rotation_axis_v2); | |||||
| /* | |||||
| if (plane_point_side_v3(plane, v1_co) > 0.0f){ | |||||
| correction_angle_half *= -1.0f; | |||||
| } | |||||
| */ | |||||
| if (dot_v3v3(d_v1, normal_t2) > 0.0f){ | |||||
| correction_angle_half *= -1.0f; | |||||
| } | |||||
| if (correction_angle_half > 0.0f) { | |||||
| printf("POSITIVE\n"); | |||||
| } | |||||
| else { | |||||
| printf("NEGATIVE\n"); | |||||
| } | |||||
| const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) * | |||||
| SCULPT_automasking_factor_get(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->radius, | ||||
| ss->cache->initial_location, | ss->cache->initial_location, | ||||
| cloth_sim->init_pos[v2]) : | cloth_sim->init_pos[v2]) : | ||||
| 1.0f; | 1.0f; | ||||
| madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, 1.0f * mask_v1 * sim_factor_v1); | float constraint_strength = 1.0f; | ||||
| madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, -1.0f * mask_v2 * sim_factor_v2); | |||||
| switch (constraint->type) { | |||||
| case SCULPT_CLOTH_CONSTRAINT_TENSION: | |||||
| case SCULPT_CLOTH_CONSTRAINT_SHEARING: | |||||
| break; | |||||
| case SCULPT_CLOTH_CONSTRAINT_BENDING: | |||||
| constraint_strength = cloth_sim->bending; | |||||
| break; | |||||
| } | |||||
| float current_pos_v1[3], current_pos_v2[3]; | |||||
| float new_pos_v1[3], new_pos_v2[3]; | |||||
| copy_v3_v3(current_pos_v1, v1_co); | |||||
| copy_v3_v3(current_pos_v2, v2_co); | |||||
| sub_v3_v3(current_pos_v1, origin_co); | |||||
| sub_v3_v3(current_pos_v2, origin_co); | |||||
| if (constraint->flip_rotation) { | |||||
| // correction_angle_half *= -1.0f; | |||||
| } | |||||
| sub_v3_v3v3(rotation_axis_v1, origin_co, cloth_sim->pos[constraint->v1_tri]); | |||||
| normalize_v3(rotation_axis_v1); | |||||
| correction_angle_half *= -1.0f; | |||||
| rotate_v3_v3v3fl(new_pos_v1, | |||||
| current_pos_v1, | |||||
| rotation_axis_v1, | |||||
| -correction_angle_half * mask_v1 * sim_factor_v1 * constraint_strength); | |||||
| rotate_v3_v3v3fl(new_pos_v2, | |||||
| current_pos_v2, | |||||
| rotation_axis_v1, | |||||
| correction_angle_half * mask_v2 * sim_factor_v2 * constraint_strength); | |||||
| /* | |||||
| madd_v3_v3v3fl(new_pos_v1, current_pos_v1, normal_t1, correction_angle_half * mask_v1 * sim_factor_v1 * constraint_strength); | |||||
| madd_v3_v3v3fl(new_pos_v2, current_pos_v2, normal_t2, correction_angle_half * mask_v2 * sim_factor_v2 * constraint_strength); | |||||
| */ | |||||
| /* | |||||
| mul_v3_fl(normal_t1, correction_angle_half * mask_v1 * sim_factor_v1 * constraint_strength); | |||||
| mul_v3_fl(normal_t2, correction_angle_half * mask_v2 * sim_factor_v2 * constraint_strength); | |||||
| */ | |||||
| /* | |||||
| cloth_brush_apply_force_to_vertex(NULL, cloth_sim, normal_t1, v1); | |||||
| cloth_brush_apply_force_to_vertex(NULL, cloth_sim, normal_t2, v2); | |||||
| */ | |||||
| add_v3_v3v3(cloth_sim->pos[v1], new_pos_v1, origin_co); | |||||
| add_v3_v3v3(cloth_sim->pos[v2], new_pos_v2, origin_co); | |||||
| } | |||||
| } | |||||
| static void cloth_brush_satisfy_constraints(SculptSession *ss, | |||||
| Brush *brush, | |||||
| SculptClothSimulation *cloth_sim) | |||||
| { | |||||
| for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) { | |||||
| for (int i = 0; i < cloth_sim->tot_angular_constraints; i++) { | |||||
| SculptClothAngularConstraint *constraint = &cloth_sim->angular_constraints[i]; | |||||
| cloth_brush_satisfy_angular_constraint(ss, brush, cloth_sim, constraint); | |||||
| } | |||||
| } | |||||
| for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) { | |||||
| for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { | |||||
| SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i]; | |||||
| cloth_brush_satisfy_length_constraint(ss, brush, cloth_sim, constraint); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void cloth_brush_do_simulation_step( | static void cloth_brush_do_simulation_step( | ||||
| Sculpt *sd, Object *ob, SculptClothSimulation *cloth_sim, PBVHNode **nodes, int totnode) | Sculpt *sd, Object *ob, SculptClothSimulation *cloth_sim, PBVHNode **nodes, int totnode) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| ▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | 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 | /* In the first brush step of each symmetry pass, build the constraints for the vertices in all | ||||
| * nodes inside the simulation's limits. */ | * 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 | /* Brush stroke types that restore the mesh on each brush step also need the cloth sim data to be | ||||
| * created on each step. */ | * created on each step. */ | ||||
| if (ss->cache->first_time || !ss->cache->cloth_sim) { | if (ss->cache->first_time || !ss->cache->cloth_sim) { | ||||
| /* The simulation structure only needs to be created on the first symmetry pass. */ | /* The simulation structure only needs to be created on the first symmetry pass. */ | ||||
| if (ss->cache->mirror_symmetry_pass == 0) { | if (ss->cache->mirror_symmetry_pass == 0) { | ||||
| ss->cache->cloth_sim = cloth_brush_simulation_create( | ss->cache->cloth_sim = cloth_brush_simulation_create(ss, | ||||
| ss, brush->cloth_mass, brush->cloth_damping); | brush->cloth_mass, | ||||
| brush->cloth_damping, | |||||
| brush->cloth_tension, | |||||
| brush->cloth_shearing, | |||||
| brush->cloth_bending); | |||||
| for (int i = 0; i < totverts; i++) { | 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->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)); | copy_v3_v3(ss->cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); | ||||
| } | } | ||||
| } | } | ||||
| /* Build the constraints. */ | /* Build the constraints. */ | ||||
| const float radius = ss->cache->initial_radius; | const float radius = ss->cache->initial_radius; | ||||
| ▲ Show 20 Lines • Show All 250 Lines • ▼ Show 20 Lines | static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| /* Needs mask data to be available as it is used when solving the constraints. */ | /* 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); | ||||
| SCULPT_undo_push_begin("Cloth filter"); | SCULPT_undo_push_begin("Cloth filter"); | ||||
| SCULPT_filter_cache_init(ob, sd); | SCULPT_filter_cache_init(ob, sd); | ||||
| const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass"); | const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass"); | ||||
| const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); | 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, cloth_mass, cloth_damping, 1.0f, 1.0f, 1.0f); | |||||
| copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss)); | copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss)); | ||||
| const int totverts = SCULPT_vertex_count_get(ss); | const int totverts = SCULPT_vertex_count_get(ss); | ||||
| for (int i = 0; i < totverts; i++) { | for (int i = 0; i < totverts; i++) { | ||||
| copy_v3_v3(ss->filter_cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); | copy_v3_v3(ss->filter_cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); | ||||
| copy_v3_v3(ss->filter_cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); | copy_v3_v3(ss->filter_cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 68 Lines • Show Last 20 Lines | |||||