Fix #119932: Weight paint loop selection fails when bones are rotated
Previous approach was using non-evaluated meshes to find the closest edge from a face index retrieved from the selection buffer. This can fail though if the mesh is deformed or even altered to not have the same indices anymore (e.g. generating/masking modifiers). In order to make this work, added `ED_mesh_pick_edge` which switches the selection context to edges directly and picks from those. No need to map anything back and forth between original and evaluated, the selection buffer contains the original indices already. Further logic (following the face loop) is untouched (and happens on the original geometry which is fine) Should go into 4.2 LTS as well Pull Request: https://projects.blender.org/blender/blender/pulls/132803
This commit is contained in:
committed by
Philipp Oeser
parent
bee534eea5
commit
eecf5787c1
@@ -572,6 +572,11 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px,
|
||||
*/
|
||||
bool ED_mesh_pick_face_vert(
|
||||
bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index);
|
||||
/**
|
||||
* Used for paint face loop selection which needs to get closest edge even though in face select
|
||||
* mode. Changes the select_buffer context to edge selection for this.
|
||||
*/
|
||||
bool ED_mesh_pick_edge(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index);
|
||||
|
||||
MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve);
|
||||
MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index);
|
||||
|
||||
@@ -347,36 +347,6 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
|
||||
paintface_flush_flags(C, ob, true, false);
|
||||
}
|
||||
|
||||
static int find_closest_edge_in_poly(ARegion *region,
|
||||
blender::Span<blender::int2> edges,
|
||||
blender::Span<int> face_edges,
|
||||
blender::Span<blender::float3> vert_positions,
|
||||
const int mval[2])
|
||||
{
|
||||
using namespace blender;
|
||||
int closest_edge_index = -1;
|
||||
|
||||
const float2 mval_f = {float(mval[0]), float(mval[1])};
|
||||
float min_distance = FLT_MAX;
|
||||
for (const int i : face_edges) {
|
||||
float2 screen_coordinate;
|
||||
const int2 edge = edges[i];
|
||||
const float3 edge_vert_average = math::midpoint(vert_positions[edge[0]],
|
||||
vert_positions[edge[1]]);
|
||||
eV3DProjStatus status = ED_view3d_project_float_object(
|
||||
region, edge_vert_average, screen_coordinate, V3D_PROJ_TEST_CLIP_DEFAULT);
|
||||
if (status != V3D_PROJ_RET_OK) {
|
||||
continue;
|
||||
}
|
||||
const float distance = math::distance_squared(mval_f, screen_coordinate);
|
||||
if (distance < min_distance) {
|
||||
min_distance = distance;
|
||||
closest_edge_index = i;
|
||||
}
|
||||
}
|
||||
return closest_edge_index;
|
||||
}
|
||||
|
||||
static int get_opposing_edge_index(const blender::IndexRange face,
|
||||
const blender::Span<int> corner_edges,
|
||||
const int current_edge_index)
|
||||
@@ -458,8 +428,12 @@ void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const boo
|
||||
return;
|
||||
}
|
||||
|
||||
uint poly_pick_index = uint(-1);
|
||||
if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &poly_pick_index)) {
|
||||
uint closest_edge_index = uint(-1);
|
||||
if (!ED_mesh_pick_edge(C, ob, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, &closest_edge_index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (closest_edge_index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -469,16 +443,7 @@ void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const boo
|
||||
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
const Span<int> corner_edges = mesh->corner_edges();
|
||||
const Span<float3> verts = mesh->vert_positions();
|
||||
const OffsetIndices faces = mesh->faces();
|
||||
const Span<int2> edges = mesh->edges();
|
||||
|
||||
const IndexRange face = faces[poly_pick_index];
|
||||
const int closest_edge_index = find_closest_edge_in_poly(
|
||||
region, edges, corner_edges.slice(face), verts, mval);
|
||||
if (closest_edge_index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> edge_to_face_offsets;
|
||||
Array<int> edge_to_face_indices;
|
||||
@@ -492,6 +457,12 @@ void paintface_select_loop(bContext *C, Object *ob, const int mval[2], const boo
|
||||
".hide_poly", bke::AttrDomain::Face, false);
|
||||
|
||||
const Span<int> faces_to_closest_edge = edge_to_face_map[closest_edge_index];
|
||||
|
||||
/* Picked edge may not be linked to a face (loose edge). */
|
||||
if (faces_to_closest_edge.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool traced_full_loop = follow_face_loop(faces_to_closest_edge[0],
|
||||
closest_edge_index,
|
||||
faces,
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "BKE_deform.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_key.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
@@ -1287,6 +1288,49 @@ bool ED_mesh_pick_face_vert(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_mesh_pick_edge(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
|
||||
BLI_assert(mesh && GS(mesh->id.name) == ID_ME);
|
||||
|
||||
if (!mesh || mesh->edges_num == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph);
|
||||
ED_view3d_select_id_validate(&vc);
|
||||
Base *base = BKE_view_layer_base_find(vc.view_layer, vc.obact);
|
||||
DRW_select_buffer_context_create(vc.depsgraph, {base}, SCE_SELECT_EDGE);
|
||||
|
||||
uint edge_idx_best = ORIGINDEX_NONE;
|
||||
|
||||
if (dist_px) {
|
||||
/* Sample rect to increase chances of selecting, so that when clicking
|
||||
* on an edge in the back-buffer, we can still select a face. */
|
||||
edge_idx_best = DRW_select_buffer_find_nearest_to_point(
|
||||
vc.depsgraph, vc.region, vc.v3d, mval, 1, mesh->edges_num + 1, &dist_px);
|
||||
}
|
||||
else {
|
||||
/* sample only on the exact position */
|
||||
edge_idx_best = DRW_select_buffer_sample_point(vc.depsgraph, vc.region, vc.v3d, mval);
|
||||
}
|
||||
|
||||
if (edge_idx_best == 0 || edge_idx_best > uint(mesh->edges_num)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
edge_idx_best--;
|
||||
|
||||
if ((edge_idx_best != ORIGINDEX_NONE)) {
|
||||
*r_index = edge_idx_best;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertex selection in object mode,
|
||||
* currently only weight paint uses this.
|
||||
|
||||
Reference in New Issue
Block a user