Sculpt: Improve mesh normals update performance
Instead of storing a boolean "update tag" for every vertex, just recalculate the normals of all the faces and vertices in affected PBVH nodes. This avoids the overhead of tracking position updates at the vertex level, and simplifies the normal calculation, since we can just calculate values for all the normals in a node. The main way we gain performance is avoiding building a `VectorSet` for all vertices and faces that need updates in the entire mesh. That process had to be single threaded, and was a large bottleneck when many vertices were affected at a time. That `VectorSet` was necessary for thread safety deduplication of work though, because neighboring nodes can't calculate the normals of the same faces or vertices at the same time. (Normals need to be calculated for all faces connected to moved vertices and all vertices connected to those faces). In this PR, we only build a set of the *boundary* faces and vertices. We calculate those in a separate step, which duplicates work from the non-boundary calculations, but at least it's threadsafe. I measured normal recalculation timing when sculpting on a 16 million vertex mesh. The removal of the `vert_bitmap` array also saves 16 MB of memory. | Nodes | Affected Vertices | Before (ms) | After (ms) | | ----- | ------------ | ----------- | ---------- | | 4 | 15625 | 0.208 | 0.304 | | 35 | 136281 | 2.98 | 0.988 | | 117 | 457156 | 15.0 | 3.21 | | 2455 | 9583782 | 566 | 84.0 | Pull Request: https://projects.blender.org/blender/blender/pulls/116209
This commit is contained in:
@@ -295,7 +295,6 @@ void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked);
|
||||
bool BKE_pbvh_node_fully_unmasked_get(const PBVHNode *node);
|
||||
|
||||
void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh);
|
||||
void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex);
|
||||
|
||||
blender::Span<int> BKE_pbvh_node_get_grid_indices(const PBVHNode &node);
|
||||
|
||||
@@ -515,13 +514,6 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node,
|
||||
float (**r_orco_coords)[3],
|
||||
BMVert ***r_orco_verts);
|
||||
|
||||
/**
|
||||
* \note doing a full search on all vertices here seems expensive,
|
||||
* however this is important to avoid having to recalculate bound-box & sync the buffers to the
|
||||
* GPU (which is far more expensive!) See: #47232.
|
||||
*/
|
||||
bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node);
|
||||
|
||||
bool pbvh_has_mask(const PBVH *pbvh);
|
||||
|
||||
bool pbvh_has_face_sets(PBVH *pbvh);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "BLI_bit_span_ops.hh"
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
@@ -723,7 +724,7 @@ PBVH *build_mesh(Mesh *mesh)
|
||||
update_mesh_pointers(pbvh.get(), mesh);
|
||||
const Span<int> tri_faces = pbvh->corner_tri_faces;
|
||||
|
||||
pbvh->vert_bitmap = blender::Array<bool>(totvert, false);
|
||||
Array<bool> vert_bitmap(totvert, false);
|
||||
pbvh->totvert = totvert;
|
||||
|
||||
#ifdef TEST_PBVH_FACE_SPLIT
|
||||
@@ -768,7 +769,7 @@ PBVH *build_mesh(Mesh *mesh)
|
||||
hide_poly,
|
||||
material_index,
|
||||
sharp_face,
|
||||
pbvh->vert_bitmap,
|
||||
vert_bitmap,
|
||||
&cb,
|
||||
prim_bounds,
|
||||
corner_tris_num);
|
||||
@@ -778,9 +779,6 @@ PBVH *build_mesh(Mesh *mesh)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Clear the bitmap so it can be used as an update tag later on. */
|
||||
pbvh->vert_bitmap.fill(false);
|
||||
|
||||
BKE_pbvh_update_active_vcol(pbvh.get(), mesh);
|
||||
|
||||
#ifdef VALIDATE_UNIQUE_NODE_FACES
|
||||
@@ -1130,65 +1128,124 @@ static bool update_search(PBVHNode *node, const int flag)
|
||||
}
|
||||
|
||||
static void normals_calc_faces(const Span<float3> positions,
|
||||
const blender::OffsetIndices<int> faces,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const Span<int> mask,
|
||||
const Span<int> face_indices,
|
||||
MutableSpan<float3> face_normals)
|
||||
{
|
||||
threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) {
|
||||
for (const int i : mask.slice(range)) {
|
||||
face_normals[i] = mesh::face_normal_calc(positions, corner_verts.slice(faces[i]));
|
||||
for (const int i : face_indices) {
|
||||
face_normals[i] = mesh::face_normal_calc(positions, corner_verts.slice(faces[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_boundary_face_normals(const Span<float3> positions,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const Span<int> face_indices,
|
||||
MutableSpan<float3> face_normals)
|
||||
{
|
||||
threading::parallel_for(face_indices.index_range(), 512, [&](const IndexRange range) {
|
||||
normals_calc_faces(positions, faces, corner_verts, face_indices.slice(range), face_normals);
|
||||
});
|
||||
}
|
||||
|
||||
static void calc_node_face_normals(const Span<float3> positions,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const PBVH &pbvh,
|
||||
const Span<const PBVHNode *> nodes,
|
||||
MutableSpan<float3> face_normals)
|
||||
{
|
||||
threading::EnumerableThreadSpecific<Vector<int>> all_index_data;
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
Vector<int> &node_faces = all_index_data.local();
|
||||
for (const PBVHNode *node : nodes.slice(range)) {
|
||||
normals_calc_faces(positions,
|
||||
faces,
|
||||
corner_verts,
|
||||
node_face_indices_calc_mesh(pbvh, *node, node_faces),
|
||||
face_normals);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void normals_calc_verts_simple(const blender::GroupedSpan<int> vert_to_face_map,
|
||||
static void normals_calc_verts_simple(const GroupedSpan<int> vert_to_face_map,
|
||||
const Span<float3> face_normals,
|
||||
const Span<int> mask,
|
||||
const Span<int> verts,
|
||||
MutableSpan<float3> vert_normals)
|
||||
{
|
||||
threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int vert : mask.slice(range)) {
|
||||
float3 normal(0.0f);
|
||||
for (const int face : vert_to_face_map[vert]) {
|
||||
normal += face_normals[face];
|
||||
}
|
||||
vert_normals[vert] = math::normalize(normal);
|
||||
for (const int vert : verts) {
|
||||
float3 normal(0.0f);
|
||||
for (const int face : vert_to_face_map[vert]) {
|
||||
normal += face_normals[face];
|
||||
}
|
||||
vert_normals[vert] = math::normalize(normal);
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_boundary_vert_normals(const GroupedSpan<int> vert_to_face_map,
|
||||
const Span<float3> face_normals,
|
||||
const Span<int> verts,
|
||||
MutableSpan<float3> vert_normals)
|
||||
{
|
||||
threading::parallel_for(verts.index_range(), 1024, [&](const IndexRange range) {
|
||||
normals_calc_verts_simple(vert_to_face_map, face_normals, verts.slice(range), vert_normals);
|
||||
});
|
||||
}
|
||||
|
||||
static void calc_node_vert_normals(const GroupedSpan<int> vert_to_face_map,
|
||||
const Span<float3> face_normals,
|
||||
const Span<PBVHNode *> nodes,
|
||||
MutableSpan<float3> vert_normals)
|
||||
{
|
||||
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
|
||||
for (const PBVHNode *node : nodes.slice(range)) {
|
||||
normals_calc_verts_simple(vert_to_face_map,
|
||||
face_normals,
|
||||
node->vert_indices.as_span().take_front(node->uniq_verts),
|
||||
vert_normals);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void pbvh_faces_update_normals(PBVH &pbvh, Span<PBVHNode *> nodes, Mesh &mesh)
|
||||
static void update_normals_faces(PBVH &pbvh, Span<PBVHNode *> nodes, Mesh &mesh)
|
||||
{
|
||||
/* Position changes are tracked on a per-node level, so all the vertex and face normals for every
|
||||
* affected node are recalculated. However, the additional complexity comes from the fact that
|
||||
* changing vertex normals also changes surrounding face normals. Those changed face normals then
|
||||
* change the normals of all connected vertices, which can be in other nodes. So the set of
|
||||
* vertices that need recalculated normals can propagate into unchanged/untagged PBVH nodes.
|
||||
*
|
||||
* Currently we have no good way of finding neighboring PBVH nodes, so we use the vertex to
|
||||
* face topology map to find the neighboring vertices that need normal recalculation.
|
||||
*
|
||||
* Those boundary face and vertex indices are deduplicated with #VectorSet in order to avoid
|
||||
* duplicate work recalculation for the same vertex, and to make parallel storage for vertices
|
||||
* during reclculation thread-safe. */
|
||||
const Span<float3> positions = pbvh.vert_positions;
|
||||
const OffsetIndices faces = mesh.faces();
|
||||
const Span<int> corner_verts = mesh.corner_verts();
|
||||
|
||||
MutableSpan<bool> update_tags = pbvh.vert_bitmap;
|
||||
|
||||
VectorSet<int> faces_to_update;
|
||||
VectorSet<int> boundary_faces;
|
||||
for (const PBVHNode *node : nodes) {
|
||||
for (const int vert : node->vert_indices.as_span().take_front(node->uniq_verts)) {
|
||||
if (update_tags[vert]) {
|
||||
faces_to_update.add_multiple(pbvh.vert_to_face_map[vert]);
|
||||
}
|
||||
for (const int vert : node->vert_indices.as_span().drop_front(node->uniq_verts)) {
|
||||
boundary_faces.add_multiple(pbvh.vert_to_face_map[vert]);
|
||||
}
|
||||
}
|
||||
|
||||
if (faces_to_update.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
VectorSet<int> verts_to_update;
|
||||
VectorSet<int> boundary_verts;
|
||||
threading::parallel_invoke(
|
||||
[&]() {
|
||||
if (pbvh.deformed) {
|
||||
normals_calc_faces(
|
||||
positions, faces, corner_verts, faces_to_update, pbvh.face_normals_deformed);
|
||||
calc_node_face_normals(
|
||||
positions, faces, corner_verts, pbvh, nodes, pbvh.face_normals_deformed);
|
||||
calc_boundary_face_normals(
|
||||
positions, faces, corner_verts, boundary_faces, pbvh.face_normals_deformed);
|
||||
}
|
||||
else {
|
||||
mesh.runtime->face_normals_cache.update([&](Vector<float3> &r_data) {
|
||||
normals_calc_faces(positions, faces, corner_verts, faces_to_update, r_data);
|
||||
calc_node_face_normals(positions, faces, corner_verts, pbvh, nodes, r_data);
|
||||
calc_boundary_face_normals(positions, faces, corner_verts, boundary_faces, r_data);
|
||||
});
|
||||
/* #SharedCache::update() reallocates cached vectors if they were shared initially. */
|
||||
pbvh.face_normals = mesh.runtime->face_normals_cache.data();
|
||||
@@ -1196,29 +1253,29 @@ static void pbvh_faces_update_normals(PBVH &pbvh, Span<PBVHNode *> nodes, Mesh &
|
||||
},
|
||||
[&]() {
|
||||
/* Update all normals connected to affected faces, even if not explicitly tagged. */
|
||||
verts_to_update.reserve(faces_to_update.size());
|
||||
for (const int face : faces_to_update) {
|
||||
verts_to_update.add_multiple(corner_verts.slice(faces[face]));
|
||||
}
|
||||
|
||||
for (const int vert : verts_to_update) {
|
||||
update_tags[vert] = false;
|
||||
}
|
||||
for (PBVHNode *node : nodes) {
|
||||
node->flag &= ~PBVH_UpdateNormals;
|
||||
boundary_verts.reserve(boundary_faces.size());
|
||||
for (const int face : boundary_faces) {
|
||||
boundary_verts.add_multiple(corner_verts.slice(faces[face]));
|
||||
}
|
||||
});
|
||||
|
||||
if (pbvh.deformed) {
|
||||
normals_calc_verts_simple(
|
||||
pbvh.vert_to_face_map, pbvh.face_normals, verts_to_update, pbvh.vert_normals_deformed);
|
||||
calc_node_vert_normals(
|
||||
pbvh.vert_to_face_map, pbvh.face_normals, nodes, pbvh.vert_normals_deformed);
|
||||
calc_boundary_vert_normals(
|
||||
pbvh.vert_to_face_map, pbvh.face_normals, boundary_verts, pbvh.vert_normals_deformed);
|
||||
}
|
||||
else {
|
||||
mesh.runtime->vert_normals_cache.update([&](Vector<float3> &r_data) {
|
||||
normals_calc_verts_simple(pbvh.vert_to_face_map, pbvh.face_normals, verts_to_update, r_data);
|
||||
calc_node_vert_normals(pbvh.vert_to_face_map, pbvh.face_normals, nodes, r_data);
|
||||
calc_boundary_vert_normals(pbvh.vert_to_face_map, pbvh.face_normals, boundary_verts, r_data);
|
||||
});
|
||||
pbvh.vert_normals = mesh.runtime->vert_normals_cache.data();
|
||||
}
|
||||
|
||||
for (PBVHNode *node : nodes) {
|
||||
node->flag &= ~PBVH_UpdateNormals;
|
||||
}
|
||||
}
|
||||
|
||||
void update_normals(PBVH &pbvh, SubdivCCG *subdiv_ccg)
|
||||
@@ -1230,7 +1287,7 @@ void update_normals(PBVH &pbvh, SubdivCCG *subdiv_ccg)
|
||||
bmesh_normals_update(nodes);
|
||||
}
|
||||
else if (pbvh.header.type == PBVH_FACES) {
|
||||
pbvh_faces_update_normals(pbvh, nodes, *pbvh.mesh);
|
||||
update_normals_faces(pbvh, nodes, *pbvh.mesh);
|
||||
}
|
||||
else if (pbvh.header.type == PBVH_GRIDS) {
|
||||
IndexMaskMemory memory;
|
||||
@@ -1729,12 +1786,6 @@ bool BKE_pbvh_node_fully_unmasked_get(const PBVHNode *node)
|
||||
return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyUnmasked);
|
||||
}
|
||||
|
||||
void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex)
|
||||
{
|
||||
BLI_assert(pbvh->header.type == PBVH_FACES);
|
||||
pbvh->vert_bitmap[vertex.i] = true;
|
||||
}
|
||||
|
||||
blender::Span<int> BKE_pbvh_node_get_loops(const PBVHNode *node)
|
||||
{
|
||||
return node->loop_indices;
|
||||
@@ -1853,17 +1904,6 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node,
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node)
|
||||
{
|
||||
BLI_assert(pbvh->header.type == PBVH_FACES);
|
||||
for (const int vert : node->vert_indices) {
|
||||
if (pbvh->vert_bitmap[vert]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************************* Ray-cast ***********************************/
|
||||
|
||||
namespace blender::bke::pbvh {
|
||||
@@ -2817,7 +2857,6 @@ void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const Span<float3> vert_positions)
|
||||
/* no need for float comparison here (memory is exactly equal or not) */
|
||||
if (memcmp(positions[a], vert_positions[a], sizeof(float[3])) != 0) {
|
||||
positions[a] = vert_positions[a];
|
||||
BKE_pbvh_vert_tag_update_normal(pbvh, BKE_pbvh_make_vref(a));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,10 +161,6 @@ struct PBVH {
|
||||
CCGKey gridkey;
|
||||
SubdivCCG *subdiv_ccg;
|
||||
|
||||
/* Used during BVH build and later to mark that a vertex needs to update
|
||||
* (its normal must be recalculated). */
|
||||
blender::Array<bool> vert_bitmap;
|
||||
|
||||
#ifdef PERFCNTRS
|
||||
int perf_modified;
|
||||
#endif
|
||||
|
||||
@@ -1930,9 +1930,6 @@ static void project_line_gesture_apply_task(SculptGestureContext *sgcontext, PBV
|
||||
continue;
|
||||
}
|
||||
add_v3_v3(vd.co, disp);
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex);
|
||||
}
|
||||
any_updated = true;
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
@@ -1370,9 +1370,6 @@ static void paint_mesh_restore_node(Object *ob, const undo::Type type, PBVHNode
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
|
||||
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
|
||||
copy_v3_v3(vd.co, orig_vert_data.co);
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
BKE_pbvh_node_mark_update(node);
|
||||
@@ -3113,10 +3110,6 @@ static void do_gravity_task(SculptSession *ss,
|
||||
ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask, vd.vertex, thread_id, nullptr);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], offset, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
@@ -684,10 +684,6 @@ static void do_boundary_brush_bend_task(Object *ob, const Brush *brush, PBVHNode
|
||||
boundary->bend.pivot_rotation_axis[vd.index],
|
||||
angle * boundary->edit_info[vd.index].strength_factor * mask * automask);
|
||||
add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -729,10 +725,6 @@ static void do_boundary_brush_slide_task(Object *ob, const Brush *brush, PBVHNod
|
||||
boundary->slide.directions[vd.index],
|
||||
boundary->edit_info[vd.index].strength_factor * disp * mask * automask *
|
||||
strength);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -774,10 +766,6 @@ static void do_boundary_brush_inflate_task(Object *ob, const Brush *brush, PBVHN
|
||||
orig_data.no,
|
||||
boundary->edit_info[vd.index].strength_factor * disp * mask * automask *
|
||||
strength);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -816,10 +804,6 @@ static void do_boundary_brush_grab_task(Object *ob, const Brush *brush, PBVHNode
|
||||
orig_data.co,
|
||||
ss->cache->grab_delta_symmetry,
|
||||
boundary->edit_info[vd.index].strength_factor * mask * automask * strength);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -869,10 +853,6 @@ static void do_boundary_brush_twist_task(Object *ob, const Brush *brush, PBVHNod
|
||||
boundary->twist.rotation_axis,
|
||||
angle * mask * automask * boundary->edit_info[vd.index].strength_factor);
|
||||
add_v3_v3(target_co, boundary->twist.pivot_position);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -924,10 +904,6 @@ static void do_boundary_brush_smooth_task(Object *ob, const Brush *brush, PBVHNo
|
||||
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
|
||||
madd_v3_v3v3fl(
|
||||
target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
@@ -297,10 +297,6 @@ static void do_draw_brush_task(Object *ob, const Brush *brush, const float *offs
|
||||
&automask_data);
|
||||
mul_v3_v3fl(proxy[vd.i], offset, fade);
|
||||
}
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -388,10 +384,6 @@ static void do_fill_brush_task(
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -479,10 +471,6 @@ static void do_scrape_brush_task(
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -593,10 +581,6 @@ static void do_clay_thumb_brush_task(Object *ob,
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -729,10 +713,6 @@ static void do_flatten_brush_task(
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
@@ -867,10 +847,6 @@ static void do_clay_brush_task(
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -984,10 +960,6 @@ static void do_clay_strips_brush_task(Object *ob,
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1174,10 +1146,6 @@ static void do_snake_hook_brush_task(Object *ob,
|
||||
auto_mask::factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data));
|
||||
copy_v3_v3(proxy[vd.i], disp);
|
||||
}
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1253,10 +1221,6 @@ static void do_thumb_brush_task(Object *ob, const Brush *brush, const float *con
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], cono, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1326,10 +1290,6 @@ static void do_rotate_brush_task(Object *ob, const Brush *brush, const float ang
|
||||
mul_v3_m3v3(proxy[vd.i], rot, vec);
|
||||
add_v3_v3(proxy[vd.i], ss->cache->location);
|
||||
sub_v3_v3(proxy[vd.i], orig_data.co);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1434,10 +1394,6 @@ static void do_layer_brush_task(Object *ob, Sculpt *sd, const Brush *brush, PBVH
|
||||
add_v3_v3v3(final_co, vd.co, vdisp);
|
||||
|
||||
SCULPT_clip(sd, ss, vd.co, final_co);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1503,10 +1459,6 @@ static void do_inflate_brush_task(Object *ob, const Brush *brush, PBVHNode *node
|
||||
|
||||
mul_v3_fl(val, fade * ss->cache->radius);
|
||||
mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1557,10 +1509,6 @@ static void do_nudge_brush_task(Object *ob, const Brush *brush, const float *con
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], cono, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1648,10 +1596,6 @@ static void do_crease_brush_task(Object *ob,
|
||||
mul_v3_v3fl(val2, offset, fade);
|
||||
|
||||
add_v3_v3v3(proxy[vd.i], val1, val2);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1761,10 +1705,6 @@ static void do_pinch_brush_task(Object *ob,
|
||||
project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal);
|
||||
}
|
||||
mul_v3_v3fl(proxy[vd.i], disp_center, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1867,10 +1807,6 @@ static void do_grab_brush_task(Object *ob,
|
||||
}
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1969,10 +1905,6 @@ static void do_elastic_deform_brush_task(Object *ob,
|
||||
auto_mask::factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data));
|
||||
|
||||
copy_v3_v3(proxy[vd.i], final_disp);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -2044,10 +1976,6 @@ static void do_draw_sharp_brush_task(Object *ob,
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], offset, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -2151,10 +2079,6 @@ static void do_topology_slide_task(Object *ob, const Brush *brush, PBVHNode *nod
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], final_disp, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -2288,9 +2212,6 @@ static void do_topology_relax_task(Object *ob, const Brush *brush, PBVHNode *nod
|
||||
&automask_data);
|
||||
|
||||
smooth::relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -2370,10 +2291,6 @@ static void do_displacement_eraser_brush_task(Object *ob, const Brush *brush, PB
|
||||
SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co);
|
||||
sub_v3_v3v3(disp, limit_co, vd.co);
|
||||
mul_v3_v3fl(proxy[vd.i], disp, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -2479,10 +2396,6 @@ static void do_displacement_smear_brush_task(Object *ob, const Brush *brush, PBV
|
||||
float new_co[3];
|
||||
add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp);
|
||||
interp_v3_v3v3(vd.co, vd.co, new_co, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -2598,10 +2511,6 @@ static void do_topology_rake_bmesh_task(
|
||||
madd_v3_v3v3fl(val, vd.co, val, fade);
|
||||
|
||||
SCULPT_clip(sd, ss, vd.co, val);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
@@ -783,10 +783,6 @@ static void do_cloth_brush_solve_simulation_task(Object *ob,
|
||||
copy_v3_fl(cloth_sim->acceleration[i], 0.0f);
|
||||
|
||||
copy_v3_v3(vd.co, cloth_sim->pos[vd.index]);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
|
||||
@@ -438,9 +438,6 @@ static void do_relax_face_sets_brush_task(Object *ob,
|
||||
&automask_data);
|
||||
|
||||
smooth::relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -1388,7 +1385,6 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob,
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
if (fair_verts[i]) {
|
||||
interp_v3_v3v3(positions[i], orig_positions[i], positions[i], strength);
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_index_to_vertex(ss->pbvh, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,9 +529,6 @@ static void mesh_filter_task(Object *ob,
|
||||
add_v3_v3v3(final_pos, orig_co, disp);
|
||||
}
|
||||
copy_v3_v3(vd.co, final_pos);
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
|
||||
@@ -180,10 +180,6 @@ static void do_multiplane_scrape_brush_task(Object *ob,
|
||||
&automask_data);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
@@ -190,10 +190,6 @@ static void do_pose_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
|
||||
|
||||
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
|
||||
copy_v3_v3(target_co, final_pos);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
@@ -234,10 +234,6 @@ static void do_enhance_details_brush_task(Object *ob,
|
||||
float disp[3];
|
||||
madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
|
||||
SCULPT_clip(sd, ss, vd.co, disp);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -384,9 +380,6 @@ static void smooth_position_node(
|
||||
sub_v3_v3v3(val, avg, vd.co);
|
||||
madd_v3_v3v3fl(val, vd.co, val, fade);
|
||||
SCULPT_clip(sd, ss, vd.co, val);
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
@@ -527,9 +520,6 @@ static void do_surface_smooth_brush_laplacian_task(Object *ob, const Brush *brus
|
||||
surface_smooth_laplacian_step(
|
||||
ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha);
|
||||
madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
@@ -173,10 +173,6 @@ static void sculpt_transform_task(Object *ob, const float transform_mats[8][4][4
|
||||
sub_v3_v3v3(disp, transformed_co, start_co);
|
||||
mul_v3_fl(disp, 1.0f - fade);
|
||||
add_v3_v3v3(vd.co, start_co, disp);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
@@ -243,10 +239,6 @@ static void sculpt_elastic_transform_task(Object *ob,
|
||||
mul_v3_fl(final_disp, 20.0f * (1.0f - fade));
|
||||
|
||||
copy_v3_v3(proxy[vd.i], final_disp);
|
||||
|
||||
if (vd.is_mesh) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
|
||||
|
||||
@@ -281,6 +281,7 @@ struct PartialUpdateData {
|
||||
bool changed_hide_vert;
|
||||
bool changed_mask;
|
||||
Span<bool> modified_grids;
|
||||
Span<bool> modified_position_verts;
|
||||
Span<bool> modified_hidden_verts;
|
||||
Span<bool> modified_hidden_faces;
|
||||
Span<bool> modified_mask_verts;
|
||||
@@ -290,10 +291,15 @@ struct PartialUpdateData {
|
||||
|
||||
static void update_modified_node_mesh(PBVHNode &node, PartialUpdateData &data)
|
||||
{
|
||||
if (BKE_pbvh_node_has_vert_with_normal_update_tag(data.pbvh, &node)) {
|
||||
BKE_pbvh_node_mark_update(&node);
|
||||
}
|
||||
const Span<int> verts = BKE_pbvh_node_get_vert_indices(&node);
|
||||
if (!data.modified_position_verts.is_empty()) {
|
||||
for (const int vert : verts) {
|
||||
if (data.modified_position_verts[vert]) {
|
||||
BKE_pbvh_node_mark_normals_update(&node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!data.modified_mask_verts.is_empty()) {
|
||||
for (const int vert : verts) {
|
||||
if (data.modified_mask_verts[vert]) {
|
||||
@@ -407,7 +413,8 @@ static bool restore_deformed(
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool restore_coords(bContext *C, Object *ob, Depsgraph *depsgraph, Node &unode)
|
||||
static bool restore_coords(
|
||||
bContext *C, Object *ob, Depsgraph *depsgraph, Node &unode, MutableSpan<bool> modified_verts)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
|
||||
@@ -471,20 +478,20 @@ static bool restore_coords(bContext *C, Object *ob, Depsgraph *depsgraph, Node &
|
||||
if (ss->deform_modifiers_active) {
|
||||
for (const int i : index.index_range()) {
|
||||
restore_deformed(ss, unode, i, index[i], positions[index[i]]);
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i]));
|
||||
modified_verts[index[i]] = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : index.index_range()) {
|
||||
swap_v3_v3(positions[index[i]], unode.orig_position[i]);
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i]));
|
||||
modified_verts[index[i]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : index.index_range()) {
|
||||
swap_v3_v3(positions[index[i]], unode.position[i]);
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i]));
|
||||
modified_verts[index[i]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -916,6 +923,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, UndoSculpt &usculpt)
|
||||
/* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep
|
||||
* track of other updates. In order to tell the corresponding PBVH nodes to update, keep track
|
||||
* of which elements were updated for specific layers. */
|
||||
Vector<bool> modified_verts_position;
|
||||
Vector<bool> modified_verts_hide;
|
||||
Vector<bool> modified_faces_hide;
|
||||
Vector<bool> modified_verts_mask;
|
||||
@@ -945,7 +953,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, UndoSculpt &usculpt)
|
||||
|
||||
switch (unode->type) {
|
||||
case Type::Position:
|
||||
if (restore_coords(C, ob, depsgraph, *unode)) {
|
||||
if (restore_coords(C, ob, depsgraph, *unode, modified_verts_position)) {
|
||||
changed_position = true;
|
||||
}
|
||||
break;
|
||||
@@ -1024,6 +1032,7 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, UndoSculpt &usculpt)
|
||||
data.changed_mask = changed_mask;
|
||||
data.pbvh = ss->pbvh;
|
||||
data.modified_grids = modified_grids;
|
||||
data.modified_hidden_verts = modified_verts_position;
|
||||
data.modified_hidden_verts = modified_verts_hide;
|
||||
data.modified_hidden_faces = modified_faces_hide;
|
||||
data.modified_mask_verts = modified_verts_mask;
|
||||
|
||||
Reference in New Issue
Block a user