Sculpt: Data oriented refactor for rotate brush
Part of #118145. A straightforward brush implementation, with just a fairly simple custom loop to calculate the translations.
This commit is contained in:
@@ -127,6 +127,7 @@ set(SRC
|
||||
brushes/mask.cc
|
||||
brushes/multiplane_scrape.cc
|
||||
brushes/multires_displacement_eraser.cc
|
||||
brushes/rotate.cc
|
||||
brushes/scrape.cc
|
||||
brushes/smooth.cc
|
||||
brushes/smooth_mask.cc
|
||||
|
||||
@@ -69,7 +69,6 @@ static void calc_faces(const Sculpt &sd,
|
||||
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);
|
||||
}
|
||||
@@ -118,7 +117,6 @@ static void calc_grids(const Sculpt &sd,
|
||||
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);
|
||||
}
|
||||
@@ -168,7 +166,6 @@ static void calc_bmesh(const Sculpt &sd,
|
||||
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);
|
||||
}
|
||||
|
||||
245
source/blender/editors/sculpt_paint/brushes/rotate.cc
Normal file
245
source/blender/editors/sculpt_paint/brushes/rotate.cc
Normal file
@@ -0,0 +1,245 @@
|
||||
/* 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_rotation.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 rotate_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
};
|
||||
|
||||
BLI_NOINLINE static void calc_translations(const Span<float3> positions,
|
||||
const float3 &axis,
|
||||
const Span<float> angles,
|
||||
const float3 ¢er,
|
||||
const MutableSpan<float3> translations)
|
||||
{
|
||||
BLI_assert(positions.size() == angles.size());
|
||||
BLI_assert(positions.size() == translations.size());
|
||||
|
||||
for (const int i : positions.index_range()) {
|
||||
const math::AxisAngle rotation(axis, angles[i]);
|
||||
const float3x3 matrix = math::from_rotation<float3x3>(rotation);
|
||||
const float3 rotated = math::transform_point(matrix, positions[i] - center);
|
||||
translations[i] = rotated + center - positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_faces(const Sculpt &sd,
|
||||
const Brush &brush,
|
||||
const float angle,
|
||||
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);
|
||||
|
||||
scale_factors(factors, angle);
|
||||
|
||||
tls.translations.reinitialize(verts.size());
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations(
|
||||
orig_data.positions, cache.sculpt_normal_symm, factors, cache.location, translations);
|
||||
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float angle,
|
||||
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);
|
||||
|
||||
scale_factors(factors, angle);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations(
|
||||
orig_data.positions, cache.sculpt_normal_symm, factors, cache.location, 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 float angle,
|
||||
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);
|
||||
|
||||
scale_factors(factors, angle);
|
||||
|
||||
tls.translations.reinitialize(verts.size());
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations(
|
||||
orig_positions, cache.sculpt_normal_symm, factors, cache.location, translations);
|
||||
|
||||
clip_and_lock_translations(sd, ss, orig_positions, translations);
|
||||
apply_translations(translations, verts);
|
||||
}
|
||||
|
||||
} // namespace rotate_cc
|
||||
|
||||
void do_rotate_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
{
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
constexpr std::array<int, 8> flip{1, -1, -1, 1, -1, 1, 1, -1};
|
||||
const float angle = ss.cache->vertex_rotation * flip[ss.cache->mirror_symmetry_pass];
|
||||
|
||||
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, angle, 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, angle, *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, angle, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
@@ -31,6 +31,7 @@ void do_inflate_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes);
|
||||
void do_mask_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
void do_multiplane_scrape_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
void do_nudge_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
void do_rotate_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
void do_scrape_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
/** Smooth positions with neighboring vertices. */
|
||||
void do_smooth_brush(const Sculpt &sd,
|
||||
|
||||
@@ -3819,7 +3819,7 @@ static void do_brush_action(const Scene &scene,
|
||||
do_grab_brush(sd, ob, nodes);
|
||||
break;
|
||||
case SCULPT_TOOL_ROTATE:
|
||||
SCULPT_do_rotate_brush(sd, ob, nodes);
|
||||
do_rotate_brush(sd, ob, nodes);
|
||||
break;
|
||||
case SCULPT_TOOL_SNAKE_HOOK:
|
||||
SCULPT_do_snake_hook_brush(sd, ob, nodes);
|
||||
@@ -5598,12 +5598,11 @@ static void sculpt_restore_mesh(const Sculpt &sd, Object &ob)
|
||||
const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
/* Brushes that also use original coordinates and will need a "restore" step.
|
||||
* - SCULPT_TOOL_ROTATE
|
||||
* - SCULPT_TOOL_ELASTIC_DEFORM
|
||||
* - SCULPT_TOOL_BOUNDARY
|
||||
* - SCULPT_TOOL_POSE
|
||||
*/
|
||||
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
|
||||
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)) {
|
||||
restore_from_undo_step(sd, ob);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,70 +274,6 @@ void SCULPT_do_snake_hook_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> n
|
||||
});
|
||||
}
|
||||
|
||||
static void do_rotate_brush_task(Object &ob, const Brush &brush, const float angle, 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);
|
||||
|
||||
float vec[3], rot[3][3];
|
||||
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);
|
||||
|
||||
sub_v3_v3v3(vec, orig_data.co, ss.cache->location);
|
||||
axis_angle_normalized_to_mat3(rot, ss.cache->sculpt_normal_symm, angle * fade);
|
||||
mul_v3_m3v3(proxy[vd.i], rot, vec);
|
||||
add_v3_v3(proxy[vd.i], ss.cache->location);
|
||||
sub_v3_v3(proxy[vd.i], orig_data.co);
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
void SCULPT_do_rotate_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);
|
||||
|
||||
static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1};
|
||||
const float angle = ss.cache->vertex_rotation * flip[ss.cache->mirror_symmetry_pass];
|
||||
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
do_rotate_brush_task(ob, brush, angle, nodes[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void do_layer_brush_task(Object &ob, const Sculpt &sd, const Brush &brush, PBVHNode *node)
|
||||
{
|
||||
using namespace blender::ed::sculpt_paint;
|
||||
|
||||
@@ -2043,7 +2043,6 @@ float clay_thumb_get_stabilized_pressure(const blender::ed::sculpt_paint::Stroke
|
||||
}
|
||||
|
||||
void SCULPT_do_snake_hook_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);
|
||||
void SCULPT_do_elastic_deform_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
|
||||
Reference in New Issue
Block a user