diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 120fe968bdf..9dd0002c714 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -515,6 +515,46 @@ int vert_face_set_get(const SculptSession &ss, PBVHVertRef vertex) return 0; } +bool vert_has_face_set(const GroupedSpan vert_to_face_map, + const int *face_sets, + const int vert, + const int face_set) +{ + if (!face_sets) { + return face_set == SCULPT_FACE_SET_NONE; + } + const Span faces = vert_to_face_map[vert]; + return std::any_of( + faces.begin(), faces.end(), [&](const int face) { return face_sets[face] == face_set; }); +} + +bool vert_has_face_set(const SubdivCCG &subdiv_ccg, + const int *face_sets, + const int grid, + const int face_set) +{ + if (!face_sets) { + return face_set == SCULPT_FACE_SET_NONE; + } + const int face = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grid); + return face_sets[face] == face_set; +} + +bool vert_has_face_set(const int face_set_offset, const BMVert &vert, const int face_set) +{ + if (face_set_offset == -1) { + return false; + } + BMIter iter; + BMFace *face; + BM_ITER_ELEM (face, &iter, &const_cast(vert), BM_FACES_OF_VERT) { + if (BM_ELEM_CD_GET_INT(face, face_set_offset) == face_set) { + return true; + } + } + return false; +} + bool vert_has_face_set(const SculptSession &ss, PBVHVertRef vertex, int face_set) { switch (ss.pbvh->type()) { diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 68236880bfc..60d296b82f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -769,7 +769,9 @@ static void calc_constraint_factors(const Object &object, tls.factors.resize(verts.size()); const MutableSpan factors = tls.factors; fill_factor_from_hide_and_mask(mesh, verts, factors); - auto_mask::calc_vert_factors(object, *automasking, *nodes[i], verts, factors); + if (automasking) { + auto_mask::calc_vert_factors(object, *automasking, *nodes[i], verts, factors); + } if (ss.cache) { const MutableSpan positions = gather_data_mesh(init_positions, verts, tls.positions); calc_brush_simulation_falloff( @@ -791,7 +793,9 @@ static void calc_constraint_factors(const Object &object, tls.factors.resize(grid_verts_num); const MutableSpan factors = tls.factors; fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors); - auto_mask::calc_grids_factors(object, *automasking, *nodes[i], grids, factors); + if (automasking) { + auto_mask::calc_grids_factors(object, *automasking, *nodes[i], grids, factors); + } if (ss.cache) { tls.positions.resize(grid_verts_num); const MutableSpan positions = tls.positions; @@ -813,7 +817,9 @@ static void calc_constraint_factors(const Object &object, tls.factors.resize(verts.size()); const MutableSpan factors = tls.factors; fill_factor_from_hide_and_mask(bm, verts, factors); - auto_mask::calc_vert_factors(object, *automasking, *nodes[i], verts, factors); + if (automasking) { + auto_mask::calc_vert_factors(object, *automasking, *nodes[i], verts, factors); + } if (ss.cache) { tls.positions.resize(verts.size()); const MutableSpan positions = tls.positions; @@ -1485,107 +1491,283 @@ static void cloth_filter_apply_forces_to_vertices(const int v_index, cloth_brush_apply_force_to_vertex(*filter_cache.cloth_sim, final_force, v_index); } -static void cloth_filter_apply_forces_task(Object &ob, - const Sculpt &sd, - const ClothFilterType filter_type, - const float filter_strength, - bke::pbvh::Node *node) +BLI_NOINLINE static void apply_gravity_to_verts(const float3 &gravity, + const Span verts, + const Span factors, + filter::Cache &filter_cache) { - SculptSession &ss = *ob.sculpt; - - SimulationData &cloth_sim = *ss.filter_cache->cloth_sim; - - const bool is_deformation_filter = cloth_filter_is_deformation_filter(filter_type); - - float3 sculpt_gravity(0.0f); - if (sd.gravity_object) { - sculpt_gravity = sd.gravity_object->object_to_world().ptr()[2]; - } - else { - sculpt_gravity[2] = -1.0f; - } - sculpt_gravity *= sd.gravity_factor * filter_strength; - auto_mask::NodeData automask_data = auto_mask::node_begin( - ob, auto_mask::active_cache_get(ss), *node); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) { - auto_mask::node_update(automask_data, vd); - - float fade = vd.mask; - fade *= auto_mask::factor_get( - ss.filter_cache->automasking.get(), ss, vd.vertex, &automask_data); - fade = 1.0f - fade; + for (const int i : verts.index_range()) { + const int vert = verts[i]; float3 force(0.0f); - float3 disp, temp; - - if (ss.filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { - if (!face_set::vert_has_face_set(ss, vd.vertex, ss.filter_cache->active_face_set)) { - continue; - } - } - - switch (filter_type) { - case ClothFilterType::Gravity: - if (ss.filter_cache->orientation == filter::FilterOrientation::View) { - /* When using the view orientation apply gravity in the -Y axis, this way objects will - * fall down instead of backwards. */ - force[1] = -filter_strength * fade; - } - else { - force[2] = -filter_strength * fade; - } - force = filter::to_object_space(*ss.filter_cache, force); - break; - case ClothFilterType::Inflate: { - float3 normal = SCULPT_vertex_normal_get(ss, vd.vertex); - force = normal * fade * filter_strength; - break; - } - case ClothFilterType::Expand: - cloth_sim.length_constraint_tweak[vd.index] += fade * filter_strength * 0.01f; - force = float3(0); - break; - case ClothFilterType::Pinch: - force = math::normalize(ss.filter_cache->cloth_sim_pinch_point - float3(vd.co)); - force *= fade * filter_strength; - break; - case ClothFilterType::Scale: { - float3x3 transform = math::from_scale(float3(1.0f + fade * filter_strength)); - temp = cloth_sim.init_pos[vd.index]; - temp = transform * temp; - disp = temp - cloth_sim.init_pos[vd.index]; - force = float3(0); - break; - } - } - - if (is_deformation_filter) { - cloth_filter_apply_displacement_to_deform_co(vd.index, disp, *ss.filter_cache); + if (filter_cache.orientation == filter::FilterOrientation::View) { + /* When using the view orientation apply gravity in the -Y axis, this way objects will + * fall down instead of backwards. */ + force[1] = -factors[i]; } else { - cloth_filter_apply_forces_to_vertices(vd.index, force, sculpt_gravity, *ss.filter_cache); + force[2] = -factors[i]; + } + force = filter::to_object_space(filter_cache, force); + cloth_filter_apply_forces_to_vertices(vert, force, gravity, filter_cache); + } +} + +BLI_NOINLINE static void apply_inflate_to_verts(const float3 &gravity, + const Span verts, + const Span factors, + const Span normals, + filter::Cache &filter_cache) +{ + for (const int i : verts.index_range()) { + const int vert = verts[i]; + const float3 force = normals[i] * factors[i]; + cloth_filter_apply_forces_to_vertices(vert, force, gravity, filter_cache); + } +} + +BLI_NOINLINE static void apply_expand_to_verts(const float3 &gravity, + const Span verts, + const Span factors, + filter::Cache &filter_cache) +{ + MutableSpan length_constraint_tweak = filter_cache.cloth_sim->length_constraint_tweak; + for (const int i : verts.index_range()) { + const int vert = verts[i]; + length_constraint_tweak[vert] += factors[i] * 0.01f; + cloth_filter_apply_forces_to_vertices(vert, float3(0), gravity, filter_cache); + } +} + +BLI_NOINLINE static void apply_pinch_to_verts(const float3 &gravity, + const Span verts, + const Span factors, + const Span positions, + filter::Cache &filter_cache) +{ + for (const int i : verts.index_range()) { + const int vert = verts[i]; + float3 force = math::normalize(filter_cache.cloth_sim_pinch_point - positions[i]); + force *= factors[i]; + cloth_filter_apply_forces_to_vertices(vert, force, gravity, filter_cache); + } +} + +BLI_NOINLINE static void apply_scale_to_verts(const Span verts, + const Span factors, + filter::Cache &filter_cache) +{ + MutableSpan init_pos = filter_cache.cloth_sim->init_pos; + for (const int i : verts.index_range()) { + const int vert = verts[i]; + float3x3 transform = math::from_scale(float3(1.0f + factors[i])); + float3 disp = transform * init_pos[vert] - init_pos[vert]; + cloth_filter_apply_displacement_to_deform_co(vert, disp, filter_cache); + } +} + +struct FilterLocalData { + Vector factors; + Vector vert_indices; + Vector positions; + Vector normals; +}; + +static void apply_filter_forces_mesh(const ClothFilterType filter_type, + const float filter_strength, + const float3 &gravity, + const Span positions_eval, + const Span vert_normals, + const bke::pbvh::Node &node, + Object &object, + FilterLocalData &tls) +{ + const SculptSession &ss = *object.sculpt; + const Mesh &mesh = *static_cast(object.data); + + const Span verts = bke::pbvh::node_unique_verts(node); + + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide_and_mask(mesh, verts, factors); + if (const auto_mask::Cache *automasking = auto_mask::active_cache_get(ss)) { + auto_mask::calc_vert_factors(object, *automasking, node, verts, factors); + } + + if (ss.filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { + for (const int i : verts.index_range()) { + const int vert = verts[i]; + if (!face_set::vert_has_face_set( + ss.vert_to_face_map, ss.face_sets, vert, ss.filter_cache->active_face_set)) + { + factors[i] = 0.0f; + } } } - BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_positions_update(node); + scale_factors(factors, filter_strength); + + switch (filter_type) { + case ClothFilterType::Gravity: + apply_gravity_to_verts(gravity, verts, factors, *ss.filter_cache); + break; + case ClothFilterType::Inflate: + apply_inflate_to_verts(gravity, + verts, + factors, + gather_data_mesh(vert_normals, verts, tls.normals), + *ss.filter_cache); + break; + case ClothFilterType::Expand: + apply_expand_to_verts(gravity, verts, factors, *ss.filter_cache); + break; + case ClothFilterType::Pinch: + apply_pinch_to_verts(gravity, + verts, + factors, + gather_data_mesh(positions_eval, verts, tls.positions), + *ss.filter_cache); + break; + case ClothFilterType::Scale: + apply_scale_to_verts(verts, factors, *ss.filter_cache); + break; + } +} + +static void apply_filter_forces_grids(const ClothFilterType filter_type, + const float filter_strength, + const float3 &gravity, + const bke::pbvh::Node &node, + Object &object, + FilterLocalData &tls) +{ + const SculptSession &ss = *object.sculpt; + const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg; + const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg); + + const Span grids = bke::pbvh::node_grid_indices(node); + const int grid_verts_num = grids.size() * key.grid_area; + + tls.factors.resize(grid_verts_num); + const MutableSpan factors = tls.factors; + fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors); + if (const auto_mask::Cache *automasking = auto_mask::active_cache_get(ss)) { + auto_mask::calc_grids_factors(object, *automasking, node, grids, factors); + } + + if (ss.filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { + for (const int i : grids.index_range()) { + if (!face_set::vert_has_face_set( + subdiv_ccg, ss.face_sets, grids[i], ss.filter_cache->active_face_set)) + { + factors.slice(i * key.grid_area, key.grid_area).fill(0.0f); + } + } + } + + scale_factors(factors, filter_strength); + + const Span verts = calc_vert_indices_grids(key, grids, tls.vert_indices); + + switch (filter_type) { + case ClothFilterType::Gravity: + apply_gravity_to_verts(gravity, verts, factors, *ss.filter_cache); + break; + case ClothFilterType::Inflate: + tls.normals.resize(grid_verts_num); + gather_grids_normals(subdiv_ccg, grids, tls.normals); + apply_inflate_to_verts(gravity, verts, factors, tls.normals, *ss.filter_cache); + break; + case ClothFilterType::Expand: + apply_expand_to_verts(gravity, verts, factors, *ss.filter_cache); + break; + case ClothFilterType::Pinch: { + apply_pinch_to_verts(gravity, + verts, + factors, + gather_grids_positions(subdiv_ccg, grids, tls.positions), + *ss.filter_cache); + break; + } + case ClothFilterType::Scale: + apply_scale_to_verts(verts, factors, *ss.filter_cache); + break; + } +} + +static void apply_filter_forces_bmesh(const ClothFilterType filter_type, + const float filter_strength, + const float3 &gravity, + bke::pbvh::Node &node, + Object &object, + FilterLocalData &tls) +{ + const SculptSession &ss = *object.sculpt; + const BMesh &bm = *ss.bm; + + const Set &verts = BKE_pbvh_bmesh_node_unique_verts(&node); + + tls.factors.resize(verts.size()); + const MutableSpan factors = tls.factors; + fill_factor_from_hide_and_mask(bm, verts, factors); + if (const auto_mask::Cache *automasking = auto_mask::active_cache_get(ss)) { + auto_mask::calc_vert_factors(object, *automasking, node, verts, factors); + } + + if (ss.filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { + const int face_set_offset = CustomData_get_offset_named( + &bm.pdata, CD_PROP_INT32, ".sculpt_face_set"); + int i = 0; + for (const BMVert *vert : verts) { + if (!face_set::vert_has_face_set(face_set_offset, *vert, ss.filter_cache->active_face_set)) { + factors[i] = 0.0f; + } + i++; + } + } + + scale_factors(factors, filter_strength); + + const Span vert_indices = calc_vert_indices_bmesh(verts, tls.vert_indices); + + switch (filter_type) { + case ClothFilterType::Gravity: + apply_gravity_to_verts(gravity, vert_indices, factors, *ss.filter_cache); + break; + case ClothFilterType::Inflate: + tls.normals.resize(verts.size()); + gather_bmesh_normals(verts, tls.normals); + apply_inflate_to_verts(gravity, vert_indices, factors, tls.normals, *ss.filter_cache); + break; + case ClothFilterType::Expand: + apply_expand_to_verts(gravity, vert_indices, factors, *ss.filter_cache); + break; + case ClothFilterType::Pinch: + apply_pinch_to_verts(gravity, + vert_indices, + factors, + gather_bmesh_positions(verts, tls.positions), + *ss.filter_cache); + break; + case ClothFilterType::Scale: + apply_scale_to_verts(vert_indices, factors, *ss.filter_cache); + break; + } } static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Object &ob = *CTX_data_active_object(C); + Object &object = *CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - SculptSession &ss = *ob.sculpt; + SculptSession &ss = *object.sculpt; const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); + const ClothFilterType filter_type = ClothFilterType(RNA_enum_get(op->ptr, "type")); float filter_strength = RNA_float_get(op->ptr, "strength"); if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { MEM_delete(ss.filter_cache); ss.filter_cache = nullptr; - undo::push_end(ob); - flush_update_done(C, ob, UpdateType::Position); + undo::push_end(object); + flush_update_done(C, object, UpdateType::Position); return OPERATOR_FINISHED; } @@ -1598,28 +1780,68 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent SCULPT_vertex_random_access_ensure(ss); - BKE_sculpt_update_object_for_edit(depsgraph, &ob, false); + BKE_sculpt_update_object_for_edit(depsgraph, &object, false); brush_store_simulation_state(ss, *ss.filter_cache->cloth_sim); const Span nodes = ss.filter_cache->nodes; - threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { - for (const int i : range) { - cloth_filter_apply_forces_task( - ob, sd, ClothFilterType(filter_type), filter_strength, nodes[i]); + float3 gravity(0.0f); + if (sd.gravity_object) { + gravity = sd.gravity_object->object_to_world().ptr()[2]; + } + else { + gravity[2] = -1.0f; + } + gravity *= sd.gravity_factor * filter_strength; + + threading::EnumerableThreadSpecific all_tls; + switch (ss.pbvh->type()) { + case bke::pbvh::Type::Mesh: { + const bke::pbvh::Tree &pbvh = *ss.pbvh; + const Span positions_eval = BKE_pbvh_get_vert_positions(pbvh); + const Span vert_normals = BKE_pbvh_get_vert_normals(pbvh); + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + FilterLocalData &tls = all_tls.local(); + for (const int i : range) { + apply_filter_forces_mesh(filter_type, + filter_strength, + gravity, + positions_eval, + vert_normals, + *nodes[i], + object, + tls); + BKE_pbvh_node_mark_positions_update(nodes[i]); + } + }); + break; } - }); + case bke::pbvh::Type::Grids: + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + FilterLocalData &tls = all_tls.local(); + for (const int i : range) { + apply_filter_forces_grids(filter_type, filter_strength, gravity, *nodes[i], object, tls); + BKE_pbvh_node_mark_positions_update(nodes[i]); + } + }); + break; + case bke::pbvh::Type::BMesh: + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + FilterLocalData &tls = all_tls.local(); + for (const int i : range) { + apply_filter_forces_bmesh(filter_type, filter_strength, gravity, *nodes[i], object, tls); + BKE_pbvh_node_mark_positions_update(nodes[i]); + } + }); + break; + } /* Activate all nodes. */ sim_activate_nodes(*ss.filter_cache->cloth_sim, nodes); /* Update and write the simulation to the nodes. */ - do_simulation_step(sd, ob, *ss.filter_cache->cloth_sim, nodes); - - for (bke::pbvh::Node *node : nodes) { - BKE_pbvh_node_mark_positions_update(node); - } + do_simulation_step(sd, object, *ss.filter_cache->cloth_sim, nodes); flush_update_step(C, UpdateType::Position); return OPERATOR_RUNNING_MODAL; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 9e3dc95e8cd..efe33f462b4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -967,6 +967,15 @@ int active_face_set_get(const SculptSession &ss); int vert_face_set_get(const SculptSession &ss, PBVHVertRef vertex); bool vert_has_face_set(const SculptSession &ss, PBVHVertRef vertex, int face_set); +bool vert_has_face_set(const GroupedSpan vert_to_face_map, + const int *face_sets, + const int vert, + const int face_set); +bool vert_has_face_set(const SubdivCCG &subdiv_ccg, + const int *face_sets, + const int grid, + const int face_set); +bool vert_has_face_set(const int face_set_offset, const BMVert &vert, const int face_set); bool vert_has_unique_face_set(const SculptSession &ss, PBVHVertRef vertex); bool vert_has_unique_face_set(const GroupedSpan vert_to_face_map, const int *face_sets,