Sculpt: Extend refactor to change multires in brushes
So far the brush refactors haven't affected the multires implementations much. Besides replacing the vertex iteration macro they are fundamentally the same. This commit refactors the multires implementations to use the same structure as the base mesh code. The motivations are improving CPU cache use, removing constant checks from hot loops, and allowing use of SIMD. This generally works by gathering the AoS multires format into local arrays. Quite a few brush evaluation functions have been duplicated to work on non-indexed data. This will be necessary as we start to handle brushes that use original data anyway. I don't think this duplication is very bad though; since the functions are right next to each other it's easy to change them at the same time. In the future we might investigate gathering data into local arrays for the mesh implementations too. In the brush benchmark file from #118145, this improved brush evaluation performance by roughly 15%, mostly by removing the separate step of accumulating changes with the proxies system. Pull Request: https://projects.blender.org/blender/blender/pulls/123703
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
namespace blender::ed::sculpt_paint {
|
||||
inline namespace clay_cc {
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -41,6 +42,18 @@ BLI_NOINLINE static void calc_closest_to_plane(const float4 &test_plane,
|
||||
}
|
||||
}
|
||||
|
||||
BLI_NOINLINE static void calc_closest_to_plane(const float4 &test_plane,
|
||||
const Span<float3> positions,
|
||||
const MutableSpan<float3> translations)
|
||||
{
|
||||
/* Equivalent to #closest_to_plane_normalized_v3 */
|
||||
BLI_assert(positions.size() == translations.size());
|
||||
for (const int i : positions.index_range()) {
|
||||
const float side = plane_point_side_v3(test_plane, positions[i]);
|
||||
translations[i] = float3(test_plane) * -side;
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_faces(const Sculpt &sd,
|
||||
const Brush &brush,
|
||||
const float4 &test_plane,
|
||||
@@ -88,65 +101,55 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(Object &object,
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float4 &test_plane,
|
||||
const float strength,
|
||||
PBVHNode &node)
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
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 StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
float3 intr;
|
||||
closest_to_plane_normalized_v3(intr, test_plane, co);
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
float3 offset;
|
||||
sub_v3_v3v3(offset, intr, co);
|
||||
tls.factors.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(subdiv_ccg, grids, factors);
|
||||
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
co,
|
||||
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] = offset * fade * strength;
|
||||
i++;
|
||||
}
|
||||
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_distance_falloff(
|
||||
ss, positions, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
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);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
|
||||
calc_closest_to_plane(test_plane, positions, translations);
|
||||
scale_translations(translations, strength);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(Object &object,
|
||||
@@ -233,9 +236,9 @@ void do_clay_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
BLI_ASSERT_UNIT_V3(test_plane);
|
||||
|
||||
const float bstrength = fabsf(ss.cache->bstrength);
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -261,8 +264,9 @@ void do_clay_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
}
|
||||
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(object, brush, test_plane, bstrength, *nodes[i]);
|
||||
calc_grids(sd, object, brush, test_plane, bstrength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace clay_strips_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -95,82 +96,66 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(Object &object,
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float4x4 &mat,
|
||||
const float4 &plane,
|
||||
const float strength,
|
||||
const bool flip,
|
||||
PBVHNode &node)
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
|
||||
SculptBrushTest test;
|
||||
SCULPT_brush_test_init(ss, test);
|
||||
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 StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!SCULPT_brush_test_cube(test, co, mat.ptr(), brush.tip_roundness, brush.tip_scale_x)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
if (flip) {
|
||||
if (plane_point_side_v3(plane, co) <= 0.0f) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (plane_point_side_v3(plane, co) > 0.0f) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, co);
|
||||
const float3 translation = closest - co;
|
||||
|
||||
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,
|
||||
co,
|
||||
ss.cache->radius * 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++;
|
||||
}
|
||||
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, 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_cube_distance_falloff(ss, brush, mat, positions, distances, factors);
|
||||
scale_factors(distances, cache.radius);
|
||||
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);
|
||||
|
||||
if (flip) {
|
||||
filter_below_plane_factors(positions, plane, factors);
|
||||
}
|
||||
else {
|
||||
filter_above_plane_factors(positions, plane, factors);
|
||||
}
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations_to_plane(positions, plane, translations);
|
||||
filter_plane_trim_limit_factors(brush, cache, translations, factors);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(Object &object,
|
||||
@@ -309,9 +294,9 @@ void do_clay_strips_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nod
|
||||
|
||||
const float strength = std::abs(ss.cache->bstrength);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -339,8 +324,9 @@ void do_clay_strips_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nod
|
||||
}
|
||||
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(object, brush, mat, plane, strength, flip, *nodes[i]);
|
||||
calc_grids(sd, object, brush, mat, plane, strength, flip, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace crease_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -47,6 +48,19 @@ BLI_NOINLINE static void translations_from_position(const Span<float3> positions
|
||||
}
|
||||
}
|
||||
|
||||
BLI_NOINLINE static void translations_from_position(const Span<float3> positions,
|
||||
const float3 &location,
|
||||
const MutableSpan<float3> translations)
|
||||
{
|
||||
for (const int i : positions.index_range()) {
|
||||
translations[i] = location - positions[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The vertices are pinched towards a line instead of a single point. Without this we get a
|
||||
* 'flat' surface surrounding the pinch.
|
||||
*/
|
||||
BLI_NOINLINE static void project_translations(const MutableSpan<float3> translations,
|
||||
const float3 &plane)
|
||||
{
|
||||
@@ -128,73 +142,65 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float3 &offset, const float strength, PBVHNode &node)
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float3 &offset,
|
||||
const float strength,
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
const StrokeCache &cache = *ss.cache;
|
||||
const float3 &location = cache.location;
|
||||
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
float3 translation = location - co;
|
||||
if (brush.falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
|
||||
project_translations({&translation, 1}, cache.view_normal);
|
||||
}
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
co,
|
||||
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);
|
||||
|
||||
translation *= fade;
|
||||
translation *= strength;
|
||||
|
||||
/* The vertices are pinched towards a line instead of a single point. Without this we get a
|
||||
* 'flat' surface surrounding the pinch. */
|
||||
project_translations({&translation, 1}, cache.sculpt_normal_symm);
|
||||
|
||||
translation += offset * fade;
|
||||
|
||||
proxy[i] = translation;
|
||||
i++;
|
||||
}
|
||||
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, 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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
translations_from_position(positions, cache.location, translations);
|
||||
|
||||
if (brush.falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
|
||||
project_translations(translations, cache.view_normal);
|
||||
}
|
||||
|
||||
scale_translations(translations, factors);
|
||||
scale_translations(translations, strength);
|
||||
|
||||
project_translations(translations, cache.sculpt_normal_symm);
|
||||
|
||||
add_offset_to_translations(translations, factors, offset);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(
|
||||
@@ -284,9 +290,9 @@ static void do_crease_or_blob_brush(const Scene &scene,
|
||||
const float strength = std::abs(cache.bstrength) * crease_correction *
|
||||
(invert_strength ? -1.0f : 1.0f);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -312,8 +318,9 @@ static void do_crease_or_blob_brush(const Scene &scene,
|
||||
}
|
||||
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(object, brush, offset, strength, *nodes[i]);
|
||||
calc_grids(sd, object, brush, offset, strength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -30,11 +30,21 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace draw_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
};
|
||||
|
||||
BLI_NOINLINE static void translations_from_offset_factors(const float3 &offset,
|
||||
const Span<float> factors,
|
||||
const MutableSpan<float3> r_translations)
|
||||
{
|
||||
for (const int i : factors.index_range()) {
|
||||
r_translations[i] = offset * factors[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_faces(const Sculpt &sd,
|
||||
const Brush &brush,
|
||||
const float3 &offset,
|
||||
@@ -74,61 +84,57 @@ static void calc_faces(const Sculpt &sd,
|
||||
|
||||
tls.translations.reinitialize(verts.size());
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
for (const int i : verts.index_range()) {
|
||||
translations[i] = offset * factors[i];
|
||||
}
|
||||
translations_from_offset_factors(offset, factors, translations);
|
||||
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(Object &object, const Brush &brush, const float3 &offset, PBVHNode &node)
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float3 &offset,
|
||||
const PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
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 StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
co,
|
||||
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] = offset * fade;
|
||||
i++;
|
||||
}
|
||||
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;
|
||||
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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
translations_from_offset_factors(offset, factors, translations);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(Object &object, const Brush &brush, const float3 &offset, PBVHNode &node)
|
||||
@@ -184,9 +190,9 @@ static void offset_positions(const Sculpt &sd,
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -211,8 +217,9 @@ static void offset_positions(const Sculpt &sd,
|
||||
}
|
||||
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(object, brush, offset, *nodes[i]);
|
||||
calc_grids(sd, object, brush, offset, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace draw_vector_displacement_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float4> colors;
|
||||
@@ -57,6 +58,26 @@ static void calc_brush_texture_colors(SculptSession &ss,
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_brush_texture_colors(SculptSession &ss,
|
||||
const Brush &brush,
|
||||
const Span<float3> positions,
|
||||
const Span<float> factors,
|
||||
const MutableSpan<float4> r_colors)
|
||||
{
|
||||
BLI_assert(positions.size() == r_colors.size());
|
||||
|
||||
const int thread_id = BLI_task_parallel_thread_id(nullptr);
|
||||
|
||||
for (const int i : positions.index_range()) {
|
||||
float texture_value;
|
||||
float4 texture_rgba;
|
||||
/* NOTE: This is not a thread-safe call. */
|
||||
sculpt_apply_texture(ss, brush, positions[i], thread_id, &texture_value, texture_rgba);
|
||||
|
||||
r_colors[i] = texture_rgba * factors[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_faces(const Sculpt &sd,
|
||||
const Brush &brush,
|
||||
const Span<float3> positions_eval,
|
||||
@@ -104,57 +125,52 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(Object &object, const Brush &brush, PBVHNode &node)
|
||||
static void calc_grids(
|
||||
const Sculpt &sd, Object &object, const Brush &brush, PBVHNode &node, LocalData &tls)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
|
||||
const StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
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);
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
auto_mask::NodeData automask_data = auto_mask::node_begin(
|
||||
object, ss.cache->automasking.get(), node);
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto_mask::node_update(automask_data, i);
|
||||
float r_rgba[4];
|
||||
SCULPT_brush_strength_color(ss,
|
||||
brush,
|
||||
co,
|
||||
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,
|
||||
r_rgba);
|
||||
SCULPT_calc_vertex_displacement(ss, brush, r_rgba, proxy[i]);
|
||||
i++;
|
||||
}
|
||||
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, 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_distance_falloff(
|
||||
ss, 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);
|
||||
}
|
||||
|
||||
tls.colors.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float4> colors = tls.colors;
|
||||
calc_brush_texture_colors(ss, brush, positions, factors, colors);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
for (const int i : positions.index_range()) {
|
||||
SCULPT_calc_vertex_displacement(ss, brush, colors[i], translations[i]);
|
||||
}
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(Object &object, const Brush &brush, PBVHNode &node)
|
||||
@@ -211,9 +227,9 @@ void do_draw_vector_displacement_brush(const Sculpt &sd, Object &object, Span<PB
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -231,8 +247,9 @@ void do_draw_vector_displacement_brush(const Sculpt &sd, Object &object, Span<PB
|
||||
}
|
||||
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(object, brush, *nodes[i]);
|
||||
calc_grids(sd, object, brush, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace fill_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -87,69 +88,59 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float4 &plane,
|
||||
const float strength,
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
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 StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!SCULPT_plane_point_side(co, plane)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, co);
|
||||
const float3 translation = closest - co;
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
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,
|
||||
co,
|
||||
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++;
|
||||
}
|
||||
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, 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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
scale_factors(factors, strength);
|
||||
|
||||
filter_above_plane_factors(positions, plane, factors);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations_to_plane(positions, plane, translations);
|
||||
filter_plane_trim_limit_factors(brush, cache, translations, factors);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(
|
||||
@@ -230,9 +221,9 @@ void do_fill_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
float4 plane;
|
||||
plane_from_point_normal_v3(plane, area_co, area_no);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -258,8 +249,9 @@ void do_fill_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
}
|
||||
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(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
calc_grids(sd, object, brush, plane, ss.cache->bstrength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace flatten_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -84,65 +85,57 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float4 &plane,
|
||||
const float strength,
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
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 StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, co);
|
||||
const float3 translation = closest - co;
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
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,
|
||||
co,
|
||||
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++;
|
||||
}
|
||||
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, 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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
scale_factors(factors, strength);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations_to_plane(positions, plane, translations);
|
||||
filter_plane_trim_limit_factors(brush, cache, translations, factors);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(
|
||||
@@ -218,9 +211,9 @@ void do_flatten_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
float4 plane;
|
||||
plane_from_point_normal_v3(plane, area_co, area_no);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -246,8 +239,9 @@ void do_flatten_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
}
|
||||
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(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
calc_grids(sd, object, brush, plane, ss.cache->bstrength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace inflate_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -89,57 +90,73 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(Object &object, const Brush &brush, const float3 &scale, PBVHNode &node)
|
||||
static BLI_NOINLINE void gather_normals(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float3> normals)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == normals.size());
|
||||
|
||||
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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const float3 &no = CCG_elem_offset_no(key, elem, j);
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
co,
|
||||
math::sqrt(test.dist),
|
||||
no,
|
||||
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] = no * scale * fade;
|
||||
i++;
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
normals[start + offset] = CCG_elem_offset_no(key, elem, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float3 &scale,
|
||||
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 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;
|
||||
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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
gather_normals(subdiv_ccg, grids, translations);
|
||||
apply_scale(translations, scale);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(Object &object, const Brush &brush, const float3 &scale, PBVHNode &node)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
@@ -192,9 +209,9 @@ void do_inflate_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
|
||||
const float3 scale = ss.cache->scale * ss.cache->radius * ss.cache->bstrength;
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -219,8 +236,9 @@ void do_inflate_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
}
|
||||
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(object, brush, scale, *nodes[i]);
|
||||
calc_grids(sd, object, brush, scale, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace mask_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float> current_masks;
|
||||
@@ -109,6 +110,91 @@ static void calc_faces(const Brush &brush,
|
||||
array_utils::scatter(new_masks.as_span(), verts, mask);
|
||||
}
|
||||
|
||||
static BLI_NOINLINE void gather_mask(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> mask)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == mask.size());
|
||||
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
mask[start + offset] = CCG_elem_offset_mask(key, elem, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BLI_NOINLINE void scatter_mask(const Span<float> mask,
|
||||
const Span<int> grids,
|
||||
SubdivCCG &subdiv_ccg)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == mask.size());
|
||||
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
CCG_elem_offset_mask(key, elem, offset) = mask[start + offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float strength, 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 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;
|
||||
fill_factor_from_hide(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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
tls.new_masks.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float> new_masks = tls.new_masks;
|
||||
gather_mask(subdiv_ccg, grids, new_masks);
|
||||
|
||||
tls.current_masks = tls.new_masks;
|
||||
const MutableSpan<float> current_masks = tls.current_masks;
|
||||
if (strength > 0.0f) {
|
||||
invert_mask(current_masks);
|
||||
}
|
||||
apply_factors(strength, current_masks, factors, new_masks);
|
||||
clamp_mask(new_masks);
|
||||
|
||||
scatter_mask(new_masks.as_span(), grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static float calc_new_mask(const float mask, const float factor, const float strength)
|
||||
{
|
||||
const float modified_value = strength > 0.0f ? (1.0f - mask) : mask;
|
||||
@@ -116,55 +202,6 @@ static float calc_new_mask(const float mask, const float factor, const float str
|
||||
return std::clamp(result, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
static void calc_grids(Object &object, const Brush &brush, 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;
|
||||
|
||||
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;
|
||||
}
|
||||
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,
|
||||
0.0f,
|
||||
BKE_pbvh_make_vref(grid_verts_start + j),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
|
||||
const float current_mask = key.has_mask ? CCG_elem_offset_mask(key, elem, j) : 0.0f;
|
||||
const float new_mask = calc_new_mask(current_mask, fade, strength);
|
||||
CCG_elem_offset_mask(key, elem, j) = new_mask;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_bmesh(Object &object, const Brush &brush, const float strength, PBVHNode &node)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
@@ -210,9 +247,9 @@ void do_mask_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
const float bstrength = ss.cache->bstrength;
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
switch (BKE_pbvh_type(*ss.pbvh)) {
|
||||
case PBVH_FACES: {
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
Mesh &mesh = *static_cast<Mesh *>(object.data);
|
||||
|
||||
const PBVH &pbvh = *ss.pbvh;
|
||||
@@ -245,8 +282,9 @@ void do_mask_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
}
|
||||
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(object, brush, bstrength, *nodes[i]);
|
||||
calc_grids(object, brush, bstrength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "BKE_pbvh.hh"
|
||||
#include "BKE_subdiv_ccg.hh"
|
||||
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_task.h"
|
||||
@@ -25,69 +26,87 @@ namespace blender::ed::sculpt_paint {
|
||||
|
||||
inline namespace multires_displacement_eraser_cc {
|
||||
|
||||
static void calc_node(
|
||||
const Sculpt &sd, Object &object, const Brush &brush, const float strength, PBVHNode &node)
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
};
|
||||
|
||||
static BLI_NOINLINE void calc_limit_positions(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float3> limit_positions)
|
||||
{
|
||||
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;
|
||||
|
||||
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];
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
for (const int grid : grids) {
|
||||
const int start = grid * key.grid_area;
|
||||
for (const int y : IndexRange(key.grid_size)) {
|
||||
for (const int x : IndexRange(key.grid_size)) {
|
||||
const int offset = CCG_grid_xy_to_index(key.grid_size, x, y);
|
||||
if (!grid_hidden.is_empty() && grid_hidden[grid][offset]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
float3 &co = CCG_elem_offset_co(key, elem, offset);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
auto_mask::node_update(automask_data, i);
|
||||
const float fade = SCULPT_brush_strength_factor(
|
||||
ss,
|
||||
brush,
|
||||
co,
|
||||
math::sqrt(test.dist),
|
||||
CCG_elem_offset_no(key, elem, offset),
|
||||
nullptr,
|
||||
key.has_mask ? CCG_elem_offset_mask(key, elem, offset) : 0.0f,
|
||||
BKE_pbvh_make_vref(grid_verts_start + offset),
|
||||
thread_id,
|
||||
&automask_data);
|
||||
|
||||
SubdivCCGCoord coord{};
|
||||
coord.grid_index = grid;
|
||||
coord.x = x;
|
||||
coord.y = y;
|
||||
float3 limit_co;
|
||||
BKE_subdiv_ccg_eval_limit_point(*ss.subdiv_ccg, coord, limit_co);
|
||||
|
||||
const float3 translation = (limit_co - co) * fade * strength;
|
||||
|
||||
SCULPT_clip(sd, ss, co, co + translation);
|
||||
|
||||
i++;
|
||||
BKE_subdiv_ccg_eval_limit_point(subdiv_ccg, coord, limit_positions[start + offset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_node(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float strength,
|
||||
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 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;
|
||||
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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
scale_factors(factors, strength);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
|
||||
calc_limit_positions(subdiv_ccg, grids, translations);
|
||||
for (const int i : positions.index_range()) {
|
||||
translations[i] -= positions[i];
|
||||
}
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
} // namespace multires_displacement_eraser_cc
|
||||
|
||||
void do_displacement_eraser_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
@@ -96,9 +115,11 @@ void do_displacement_eraser_brush(const Sculpt &sd, Object &object, Span<PBVHNod
|
||||
const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
|
||||
const float strength = std::min(ss.cache->bstrength, 1.0f);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
LocalData &tls = all_tls.local();
|
||||
for (const int i : range) {
|
||||
calc_node(sd, object, brush, strength, *nodes[i]);
|
||||
calc_node(sd, object, brush, strength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace blender::ed::sculpt_paint {
|
||||
inline namespace scrape_cc {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float3> positions;
|
||||
Vector<float> factors;
|
||||
Vector<float> distances;
|
||||
Vector<float3> translations;
|
||||
@@ -87,69 +88,59 @@ static void calc_faces(const Sculpt &sd,
|
||||
write_translations(sd, object, positions_eval, verts, translations, positions_orig);
|
||||
}
|
||||
|
||||
static void calc_grids(
|
||||
Object &object, const Brush &brush, const float4 &plane, const float strength, PBVHNode &node)
|
||||
static void calc_grids(const Sculpt &sd,
|
||||
Object &object,
|
||||
const Brush &brush,
|
||||
const float4 &plane,
|
||||
const float strength,
|
||||
PBVHNode &node,
|
||||
LocalData &tls)
|
||||
{
|
||||
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 StrokeCache &cache = *ss.cache;
|
||||
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;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
const float3 &co = CCG_elem_offset_co(key, elem, j);
|
||||
if (!sculpt_brush_test_sq_fn(test, co)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (SCULPT_plane_point_side(co, plane)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const Span<int> grids = bke::pbvh::node_grid_indices(node);
|
||||
const int grid_verts_num = grids.size() * key.grid_area;
|
||||
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, co);
|
||||
const float3 translation = closest - co;
|
||||
tls.positions.reinitialize(grid_verts_num);
|
||||
MutableSpan<float3> positions = tls.positions;
|
||||
gather_grids_positions(subdiv_ccg, grids, positions);
|
||||
|
||||
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,
|
||||
co,
|
||||
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++;
|
||||
}
|
||||
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, 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_distance_falloff(
|
||||
ss, 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, positions, factors);
|
||||
|
||||
scale_factors(factors, strength);
|
||||
|
||||
filter_below_plane_factors(positions, plane, factors);
|
||||
|
||||
tls.translations.reinitialize(grid_verts_num);
|
||||
const MutableSpan<float3> translations = tls.translations;
|
||||
calc_translations_to_plane(positions, plane, translations);
|
||||
filter_plane_trim_limit_factors(brush, cache, translations, factors);
|
||||
scale_translations(translations, factors);
|
||||
|
||||
clip_and_lock_translations(sd, ss, positions, translations);
|
||||
apply_translations(translations, grids, subdiv_ccg);
|
||||
}
|
||||
|
||||
static void calc_bmesh(
|
||||
@@ -230,9 +221,9 @@ void do_scrape_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
float4 plane;
|
||||
plane_from_point_normal_v3(plane, area_co, area_no);
|
||||
|
||||
threading::EnumerableThreadSpecific<LocalData> all_tls;
|
||||
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);
|
||||
@@ -258,8 +249,9 @@ void do_scrape_brush(const Sculpt &sd, Object &object, Span<PBVHNode *> nodes)
|
||||
}
|
||||
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(object, brush, plane, ss.cache->bstrength, *nodes[i]);
|
||||
calc_grids(sd, object, brush, plane, ss.cache->bstrength, *nodes[i], tls);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -56,10 +56,18 @@ void scale_factors(MutableSpan<float> factors, float strength);
|
||||
* are built for these values, then applied to `positions_orig`.
|
||||
*/
|
||||
|
||||
/** Fill the output array with all positions in the grids referenced by the indices. */
|
||||
void gather_grids_positions(const SubdivCCG &subdiv_ccg,
|
||||
Span<int> grids,
|
||||
MutableSpan<float3> positions);
|
||||
|
||||
/**
|
||||
* Calculate initial influence factors based on vertex visibility.
|
||||
*/
|
||||
void fill_factor_from_hide(const Mesh &mesh, Span<int> vert_indices, MutableSpan<float> r_factors);
|
||||
void fill_factor_from_hide(const SubdivCCG &subdiv_ccg,
|
||||
Span<int> grids,
|
||||
MutableSpan<float> r_factors);
|
||||
|
||||
/**
|
||||
* Calculate initial influence factors based on vertex visibility and masking.
|
||||
@@ -67,6 +75,9 @@ void fill_factor_from_hide(const Mesh &mesh, Span<int> vert_indices, MutableSpan
|
||||
void fill_factor_from_hide_and_mask(const Mesh &mesh,
|
||||
Span<int> vert_indices,
|
||||
MutableSpan<float> r_factors);
|
||||
void fill_factor_from_hide_and_mask(const SubdivCCG &subdiv_ccg,
|
||||
Span<int> grids,
|
||||
MutableSpan<float> r_factors);
|
||||
|
||||
/**
|
||||
* Disable brush influence when vertex normals point away from the view.
|
||||
@@ -75,6 +86,10 @@ void calc_front_face(const float3 &view_normal,
|
||||
Span<float3> vert_normals,
|
||||
Span<int> vert_indices,
|
||||
MutableSpan<float> factors);
|
||||
void calc_front_face(const float3 &view_normal,
|
||||
const SubdivCCG &subdiv_ccg,
|
||||
Span<int> grids,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* When the 3D view's clipping planes are enabled, brushes shouldn't have any effect on vertices
|
||||
@@ -85,6 +100,9 @@ void filter_region_clip_factors(const SculptSession &ss,
|
||||
Span<float3> vert_positions,
|
||||
Span<int> verts,
|
||||
MutableSpan<float> factors);
|
||||
void filter_region_clip_factors(const SculptSession &ss,
|
||||
Span<float3> positions,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* Calculate distances based on the distance from the brush cursor and various other settings.
|
||||
@@ -96,6 +114,11 @@ void calc_distance_falloff(const SculptSession &ss,
|
||||
eBrushFalloffShape falloff_shape,
|
||||
MutableSpan<float> r_distances,
|
||||
MutableSpan<float> factors);
|
||||
void calc_distance_falloff(const SculptSession &ss,
|
||||
Span<float3> positions,
|
||||
const eBrushFalloffShape falloff_shape,
|
||||
MutableSpan<float> r_distances,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* Calculate distances based on a "square" brush tip falloff and ignore vertices that are too far
|
||||
@@ -108,6 +131,12 @@ void calc_cube_distance_falloff(SculptSession &ss,
|
||||
Span<int> verts,
|
||||
MutableSpan<float> r_distances,
|
||||
MutableSpan<float> factors);
|
||||
void calc_cube_distance_falloff(SculptSession &ss,
|
||||
const Brush &brush,
|
||||
const float4x4 &mat,
|
||||
const Span<float3> positions,
|
||||
const MutableSpan<float> r_distances,
|
||||
const MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* Scale the distances based on the brush radius and the cached "hardness" setting, which increases
|
||||
@@ -131,6 +160,10 @@ void calc_brush_texture_factors(SculptSession &ss,
|
||||
Span<float3> vert_positions,
|
||||
Span<int> vert_indices,
|
||||
MutableSpan<float> factors);
|
||||
void calc_brush_texture_factors(SculptSession &ss,
|
||||
const Brush &brush,
|
||||
Span<float3> positions,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
namespace auto_mask {
|
||||
|
||||
@@ -142,6 +175,11 @@ void calc_vert_factors(const Object &object,
|
||||
const PBVHNode &node,
|
||||
Span<int> verts,
|
||||
MutableSpan<float> factors);
|
||||
void calc_grids_factors(const Object &object,
|
||||
const Cache &cache,
|
||||
const PBVHNode &node,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> factors);
|
||||
|
||||
} // namespace auto_mask
|
||||
|
||||
@@ -152,6 +190,7 @@ void calc_vert_factors(const Object &object,
|
||||
* simply add them to the final vertex positions.
|
||||
*/
|
||||
void apply_translations(Span<float3> translations, Span<int> verts, MutableSpan<float3> positions);
|
||||
void apply_translations(Span<float3> translations, Span<int> grids, SubdivCCG &subdiv_ccg);
|
||||
|
||||
/**
|
||||
* Rotate translations to account for rotations from procedural deformation.
|
||||
@@ -172,6 +211,10 @@ void clip_and_lock_translations(const Sculpt &sd,
|
||||
Span<float3> positions,
|
||||
Span<int> verts,
|
||||
MutableSpan<float3> translations);
|
||||
void clip_and_lock_translations(const Sculpt &sd,
|
||||
const SculptSession &ss,
|
||||
Span<float3> positions,
|
||||
MutableSpan<float3> translations);
|
||||
|
||||
/**
|
||||
* Applying final positions to shape keys is non-trivial because the mesh positions and the active
|
||||
@@ -249,6 +292,9 @@ void calc_translations_to_plane(Span<float3> vert_positions,
|
||||
Span<int> verts,
|
||||
const float4 &plane,
|
||||
MutableSpan<float3> translations);
|
||||
void calc_translations_to_plane(Span<float3> positions,
|
||||
const float4 &plane,
|
||||
MutableSpan<float3> translations);
|
||||
|
||||
/** Ignore points that fall below the "plane trim" threshold for the brush. */
|
||||
void filter_plane_trim_limit_factors(const Brush &brush,
|
||||
@@ -261,11 +307,17 @@ void filter_below_plane_factors(Span<float3> vert_positions,
|
||||
Span<int> verts,
|
||||
const float4 &plane,
|
||||
MutableSpan<float> factors);
|
||||
void filter_below_plane_factors(Span<float3> positions,
|
||||
const float4 &plane,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/* Ignore points above the plane. */
|
||||
void filter_above_plane_factors(Span<float3> vert_positions,
|
||||
Span<int> verts,
|
||||
const float4 &plane,
|
||||
MutableSpan<float> factors);
|
||||
void filter_above_plane_factors(Span<float3> positions,
|
||||
const float4 &plane,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
@@ -6549,6 +6549,23 @@ void SCULPT_cube_tip_init(const Sculpt & /*sd*/,
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
void gather_grids_positions(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float3> positions)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == positions.size());
|
||||
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
positions[start + offset] = CCG_elem_offset_co(key, elem, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fill_factor_from_hide(const Mesh &mesh,
|
||||
const Span<int> verts,
|
||||
const MutableSpan<float> r_factors)
|
||||
@@ -6568,6 +6585,27 @@ void fill_factor_from_hide(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
void fill_factor_from_hide(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> r_factors)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
BLI_assert(grids.size() * key.grid_area == r_factors.size());
|
||||
|
||||
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
|
||||
if (grid_hidden.is_empty()) {
|
||||
r_factors.fill(1.0f);
|
||||
return;
|
||||
}
|
||||
for (const int i : grids.index_range()) {
|
||||
const BitSpan hidden = grid_hidden[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
r_factors[start + offset] = hidden[offset] ? 0.0f : 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fill_factor_from_hide_and_mask(const Mesh &mesh,
|
||||
const Span<int> verts,
|
||||
const MutableSpan<float> r_factors)
|
||||
@@ -6596,6 +6634,41 @@ void fill_factor_from_hide_and_mask(const Mesh &mesh,
|
||||
}
|
||||
}
|
||||
|
||||
void fill_factor_from_hide_and_mask(const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> r_factors)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == r_factors.size());
|
||||
|
||||
if (key.has_mask) {
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
r_factors[start + offset] = 1.0f - CCG_elem_offset_mask(key, elem, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
r_factors.fill(1.0f);
|
||||
}
|
||||
|
||||
const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
|
||||
if (!grid_hidden.is_empty()) {
|
||||
for (const int i : grids.index_range()) {
|
||||
const BitSpan hidden = grid_hidden[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
if (hidden[offset]) {
|
||||
r_factors[start + offset] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calc_front_face(const float3 &view_normal,
|
||||
const Span<float3> vert_normals,
|
||||
const Span<int> verts,
|
||||
@@ -6609,6 +6682,25 @@ void calc_front_face(const float3 &view_normal,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_front_face(const float3 &view_normal,
|
||||
const SubdivCCG &subdiv_ccg,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == factors.size());
|
||||
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
const float dot = math::dot(view_normal, CCG_elem_offset_no(key, elem, offset));
|
||||
factors[start + offset] *= std::max(dot, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void filter_region_clip_factors(const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const Span<int> verts,
|
||||
@@ -6638,6 +6730,32 @@ void filter_region_clip_factors(const SculptSession &ss,
|
||||
}
|
||||
}
|
||||
|
||||
void filter_region_clip_factors(const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ePaintSymmetryFlags mirror_symmetry_pass = ss.cache ? ss.cache->mirror_symmetry_pass :
|
||||
ePaintSymmetryFlags(0);
|
||||
const int radial_symmetry_pass = ss.cache ? ss.cache->radial_symmetry_pass : 0;
|
||||
const float4x4 symm_rot_mat_inv = ss.cache ? ss.cache->symm_rot_mat_inv : float4x4::identity();
|
||||
for (const int i : positions.index_range()) {
|
||||
float3 symm_co;
|
||||
flip_v3_v3(symm_co, positions[i], mirror_symmetry_pass);
|
||||
if (radial_symmetry_pass) {
|
||||
symm_co = math::transform_point(symm_rot_mat_inv, symm_co);
|
||||
}
|
||||
if (ED_view3d_clipping_test(rv3d, symm_co, true)) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calc_distance_falloff(const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const Span<int> verts,
|
||||
@@ -6678,6 +6796,45 @@ void calc_distance_falloff(const SculptSession &ss,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_distance_falloff(const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const eBrushFalloffShape falloff_shape,
|
||||
const MutableSpan<float> r_distances,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
BLI_assert(positions.size() == factors.size());
|
||||
BLI_assert(positions.size() == r_distances.size());
|
||||
|
||||
const float3 &test_location = ss.cache ? ss.cache->location : ss.cursor_location;
|
||||
if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE && (ss.cache || ss.filter_cache)) {
|
||||
/* The tube falloff shape requires the cached view normal. */
|
||||
const float3 &view_normal = ss.cache ? ss.cache->view_normal : ss.filter_cache->view_normal;
|
||||
float4 test_plane;
|
||||
plane_from_point_normal_v3(test_plane, test_location, view_normal);
|
||||
for (const int i : positions.index_range()) {
|
||||
float3 projected;
|
||||
closest_to_plane_normalized_v3(projected, test_plane, positions[i]);
|
||||
r_distances[i] = math::distance_squared(projected, test_location);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : positions.index_range()) {
|
||||
r_distances[i] = math::distance_squared(test_location, positions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const float radius_sq = ss.cache ? ss.cache->radius_squared :
|
||||
ss.cursor_radius * ss.cursor_radius;
|
||||
for (const int i : r_distances.index_range()) {
|
||||
if (r_distances[i] < radius_sq) {
|
||||
r_distances[i] = std::sqrt(r_distances[i]);
|
||||
}
|
||||
else {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calc_cube_distance_falloff(SculptSession &ss,
|
||||
const Brush &brush,
|
||||
const float4x4 &mat,
|
||||
@@ -6708,6 +6865,34 @@ void calc_cube_distance_falloff(SculptSession &ss,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_cube_distance_falloff(SculptSession &ss,
|
||||
const Brush &brush,
|
||||
const float4x4 &mat,
|
||||
const Span<float3> positions,
|
||||
const MutableSpan<float> r_distances,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
BLI_assert(positions.size() == factors.size());
|
||||
BLI_assert(positions.size() == r_distances.size());
|
||||
|
||||
SculptBrushTest test;
|
||||
SCULPT_brush_test_init(ss, test);
|
||||
const float tip_roundness = brush.tip_roundness;
|
||||
const float tip_scale_x = brush.tip_scale_x;
|
||||
for (const int i : positions.index_range()) {
|
||||
if (factors[i] == 0.0f) {
|
||||
r_distances[i] = FLT_MAX;
|
||||
continue;
|
||||
}
|
||||
/* TODO: Break up #SCULPT_brush_test_cube. */
|
||||
if (!SCULPT_brush_test_cube(test, positions[i], mat.ptr(), tip_roundness, tip_scale_x)) {
|
||||
factors[i] = 0.0f;
|
||||
r_distances[i] = FLT_MAX;
|
||||
}
|
||||
r_distances[i] = test.dist;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_hardness_to_distances(const StrokeCache &cache, const MutableSpan<float> distances)
|
||||
{
|
||||
const float hardness = cache.paint_brush.hardness;
|
||||
@@ -6767,6 +6952,29 @@ void calc_brush_texture_factors(SculptSession &ss,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_brush_texture_factors(SculptSession &ss,
|
||||
const Brush &brush,
|
||||
const Span<float3> positions,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
BLI_assert(positions.size() == factors.size());
|
||||
|
||||
const int thread_id = BLI_task_parallel_thread_id(nullptr);
|
||||
const MTex *mtex = BKE_brush_mask_texture_get(&brush, OB_MODE_SCULPT);
|
||||
if (!mtex->tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const int i : positions.index_range()) {
|
||||
float texture_value;
|
||||
float4 texture_rgba;
|
||||
/* NOTE: This is not a thread-safe call. */
|
||||
sculpt_apply_texture(ss, brush, positions[i], thread_id, &texture_value, texture_rgba);
|
||||
|
||||
factors[i] *= texture_value;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_translations(const Span<float3> translations,
|
||||
const Span<int> verts,
|
||||
const MutableSpan<float3> positions)
|
||||
@@ -6779,6 +6987,23 @@ void apply_translations(const Span<float3> translations,
|
||||
}
|
||||
}
|
||||
|
||||
void apply_translations(const Span<float3> translations,
|
||||
const Span<int> grids,
|
||||
SubdivCCG &subdiv_ccg)
|
||||
{
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<CCGElem *> elems = subdiv_ccg.grids;
|
||||
BLI_assert(grids.size() * key.grid_area == translations.size());
|
||||
|
||||
for (const int i : grids.index_range()) {
|
||||
CCGElem *elem = elems[grids[i]];
|
||||
const int start = i * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
CCG_elem_offset_co(key, elem, offset) += translations[start + offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply_crazyspace_to_translations(const Span<float3x3> deform_imats,
|
||||
const Span<int> verts,
|
||||
const MutableSpan<float3> translations)
|
||||
@@ -6832,6 +7057,45 @@ void clip_and_lock_translations(const Sculpt &sd,
|
||||
}
|
||||
}
|
||||
|
||||
void clip_and_lock_translations(const Sculpt &sd,
|
||||
const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const MutableSpan<float3> translations)
|
||||
{
|
||||
BLI_assert(positions.size() == translations.size());
|
||||
|
||||
const StrokeCache *cache = ss.cache;
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
for (const int axis : IndexRange(3)) {
|
||||
if (sd.flags & (SCULPT_LOCK_X << axis)) {
|
||||
for (float3 &translation : translations) {
|
||||
translation[axis] = 0.0f;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(cache->flag & (CLIP_X << axis))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float4x4 mirror(cache->clip_mirror_mtx);
|
||||
const float4x4 mirror_inverse = math::invert(mirror);
|
||||
for (const int i : positions.index_range()) {
|
||||
/* Transform into the space of the mirror plane, check translations, then transform back. */
|
||||
float3 co_mirror = math::transform_point(mirror, positions[i]);
|
||||
if (math::abs(co_mirror[axis]) > cache->clip_tolerance[axis]) {
|
||||
continue;
|
||||
}
|
||||
/* Clear the translation in the local space of the mirror object. */
|
||||
co_mirror[axis] = 0.0f;
|
||||
const float3 co_local = math::transform_point(mirror_inverse, co_mirror);
|
||||
translations[i][axis] = co_local[axis] - positions[i][axis];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply_translations_to_shape_keys(Object &object,
|
||||
const Span<int> verts,
|
||||
const Span<float3> translations,
|
||||
@@ -7011,6 +7275,18 @@ void calc_translations_to_plane(const Span<float3> vert_positions,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_translations_to_plane(const Span<float3> positions,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float3> translations)
|
||||
{
|
||||
for (const int i : positions.index_range()) {
|
||||
const float3 &position = positions[i];
|
||||
float3 closest;
|
||||
closest_to_plane_normalized_v3(closest, plane, position);
|
||||
translations[i] = closest - position;
|
||||
}
|
||||
}
|
||||
|
||||
void filter_plane_trim_limit_factors(const Brush &brush,
|
||||
const StrokeCache &cache,
|
||||
const Span<float3> translations,
|
||||
@@ -7039,6 +7315,17 @@ void filter_below_plane_factors(const Span<float3> vert_positions,
|
||||
}
|
||||
}
|
||||
|
||||
void filter_below_plane_factors(const Span<float3> positions,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
for (const int i : positions.index_range()) {
|
||||
if (plane_point_side_v3(plane, positions[i]) <= 0.0f) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void filter_above_plane_factors(const Span<float3> vert_positions,
|
||||
const Span<int> verts,
|
||||
const float4 &plane,
|
||||
@@ -7051,4 +7338,15 @@ void filter_above_plane_factors(const Span<float3> vert_positions,
|
||||
}
|
||||
}
|
||||
|
||||
void filter_above_plane_factors(const Span<float3> positions,
|
||||
const float4 &plane,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
for (const int i : positions.index_range()) {
|
||||
if (plane_point_side_v3(plane, positions[i]) > 0.0f) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_pbvh_api.hh"
|
||||
#include "BKE_subdiv_ccg.hh"
|
||||
|
||||
#include "mesh_brush_common.hh"
|
||||
#include "paint_intern.hh"
|
||||
@@ -588,10 +589,10 @@ float factor_get(const Cache *automasking,
|
||||
return automasking_factor_end(ss, automasking, vert, mask);
|
||||
}
|
||||
|
||||
static void mesh_orig_vert_data_update(SculptOrigVertData &orig_data, const int vert)
|
||||
static void mesh_orig_vert_data_update(SculptOrigVertData &orig_data, const int i)
|
||||
{
|
||||
orig_data.co = orig_data.coords[vert];
|
||||
orig_data.no = orig_data.normals[vert];
|
||||
orig_data.co = orig_data.coords[i];
|
||||
orig_data.no = orig_data.normals[i];
|
||||
}
|
||||
|
||||
void calc_vert_factors(const Object &object,
|
||||
@@ -612,6 +613,30 @@ void calc_vert_factors(const Object &object,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_grids_factors(const Object &object,
|
||||
const Cache &cache,
|
||||
const PBVHNode &node,
|
||||
const Span<int> grids,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
SculptSession &ss = *object.sculpt;
|
||||
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
NodeData data = node_begin(object, &cache, node);
|
||||
|
||||
for (const int i : grids.index_range()) {
|
||||
const int node_start = i * key.grid_area;
|
||||
const int grids_start = grids[i] * key.grid_area;
|
||||
for (const int offset : IndexRange(key.grid_area)) {
|
||||
if (data.orig_data) {
|
||||
mesh_orig_vert_data_update(*data.orig_data, node_start + offset);
|
||||
}
|
||||
factors[node_start + offset] *= factor_get(
|
||||
&cache, ss, BKE_pbvh_make_vref(grids_start + offset), &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeData node_begin(const Object &object, const Cache *automasking, const PBVHNode &node)
|
||||
{
|
||||
if (!automasking) {
|
||||
|
||||
Reference in New Issue
Block a user