Sculpt: Initial data oriented refactor for draw face sets brush

Part of #118145.

Performance after this change results in an 156% speedup. This is
measured on the function `stroke_update_step` on a mesh with 24k verts
across 20k instances of the function being timed. In absolute values,
this represents a 0.09ms speedup, from 0.16ms to 0.25ms.

For BMesh, the brush now takes mask values into consideration by
averaging all of the vert mask values for a face.

Co-authored-by: Hans Goudey <hans@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/123811
This commit is contained in:
Sean Kim
2024-07-03 20:46:36 +02:00
committed by Sean Kim
parent 38681f3a63
commit b2ec10184c
8 changed files with 539 additions and 245 deletions

View File

@@ -118,6 +118,7 @@ set(SRC
brushes/clay_strips.cc
brushes/clay_thumb.cc
brushes/crease.cc
brushes/draw_face_sets.cc
brushes/draw_vector_displacement.cc
brushes/draw.cc
brushes/fill.cc

View File

@@ -0,0 +1,457 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "editors/sculpt_paint/brushes/types.hh"
#include "editors/sculpt_paint/mesh_brush_common.hh"
#include "DNA_brush_types.h"
#include "BKE_mesh.hh"
#include "BKE_pbvh.hh"
#include "BKE_subdiv_ccg.hh"
#include "BLI_array_utils.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_math_base.hh"
#include "BLI_task.h"
#include "BLI_task.hh"
#include "editors/sculpt_paint/sculpt_intern.hh"
namespace blender::ed::sculpt_paint {
inline namespace draw_face_sets_cc {
constexpr float FACE_SET_BRUSH_MIN_FADE = 0.05f;
struct MeshLocalData {
Vector<int> face_indices;
Vector<float3> positions;
Vector<float3> normals;
Vector<float> factors;
Vector<float> distances;
};
static void calc_face_centers(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<float3> vert_positions,
const Span<int> face_indices,
const MutableSpan<float3> positions)
{
BLI_assert(face_indices.size() == positions.size());
for (const int i : face_indices.index_range()) {
positions[i] = bke::mesh::face_center_calc(vert_positions,
corner_verts.slice(faces[face_indices[i]]));
}
}
static void calc_face_normals(const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Span<float3> vert_positions,
const Span<int> face_indices,
const MutableSpan<float3> normals)
{
BLI_assert(face_indices.size() == normals.size());
for (const int i : face_indices.index_range()) {
normals[i] = bke::mesh::face_normal_calc(vert_positions,
corner_verts.slice(faces[face_indices[i]]));
}
}
BLI_NOINLINE static void fill_factor_from_hide_and_mask(const Mesh &mesh,
const Span<int> face_indices,
const MutableSpan<float> r_factors)
{
BLI_assert(face_indices.size() == r_factors.size());
const OffsetIndices<int> faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
/* TODO: Avoid overhead of accessing attributes for every PBVH node. */
const bke::AttributeAccessor attributes = mesh.attributes();
if (const VArray mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point)) {
const VArraySpan span(mask);
for (const int i : face_indices.index_range()) {
const Span<int> face_verts = corner_verts.slice(faces[face_indices[i]]);
const float inv_size = math::rcp(float(face_verts.size()));
float sum = 0.0f;
for (const int vert : face_verts) {
sum += span[vert];
}
r_factors[i] = 1.0f - sum * inv_size;
}
}
else {
r_factors.fill(1.0f);
}
if (const VArray hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Point)) {
const VArraySpan span(hide_poly);
for (const int i : face_indices.index_range()) {
if (span[face_indices[i]]) {
r_factors[i] = 0.0f;
}
}
}
}
BLI_NOINLINE static void apply_face_set(const int face_set_id,
const Span<int> face_indices,
const Span<float> factors,
const MutableSpan<int> face_sets)
{
BLI_assert(face_indices.size() == factors.size());
for (const int i : face_indices.index_range()) {
if (factors[i] > FACE_SET_BRUSH_MIN_FADE) {
face_sets[face_indices[i]] = face_set_id;
}
}
}
static void calc_faces(Object &object,
const Brush &brush,
const float strength,
const int face_set_id,
Span<float3> positions_eval,
const PBVHNode &node,
const Span<int> face_indices,
MeshLocalData &tls,
const MutableSpan<int> face_sets)
{
SculptSession &ss = *object.sculpt;
const StrokeCache &cache = *ss.cache;
Mesh &mesh = *static_cast<Mesh *>(object.data);
const OffsetIndices<int> faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
tls.positions.reinitialize(face_indices.size());
const MutableSpan<float3> face_centers = tls.positions;
calc_face_centers(faces, corner_verts, positions_eval, face_indices, face_centers);
tls.normals.reinitialize(face_indices.size());
const MutableSpan<float3> face_normals = tls.normals;
calc_face_normals(faces, corner_verts, positions_eval, face_indices, face_normals);
tls.factors.reinitialize(face_indices.size());
const MutableSpan<float> factors = tls.factors;
fill_factor_from_hide_and_mask(mesh, face_indices, factors);
filter_region_clip_factors(ss, face_centers, factors);
if (brush.flag & BRUSH_FRONTFACE) {
calc_front_face(cache.view_normal, face_normals, factors);
}
tls.distances.reinitialize(face_indices.size());
const MutableSpan<float> distances = tls.distances;
calc_brush_distances(ss, face_centers, eBrushFalloffShape(brush.falloff_shape), distances);
filter_distances_with_radius(cache.radius, distances, factors);
apply_hardness_to_distances(cache, distances);
calc_brush_strength_factors(cache, brush, distances, factors);
if (cache.automasking) {
const OffsetIndices<int> faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
auto_mask::calc_face_factors(
object, faces, corner_verts, *cache.automasking, node, face_indices, factors);
}
calc_brush_texture_factors(ss, brush, face_centers, factors);
scale_factors(factors, strength);
apply_face_set(face_set_id, face_indices, factors, face_sets);
}
static void do_draw_face_sets_brush_mesh(Object &object,
const Brush &brush,
const Span<PBVHNode *> nodes)
{
const SculptSession &ss = *object.sculpt;
const PBVH &pbvh = *ss.pbvh;
const Span<float3> positions_eval = BKE_pbvh_get_vert_positions(pbvh);
Mesh &mesh = *static_cast<Mesh *>(object.data);
const Span<int> corner_tris = mesh.corner_tri_faces();
bke::SpanAttributeWriter<int> attribute = face_set::ensure_face_sets_mesh(object);
MutableSpan<int> face_sets = attribute.span;
threading::EnumerableThreadSpecific<MeshLocalData> all_tls;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
MeshLocalData &tls = all_tls.local();
for (const int i : range) {
const Span<int> face_indices = bke::pbvh::node_face_indices_calc_mesh(
corner_tris, *nodes[i], tls.face_indices);
undo::push_node(object, nodes[i], undo::Type::FaceSet);
calc_faces(object,
brush,
ss.cache->bstrength,
ss.cache->paint_face_set,
positions_eval,
*nodes[i],
face_indices,
tls,
face_sets);
}
});
attribute.finish();
}
struct GridLocalData {
Vector<int> face_indices;
Vector<float3> positions;
Vector<float> factors;
Vector<float> distances;
};
BLI_NOINLINE static void calc_face_indices_grids(const SubdivCCG &subdiv_ccg,
const Span<int> grids,
const MutableSpan<int> face_indices)
{
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
BLI_assert(grids.size() * key.grid_area == face_indices.size());
for (const int i : grids.index_range()) {
const int start = i * key.grid_area;
for (const int offset : IndexRange(key.grid_area)) {
face_indices[start + offset] = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grids[i]);
}
}
}
static void calc_grids(Object &object,
const Brush &brush,
const float strength,
const int face_set_id,
const PBVHNode &node,
GridLocalData &tls,
const MutableSpan<int> face_sets)
{
SculptSession &ss = *object.sculpt;
const StrokeCache &cache = *ss.cache;
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.positions.reinitialize(grid_verts_num);
MutableSpan<float3> positions = tls.positions;
gather_grids_positions(subdiv_ccg, grids, positions);
tls.factors.reinitialize(grid_verts_num);
const MutableSpan<float> factors = tls.factors;
blender::ed::sculpt_paint::fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
filter_region_clip_factors(ss, positions, factors);
if (brush.flag & BRUSH_FRONTFACE) {
calc_front_face(cache.view_normal, subdiv_ccg, grids, factors);
}
tls.distances.reinitialize(grid_verts_num);
const MutableSpan<float> distances = tls.distances;
calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances);
filter_distances_with_radius(cache.radius, distances, factors);
apply_hardness_to_distances(cache, distances);
calc_brush_strength_factors(cache, brush, distances, factors);
if (cache.automasking) {
auto_mask::calc_grids_factors(object, *cache.automasking, node, grids, factors);
}
calc_brush_texture_factors(ss, brush, positions, factors);
scale_factors(factors, strength);
tls.face_indices.reinitialize(grid_verts_num);
MutableSpan<int> face_indices = tls.face_indices;
calc_face_indices_grids(subdiv_ccg, grids, face_indices);
apply_face_set(face_set_id, face_indices, factors, face_sets);
}
static void do_draw_face_sets_brush_grids(Object &object,
const Brush &brush,
const Span<PBVHNode *> nodes)
{
SculptSession &ss = *object.sculpt;
bke::SpanAttributeWriter<int> attribute = face_set::ensure_face_sets_mesh(object);
MutableSpan<int> face_sets = attribute.span;
threading::EnumerableThreadSpecific<GridLocalData> all_tls;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
GridLocalData &tls = all_tls.local();
for (PBVHNode *node : nodes.slice(range)) {
for (const int i : range) {
undo::push_node(object, node, undo::Type::FaceSet);
calc_grids(object,
brush,
ss.cache->bstrength,
ss.cache->paint_face_set,
*nodes[i],
tls,
face_sets);
}
}
});
attribute.finish();
}
struct BMeshLocalData {
Vector<float3> positions;
Vector<float> factors;
Vector<float> distances;
};
BLI_NOINLINE static void fill_factor_from_hide_and_mask(const BMesh &bm,
const Set<BMFace *, 0L> &faces,
const MutableSpan<float> r_factors)
{
BLI_assert(faces.size() == r_factors.size());
/* TODO: Avoid overhead of accessing attributes for every PBVH node. */
const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
int i = 0;
for (BMFace *f : faces) {
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
r_factors[i] = 0.0f;
continue;
}
if (mask_offset == -1) {
r_factors[i] = 1.0f;
continue;
}
const BMLoop *l_iter = f->l_first = BM_FACE_FIRST_LOOP(f);
int total_verts = 0;
float sum;
do {
BMVert *vert = l_iter->v;
sum += BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
total_verts++;
} while ((l_iter = l_iter->next) != f->l_first);
r_factors[i] = 1.0f - sum * math::rcp(float(total_verts));
i++;
}
}
static void calc_face_centers(const Set<BMFace *, 0L> &faces, const MutableSpan<float3> centers)
{
BLI_assert(faces.size() == centers.size());
int i = 0;
for (const BMFace *f : faces) {
float3 face_center;
BM_face_calc_center_median(f, face_center);
centers[i] = face_center;
i++;
}
}
BLI_NOINLINE static void apply_face_set(const int face_set_id,
const Set<BMFace *, 0> &faces,
const MutableSpan<float> factors,
const int cd_offset)
{
int i = 0;
for (BMFace *face : faces) {
if (factors[i] > FACE_SET_BRUSH_MIN_FADE) {
BM_ELEM_CD_SET_INT(face, cd_offset, face_set_id);
}
i++;
}
}
static void calc_bmesh(Object &object,
const Brush &brush,
const float strength,
const int face_set_id,
PBVHNode &node,
BMeshLocalData &tls,
const int cd_offset)
{
SculptSession &ss = *object.sculpt;
const StrokeCache &cache = *ss.cache;
const Set<BMFace *, 0> &faces = BKE_pbvh_bmesh_node_faces(&node);
tls.positions.reinitialize(faces.size());
const MutableSpan<float3> positions = tls.positions;
calc_face_centers(faces, positions);
tls.factors.reinitialize(faces.size());
const MutableSpan<float> factors = tls.factors;
fill_factor_from_hide_and_mask(*ss.bm, faces, factors);
filter_region_clip_factors(ss, positions, factors);
if (brush.flag & BRUSH_FRONTFACE) {
calc_front_face(cache.view_normal, faces, factors);
}
tls.distances.reinitialize(faces.size());
const MutableSpan<float> distances = tls.distances;
calc_brush_distances(ss, positions, eBrushFalloffShape(brush.falloff_shape), distances);
filter_distances_with_radius(cache.radius, distances, factors);
apply_hardness_to_distances(cache, distances);
calc_brush_strength_factors(cache, brush, distances, factors);
/* Disable auto-masking code path which rely on an undo step to access original data.
*
* This is because the dynamic topology uses BMesh Log based undo system, which creates a
* single node for the undo step, and its type could be different for the needs of the
* brush undo and the original data access.
*
* For the brushes like Draw the ss.cache->automasking is set to nullptr at the first step
* of the brush, as there is an explicit check there for the brushes which support dynamic
* topology. Do it locally here for the Draw Face Set brush here, to mimic the behavior of
* the other brushes but without marking the brush as supporting dynamic topology. */
auto_mask::node_begin(object, nullptr, node);
calc_brush_texture_factors(ss, brush, positions, factors);
scale_factors(factors, strength);
apply_face_set(face_set_id, faces, factors, cd_offset);
}
static void do_draw_face_sets_brush_bmesh(Object &object,
const Brush &brush,
const Span<PBVHNode *> nodes)
{
SculptSession &ss = *object.sculpt;
const int cd_offset = face_set::ensure_face_sets_bmesh(object);
threading::EnumerableThreadSpecific<BMeshLocalData> all_tls;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BMeshLocalData &tls = all_tls.local();
for (const int i : range) {
undo::push_node(object, nodes[i], undo::Type::FaceSet);
calc_bmesh(
object, brush, ss.cache->bstrength, ss.cache->paint_face_set, *nodes[i], tls, cd_offset);
}
});
}
} // namespace draw_face_sets_cc
void do_draw_face_sets_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
{
SculptSession &ss = *object.sculpt;
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
switch (BKE_pbvh_type(*ss.pbvh)) {
case PBVH_FACES:
do_draw_face_sets_brush_mesh(object, brush, nodes);
break;
case PBVH_GRIDS:
do_draw_face_sets_brush_grids(object, brush, nodes);
break;
case PBVH_BMESH:
do_draw_face_sets_brush_bmesh(object, brush, nodes);
break;
}
}
} // namespace blender::ed::sculpt_paint

View File

@@ -20,6 +20,7 @@ void do_crease_brush(const Scene &scene, const Sculpt &sd, Object &ob, Span<PBVH
void do_blob_brush(const Scene &scene, const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes);
void do_displacement_eraser_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes);
void do_displacement_smear_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes);
void do_draw_face_sets_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
/** A simple normal-direction displacement. */
void do_draw_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
/** A simple normal-direction displacement based on image texture RGB/XYZ values. */

View File

@@ -34,6 +34,7 @@
struct BMesh;
struct BMVert;
struct BMFace;
struct Brush;
struct Mesh;
struct Object;
@@ -115,6 +116,9 @@ void calc_front_face(const float3 &view_normal,
void calc_front_face(const float3 &view_normal,
const Set<BMVert *, 0> &verts,
const MutableSpan<float> factors);
void calc_front_face(const float3 &view_normal,
const Set<BMFace *, 0> &faces,
const MutableSpan<float> factors);
/**
* When the 3D view's clipping planes are enabled, brushes shouldn't have any effect on vertices
@@ -217,6 +221,17 @@ void calc_vert_factors(const Object &object,
const Set<BMVert *, 0> &verts,
MutableSpan<float> factors);
/**
* Calculate all auto-masking influence on each face.
*/
void calc_face_factors(const Object &object,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Cache &cache,
const PBVHNode &node,
const Span<int> face_indices,
const MutableSpan<float> factors);
} // namespace auto_mask
/**

View File

@@ -3893,7 +3893,12 @@ static void do_brush_action(const Scene &scene,
cloth::do_cloth_brush(sd, ob, nodes);
break;
case SCULPT_TOOL_DRAW_FACE_SETS:
face_set::do_draw_face_sets_brush(sd, ob, nodes);
if (!ss.cache->alt_smooth) {
do_draw_face_sets_brush(sd, ob, nodes);
}
else {
face_set::do_relax_face_sets_brush(sd, ob, nodes);
}
break;
case SCULPT_TOOL_DISPLACEMENT_ERASER:
do_displacement_eraser_brush(sd, ob, nodes);
@@ -6859,6 +6864,20 @@ void calc_front_face(const float3 &view_normal,
}
}
void calc_front_face(const float3 &view_normal,
const Set<BMFace *, 0> &faces,
const MutableSpan<float> factors)
{
BLI_assert(faces.size() == factors.size());
int i = 0;
for (const BMFace *face : faces) {
const float dot = math::dot(view_normal, float3(face->no));
factors[i] *= std::max(dot, 0.0f);
i++;
}
}
void filter_region_clip_factors(const SculptSession &ss,
const Span<float3> positions,
const Span<int> verts,
@@ -6892,6 +6911,8 @@ void filter_region_clip_factors(const SculptSession &ss,
const Span<float3> positions,
const MutableSpan<float> factors)
{
BLI_assert(positions.size() == factors.size());
const RegionView3D *rv3d = ss.cache ? ss.cache->vc->rv3d : ss.rv3d;
const View3D *v3d = ss.cache ? ss.cache->vc->v3d : ss.v3d;
if (!RV3D_CLIPPING_ENABLED(v3d, rv3d)) {

View File

@@ -11,6 +11,7 @@
#include "BLI_array.hh"
#include "BLI_hash.h"
#include "BLI_index_range.hh"
#include "BLI_math_base.hh"
#include "BLI_math_base_safe.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
@@ -613,6 +614,34 @@ void calc_vert_factors(const Object &object,
}
}
void calc_face_factors(const Object &object,
const OffsetIndices<int> faces,
const Span<int> corner_verts,
const Cache &cache,
const PBVHNode &node,
const Span<int> face_indices,
const MutableSpan<float> factors)
{
SculptSession &ss = *object.sculpt;
NodeData data = node_begin(object, &cache, node);
/* NOTE: We explicitly nullify data.orig_data here as we currently cannot go from mesh vert index
* to the undo node array index. The only brush this method is currently used for is the Draw
* Face Set brush, which never modifies the position of the vertices in a brush stroke. This
* needs to be implemented in the future if brushes that iterate over faces need original
* position and normal data. */
data.orig_data = std::nullopt;
for (const int i : face_indices.index_range()) {
const Span<int> face_verts = corner_verts.slice(faces[face_indices[i]]);
float sum = 0.0f;
for (const int vert : face_verts) {
sum += factor_get(&cache, ss, BKE_pbvh_make_vref(vert), &data);
}
factors[i] *= sum * math::rcp(float(face_verts.size()));
}
}
void calc_grids_factors(const Object &object,
const Cache &cache,
const PBVHNode &node,

View File

@@ -211,222 +211,6 @@ Array<int> duplicate_face_sets(const Mesh &mesh)
/** \name Draw Face Sets Brush
* \{ */
constexpr float FACE_SET_BRUSH_MIN_FADE = 0.05f;
static void do_draw_face_sets_brush_faces(Object &ob,
const Brush &brush,
const Span<PBVHNode *> nodes)
{
SculptSession &ss = *ob.sculpt;
const Span<float3> positions = SCULPT_mesh_deformed_positions_get(ss);
Mesh &mesh = *static_cast<Mesh *>(ob.data);
const OffsetIndices<int> faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
const bke::AttributeAccessor attributes = mesh.attributes();
const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
bke::SpanAttributeWriter<int> attribute = ensure_face_sets_mesh(ob);
MutableSpan<int> face_sets = attribute.span;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
const float bstrength = ss.cache->bstrength;
const int thread_id = BLI_task_parallel_thread_id(nullptr);
for (PBVHNode *node : nodes.slice(range)) {
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, test, brush.falloff_shape);
auto_mask::NodeData automask_data = auto_mask::node_begin(
ob, ss.cache->automasking.get(), *node);
bool changed = false;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) {
auto_mask::node_update(automask_data, vd);
for (const int face_i : ss.vert_to_face_map[vd.index]) {
const IndexRange face = faces[face_i];
const float3 center = bke::mesh::face_center_calc(positions, corner_verts.slice(face));
if (!sculpt_brush_test_sq_fn(test, center)) {
continue;
}
if (!hide_poly.is_empty() && hide_poly[face_i]) {
continue;
}
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask,
vd.vertex,
thread_id,
&automask_data);
if (fade > FACE_SET_BRUSH_MIN_FADE) {
face_sets[face_i] = ss.cache->paint_face_set;
changed = true;
}
}
}
BKE_pbvh_vertex_iter_end;
if (changed) {
undo::push_node(ob, node, undo::Type::FaceSet);
}
}
});
attribute.finish();
}
static void do_draw_face_sets_brush_grids(Object &ob,
const Brush &brush,
const Span<PBVHNode *> nodes)
{
SculptSession &ss = *ob.sculpt;
const float bstrength = ss.cache->bstrength;
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
bke::SpanAttributeWriter<int> attribute = ensure_face_sets_mesh(ob);
MutableSpan<int> face_sets = attribute.span;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
const int thread_id = BLI_task_parallel_thread_id(nullptr);
for (PBVHNode *node : nodes.slice(range)) {
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, test, brush.falloff_shape);
auto_mask::NodeData automask_data = auto_mask::node_begin(
ob, ss.cache->automasking.get(), *node);
bool changed = false;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) {
auto_mask::node_update(automask_data, vd);
if (!sculpt_brush_test_sq_fn(test, vd.co)) {
continue;
}
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask,
vd.vertex,
thread_id,
&automask_data);
if (fade > FACE_SET_BRUSH_MIN_FADE) {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg,
vd.grid_indices[vd.g]);
face_sets[face_index] = ss.cache->paint_face_set;
changed = true;
}
}
BKE_pbvh_vertex_iter_end;
if (changed) {
undo::push_node(ob, node, undo::Type::FaceSet);
}
}
});
attribute.finish();
}
static void do_draw_face_sets_brush_bmesh(Object &ob,
const Brush &brush,
const Span<PBVHNode *> nodes)
{
SculptSession &ss = *ob.sculpt;
const float bstrength = ss.cache->bstrength;
const int cd_offset = ensure_face_sets_bmesh(ob);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
const int thread_id = BLI_task_parallel_thread_id(nullptr);
for (PBVHNode *node : nodes.slice(range)) {
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, test, brush.falloff_shape);
/* Disable auto-masking code path which rely on an undo step to access original data.
*
* This is because the dynamic topology uses BMesh Log based undo system, which creates a
* single node for the undo step, and its type could be different for the needs of the brush
* undo and the original data access.
*
* For the brushes like Draw the ss.cache->automasking is set to nullptr at the first step
* of the brush, as there is an explicit check there for the brushes which support dynamic
* topology. Do it locally here for the Draw Face Set brush here, to mimic the behavior of
* the other brushes but without marking the brush as supporting dynamic topology. */
auto_mask::NodeData automask_data = auto_mask::node_begin(ob, nullptr, *node);
bool changed = false;
for (BMFace *f : BKE_pbvh_bmesh_node_faces(node)) {
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
continue;
}
float3 face_center;
BM_face_calc_center_median(f, face_center);
const BMLoop *l_iter = f->l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!sculpt_brush_test_sq_fn(test, l_iter->v->co)) {
continue;
}
BMVert *vert = l_iter->v;
/* There is no need to update the automasking data as it is disabled above. Additionally,
* there is no access to the PBVHVertexIter as iteration happens over faces.
*
* The full auto-masking support would be very good to be implemented here, so keeping
* the typical code flow for it here for the reference, and ease of looking at what needs
* to be done for such integration.
*
* auto_mask::node_update(automask_data, vd); */
const float fade = bstrength *
SCULPT_brush_strength_factor(ss,
brush,
face_center,
sqrtf(test.dist),
f->no,
f->no,
0.0f,
BKE_pbvh_make_vref(intptr_t(vert)),
thread_id,
&automask_data);
if (fade <= FACE_SET_BRUSH_MIN_FADE) {
continue;
}
int &fset = *static_cast<int *>(POINTER_OFFSET(f->head.data, cd_offset));
fset = ss.cache->paint_face_set;
changed = true;
break;
} while ((l_iter = l_iter->next) != f->l_first);
}
if (changed) {
undo::push_node(ob, node, undo::Type::FaceSet);
}
}
});
}
static void do_relax_face_sets_brush_task(Object &ob,
const Brush &brush,
const float strength,
@@ -483,38 +267,24 @@ static std::array<float, 4> iteration_strengths(const float strength, const int
return {modified_strength, modified_strength, strength, strength};
}
void do_draw_face_sets_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
void do_relax_face_sets_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
{
SculptSession &ss = *ob.sculpt;
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
BKE_curvemapping_init(brush.curve);
if (ss.cache->alt_smooth) {
SCULPT_boundary_info_ensure(ob);
const SculptSession &ss = *ob.sculpt;
const std::array<float, 4> strengths = iteration_strengths(ss.cache->bstrength,
ss.cache->iteration_count);
for (const float strength : strengths) {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_relax_face_sets_brush_task(ob, brush, strength, nodes[i]);
}
});
}
}
else {
switch (BKE_pbvh_type(*ss.pbvh)) {
case PBVH_FACES:
do_draw_face_sets_brush_faces(ob, brush, nodes);
break;
case PBVH_GRIDS:
do_draw_face_sets_brush_grids(ob, brush, nodes);
break;
case PBVH_BMESH:
do_draw_face_sets_brush_bmesh(ob, brush, nodes);
break;
}
SCULPT_boundary_info_ensure(ob);
const SculptSession &ss = *ob.sculpt;
const std::array<float, 4> strengths = iteration_strengths(ss.cache->bstrength,
ss.cache->iteration_count);
for (const float strength : strengths) {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_relax_face_sets_brush_task(ob, brush, strength, nodes[i]);
}
});
}
}

View File

@@ -1968,7 +1968,7 @@ void multiplane_scrape_preview_draw(uint gpuattr,
namespace face_set {
void do_draw_face_sets_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes);
void do_relax_face_sets_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes);
}