Animation: Weight Paint select more/less for faces #105607
|
@ -401,6 +401,30 @@ void paintface_select_more(bContext *C, Object *ob, const bool face_step)
|
|||
paintface_flush_flags(C, ob, true, false);
|
||||
}
|
||||
|
||||
static bool poly_has_unselected_neighbour(const MPoly &poly,
|
||||
|
||||
blender::Span<MLoop> poly_loops,
|
||||
blender::Span<MEdge> edges,
|
||||
blender::BitVector<> &verts_of_unselected_faces,
|
||||
ChrisLend marked this conversation as resolved
Outdated
Hans Goudey
commented
Replace the Replace the `BitVector` reference with `const BitSpan`, that will give a better idea of ownership and the fact that this doesn't need to modify the data.
|
||||
const bool face_step)
|
||||
{
|
||||
for (const MLoop &loop : poly_loops.slice(poly.loopstart, poly.totloop)) {
|
||||
const MEdge &edge = edges[loop.e];
|
||||
bool unselected_neighbor = false;
|
||||
Hans Goudey
commented
It's not a big deal, but the function could be a bit simpler without the It's not a big deal, but the function could be a bit simpler without the `unselected_neighbor` variable. It's nice to assign a variable a meaningful value in the same expression you initialize it, and that's not really the case here. Given the function name, returning early reads as "poly has an unselected neighbor" anyway.
|
||||
if (face_step) {
|
||||
unselected_neighbor = verts_of_unselected_faces[edge.v1].test() ||
|
||||
ChrisLend marked this conversation as resolved
Outdated
Hans Goudey
commented
What do you think about removing the What do you think about removing the `.test()` and using `BitRef`'s implicit bool conversion?
|
||||
verts_of_unselected_faces[edge.v2].test();
|
||||
}
|
||||
else {
|
||||
unselected_neighbor = verts_of_unselected_faces[edge.v1].test() &&
|
||||
verts_of_unselected_faces[edge.v2].test();
|
||||
}
|
||||
if (unselected_neighbor) {
|
||||
return true;
|
||||
ChrisLend marked this conversation as resolved
Outdated
Hans Goudey
commented
I assume you're using I assume you're using `std::vector<bool>` because it works as a bitmap internally? `blender::BitVector` should be a better choice here. See the reasoning at the top of that header.
|
||||
}
|
||||
}
|
||||
return false;
|
||||
ChrisLend marked this conversation as resolved
Outdated
Hans Goudey
commented
Small thing, but maybe it's a bit clearer to write Small thing, but maybe it's a bit clearer to write `polys.index_range()` than `select_poly.span.index_range()`. That's just semantically closer to the goal of iterating over all polys.
|
||||
}
|
||||
|
||||
void paintface_select_less(bContext *C, Object *ob, const bool face_step)
|
||||
{
|
||||
using namespace blender;
|
||||
|
@ -419,7 +443,7 @@ void paintface_select_less(bContext *C, Object *ob, const bool face_step)
|
|||
const Span<MLoop> loops = mesh->loops();
|
||||
Hans Goudey
commented
`MLoop` has been replaced by two arrays in `main`. This loop can be simplified now:
```
for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) {
...
}
```
|
||||
const Span<MEdge> edges = mesh->edges();
|
||||
|
||||
std::vector<bool> verts_of_unselected_faces(mesh->totvert, false);
|
||||
BitVector<> verts_of_unselected_faces(mesh->totvert, false);
|
||||
|
||||
/* Find all vertices of unselected faces to help find neighboring faces after. */
|
||||
for (const int i : select_poly.span.index_range()) {
|
||||
Hans Goudey
commented
Might as well change this to I realize that's a bit nitpicky, just hoping you might agree and appreciate the more literal semantic argument :P Might as well change this to `polys.index_range()` instead of `select_poly.span.index_range()` for the same reason I mentioned earlier too-- it just says more clearly "we're iterating over all faces" rather than "we're iterating over the face selection".
I realize that's a bit nitpicky, just hoping you might agree and appreciate the more literal semantic argument :P
|
||||
|
@ -428,32 +452,19 @@ void paintface_select_less(bContext *C, Object *ob, const bool face_step)
|
|||
}
|
||||
const MPoly &poly = polys[i];
|
||||
for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
|
||||
verts_of_unselected_faces[loop.v] = true;
|
||||
verts_of_unselected_faces[loop.v].set(true);
|
||||
Hans Goudey
commented
`vert_index` -> `vert` here too, though I mentioned that in chat
|
||||
}
|
||||
}
|
||||
|
||||
threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) {
|
||||
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
if (!select_poly.span[i] || hide_poly[i]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = polys[i];
|
||||
|
||||
for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
|
||||
const MEdge &edge = edges[loop.e];
|
||||
bool unselected_neighbor = false;
|
||||
if (face_step) {
|
||||
unselected_neighbor = verts_of_unselected_faces[edge.v1] ||
|
||||
verts_of_unselected_faces[edge.v2];
|
||||
}
|
||||
else {
|
||||
unselected_neighbor = verts_of_unselected_faces[edge.v1] &&
|
||||
verts_of_unselected_faces[edge.v2];
|
||||
}
|
||||
if (unselected_neighbor) {
|
||||
select_poly.span[i] = false;
|
||||
break;
|
||||
}
|
||||
if (poly_has_unselected_neighbour(
|
||||
poly, loops, edges, verts_of_unselected_faces, face_step)) {
|
||||
select_poly.span[i] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
It might be clearer for these functions to have slightly lower level arguments, like
(Mesh &mesh, const bool face_step)
in this case. That separates the abstraction levels more clearly, and means this function could be used in other situations where the context is different or the update tags aren't the same.This might be my limited understanding of C++ but I can't get this to work.
Could it be that because the header file is
ED_mesh.h
meaning it's pure C so it doesn't understand references?Edit since it seems to not have linked to your comment.
It was about passing in
Mesh &mesh
instead of bContext and ObjectYeah right, a couple options-- keep a function with this signature in the public header, and a static function with the signature I suggested, or use the signature I suggested with a pointer instead of a reference (that's probably the better option IMO).
Mainly I think it's nice to avoid just using the object argument to retrieve the mesh, and it's nice to avoid the null check because this function really shouldn't be concerned with whether there is no mesh, that's the job of somewhere else.
now takes a
Mesh*
a potential clean up is to check if that can be done for other functions as well