Fix #114981 : Add box, circle and lasso selection tools to weight paint + bone select mode #115807
|
@ -1660,10 +1660,9 @@ class _defs_weight_paint:
|
|||
ob = context.active_object
|
||||
if (ob and ob.type == 'MESH' and
|
||||
(ob.data.use_paint_mask or
|
||||
ob.data.use_paint_mask_vertex)):
|
||||
ob.data.use_paint_mask_vertex or
|
||||
context.pose_object)):
|
||||
return VIEW3D_PT_tools_active._tools_select
|
||||
elif context.pose_object:
|
||||
return (_defs_view3d_select.select,)
|
||||
return ()
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -199,13 +199,18 @@ void BKE_paint_palette_set(Paint *p, Palette *palette);
|
|||
void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, int add_index);
|
||||
|
||||
/**
|
||||
* Return true when in vertex/weight/texture paint + face-select mode?
|
||||
* Return true when in vertex/weight/texture paint + face-select mode.
|
||||
*/
|
||||
bool BKE_paint_select_face_test(const Object *ob);
|
||||
/**
|
||||
* Return true when in vertex/weight paint + vertex-select mode?
|
||||
* Return true when in vertex/weight paint + vertex-select mode.
|
||||
*/
|
||||
bool BKE_paint_select_vert_test(const Object *ob);
|
||||
/**
|
||||
* Return true when in weight paint + bone-select mode.
|
||||
*/
|
||||
daisea3e1203 marked this conversation as resolved
Outdated
|
||||
bool BKE_paint_select_bone_test(const Object *ob);
|
||||
|
||||
/**
|
||||
* used to check if selection is possible
|
||||
* (when we don't care if its face or vert)
|
||||
|
|
|
@ -1023,16 +1023,36 @@ bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, co
|
|||
|
||||
bool BKE_paint_select_face_test(const Object *ob)
|
||||
{
|
||||
return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
|
||||
(((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) &&
|
||||
(ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)));
|
||||
if (ob == nullptr || ob->type != OB_MESH ||
|
||||
(ob->mode & (OB_MODE_WEIGHT_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
return mesh->editflag & ME_EDIT_PAINT_FACE_SEL;
|
||||
}
|
||||
|
||||
bool BKE_paint_select_vert_test(const Object *ob)
|
||||
{
|
||||
return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
|
||||
(((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_VERT_SEL) &&
|
||||
(ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT));
|
||||
if (ob == nullptr || ob->type != OB_MESH ||
|
||||
(ob->mode & (OB_MODE_WEIGHT_PAINT | OB_MODE_VERTEX_PAINT)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
return mesh->editflag & ME_EDIT_PAINT_VERT_SEL;
|
||||
}
|
||||
|
||||
bool BKE_paint_select_bone_test(const Object *ob)
|
||||
{
|
||||
Sybren A. Stüvel
commented
This will be clearer when written as separate expressions. I know the current code is consistent with the existing code, but I find the existing code really hard to read too, and I don't think we should duplicate that.
This separates the precondition checks from the actual query. I also think the query is incorrect, as it checks whether any flag is set, ignoring
Note that I didn't include the check for This will be clearer when written as separate expressions. I know the current code is consistent with the existing code, but I find the existing code really hard to read too, and I don't think we should duplicate that.
```cpp
if (ob == nullptr || ob->type != OB_MESH || (ob->mode & OB_MODE_WEIGHT_PAINT) == 0) {
return false;
}
Mesh *mesh = static_cast<Mesh *>(ob->data);
return mesh->editflag & ~(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL);
```
This separates the precondition checks from the actual query.
I also think the query is incorrect, as it checks whether any flag is set, ignoring `ME_EDIT_PAINT_FACE_SEL` and `ME_EDIT_PAINT_VERT_SEL`. IMO it should check that these flags are *not* set. I suspect this is the right check, but please do verify
```cpp
return (mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) == 0;
```
Note that I didn't include the check for `ob->data != nullptr`, as this is implied by `ob->type == OB_MESH`. If a Mesh object doesn't have any data, there is something very wrong and other areas in the code will have stopped working already. This function is not the place to try and solve this problem.
Daiki Hashimoto
commented
Thank you very much for the review and the insights about mesh data 🙏 The query is incorrect as you have pointed out, and have probably lead to the tools not functioning properly in some situations as @nathanvegdahl has demonstrated below. This has been fixed in I have also refactored the two functions above in a similar fashion in Thank you very much for the review and the insights about mesh data 🙏
The query is incorrect as you have pointed out, and have probably lead to the tools not functioning properly in some situations as @nathanvegdahl has demonstrated below. This has been fixed in e11fce8298 .
I have also refactored the two functions above in a similar fashion in 93a58c7601, but if you think this change should occur in a different PR or occasion, please let me know.
Sybren A. Stüvel
commented
Ideally, such cleanups would happen before this PR (first cleanup, then change functionality). In this case I don't think that would be worth the effort, though. > This has been fixed in e11fce8298 .
:+1:
> if you think this change should occur in a different PR or occasion, please let me know.
Yup, that's for a different PR.
Ideally, such cleanups would happen *before* this PR (first cleanup, then change functionality). In this case I don't think that would be worth the effort, though.
Daiki Hashimoto
commented
Thank you very much for the feedback!
When I attempt to make a large change to the codebase in the future, I will make sure to create a separate clean up PR beforehand. Thank you very much for the feedback!
> Ideally, such cleanups would happen before this PR (first cleanup, then change functionality). In this case I don't think that would be worth the effort, though.
When I attempt to make a large change to the codebase in the future, I will make sure to create a separate clean up PR beforehand.
Sybren A. Stüvel
commented
Sorry for the back-and-forth, but looking back at this now, I do think that it's better to have this change as a small separate PR that we can land before this one. That way this little fix can be described by itself, and in the hypothetical case that it causes any issues, rolled back by itself, without having to deal with this entire PR. Sorry for the back-and-forth, but looking back at this now, I do think that it's better to have this change as a small separate PR that we can land before this one. That way this little fix can be described by itself, and in the hypothetical case that it causes any issues, rolled back by itself, without having to deal with this entire PR.
|
||||
if (ob == nullptr || ob->type != OB_MESH || (ob->mode & OB_MODE_WEIGHT_PAINT) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
return (mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) == 0;
|
||||
}
|
||||
|
||||
bool BKE_paint_select_elem_test(const Object *ob)
|
||||
|
|
|
@ -468,9 +468,14 @@ static bool view3d_selectable_data(bContext *C)
|
|||
}
|
||||
}
|
||||
else {
|
||||
if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
|
||||
!BKE_paint_select_elem_test(ob))
|
||||
{
|
||||
if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) &&
|
||||
!BKE_paint_select_elem_test(ob)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *ob_armature = BKE_object_pose_armature_get(ob);
|
||||
if (ob->mode & OB_MODE_WEIGHT_PAINT && !BKE_paint_select_elem_test(ob) && !ob_armature) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1378,6 +1383,12 @@ static bool view3d_lasso_select(bContext *C,
|
|||
else if (BKE_paint_select_vert_test(ob)) {
|
||||
changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcoords, mcoords_len, sel_op);
|
||||
}
|
||||
else if (ob && (ob->mode & OB_MODE_POSE || BKE_paint_select_bone_test(ob))) {
|
||||
changed_multi |= do_lasso_select_pose(vc, mcoords, mcoords_len, sel_op);
|
||||
if (changed_multi) {
|
||||
ED_outliner_select_sync_from_pose_bone_tag(C);
|
||||
}
|
||||
}
|
||||
else if (ob &&
|
||||
(ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
|
||||
/* pass */
|
||||
|
@ -1385,12 +1396,6 @@ static bool view3d_lasso_select(bContext *C,
|
|||
else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
|
||||
changed_multi |= PE_lasso_select(C, mcoords, mcoords_len, sel_op) != OPERATOR_CANCELLED;
|
||||
}
|
||||
else if (ob && (ob->mode & OB_MODE_POSE)) {
|
||||
changed_multi |= do_lasso_select_pose(vc, mcoords, mcoords_len, sel_op);
|
||||
if (changed_multi) {
|
||||
ED_outliner_select_sync_from_pose_bone_tag(C);
|
||||
}
|
||||
}
|
||||
else {
|
||||
changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op);
|
||||
if (changed_multi) {
|
||||
|
@ -2911,6 +2916,14 @@ static bool ed_object_select_pick(bContext *C,
|
|||
if ((found && select_passthrough) && (basact->flag & BASE_SELECTED)) {
|
||||
found = false;
|
||||
}
|
||||
else if (BKE_paint_select_bone_test(vc.obact)) {
|
||||
/* pass */
|
||||
|
||||
/* During weight paint mode, when there are multiple armatures deforming a single mesh,
|
||||
* object_deselect_all_except might switch the active armature away from pose mode, making
|
||||
* selection of bones with the select tools not possible afterwards.
|
||||
* Passing here makes sure armatures stay in pose mode. */
|
||||
}
|
||||
else if (found || params->deselect_all) {
|
||||
/* Deselect everything. */
|
||||
/* `basact` may be nullptr. */
|
||||
|
@ -4353,7 +4366,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
|
|||
else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
|
||||
changed_multi = PE_box_select(C, &rect, sel_op);
|
||||
}
|
||||
else if (vc.obact && vc.obact->mode & OB_MODE_POSE) {
|
||||
else if (vc.obact && (vc.obact->mode & OB_MODE_POSE || BKE_paint_select_bone_test(vc.obact))) {
|
||||
changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
|
||||
if (changed_multi) {
|
||||
ED_outliner_select_sync_from_pose_bone_tag(C);
|
||||
|
@ -5307,6 +5320,21 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
FOREACH_OBJECT_IN_MODE_END;
|
||||
}
|
||||
/* Select bones from pose mode armature in weight paint + bone-select mode. */
|
||||
daisea3e1203 marked this conversation as resolved
Outdated
Daiki Hashimoto
commented
Addressed in Addressed in 05bb4f72e0
Thank you for spotting these mistakes! Will be careful with comments in future PRs 🙏
|
||||
else if (BKE_paint_select_bone_test(obact)) {
|
||||
view3d_operator_needs_opengl(C);
|
||||
BKE_object_update_select_id(CTX_data_main(C));
|
||||
|
||||
ViewContext vc_tmp;
|
||||
FOREACH_OBJECT_IN_MODE_BEGIN (
|
||||
vc.scene, vc.view_layer, vc.v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
|
||||
vc_tmp = vc;
|
||||
vc_tmp.obact = ob_iter;
|
||||
pose_circle_select(&vc_tmp, sel_op, mval, float(radius));
|
||||
ED_outliner_select_sync_from_pose_bone_tag(C);
|
||||
}
|
||||
FOREACH_OBJECT_IN_MODE_END;
|
||||
}
|
||||
else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
|
||||
if (PE_circle_select(C, wm_userdata, sel_op, mval, float(radius))) {
|
||||
return OPERATOR_FINISHED;
|
||||
|
|
The blender comment style is without the double slash (//) at the start
Also I feel like the comment should not be a question. I know this is the same as the functions above,
but it's confusing to read.
I assume its just a statement. As in it returns true when in weight paint + bone-select mode.
I applied the same change in
09a3f4b9b1
to the two functions above (change the question mark to a period) since I encountered them many times when coding for this PR, and were used as a statement to check for their respective modes.