Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_cloth.c
| Show First 20 Lines • Show All 499 Lines • ▼ Show 20 Lines | if (cmd && cmd->bvhtree) { | ||||
| collision_move_object(cmd, 1.0, 0.0, true); | collision_move_object(cmd, 1.0, 0.0, true); | ||||
| BLI_addtail(cache, col); | BLI_addtail(cache, col); | ||||
| } | } | ||||
| } | } | ||||
| DEG_OBJECT_ITER_END; | DEG_OBJECT_ITER_END; | ||||
| return cache; | return cache; | ||||
| } | } | ||||
| static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, | |||||
| Brush *brush, | |||||
| const float cloth_mass, | |||||
| const float cloth_damping, | |||||
| const bool use_collisions) | |||||
| { | |||||
| const int totverts = SCULPT_vertex_count_get(ss); | |||||
| SculptClothSimulation *cloth_sim; | |||||
| cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); | |||||
| cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) * | |||||
| CLOTH_LENGTH_CONSTRAINTS_BLOCK, | |||||
| "cloth length constraints"); | |||||
| cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; | |||||
| cloth_sim->acceleration = MEM_calloc_arrayN( | |||||
| totverts, sizeof(float[3]), "cloth sim acceleration"); | |||||
| cloth_sim->pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim pos"); | |||||
| cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim prev pos"); | |||||
| cloth_sim->last_iteration_pos = MEM_calloc_arrayN( | |||||
| totverts, sizeof(float[3]), "cloth sim last iteration pos"); | |||||
| cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "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, sizeof(float[3]), "cloth sim deformation positions"); | |||||
| } | |||||
| cloth_sim->mass = cloth_mass; | |||||
| cloth_sim->damping = cloth_damping; | |||||
| if (use_collisions) { | |||||
| cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); | |||||
| } | |||||
| return cloth_sim; | |||||
| } | |||||
| typedef struct ClothBrushCollision { | typedef struct ClothBrushCollision { | ||||
| CollisionModifierData *col_data; | CollisionModifierData *col_data; | ||||
| struct IsectRayPrecalc isect_precalc; | struct IsectRayPrecalc isect_precalc; | ||||
| } ClothBrushCollision; | } ClothBrushCollision; | ||||
| static void cloth_brush_collision_cb(void *userdata, | static void cloth_brush_collision_cb(void *userdata, | ||||
| int index, | int index, | ||||
| const BVHTreeRay *ray, | const BVHTreeRay *ray, | ||||
| ▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | if (sim_factor > 0.0f) { | ||||
| if (vd.mvert) { | if (vd.mvert) { | ||||
| vd.mvert->flag |= ME_VERT_PBVH_UPDATE; | vd.mvert->flag |= ME_VERT_PBVH_UPDATE; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| 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); | |||||
| /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of | |||||
| * storing the constraints per node. */ | |||||
| /* Currently all constrains are added to the same global array which can't be accessed from | |||||
| * different threads. */ | |||||
| 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, | |||||
| .brush = brush, | |||||
| .nodes = nodes, | |||||
| .cloth_sim = cloth_sim, | |||||
| .cloth_sim_initial_location = initial_location, | |||||
| .cloth_sim_radius = radius, | |||||
| }; | |||||
| 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, | static void cloth_brush_satisfy_constraints(SculptSession *ss, | ||||
| Brush *brush, | Brush *brush, | ||||
| SculptClothSimulation *cloth_sim) | SculptClothSimulation *cloth_sim) | ||||
| { | { | ||||
| for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) { | for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) { | ||||
| for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { | for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { | ||||
| const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i]; | const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i]; | ||||
| ▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range( | BLI_task_parallel_range( | ||||
| 0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings); | 0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings); | ||||
| } | } | ||||
| /* Public functions. */ | /* 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 int totverts = SCULPT_vertex_count_get(ss); | |||||
| SculptClothSimulation *cloth_sim; | |||||
| cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); | |||||
| cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) * | |||||
| CLOTH_LENGTH_CONSTRAINTS_BLOCK, | |||||
| "cloth length constraints"); | |||||
| cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; | |||||
| cloth_sim->acceleration = MEM_calloc_arrayN( | |||||
| totverts, sizeof(float[3]), "cloth sim acceleration"); | |||||
| cloth_sim->pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim pos"); | |||||
| cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "cloth sim prev pos"); | |||||
| cloth_sim->last_iteration_pos = MEM_calloc_arrayN( | |||||
| totverts, sizeof(float[3]), "cloth sim last iteration pos"); | |||||
| cloth_sim->init_pos = MEM_calloc_arrayN(totverts, sizeof(float[3]), "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, sizeof(float[3]), "cloth sim deformation positions"); | |||||
| } | |||||
| cloth_sim->mass = cloth_mass; | |||||
| cloth_sim->damping = cloth_damping; | |||||
| if (use_collisions) { | |||||
| cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); | |||||
| } | |||||
| return cloth_sim; | |||||
| } | |||||
| void SCULPT_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); | |||||
| /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of | |||||
| * storing the constraints per node. */ | |||||
| /* Currently all constrains are added to the same global array which can't be accessed from | |||||
| * different threads. */ | |||||
| 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, | |||||
| .brush = brush, | |||||
| .nodes = nodes, | |||||
| .cloth_sim = cloth_sim, | |||||
| .cloth_sim_initial_location = initial_location, | |||||
| .cloth_sim_radius = radius, | |||||
| }; | |||||
| 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); | |||||
| } | |||||
| void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation *cloth_sim) | |||||
| { | |||||
| const int totverts = SCULPT_vertex_count_get(ss); | |||||
| const bool has_deformation_pos = cloth_sim->deformation_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)); | |||||
| copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); | |||||
| if (has_deformation_pos) { | |||||
| copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); | |||||
| } | |||||
| } | |||||
| } | |||||
| void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSimulation *cloth_sim) | |||||
| { | |||||
| const int totverts = SCULPT_vertex_count_get(ss); | |||||
| for (int i = 0; i < totverts; i++) { | |||||
| copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); | |||||
| } | |||||
| } | |||||
| /* Main Brush Function. */ | /* Main Brush Function. */ | ||||
| void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) | void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| const int totverts = SCULPT_vertex_count_get(ss); | const int totverts = SCULPT_vertex_count_get(ss); | ||||
| /* 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. */ | ||||
| /* Brushes that use anchored strokes and restore the mesh can't rely on symmetry passes and steps | /* 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 | * count as it is always the first step, so the simulation needs to be created when it does not | ||||
| * exist for this stroke. */ | * exist for this stroke. */ | ||||
| if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) || !ss->cache->cloth_sim) { | 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. */ | /* 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) { | if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) { | ||||
| const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush); | ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( | ||||
| ss->cache->cloth_sim = cloth_brush_simulation_create( | |||||
| ss, | ss, | ||||
| brush, | brush, | ||||
| brush->cloth_mass, | brush->cloth_mass, | ||||
| brush->cloth_damping, | brush->cloth_damping, | ||||
| (brush->flag2 & BRUSH_CLOTH_USE_COLLISION)); | (brush->flag2 & BRUSH_CLOTH_USE_COLLISION)); | ||||
| for (int i = 0; i < totverts; i++) { | SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim); | ||||
| copy_v3_v3(ss->cache->cloth_sim->last_iteration_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->prev_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)); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /* Build the constraints. */ | /* Build the constraints. */ | ||||
| const float radius = ss->cache->initial_radius; | const float radius = ss->cache->initial_radius; | ||||
| const float limit = radius + (radius * brush->cloth_sim_limit); | const float limit = radius + (radius * brush->cloth_sim_limit); | ||||
| cloth_brush_build_nodes_constraints( | SCULPT_cloth_brush_build_nodes_constraints( | ||||
| sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit); | sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, limit); | ||||
| return; | return; | ||||
| } | } | ||||
| /* Store the initial state in the simulation. */ | /* Store the initial state in the simulation. */ | ||||
| for (int i = 0; i < totverts; i++) { | for (int i = 0; i < totverts; i++) { | ||||
| copy_v3_v3(ss->cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); | copy_v3_v3(ss->cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); | ||||
| ▲ Show 20 Lines • Show All 241 Lines • ▼ Show 20 Lines | static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); | BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); | ||||
| SCULPT_undo_push_begin("Cloth filter"); | SCULPT_undo_push_begin("Cloth filter"); | ||||
| SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS); | SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS); | ||||
| 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"); | ||||
| const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions"); | const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions"); | ||||
| ss->filter_cache->cloth_sim = cloth_brush_simulation_create( | ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create( | ||||
| ss, NULL, cloth_mass, cloth_damping, use_collisions); | ss, NULL, cloth_mass, cloth_damping, use_collisions); | ||||
| 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); | SCULPT_cloth_brush_simulation_init(ss, ss->filter_cache->cloth_sim); | ||||
| for (int i = 0; i < totverts; i++) { | |||||
| copy_v3_v3(ss->filter_cache->cloth_sim->last_iteration_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)); | |||||
| } | |||||
| float origin[3] = {0.0f, 0.0f, 0.0f}; | float origin[3] = {0.0f, 0.0f, 0.0f}; | ||||
| cloth_brush_build_nodes_constraints(sd, | SCULPT_cloth_brush_build_nodes_constraints(sd, | ||||
| ob, | ob, | ||||
| ss->filter_cache->nodes, | ss->filter_cache->nodes, | ||||
| ss->filter_cache->totnode, | ss->filter_cache->totnode, | ||||
| ss->filter_cache->cloth_sim, | ss->filter_cache->cloth_sim, | ||||
| origin, | origin, | ||||
| FLT_MAX); | FLT_MAX); | ||||
| const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); | const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); | ||||
| if (use_face_sets) { | if (use_face_sets) { | ||||
| ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss); | ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss); | ||||
| } | } | ||||
| else { | else { | ||||
| ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; | ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 57 Lines • Show Last 20 Lines | |||||