Sculpt: Initial data oriented refactor for scrape and fill brushes
Part of #118145. These two are almost exactly the same. The difference between them is just an inverted condition and negating the radius. That can be changed later, maybe by implementing a more advanced fill algorithm. But for the purposes of this refactor, mainly keep the duplication. I just shared two functions as a compromise to not duplicate *everything*. It also shows the reader that the implementations are currently linked without fully conflating the two and making future changes harder. Pull Request: https://projects.blender.org/blender/blender/pulls/123038
This commit is contained in:
@@ -117,6 +117,8 @@ set(SRC
|
||||
|
||||
brushes/draw.cc
|
||||
brushes/draw_vector_displacement.cc
|
||||
brushes/fill.cc
|
||||
brushes/scrape.cc
|
||||
|
||||
brushes/types.hh
|
||||
)
|
||||
|
||||
295
source/blender/editors/sculpt_paint/brushes/fill.cc
Normal file
295
source/blender/editors/sculpt_paint/brushes/fill.cc
Normal file
@@ -0,0 +1,295 @@
|
||||
/* 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_geom.h"
|
||||
#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 fill_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
};
|
||||
|
||||
BLI_NOINLINE static void calc_plane_side_factors(const Span<float3> vert_positions,
|
||||
const Span<int> verts,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
for (const int i : verts.index_range()) {
|
||||
if (plane_point_side_v3(plane, vert_positions[verts[i]]) > 0.0f) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_faces(const Sculpt &sd,
|
||||
const Brush &brush,
|
||||
const float4 &plane,
|
||||
const float strength,
|
||||
const Span<float3> positions_eval,
|
||||
const Span<float3> vert_normals,
|
||||
const PBVHNode &node,
|
||||
Object &object,
|
||||
LocalData &tls,
|
||||
const MutableSpan<float3> positions_orig)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
StrokeCache &cache = *ss.cache;
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
|
||||
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);
|
||||
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
|
||||
tls.distances.reinitialize(verts.size());
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
calc_brush_strength_factors(ss, brush, verts, distances, factors);
|
||||
|
||||
if (ss.cache->automasking) {
|
||||
auto_mask::calc_vert_factors(object, *ss.cache->automasking, node, verts, factors);
|
||||
}
|
||||
|
||||
calc_brush_texture_factors(ss, brush, positions_eval, verts, factors);
|
||||
|
||||
scale_factors(factors, strength);
|
||||
|
||||
calc_plane_side_factors(positions_eval, verts, plane, factors);
|
||||
|
||||
tls.translations.reinitialize(verts.size());
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
scrape_calc_translations(positions_eval, verts, plane, translations);
|
||||
scrape_calc_plane_trim_limit(brush, *ss.cache, translations, factors);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions_eval, verts, translations);
|
||||
|
||||
apply_translations_to_pbvh(*ss.pbvh, verts, translations);
|
||||
|
||||
if (!ss.deform_imats.is_empty()) {
|
||||
apply_crazyspace_to_translations(ss.deform_imats, verts, translations);
|
||||
}
|
||||
|
||||
apply_translations(translations, verts, positions_orig);
|
||||
apply_translations_to_shape_keys(object, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
|
||||
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(
|
||||
object, ss.cache->automasking.get(), node);
|
||||
|
||||
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const CCGKey key = *BKE_pbvh_get_grid_key(*ss.pbvh);
|
||||
const Span<CCGElem *> grids = subdiv_ccg.grids;
|
||||
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
|
||||
|
||||
/* TODO: Remove usage of proxies. */
|
||||
const MutableSpan<float3> proxy = BKE_pbvh_node_add_proxy(*ss.pbvh, node).co;
|
||||
int i = 0;
|
||||
for (const int grid : bke::pbvh::node_grid_indices(node)) {
|
||||
const int grid_verts_start = grid * key.grid_area;
|
||||
CCGElem *elem = grids[grid];
|
||||
for (const int j : IndexRange(key.grid_area)) {
|
||||
if (!grid_hidden.is_empty() && grid_hidden[grid][j]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!sculpt_brush_test_sq_fn(test, CCG_elem_offset_co(key, elem, j))) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!SCULPT_plane_point_side(CCG_elem_offset_co(key, elem, j), plane)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, CCG_elem_offset_co(key, elem, j));
|
||||
const float3 translation = closest - CCG_elem_offset_co(key, elem, j);
|
||||
|
||||
if (!SCULPT_plane_trim(*ss.cache, brush, translation)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
CCG_elem_offset_co(key, elem, j),
|
||||
math::sqrt(test.dist),
|
||||
CCG_elem_offset_no(key, elem, j),
|
||||
nullptr,
|
||||
key.has_mask ? CCG_elem_offset_mask(key, elem, j) : 0.0f,
|
||||
BKE_pbvh_make_vref(grid_verts_start + j),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
proxy[i] = translation * fade * strength;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_bmesh(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
|
||||
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(
|
||||
object, ss.cache->automasking.get(), node);
|
||||
|
||||
const int mask_offset = CustomData_get_offset_named(
|
||||
&ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
/* TODO: Remove usage of proxies. */
|
||||
const MutableSpan<float3> proxy = BKE_pbvh_node_add_proxy(*ss.pbvh, node).co;
|
||||
int i = 0;
|
||||
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&node)) {
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!sculpt_brush_test_sq_fn(test, vert->co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!SCULPT_plane_point_side(float3(vert->co), plane)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, vert->co);
|
||||
const float3 translation = closest - float3(vert->co);
|
||||
|
||||
if (!SCULPT_plane_trim(*ss.cache, brush, translation)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, *vert);
|
||||
const float mask = mask_offset == -1 ? 0.0f : BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
|
||||
const float fade = SCULPT_brush_strength_factor(ss,
|
||||
brush,
|
||||
vert->co,
|
||||
math::sqrt(test.dist),
|
||||
vert->no,
|
||||
nullptr,
|
||||
mask,
|
||||
BKE_pbvh_make_vref(intptr_t(vert)),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
proxy[i] = translation * fade * strength;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fill_cc
|
||||
|
||||
void do_fill_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
{
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
float3 area_no;
|
||||
float3 area_co;
|
||||
calc_brush_plane(brush, object, nodes, area_no, area_co);
|
||||
SCULPT_tilt_apply_to_normal(area_no, ss.cache, brush.tilt_strength_factor);
|
||||
|
||||
const float offset = SCULPT_brush_plane_offset_get(sd, ss);
|
||||
const float displace = ss.cache->radius * offset;
|
||||
area_co += area_no * ss.cache->scale * displace;
|
||||
|
||||
float4 plane;
|
||||
plane_from_point_normal_v3(plane, area_co, area_no);
|
||||
|
||||
switch (BKE_pbvh_type(*object.sculpt->pbvh)) {
|
||||
case PBVH_FACES: {
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
const PBVH &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);
|
||||
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,
|
||||
plane,
|
||||
ss.cache->bstrength,
|
||||
positions_eval,
|
||||
vert_normals,
|
||||
*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) {
|
||||
for (const int i : range) {
|
||||
calc_grids(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case PBVH_BMESH:
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
calc_bmesh(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
324
source/blender/editors/sculpt_paint/brushes/scrape.cc
Normal file
324
source/blender/editors/sculpt_paint/brushes/scrape.cc
Normal file
@@ -0,0 +1,324 @@
|
||||
/* 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_geom.h"
|
||||
#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 {
|
||||
|
||||
BLI_NOINLINE void scrape_calc_translations(const Span<float3> vert_positions,
|
||||
const Span<int> verts,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float3> translations)
|
||||
{
|
||||
for (const int i : verts.index_range()) {
|
||||
const float3 &position = vert_positions[verts[i]];
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, position);
|
||||
translations[i] = closest - position;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_NOINLINE void scrape_calc_plane_trim_limit(const Brush &brush,
|
||||
const StrokeCache &cache,
|
||||
const Span<float3> translations,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
if (!(brush.flag & BRUSH_PLANE_TRIM)) {
|
||||
return;
|
||||
}
|
||||
const float threshold = cache.radius_squared * cache.plane_trim_squared;
|
||||
for (const int i : translations.index_range()) {
|
||||
if (math::length_squared(translations[i]) <= threshold) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline namespace scrape_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
};
|
||||
|
||||
BLI_NOINLINE static void calc_plane_side_factors(const Span<float3> vert_positions,
|
||||
const Span<int> verts,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
for (const int i : verts.index_range()) {
|
||||
if (plane_point_side_v3(plane, vert_positions[verts[i]]) <= 0.0f) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_faces(const Sculpt &sd,
|
||||
const Brush &brush,
|
||||
const float4 &plane,
|
||||
const float strength,
|
||||
const Span<float3> positions_eval,
|
||||
const Span<float3> vert_normals,
|
||||
const PBVHNode &node,
|
||||
Object &object,
|
||||
LocalData &tls,
|
||||
const MutableSpan<float3> positions_orig)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
StrokeCache &cache = *ss.cache;
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
|
||||
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);
|
||||
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
|
||||
tls.distances.reinitialize(verts.size());
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
calc_brush_strength_factors(ss, brush, verts, distances, factors);
|
||||
|
||||
if (ss.cache->automasking) {
|
||||
auto_mask::calc_vert_factors(object, *ss.cache->automasking, node, verts, factors);
|
||||
}
|
||||
|
||||
calc_brush_texture_factors(ss, brush, positions_eval, verts, factors);
|
||||
|
||||
scale_factors(factors, strength);
|
||||
|
||||
calc_plane_side_factors(positions_eval, verts, plane, factors);
|
||||
|
||||
tls.translations.reinitialize(verts.size());
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
scrape_calc_translations(positions_eval, verts, plane, translations);
|
||||
scrape_calc_plane_trim_limit(brush, *ss.cache, translations, factors);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions_eval, verts, translations);
|
||||
|
||||
apply_translations_to_pbvh(*ss.pbvh, verts, translations);
|
||||
|
||||
if (!ss.deform_imats.is_empty()) {
|
||||
apply_crazyspace_to_translations(ss.deform_imats, verts, translations);
|
||||
}
|
||||
|
||||
apply_translations(translations, verts, positions_orig);
|
||||
apply_translations_to_shape_keys(object, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
|
||||
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(
|
||||
object, ss.cache->automasking.get(), node);
|
||||
|
||||
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const CCGKey key = *BKE_pbvh_get_grid_key(*ss.pbvh);
|
||||
const Span<CCGElem *> grids = subdiv_ccg.grids;
|
||||
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
|
||||
|
||||
/* TODO: Remove usage of proxies. */
|
||||
const MutableSpan<float3> proxy = BKE_pbvh_node_add_proxy(*ss.pbvh, node).co;
|
||||
int i = 0;
|
||||
for (const int grid : bke::pbvh::node_grid_indices(node)) {
|
||||
const int grid_verts_start = grid * key.grid_area;
|
||||
CCGElem *elem = grids[grid];
|
||||
for (const int j : IndexRange(key.grid_area)) {
|
||||
if (!grid_hidden.is_empty() && grid_hidden[grid][j]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!sculpt_brush_test_sq_fn(test, CCG_elem_offset_co(key, elem, j))) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (SCULPT_plane_point_side(CCG_elem_offset_co(key, elem, j), plane)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, CCG_elem_offset_co(key, elem, j));
|
||||
const float3 translation = closest - CCG_elem_offset_co(key, elem, j);
|
||||
|
||||
if (!SCULPT_plane_trim(*ss.cache, brush, translation)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
CCG_elem_offset_co(key, elem, j),
|
||||
math::sqrt(test.dist),
|
||||
CCG_elem_offset_no(key, elem, j),
|
||||
nullptr,
|
||||
key.has_mask ? CCG_elem_offset_mask(key, elem, j) : 0.0f,
|
||||
BKE_pbvh_make_vref(grid_verts_start + j),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
proxy[i] = translation * fade * strength;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_bmesh(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
|
||||
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(
|
||||
object, ss.cache->automasking.get(), node);
|
||||
|
||||
const int mask_offset = CustomData_get_offset_named(
|
||||
&ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
|
||||
/* TODO: Remove usage of proxies. */
|
||||
const MutableSpan<float3> proxy = BKE_pbvh_node_add_proxy(*ss.pbvh, node).co;
|
||||
int i = 0;
|
||||
for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&node)) {
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!sculpt_brush_test_sq_fn(test, vert->co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SCULPT_plane_point_side(float3(vert->co), plane)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, vert->co);
|
||||
const float3 translation = closest - float3(vert->co);
|
||||
|
||||
if (!SCULPT_plane_trim(*ss.cache, brush, translation)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, *vert);
|
||||
const float mask = mask_offset == -1 ? 0.0f : BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
|
||||
const float fade = SCULPT_brush_strength_factor(ss,
|
||||
brush,
|
||||
vert->co,
|
||||
math::sqrt(test.dist),
|
||||
vert->no,
|
||||
nullptr,
|
||||
mask,
|
||||
BKE_pbvh_make_vref(intptr_t(vert)),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
proxy[i] = translation * fade * strength;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace scrape_cc
|
||||
|
||||
void do_scrape_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
{
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
float3 area_no;
|
||||
float3 area_co;
|
||||
calc_brush_plane(brush, object, nodes, area_no, area_co);
|
||||
SCULPT_tilt_apply_to_normal(area_no, ss.cache, brush.tilt_strength_factor);
|
||||
|
||||
const float offset = SCULPT_brush_plane_offset_get(sd, ss);
|
||||
const float displace = -ss.cache->radius * offset;
|
||||
area_co += area_no * ss.cache->scale * displace;
|
||||
|
||||
float4 plane;
|
||||
plane_from_point_normal_v3(plane, area_co, area_no);
|
||||
|
||||
switch (BKE_pbvh_type(*object.sculpt->pbvh)) {
|
||||
case PBVH_FACES: {
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
const PBVH &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);
|
||||
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,
|
||||
plane,
|
||||
ss.cache->bstrength,
|
||||
positions_eval,
|
||||
vert_normals,
|
||||
*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) {
|
||||
for (const int i : range) {
|
||||
calc_grids(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case PBVH_BMESH:
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
calc_bmesh(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
@@ -16,5 +16,7 @@ namespace blender::ed::sculpt_paint {
|
||||
void do_draw_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
/** A simple normal-direction displacement based on image texture RGB/XYZ values. */
|
||||
void do_draw_vector_displacement_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
void do_scrape_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
void do_fill_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes);
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
@@ -39,6 +39,9 @@ namespace auto_mask {
|
||||
struct Cache;
|
||||
};
|
||||
|
||||
void scale_translations(MutableSpan<float3> translations, Span<float> factors);
|
||||
void scale_factors(MutableSpan<float> factors, float strength);
|
||||
|
||||
/**
|
||||
* Note on the various positions arrays:
|
||||
* - positions_orig: Positions owned by the original mesh. Not the same as `positions_eval` if
|
||||
@@ -149,4 +152,13 @@ void apply_translations_to_shape_keys(Object &object,
|
||||
*/
|
||||
void apply_translations_to_pbvh(PBVH &pbvh, Span<int> verts, Span<float3> positions_orig);
|
||||
|
||||
void scrape_calc_translations(const Span<float3> vert_positions,
|
||||
const Span<int> verts,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float3> translations);
|
||||
void scrape_calc_plane_trim_limit(const Brush &brush,
|
||||
const StrokeCache &cache,
|
||||
const Span<float3> translations,
|
||||
const MutableSpan<float> factors);
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
@@ -3817,18 +3817,18 @@ static void do_brush_action(const Sculpt &sd,
|
||||
break;
|
||||
case SCULPT_TOOL_FILL:
|
||||
if (invert && brush.flag & BRUSH_INVERT_TO_SCRAPE_FILL) {
|
||||
SCULPT_do_scrape_brush(sd, ob, nodes);
|
||||
do_scrape_brush(sd, ob, nodes);
|
||||
}
|
||||
else {
|
||||
SCULPT_do_fill_brush(sd, ob, nodes);
|
||||
do_fill_brush(sd, ob, nodes);
|
||||
}
|
||||
break;
|
||||
case SCULPT_TOOL_SCRAPE:
|
||||
if (invert && brush.flag & BRUSH_INVERT_TO_SCRAPE_FILL) {
|
||||
SCULPT_do_fill_brush(sd, ob, nodes);
|
||||
do_fill_brush(sd, ob, nodes);
|
||||
}
|
||||
else {
|
||||
SCULPT_do_scrape_brush(sd, ob, nodes);
|
||||
do_scrape_brush(sd, ob, nodes);
|
||||
}
|
||||
break;
|
||||
case SCULPT_TOOL_MASK:
|
||||
@@ -5867,7 +5867,9 @@ static void sculpt_stroke_update_step(bContext *C,
|
||||
*
|
||||
* For some brushes, flushing is done in the brush code itself.
|
||||
*/
|
||||
if (!(ELEM(brush.sculpt_tool, SCULPT_TOOL_DRAW) && BKE_pbvh_type(*ss.pbvh) == PBVH_FACES)) {
|
||||
if (!(ELEM(brush.sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) &&
|
||||
BKE_pbvh_type(*ss.pbvh) == PBVH_FACES))
|
||||
{
|
||||
if (ss.deform_modifiers_active) {
|
||||
SCULPT_flush_stroke_deform(sd, ob, sculpt_tool_is_proxy_used(brush.sculpt_tool));
|
||||
}
|
||||
@@ -6728,4 +6730,21 @@ void apply_translations_to_pbvh(PBVH &pbvh, Span<int> verts, const Span<float3>
|
||||
}
|
||||
}
|
||||
|
||||
void scale_translations(const MutableSpan<float3> translations, const Span<float> factors)
|
||||
{
|
||||
for (const int i : translations.index_range()) {
|
||||
translations[i] *= factors[i];
|
||||
}
|
||||
}
|
||||
|
||||
void scale_factors(const MutableSpan<float> factors, const float strength)
|
||||
{
|
||||
if (strength == 1.0f) {
|
||||
return;
|
||||
}
|
||||
for (float &factor : factors) {
|
||||
factor *= strength;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
@@ -151,191 +151,6 @@ static void sculpt_project_v3_normal_align(const SculptSession &ss,
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sculpt Fill Brush
|
||||
* \{ */
|
||||
|
||||
static void do_fill_brush_task(
|
||||
Object &ob, const Brush &brush, const float *area_no, const float *area_co, 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;
|
||||
|
||||
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);
|
||||
|
||||
plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
|
||||
|
||||
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) {
|
||||
if (!sculpt_brush_test_sq_fn(test, vd.co)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!SCULPT_plane_point_side(vd.co, test.plane_tool)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float intr[3];
|
||||
float val[3];
|
||||
closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
|
||||
sub_v3_v3v3(val, intr, vd.co);
|
||||
|
||||
if (!SCULPT_plane_trim(*ss.cache, brush, val)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, vd);
|
||||
|
||||
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);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
void SCULPT_do_fill_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::ed::sculpt_paint;
|
||||
SculptSession &ss = *ob.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
const float radius = ss.cache->radius;
|
||||
|
||||
float area_no[3];
|
||||
float area_co[3];
|
||||
float offset = SCULPT_brush_plane_offset_get(sd, ss);
|
||||
|
||||
float displace;
|
||||
|
||||
float temp[3];
|
||||
|
||||
calc_brush_plane(brush, ob, nodes, area_no, area_co);
|
||||
|
||||
SCULPT_tilt_apply_to_normal(area_no, ss.cache, brush.tilt_strength_factor);
|
||||
|
||||
displace = radius * offset;
|
||||
|
||||
mul_v3_v3v3(temp, area_no, ss.cache->scale);
|
||||
mul_v3_fl(temp, displace);
|
||||
add_v3_v3(area_co, temp);
|
||||
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
do_fill_brush_task(ob, brush, area_no, area_co, nodes[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void do_scrape_brush_task(
|
||||
Object &ob, const Brush &brush, const float *area_no, const float *area_co, 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;
|
||||
|
||||
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);
|
||||
plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
|
||||
|
||||
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) {
|
||||
if (!sculpt_brush_test_sq_fn(test, vd.co)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SCULPT_plane_point_side(vd.co, test.plane_tool)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float intr[3];
|
||||
float val[3];
|
||||
closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
|
||||
sub_v3_v3v3(val, intr, vd.co);
|
||||
|
||||
if (!SCULPT_plane_trim(*ss.cache, brush, val)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, vd);
|
||||
|
||||
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);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
void SCULPT_do_scrape_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::ed::sculpt_paint;
|
||||
SculptSession &ss = *ob.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
const float radius = ss.cache->radius;
|
||||
|
||||
float area_no[3];
|
||||
float area_co[3];
|
||||
float offset = SCULPT_brush_plane_offset_get(sd, ss);
|
||||
|
||||
float displace;
|
||||
|
||||
float temp[3];
|
||||
|
||||
calc_brush_plane(brush, ob, nodes, area_no, area_co);
|
||||
|
||||
SCULPT_tilt_apply_to_normal(area_no, ss.cache, brush.tilt_strength_factor);
|
||||
|
||||
displace = -radius * offset;
|
||||
|
||||
mul_v3_v3v3(temp, area_no, ss.cache->scale);
|
||||
mul_v3_fl(temp, displace);
|
||||
add_v3_v3(area_co, temp);
|
||||
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
do_scrape_brush_task(ob, brush, area_no, area_co, nodes[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sculpt Clay Thumb Brush
|
||||
* \{ */
|
||||
|
||||
@@ -2018,8 +2018,6 @@ float SCULPT_clay_thumb_get_stabilized_pressure(
|
||||
|
||||
void SCULPT_do_draw_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
|
||||
void SCULPT_do_fill_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
void SCULPT_do_scrape_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
void SCULPT_do_clay_thumb_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
void SCULPT_do_flatten_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
void SCULPT_do_clay_brush(const Sculpt &sd, Object &ob, blender::Span<PBVHNode *> nodes);
|
||||
|
||||
Reference in New Issue
Block a user