Sculpt: Data oriented refactor for thumb brush

Part of #118145.
This implementation is quite simple, it's similar to the draw
brush and the draw sharp brush, but it also restores from the
original positions on each update step.
This commit is contained in:
Hans Goudey
2024-07-01 22:18:04 -04:00
parent 6ee9641e5c
commit ee73b4f978
6 changed files with 226 additions and 66 deletions

View File

@@ -129,6 +129,7 @@ set(SRC
brushes/scrape.cc
brushes/smooth.cc
brushes/smooth_mask.cc
brushes/thumb.cc
brushes/types.hh
)

View File

@@ -0,0 +1,221 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "editors/sculpt_paint/brushes/types.hh"
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_key.hh"
#include "BKE_mesh.hh"
#include "BKE_paint.hh"
#include "BKE_pbvh.hh"
#include "BKE_subdiv_ccg.hh"
#include "BLI_array.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.hh"
#include "BLI_task.h"
#include "BLI_task.hh"
#include "editors/sculpt_paint/mesh_brush_common.hh"
#include "editors/sculpt_paint/sculpt_intern.hh"
namespace blender::ed::sculpt_paint {
inline namespace thumb_cc {
struct LocalData {
Vector<float> factors;
Vector<float> distances;
Vector<float3> translations;
};
static void calc_faces(const Sculpt &sd,
const Brush &brush,
const float3 &offset,
const Span<float3> positions_eval,
const PBVHNode &node,
Object &object,
LocalData &tls,
const MutableSpan<float3> positions_orig)
{
SculptSession &ss = *object.sculpt;
const StrokeCache &cache = *ss.cache;
Mesh &mesh = *static_cast<Mesh *>(object.data);
const OrigPositionData orig_data = orig_position_data_get_mesh(object, node);
const Span<int> verts = bke::pbvh::node_unique_verts(node);
tls.factors.reinitialize(verts.size());
const MutableSpan<float> factors = tls.factors;
fill_factor_from_hide_and_mask(mesh, verts, factors);
filter_region_clip_factors(ss, orig_data.positions, factors);
if (brush.flag & BRUSH_FRONTFACE) {
calc_front_face(cache.view_normal, orig_data.normals, factors);
}
tls.distances.reinitialize(verts.size());
const MutableSpan<float> distances = tls.distances;
calc_distance_falloff(
ss, orig_data.positions, eBrushFalloffShape(brush.falloff_shape), distances, factors);
apply_hardness_to_distances(cache, distances);
calc_brush_strength_factors(cache, brush, distances, factors);
if (cache.automasking) {
auto_mask::calc_vert_factors(object, *cache.automasking, node, verts, factors);
}
calc_brush_texture_factors(ss, brush, orig_data.positions, factors);
tls.translations.reinitialize(verts.size());
const MutableSpan<float3> translations = tls.translations;
translations_from_offset_and_factors(offset, factors, translations);
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
}
static void calc_grids(const Sculpt &sd,
Object &object,
const Brush &brush,
const float3 &offset,
PBVHNode &node,
LocalData &tls)
{
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 OrigPositionData orig_data = orig_position_data_get_grids(object, node);
const Span<int> grids = bke::pbvh::node_grid_indices(node);
const int grid_verts_num = grids.size() * key.grid_area;
tls.factors.reinitialize(grid_verts_num);
const MutableSpan<float> factors = tls.factors;
fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
filter_region_clip_factors(ss, orig_data.positions, factors);
if (brush.flag & BRUSH_FRONTFACE) {
calc_front_face(cache.view_normal, orig_data.normals, factors);
}
tls.distances.reinitialize(grid_verts_num);
const MutableSpan<float> distances = tls.distances;
calc_distance_falloff(
ss, orig_data.positions, eBrushFalloffShape(brush.falloff_shape), 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, orig_data.positions, factors);
tls.translations.reinitialize(grid_verts_num);
const MutableSpan<float3> translations = tls.translations;
translations_from_offset_and_factors(offset, factors, translations);
clip_and_lock_translations(sd, ss, orig_data.positions, translations);
apply_translations(translations, grids, subdiv_ccg);
}
static void calc_bmesh(const Sculpt &sd,
Object &object,
const Brush &brush,
const float3 &offset,
PBVHNode &node,
LocalData &tls)
{
SculptSession &ss = *object.sculpt;
const StrokeCache &cache = *ss.cache;
const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(&node);
Array<float3> orig_positions(verts.size());
Array<float3> orig_normals(verts.size());
orig_position_data_gather_bmesh(*ss.bm_log, verts, orig_positions, orig_normals);
tls.factors.reinitialize(verts.size());
const MutableSpan<float> factors = tls.factors;
fill_factor_from_hide_and_mask(*ss.bm, verts, factors);
filter_region_clip_factors(ss, orig_positions, factors);
if (brush.flag & BRUSH_FRONTFACE) {
calc_front_face(cache.view_normal, orig_normals, factors);
}
tls.distances.reinitialize(verts.size());
const MutableSpan<float> distances = tls.distances;
calc_distance_falloff(
ss, orig_positions, eBrushFalloffShape(brush.falloff_shape), distances, factors);
apply_hardness_to_distances(cache, distances);
calc_brush_strength_factors(cache, brush, distances, factors);
if (cache.automasking) {
auto_mask::calc_vert_factors(object, *cache.automasking, node, verts, factors);
}
calc_brush_texture_factors(ss, brush, orig_positions, factors);
tls.translations.reinitialize(verts.size());
const MutableSpan<float3> translations = tls.translations;
translations_from_offset_and_factors(offset, factors, translations);
clip_and_lock_translations(sd, ss, orig_positions, translations);
apply_translations(translations, verts);
}
} // namespace thumb_cc
void do_thumb_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
{
const SculptSession &ss = *object.sculpt;
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
const float3 &grab_delta = ss.cache->grab_delta_symmetry;
const float3 &normal = ss.cache->sculpt_normal_symm;
const float3 offset = math::cross(math::cross(normal, grab_delta), normal) * ss.cache->bstrength;
threading::EnumerableThreadSpecific<LocalData> all_tls;
switch (BKE_pbvh_type(*object.sculpt->pbvh)) {
case PBVH_FACES: {
Mesh &mesh = *static_cast<Mesh *>(object.data);
const PBVH &pbvh = *ss.pbvh;
const Span<float3> positions_eval = BKE_pbvh_get_vert_positions(pbvh);
MutableSpan<float3> positions_orig = mesh.vert_positions_for_write();
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
for (const int i : range) {
calc_faces(sd, brush, offset, positions_eval, *nodes[i], object, tls, positions_orig);
BKE_pbvh_node_mark_positions_update(nodes[i]);
}
});
break;
}
case PBVH_GRIDS:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
for (const int i : range) {
calc_grids(sd, object, brush, offset, *nodes[i], tls);
}
});
break;
case PBVH_BMESH:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
for (const int i : range) {
calc_bmesh(sd, object, brush, offset, *nodes[i], tls);
}
});
break;
}
}
} // namespace blender::ed::sculpt_paint

View File

@@ -41,5 +41,6 @@ void do_smooth_mask_brush(const Sculpt &sd,
Object &object,
Span<PBVHNode *> nodes,
float brush_strength);
void do_thumb_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
} // namespace blender::ed::sculpt_paint

View File

@@ -3828,7 +3828,7 @@ static void do_brush_action(const Scene &scene,
do_nudge_brush(sd, ob, nodes);
break;
case SCULPT_TOOL_THUMB:
SCULPT_do_thumb_brush(sd, ob, nodes);
do_thumb_brush(sd, ob, nodes);
break;
case SCULPT_TOOL_LAYER:
SCULPT_do_layer_brush(sd, ob, nodes);
@@ -5599,12 +5599,11 @@ static void sculpt_restore_mesh(const Sculpt &sd, Object &ob)
/* Brushes that also use original coordinates and will need a "restore" step.
* - SCULPT_TOOL_ROTATE
* - SCULPT_TOOL_THUMB
* - SCULPT_TOOL_ELASTIC_DEFORM
* - SCULPT_TOOL_BOUNDARY
* - SCULPT_TOOL_POSE
*/
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB)) {
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
restore_from_undo_step(sd, ob);
return;
}
@@ -5982,6 +5981,7 @@ static void sculpt_stroke_update_step(bContext *C,
SCULPT_TOOL_CLAY_STRIPS,
SCULPT_TOOL_CREASE,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_THUMB,
SCULPT_TOOL_DRAW,
SCULPT_TOOL_FILL,
SCULPT_TOOL_SCRAPE) &&

View File

@@ -430,68 +430,6 @@ void SCULPT_do_snake_hook_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> n
});
}
static void do_thumb_brush_task(Object &ob, const Brush &brush, const float *cono, PBVHNode *node)
{
using namespace blender::ed::sculpt_paint;
SculptSession &ss = *ob.sculpt;
PBVHVertexIter vd;
const MutableSpan<float3> proxy = BKE_pbvh_node_add_proxy(*ss.pbvh, *node).co;
const float bstrength = ss.cache->bstrength;
SculptOrigVertData orig_data = SCULPT_orig_vert_data_init(ob, *node, undo::Type::Position);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, test, brush.falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
auto_mask::NodeData automask_data = auto_mask::node_begin(
ob, ss.cache->automasking.get(), *node);
BKE_pbvh_vertex_iter_begin (*ss.pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(orig_data, vd);
if (!sculpt_brush_test_sq_fn(test, orig_data.co)) {
continue;
}
auto_mask::node_update(automask_data, vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
sqrtf(test.dist),
orig_data.no,
nullptr,
vd.mask,
vd.vertex,
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], cono, fade);
}
BKE_pbvh_vertex_iter_end;
}
void SCULPT_do_thumb_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession &ss = *ob.sculpt;
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
float grab_delta[3];
float tmp[3], cono[3];
copy_v3_v3(grab_delta, ss.cache->grab_delta_symmetry);
cross_v3_v3v3(tmp, ss.cache->sculpt_normal_symm, grab_delta);
cross_v3_v3v3(cono, tmp, ss.cache->sculpt_normal_symm);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_thumb_brush_task(ob, brush, cono, nodes[i]);
}
});
}
static void do_rotate_brush_task(Object &ob, const Brush &brush, const float angle, PBVHNode *node)
{
using namespace blender::ed::sculpt_paint;

View File

@@ -2041,7 +2041,6 @@ float SCULPT_clay_thumb_get_stabilized_pressure(
void SCULPT_do_clay_thumb_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
void SCULPT_do_snake_hook_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
void SCULPT_do_thumb_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
void SCULPT_do_rotate_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
void SCULPT_do_layer_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
void SCULPT_do_pinch_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);