Refactor: Weight Paint Select Linked Faces #104577
No reviewers
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset System
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Code Documentation
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Viewport & EEVEE
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Asset Browser Project
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Module
Viewport & EEVEE
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Severity
High
Severity
Low
Severity
Normal
Severity
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#104577
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "ChrisLend/blender:weight_paint_refactor_face_select"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
When implementing the operator to select linked vertices in weight paint mode, the new
AtomicDisjointSet
was used.In order to keep the code consistent, and also prepare it to add things like Extending/Shrinking selection, the select linked faces logic was also updated.
It now also makes use of the
AtomicDisjointSet
by connecting all edges of each poly. In order to find connecting Faces you then have to check if edges of that poly share a connection.I tried to connect poly indices directly but there didn't seem to be a good way to find out which faces are connected without going through the edges first. So I stuck with the edges.
Very nice! Using edge indices in the disjoint set is a nice way to avoid topology mappings. It doesn't seem obvious though, compared to the recently added logic from
FaceSetFromBoundariesInput
, for example. I'd think that it wouldn't let two faces separated by a seam be in different islands/groups. What do you think about that? I'll test it after the next update, for now I just looked at the code.@ -228,4 +264,0 @@
const Span<MLoop> loops = me->loops();
bke::MutableAttributeAccessor attributes = me->attributes_for_write();
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
".hide_poly", ATTR_DOMAIN_FACE, false);
Maybe I'm forgetting something, but why is it that we can ignore
hide_poly
now?thanks I missed that. Of course we can't ignore that :)
@ -214,2 +214,3 @@
/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */
/**
* Join all edges of each poly in the AtomicDisjointSet. This can be used to find out which polys
Mesh *mesh
->Mesh &mesh
Using a reference indicates that the pointer won't be null
@ -216,0 +233,4 @@
for (const MPoly &poly : polys.slice(range)) {
/* All edges of the face need to be joined in the DisjointSet. Unless the are marked as seam.
*/
for (int outer_loop_index = 0; outer_loop_index < poly.totloop; outer_loop_index++) {
Grammar: "Unless the are"
Also, the previous comment said the same thing, this is redundant.
@ -216,0 +235,4 @@
*/
for (int outer_loop_index = 0; outer_loop_index < poly.totloop; outer_loop_index++) {
const MLoop *outer_mloop = &loops[poly.loopstart + outer_loop_index];
if (skip_seams && (edges[outer_mloop->e].flag & ME_SEAM) != 0) {
for (const int loop : IndexRange(poly.loopstart, poly.totloop)) {
@ -232,2 +267,4 @@
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
".select_poly", ATTR_DOMAIN_FACE);
MPoly poly
->const MPoly &poly
@ -248,3 +272,1 @@
const MPoly &poly = polys[i];
BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]);
BLI_BITMAP_ENABLE(poly_tag, i);
Set<int> selected_roots;
Try to structure C++ code so that you don't resort to using raw pointers. That will usually lead to more readable code. The C-style for loop syntax is another good thing to avoid.
In this case:
for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
This applies to other areas in this function too
I changed out all for loops except
for (int inner_loop_index = outer_loop_index + 1; inner_loop_index < poly.totloop; inner_loop_index++)
I found it a bit confusing to use
IndexRange
for that use case since I'd have to subtract from the length each time. Might just be me but I find the classic syntax reads clearer in that caseYeah, since the constructor uses
start, size
rather thanstart, end
, that makes sense.using
const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
as you suggested also solved that issue :)@ -282,0 +285,4 @@
threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) {
for (const int poly_index : range) {
MPoly poly = polys[poly_index];
const MLoop *loop = &loops[poly.loopstart];
Add
using namespace blender
here so it's not necessary below.@ -296,3 +299,4 @@
select_poly.finish();
}
void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select)
Looks like this early return will skip
select_poly.finish();
. That will probably print a warning to the terminal.@HooglyBoogly
I had a look at
FaceSetFromBoundariesInput
honestly I didn't even think of using a map. It could work though if there is a way to ignore hidden faces and seams.@ -218,0 +215,4 @@
/**
* Join all edges of each poly in the AtomicDisjointSet. This can be used to find out which polys
* are connected to each other.
* @param islands Is expected to be of length mesh->totedge.
@param islands
->\param islands:
https://wiki.blender.org/wiki/Style_Guide/C_Cpp#Comments
@ -218,0 +220,4 @@
*/
static void build_poly_connections(blender::AtomicDisjointSet &islands,
Mesh &mesh,
const bool skip_seams = true)
Is there a reason to have the
skip_seams
argument right now? If not, it can always be added easily later.My idea was that it makes it explicit what the function is doing. I thought it's not immediately obvious that it would stop at seams.
Let me know what you think. I can remove it until needed otherwise
Not sure, I feel like there must be a better way to indicate the purpose besides adding an unused argument-- the name or the docstring? But it's also not a big deal at all.
@ -249,2 +241,2 @@
BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]);
BLI_BITMAP_ENABLE(poly_tag, i);
const MPoly &poly = polys[poly_index];
for (const int outer_loop_index : IndexRange(0, poly.totloop)) {
I suggest using a separate
const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
variable. Then the next loop can usefor (const int outer_loop_index : poly_loops.index_range()) {
and you don't have to addpoly.totloop
every time a loop in the poly is accessed.@ -250,1 +241,3 @@
BLI_BITMAP_ENABLE(poly_tag, i);
const MPoly &poly = polys[poly_index];
for (const int outer_loop_index : IndexRange(0, poly.totloop)) {
const MLoop *outer_mloop = &loops[poly.loopstart + outer_loop_index];
Use references instead of pointers, that should be your default in C++ code.
sure thing, missed that
out of curiosity: what is the reason for it?
There are quite a few benefits-- they indicate non-null, they allow using
.
instead of->
, they can't be indexed (so they can't be mistaken for pointers to arrays). There's more info here:https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable
@ -304,3 +311,4 @@
return;
}
Vector<int> indices;
Declare this closer to where it's first used, right above
if (mval) {
I've quickly browsed through the code, looks good! The newer C++ structures sure help a lot.
@ChrisLend next time don't forget to edit the commit message here for the style guide-- the line length in particular here. Thanks.