Fix #120239: Snap in edit mode is ignoring some self elements
Fix snapping issues caused by commit 1c77779160, where a mesh
representing the edited mesh was introduced.
Mesh snap to vertex now works in the following order:
- Snap to vertices of visible triangles
- Snap to vertices of loose edges
- Snap to loose vertices
The problem arises because in editing mode, faces whose vertices are
being transformed are ignored, marked as hidden in the snap, resulting
in the loss of some vertices in triangles.
The solution involves considering the edges and vertices of hidden
faces as loose elements since, despite being connected to faces, they
are still visible to snap. Two new types of BVHTree were created for
this purpose:
- BVHTREE_FROM_LOOSEVERTS_NO_HIDDEN
- BVHTREE_FROM_LOOSEEDGES_NO_HIDDEN
This modification addresses two related issues:
- snapping in edit mode to vertices and edges of a face being
transformed
- snapping to mesh with hidden loose elements
Optionally, the first issue could be tackled separately by generating
BVHTrees within the snap system itself and storing them in a
`SnapCache_EditMesh *em_cache`, then passing this cache as a parameter
to the `snap_object_mesh` function.
Pull Request: https://projects.blender.org/blender/blender/pulls/120270
This commit is contained in:
committed by
Germano Cavalcante
parent
b5a82cd722
commit
3fc29d8080
@@ -55,6 +55,12 @@ enum BVHCacheType {
|
||||
BVHTREE_FROM_LOOSEVERTS,
|
||||
BVHTREE_FROM_LOOSEEDGES,
|
||||
|
||||
/* These types consider geometry visibility when getting loose elements.
|
||||
* NOTE: If the element is linked to a face or edge that is hidden, but the element itself is not
|
||||
* hidden, it is considered a loose element. */
|
||||
BVHTREE_FROM_LOOSEVERTS_NO_HIDDEN,
|
||||
BVHTREE_FROM_LOOSEEDGES_NO_HIDDEN,
|
||||
|
||||
/* Keep `BVHTREE_MAX_ITEM` as last item. */
|
||||
BVHTREE_MAX_ITEM,
|
||||
};
|
||||
|
||||
@@ -509,14 +509,15 @@ static void bvhtree_from_mesh_setup_data(BVHTree *tree,
|
||||
switch (bvh_cache_type) {
|
||||
case BVHTREE_FROM_VERTS:
|
||||
case BVHTREE_FROM_LOOSEVERTS:
|
||||
case BVHTREE_FROM_LOOSEVERTS_NO_HIDDEN:
|
||||
/* a nullptr nearest callback works fine
|
||||
* remember the min distance to point is the same as the min distance to BV of point */
|
||||
r_data->nearest_callback = nullptr;
|
||||
r_data->raycast_callback = mesh_verts_spherecast;
|
||||
break;
|
||||
|
||||
case BVHTREE_FROM_EDGES:
|
||||
case BVHTREE_FROM_LOOSEEDGES:
|
||||
case BVHTREE_FROM_LOOSEEDGES_NO_HIDDEN:
|
||||
r_data->nearest_callback = mesh_edges_nearest_point;
|
||||
r_data->raycast_callback = mesh_edges_spherecast;
|
||||
break;
|
||||
@@ -780,6 +781,89 @@ BVHTree *bvhtree_from_mesh_corner_tris_ex(BVHTreeFromMesh *data,
|
||||
return tree;
|
||||
}
|
||||
|
||||
static BitVector<> loose_verts_no_hidden_mask_get(const Mesh &mesh, int *r_elem_active_len)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
|
||||
int count = mesh.verts_num;
|
||||
BitVector<> verts_mask(count, true);
|
||||
|
||||
const AttributeAccessor attributes = mesh.attributes();
|
||||
const Span<int2> edges = mesh.edges();
|
||||
const VArray<bool> hide_edge = *attributes.lookup_or_default(
|
||||
".hide_edge", AttrDomain::Edge, false);
|
||||
const VArray<bool> hide_vert = *attributes.lookup_or_default(
|
||||
".hide_vert", AttrDomain::Point, false);
|
||||
|
||||
for (const int i : edges.index_range()) {
|
||||
if (hide_edge[i]) {
|
||||
continue;
|
||||
}
|
||||
for (const int vert : {edges[i][0], edges[i][1]}) {
|
||||
if (verts_mask[vert]) {
|
||||
verts_mask[vert].reset();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
for (const int vert : verts_mask.index_range()) {
|
||||
if (verts_mask[vert] && hide_vert[vert]) {
|
||||
verts_mask[vert].reset();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*r_elem_active_len = count;
|
||||
|
||||
return verts_mask;
|
||||
}
|
||||
|
||||
static BitVector<> loose_edges_no_hidden_mask_get(const Mesh &mesh, int *r_elem_active_len)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
|
||||
int count = mesh.edges_num;
|
||||
BitVector<> edge_mask(count, true);
|
||||
|
||||
const AttributeAccessor attributes = mesh.attributes();
|
||||
const OffsetIndices faces = mesh.faces();
|
||||
const Span<int> corner_edges = mesh.corner_edges();
|
||||
const VArray<bool> hide_poly = *attributes.lookup_or_default(
|
||||
".hide_poly", AttrDomain::Face, false);
|
||||
const VArray<bool> hide_edge = *attributes.lookup_or_default(
|
||||
".hide_edge", AttrDomain::Edge, false);
|
||||
|
||||
for (const int i : faces.index_range()) {
|
||||
if (hide_poly[i]) {
|
||||
continue;
|
||||
}
|
||||
for (const int edge : corner_edges.slice(faces[i])) {
|
||||
if (edge_mask[edge]) {
|
||||
edge_mask[edge].reset();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
for (const int edge : edge_mask.index_range()) {
|
||||
if (edge_mask[edge] && hide_edge[edge]) {
|
||||
edge_mask[edge].reset();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*r_elem_active_len = count;
|
||||
|
||||
return edge_mask;
|
||||
}
|
||||
|
||||
static BitVector<> corner_tris_no_hidden_map_get(const blender::OffsetIndices<int> faces,
|
||||
const VArray<bool> &hide_poly,
|
||||
const int corner_tris_len,
|
||||
@@ -860,6 +944,13 @@ BVHTree *BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data,
|
||||
0.0f, tree_type, 6, positions, loose_verts.is_loose_bits, loose_verts.count);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_LOOSEVERTS_NO_HIDDEN: {
|
||||
int mask_bits_act_len = -1;
|
||||
const BitVector<> mask = loose_verts_no_hidden_mask_get(*mesh, &mask_bits_act_len);
|
||||
data->tree = bvhtree_from_mesh_verts_create_tree(
|
||||
0.0f, tree_type, 6, positions, mask, mask_bits_act_len);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_VERTS: {
|
||||
data->tree = bvhtree_from_mesh_verts_create_tree(0.0f, tree_type, 6, positions, {}, -1);
|
||||
break;
|
||||
@@ -870,6 +961,13 @@ BVHTree *BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data,
|
||||
positions, edges, loose_edges.is_loose_bits, loose_edges.count, 0.0f, tree_type, 6);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_LOOSEEDGES_NO_HIDDEN: {
|
||||
int mask_bits_act_len = -1;
|
||||
const BitVector<> mask = loose_edges_no_hidden_mask_get(*mesh, &mask_bits_act_len);
|
||||
data->tree = bvhtree_from_mesh_edges_create_tree(
|
||||
positions, edges, mask, mask_bits_act_len, 0.0f, tree_type, 6);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_EDGES: {
|
||||
data->tree = bvhtree_from_mesh_edges_create_tree(
|
||||
positions, edges, {}, -1, 0.0f, tree_type, 6);
|
||||
|
||||
@@ -227,7 +227,7 @@ eSnapMode snap_object_mesh(SnapObjectContext *sctx,
|
||||
const ID *id,
|
||||
const blender::float4x4 &obmat,
|
||||
eSnapMode snap_to_flag,
|
||||
bool use_hide);
|
||||
bool skip_hidden);
|
||||
|
||||
eSnapMode snap_polygon_mesh(SnapObjectContext *sctx,
|
||||
const Object *ob_eval,
|
||||
|
||||
@@ -29,14 +29,14 @@ using namespace blender;
|
||||
* \{ */
|
||||
|
||||
static void snap_object_data_mesh_get(const Mesh *mesh_eval,
|
||||
bool use_hide,
|
||||
bool skip_hidden,
|
||||
BVHTreeFromMesh *r_treedata)
|
||||
{
|
||||
/* The BVHTree from corner_tris is always required. */
|
||||
BKE_bvhtree_from_mesh_get(r_treedata,
|
||||
mesh_eval,
|
||||
use_hide ? BVHTREE_FROM_CORNER_TRIS_NO_HIDDEN :
|
||||
BVHTREE_FROM_CORNER_TRIS,
|
||||
skip_hidden ? BVHTREE_FROM_CORNER_TRIS_NO_HIDDEN :
|
||||
BVHTREE_FROM_CORNER_TRIS,
|
||||
4);
|
||||
}
|
||||
|
||||
@@ -424,9 +424,13 @@ eSnapMode snap_edge_points_mesh(SnapObjectContext *sctx,
|
||||
return elem;
|
||||
}
|
||||
|
||||
static eSnapMode mesh_snap_mode_supported(const Mesh *mesh)
|
||||
static eSnapMode mesh_snap_mode_supported(const Mesh *mesh, bool skip_hidden)
|
||||
{
|
||||
eSnapMode snap_mode_supported = mesh->loose_verts().count ? SCE_SNAP_TO_POINT : SCE_SNAP_TO_NONE;
|
||||
/* When skipping hidden geometry, we still cannot obtain the number of loose verts
|
||||
* until computing #BVHTREE_FROM_LOOSEVERTS_NO_HIDDEN. Therefore, consider #SCE_SNAP_TO_POINT
|
||||
* supported even if the mesh has no loose vertices in this case. */
|
||||
eSnapMode snap_mode_supported = (skip_hidden || mesh->loose_verts().count) ? SCE_SNAP_TO_POINT :
|
||||
SCE_SNAP_TO_NONE;
|
||||
if (mesh->faces_num) {
|
||||
snap_mode_supported |= SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST | SNAP_TO_EDGE_ELEMENTS;
|
||||
}
|
||||
@@ -441,7 +445,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
|
||||
const Object *ob_eval,
|
||||
const Mesh *mesh_eval,
|
||||
const float4x4 &obmat,
|
||||
bool use_hide,
|
||||
bool skip_hidden,
|
||||
eSnapMode snap_to)
|
||||
{
|
||||
BLI_assert(snap_to != SCE_SNAP_TO_FACE);
|
||||
@@ -455,19 +459,28 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
|
||||
}
|
||||
}
|
||||
|
||||
snap_to &= mesh_snap_mode_supported(mesh_eval) & (SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT);
|
||||
snap_to &= mesh_snap_mode_supported(mesh_eval, skip_hidden) &
|
||||
(SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT);
|
||||
if (snap_to == SCE_SNAP_TO_NONE) {
|
||||
return SCE_SNAP_TO_NONE;
|
||||
}
|
||||
|
||||
BVHTreeFromMesh treedata, treedata_dummy;
|
||||
snap_object_data_mesh_get(mesh_eval, use_hide, &treedata);
|
||||
snap_object_data_mesh_get(mesh_eval, skip_hidden, &treedata);
|
||||
|
||||
BVHTree *bvhtree[2] = {nullptr};
|
||||
bvhtree[0] = BKE_bvhtree_from_mesh_get(&treedata_dummy, mesh_eval, BVHTREE_FROM_LOOSEEDGES, 2);
|
||||
bvhtree[0] = BKE_bvhtree_from_mesh_get(&treedata_dummy,
|
||||
mesh_eval,
|
||||
skip_hidden ? BVHTREE_FROM_LOOSEEDGES_NO_HIDDEN :
|
||||
BVHTREE_FROM_LOOSEEDGES,
|
||||
2);
|
||||
BLI_assert(treedata_dummy.cached);
|
||||
if (snap_to & SCE_SNAP_TO_POINT) {
|
||||
bvhtree[1] = BKE_bvhtree_from_mesh_get(&treedata_dummy, mesh_eval, BVHTREE_FROM_LOOSEVERTS, 2);
|
||||
bvhtree[1] = BKE_bvhtree_from_mesh_get(&treedata_dummy,
|
||||
mesh_eval,
|
||||
skip_hidden ? BVHTREE_FROM_LOOSEVERTS_NO_HIDDEN :
|
||||
BVHTREE_FROM_LOOSEVERTS,
|
||||
2);
|
||||
BLI_assert(treedata_dummy.cached);
|
||||
}
|
||||
|
||||
@@ -583,26 +596,26 @@ eSnapMode snap_object_mesh(SnapObjectContext *sctx,
|
||||
const ID *id,
|
||||
const float4x4 &obmat,
|
||||
eSnapMode snap_to_flag,
|
||||
bool use_hide)
|
||||
bool skip_hidden)
|
||||
{
|
||||
eSnapMode elem = SCE_SNAP_TO_NONE;
|
||||
const Mesh *mesh_eval = reinterpret_cast<const Mesh *>(id);
|
||||
|
||||
if (snap_to_flag & (SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT)) {
|
||||
elem = snapMesh(sctx, ob_eval, mesh_eval, obmat, use_hide, snap_to_flag);
|
||||
elem = snapMesh(sctx, ob_eval, mesh_eval, obmat, skip_hidden, snap_to_flag);
|
||||
if (elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
if (snap_to_flag & SCE_SNAP_TO_FACE) {
|
||||
if (raycastMesh(sctx, ob_eval, mesh_eval, obmat, sctx->runtime.object_index++, use_hide)) {
|
||||
if (raycastMesh(sctx, ob_eval, mesh_eval, obmat, sctx->runtime.object_index++, skip_hidden)) {
|
||||
return SCE_SNAP_TO_FACE;
|
||||
}
|
||||
}
|
||||
|
||||
if (snap_to_flag & SCE_SNAP_INDIVIDUAL_NEAREST) {
|
||||
if (nearest_world_mesh(sctx, ob_eval, mesh_eval, obmat, use_hide)) {
|
||||
if (nearest_world_mesh(sctx, ob_eval, mesh_eval, obmat, skip_hidden)) {
|
||||
return SCE_SNAP_INDIVIDUAL_NEAREST;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user