Sculpt: Data oriented refactor for cloth filter force calculation

Part of #118145.
This commit is contained in:
Hans Goudey
2024-08-01 10:18:31 -04:00
parent a6a0eae3e2
commit eb132fe792
3 changed files with 368 additions and 97 deletions

View File

@@ -515,6 +515,46 @@ int vert_face_set_get(const SculptSession &ss, PBVHVertRef vertex)
return 0;
}
bool vert_has_face_set(const GroupedSpan<int> 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<int> 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<BMVert &>(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()) {

View File

@@ -769,7 +769,9 @@ static void calc_constraint_factors(const Object &object,
tls.factors.resize(verts.size());
const MutableSpan<float> 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<float> 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<float3> positions = tls.positions;
@@ -813,7 +817,9 @@ static void calc_constraint_factors(const Object &object,
tls.factors.resize(verts.size());
const MutableSpan<float> 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<float3> 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<int> verts,
const Span<float> 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<float3x3>(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<int> verts,
const Span<float> factors,
const Span<float3> 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<int> verts,
const Span<float> factors,
filter::Cache &filter_cache)
{
MutableSpan<float> 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<int> verts,
const Span<float> factors,
const Span<float3> 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<int> verts,
const Span<float> factors,
filter::Cache &filter_cache)
{
MutableSpan<float3> 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<float3x3>(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<float> factors;
Vector<int> vert_indices;
Vector<float3> positions;
Vector<float3> normals;
};
static void apply_filter_forces_mesh(const ClothFilterType filter_type,
const float filter_strength,
const float3 &gravity,
const Span<float3> positions_eval,
const Span<float3> vert_normals,
const bke::pbvh::Node &node,
Object &object,
FilterLocalData &tls)
{
const SculptSession &ss = *object.sculpt;
const Mesh &mesh = *static_cast<Mesh *>(object.data);
const Span<int> verts = bke::pbvh::node_unique_verts(node);
tls.factors.resize(verts.size());
const MutableSpan<float> 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<int> 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<float> 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<int> 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<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(&node);
tls.factors.resize(verts.size());
const MutableSpan<float> 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<int> 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<bke::pbvh::Node *> 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<FilterLocalData> all_tls;
switch (ss.pbvh->type()) {
case bke::pbvh::Type::Mesh: {
const bke::pbvh::Tree &pbvh = *ss.pbvh;
const Span<float3> positions_eval = BKE_pbvh_get_vert_positions(pbvh);
const Span<float3> 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;

View File

@@ -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<int> 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<int> vert_to_face_map,
const int *face_sets,