Refactor: Store a 'Mesh' in the editmesh snap cache and use it for snapping

In this commit, a temporary mesh is created representing the edit mesh.
This mesh is then used in the Edit Mesh snapping system instead of the
BMesh.

By using a Mesh object for snapping, we remove a considerable amount of
code and use a more optimized version of snapping.

This simplifies the code and makes it easier to implement new features.

## Performance test: Face + Edge + Vert
|        | Cache     | Gen. Snap |
|--------|-----------|-----------|
| Before | 680.88 ms | 0.1250 ms |
| After  | 489.06 ms | 0.1064 ms |
| Improv | 28.65%    | 14.88%    |

## Performance test: Face
|        | Cache     | Gen. Snap |
|--------|-----------|-----------|
| Before | 293.90 ms | 0.0230 ms |
| After  | 411.92 ms | 0.0256 ms |
| Improv | -40.15%   | -11.30%   |

Pull Request: https://projects.blender.org/blender/blender/pulls/117047
This commit is contained in:
Germano Cavalcante
2024-02-12 18:59:13 +01:00
committed by Germano Cavalcante
parent 7ca46bb25d
commit 1c77779160
8 changed files with 229 additions and 1183 deletions

View File

@@ -16,27 +16,12 @@
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
struct BMEditMesh;
struct BVHCache;
struct BVHTree;
struct MFace;
struct Mesh;
struct PointCloud;
/**
* Struct that stores basic information about a BVHTree built from a edit-mesh.
*/
struct BVHTreeFromEditMesh {
BVHTree *tree;
/** Default callbacks to BVH nearest and ray-cast. */
BVHTree_NearestPointCallback nearest_callback;
BVHTree_RayCastCallback raycast_callback;
/* Private data */
bool cached;
};
/**
* Struct that stores basic information about a #BVHTree built from a mesh.
*/
@@ -69,39 +54,10 @@ enum BVHCacheType {
BVHTREE_FROM_LOOSEVERTS,
BVHTREE_FROM_LOOSEEDGES,
BVHTREE_FROM_EM_LOOSEVERTS,
BVHTREE_FROM_EM_EDGES,
BVHTREE_FROM_EM_LOOPTRIS,
/* Keep `BVHTREE_MAX_ITEM` as last item. */
BVHTREE_MAX_ITEM,
};
/**
* Builds a BVH tree where nodes are the relevant elements of the given mesh.
* Configures #BVHTreeFromMesh.
*
* The tree is build in mesh space coordinates, this means special care must be made on queries
* so that the coordinates and rays are first translated on the mesh local coordinates.
* Reason for this is that bvh_from_mesh_* can use a cache in some cases and so it
* becomes possible to reuse a #BVHTree.
*
* #free_bvhtree_from_mesh should be called when the tree is no longer needed.
*/
BVHTree *bvhtree_from_editmesh_verts(
BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis);
/**
* Builds a BVH-tree where nodes are the vertices of the given `em`.
*/
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
blender::BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
int axis);
/**
* Builds a BVH-tree where nodes are the given vertices (NOTE: does not copy given `vert`!).
* \param vert_allocated: if true, vert freeing will be done when freeing data.
@@ -117,20 +73,6 @@ BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
int tree_type,
int axis);
BVHTree *bvhtree_from_editmesh_edges(
BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis);
/**
* Builds a BVH-tree where nodes are the edges of the given `em`.
*/
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
blender::BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
int axis);
/**
* Builds a BVH-tree where nodes are the given edges.
* \param vert, vert_allocated: if true, elem freeing will be done when freeing data.
@@ -148,20 +90,6 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
int tree_type,
int axis);
BVHTree *bvhtree_from_editmesh_corner_tris(
BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis);
/**
* Builds a BVH-tree where nodes are triangles faces (#MLoopTri) of the given `bm`.
*/
BVHTree *bvhtree_from_editmesh_looptris_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
blender::BitSpan corner_tris_mask,
int corner_tris_num_active,
float epsilon,
int tree_type,
int axis);
/**
* Builds a BVH-tree where nodes are the triangle faces (#MLoopTri) of the given mesh.
*/
@@ -186,20 +114,6 @@ BVHTree *BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data,
BVHCacheType bvh_cache_type,
int tree_type);
/**
* Builds or queries a BVH-cache for the cache BVH-tree of the request type.
*/
BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data,
BMEditMesh *em,
int tree_type,
BVHCacheType bvh_cache_type,
BVHCache **bvh_cache_p,
std::mutex *mesh_eval_mutex);
/**
* Frees data allocated by a call to `bvhtree_from_editmesh_*`.
*/
void free_bvhtree_from_editmesh(BVHTreeFromEditMesh *data);
/**
* Frees data allocated by a call to `bvhtree_from_mesh_*`.
*/

View File

@@ -6,31 +6,16 @@
* \ingroup bke
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BLI_bit_vector.hh"
#include "BLI_linklist.h"
#include "BLI_math_geom.h"
#include "BLI_math_vector.h"
#include "BLI_span.hh"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BKE_attribute.hh"
#include "BKE_bvhutils.hh"
#include "BKE_editmesh.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_pointcloud.hh"
#include "MEM_guardedalloc.h"
using blender::BitSpan;
using blender::BitVector;
@@ -294,34 +279,6 @@ static void mesh_corner_tris_nearest_point(void *userdata,
normal_tri_v3(nearest->no, UNPACK3(vtri_co));
}
}
/* Copy of function above (warning, should de-duplicate with `editmesh_bvh.cc`). */
static void editmesh_corner_tris_nearest_point(void *userdata,
int index,
const float co[3],
BVHTreeNearest *nearest)
{
BMEditMesh *em = static_cast<BMEditMesh *>(userdata);
const BMLoop **ltri = const_cast<const BMLoop **>(em->looptris[index]);
const float *t0, *t1, *t2;
t0 = ltri[0]->v->co;
t1 = ltri[1]->v->co;
t2 = ltri[2]->v->co;
{
float nearest_tmp[3], dist_sq;
closest_on_tri_to_point_v3(nearest_tmp, co, t0, t1, t2);
dist_sq = len_squared_v3v3(co, nearest_tmp);
if (dist_sq < nearest->dist_sq) {
nearest->index = index;
nearest->dist_sq = dist_sq;
copy_v3_v3(nearest->co, nearest_tmp);
normal_tri_v3(nearest->no, t0, t1, t2);
}
}
}
/**
* Callback to BVH-tree ray-cast.
@@ -397,38 +354,6 @@ static void mesh_corner_tris_spherecast(void *userdata,
normal_tri_v3(hit->no, UNPACK3(vtri_co));
}
}
/* Copy of function above (warning, should de-duplicate with `editmesh_bvh.cc`). */
static void editmesh_corner_tris_spherecast(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
BMEditMesh *em = static_cast<BMEditMesh *>(userdata);
const BMLoop **ltri = const_cast<const BMLoop **>(em->looptris[index]);
const float *t0, *t1, *t2;
t0 = ltri[0]->v->co;
t1 = ltri[1]->v->co;
t2 = ltri[2]->v->co;
{
float dist;
if (ray->radius == 0.0f) {
dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2);
}
else {
dist = bvhtree_sphereray_tri_intersection(ray, ray->radius, hit->dist, t0, t1, t2);
}
if (dist >= 0 && dist < hit->dist) {
hit->index = index;
hit->dist = dist;
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
normal_tri_v3(hit->no, t0, t1, t2);
}
}
}
/**
* Callback to BVH-tree nearest point.
@@ -484,17 +409,6 @@ static void mesh_verts_spherecast_do(int index,
}
}
static void editmesh_verts_spherecast(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
BMEditMesh *em = static_cast<BMEditMesh *>(userdata);
BMVert *eve = BM_vert_at_index(em->bm, index);
mesh_verts_spherecast_do(index, eve->co, ray, hit);
}
/**
* Callback to BVH-tree ray-cast.
* The tree must have been built using bvhtree_from_mesh_verts.
@@ -615,44 +529,6 @@ static void bvhtree_from_mesh_setup_data(BVHTree *tree,
r_data->nearest_callback = mesh_corner_tris_nearest_point;
r_data->raycast_callback = mesh_corner_tris_spherecast;
break;
case BVHTREE_FROM_EM_LOOSEVERTS:
case BVHTREE_FROM_EM_EDGES:
case BVHTREE_FROM_EM_LOOPTRIS:
case BVHTREE_MAX_ITEM:
BLI_assert(false);
break;
}
}
static void bvhtree_from_editmesh_setup_data(BVHTree *tree,
const BVHCacheType bvh_cache_type,
BVHTreeFromEditMesh *r_data)
{
memset(r_data, 0, sizeof(*r_data));
r_data->tree = tree;
switch (bvh_cache_type) {
case BVHTREE_FROM_EM_LOOSEVERTS:
r_data->nearest_callback = nullptr;
r_data->raycast_callback = editmesh_verts_spherecast;
break;
case BVHTREE_FROM_EM_EDGES:
r_data->nearest_callback = nullptr; /* TODO */
r_data->raycast_callback = nullptr; /* TODO */
break;
case BVHTREE_FROM_EM_LOOPTRIS:
r_data->nearest_callback = editmesh_corner_tris_nearest_point;
r_data->raycast_callback = editmesh_corner_tris_spherecast;
break;
case BVHTREE_FROM_VERTS:
case BVHTREE_FROM_LOOSEVERTS:
case BVHTREE_FROM_EDGES:
case BVHTREE_FROM_LOOSEEDGES:
case BVHTREE_FROM_FACES:
case BVHTREE_FROM_CORNER_TRIS:
case BVHTREE_FROM_CORNER_TRIS_NO_HIDDEN:
case BVHTREE_MAX_ITEM:
BLI_assert(false);
break;
@@ -682,34 +558,6 @@ static BVHTree *bvhtree_new_common(
/** \name Vertex Builder
* \{ */
static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitSpan verts_mask,
int verts_num_active)
{
const int verts_num = em->bm->totvert;
BVHTree *tree = bvhtree_new_common(epsilon, tree_type, axis, verts_num, verts_num_active);
if (!tree) {
return nullptr;
}
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
for (int i = 0; i < verts_num; i++) {
if (!verts_mask.is_empty() && !verts_mask[i]) {
continue;
}
BMVert *eve = BM_vert_at_index(em->bm, i);
BLI_bvhtree_insert(tree, i, eve->co, 1);
}
BLI_assert(BLI_bvhtree_get_len(tree) == verts_num_active);
return tree;
}
static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
int tree_type,
int axis,
@@ -733,32 +581,6 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
return tree;
}
BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitSpan verts_mask,
int verts_num_active,
float epsilon,
int tree_type,
int axis)
{
BVHTree *tree = bvhtree_from_editmesh_verts_create_tree(
epsilon, tree_type, axis, em, verts_mask, verts_num_active);
bvhtree_balance(tree, false);
if (data) {
bvhtree_from_editmesh_setup_data(tree, BVHTREE_FROM_EM_LOOSEVERTS, data);
}
return tree;
}
BVHTree *bvhtree_from_editmesh_verts(
BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis)
{
return bvhtree_from_editmesh_verts_ex(data, em, {}, -1, epsilon, tree_type, axis);
}
BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
const Span<float3> vert_positions,
const BitSpan verts_mask,
@@ -786,40 +608,6 @@ BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
/** \name Edge Builder
* \{ */
static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitSpan edges_mask,
int edges_num_active)
{
const int edges_num = em->bm->totedge;
BVHTree *tree = bvhtree_new_common(epsilon, tree_type, axis, edges_num, edges_num_active);
if (!tree) {
return nullptr;
}
BM_mesh_elem_table_ensure(em->bm, BM_EDGE);
int i;
BMIter iter;
BMEdge *eed;
BM_ITER_MESH_INDEX (eed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
if (!edges_mask.is_empty() && !edges_mask[i]) {
continue;
}
float co[2][3];
copy_v3_v3(co[0], eed->v1->co);
copy_v3_v3(co[1], eed->v2->co);
BLI_bvhtree_insert(tree, i, co[0], 2);
}
BLI_assert(BLI_bvhtree_get_len(tree) == edges_num_active);
return tree;
}
static BVHTree *bvhtree_from_mesh_edges_create_tree(const Span<float3> positions,
const blender::Span<blender::int2> edges,
const BitSpan edges_mask,
@@ -847,32 +635,6 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const Span<float3> positions
return tree;
}
BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
int tree_type,
int axis)
{
BVHTree *tree = bvhtree_from_editmesh_edges_create_tree(
epsilon, tree_type, axis, em, edges_mask, edges_num_active);
bvhtree_balance(tree, false);
if (data) {
bvhtree_from_editmesh_setup_data(tree, BVHTREE_FROM_EM_EDGES, data);
}
return tree;
}
BVHTree *bvhtree_from_editmesh_edges(
BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis)
{
return bvhtree_from_editmesh_edges_ex(data, em, {}, -1, epsilon, tree_type, axis);
}
BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
const Span<float3> vert_positions,
const Span<blender::int2> edges,
@@ -944,46 +706,6 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon,
/** \name corner_tri Face Builder
* \{ */
static BVHTree *bvhtree_from_editmesh_corner_tris_create_tree(float epsilon,
int tree_type,
int axis,
BMEditMesh *em,
const BitSpan corner_tris_mask,
int corner_tris_num_active)
{
const int corner_tris_num = em->tottri;
BVHTree *tree = bvhtree_new_common(
epsilon, tree_type, axis, corner_tris_num, corner_tris_num_active);
if (!tree) {
return nullptr;
}
const BMLoop *(*corner_tris)[3] = const_cast<const BMLoop *(*)[3]>(em->looptris);
/* Insert BMesh-tessellation triangles into the BVH-tree, unless they are hidden
* and/or selected. Even if the faces themselves are not selected for the snapped
* transform, having a vertex selected means the face (and thus it's tessellated
* triangles) will be moving and will not be a good snap targets. */
for (int i = 0; i < corner_tris_num; i++) {
const BMLoop **ltri = corner_tris[i];
bool insert = !corner_tris_mask.is_empty() ? corner_tris_mask[i] : true;
if (insert) {
/* No reason found to block hit-testing the triangle for snap, so insert it now. */
float co[3][3];
copy_v3_v3(co[0], ltri[0]->v->co);
copy_v3_v3(co[1], ltri[1]->v->co);
copy_v3_v3(co[2], ltri[2]->v->co);
BLI_bvhtree_insert(tree, i, co[0], 3);
}
}
BLI_assert(BLI_bvhtree_get_len(tree) == corner_tris_num_active);
return tree;
}
static BVHTree *bvhtree_from_mesh_corner_tris_create_tree(float epsilon,
int tree_type,
int axis,
@@ -1022,33 +744,6 @@ static BVHTree *bvhtree_from_mesh_corner_tris_create_tree(float epsilon,
return tree;
}
BVHTree *bvhtree_from_editmesh_looptris_ex(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const BitSpan corner_tris_mask,
int corner_tris_num_active,
float epsilon,
int tree_type,
int axis)
{
/* BMESH specific check that we have tessfaces,
* we _could_ tessellate here but rather not - campbell */
BVHTree *tree = bvhtree_from_editmesh_corner_tris_create_tree(
epsilon, tree_type, axis, em, corner_tris_mask, corner_tris_num_active);
bvhtree_balance(tree, false);
if (data) {
bvhtree_from_editmesh_setup_data(tree, BVHTREE_FROM_EM_LOOPTRIS, data);
}
return tree;
}
BVHTree *bvhtree_from_editmesh_corner_tris(
BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis)
{
return bvhtree_from_editmesh_looptris_ex(data, em, {}, -1, epsilon, tree_type, axis);
}
BVHTree *bvhtree_from_mesh_corner_tris_ex(BVHTreeFromMesh *data,
const Span<float3> vert_positions,
const Span<int> corner_verts,
@@ -1210,9 +905,6 @@ BVHTree *BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data,
0.0f, tree_type, 6, positions, corner_verts, corner_tris, {}, -1);
break;
}
case BVHTREE_FROM_EM_LOOSEVERTS:
case BVHTREE_FROM_EM_EDGES:
case BVHTREE_FROM_EM_LOOPTRIS:
case BVHTREE_MAX_ITEM:
BLI_assert_unreachable();
break;
@@ -1240,112 +932,12 @@ BVHTree *BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data,
return data->tree;
}
static BitVector<> bmverts_loose_map_get(BMesh *bm, int *r_bmvert_active_len)
{
BitVector<> bmvert_mask(bm->totvert);
int i, bmvert_loose_len = 0;
BMIter iter;
BMVert *v;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
if (v->e == nullptr) {
bmvert_mask[i].set();
bmvert_loose_len++;
}
}
*r_bmvert_active_len = bmvert_loose_len;
return bmvert_mask;
}
BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data,
BMEditMesh *em,
const int tree_type,
const BVHCacheType bvh_cache_type,
BVHCache **bvh_cache_p,
std::mutex *mesh_eval_mutex)
{
bool lock_started = false;
bvhtree_from_editmesh_setup_data(nullptr, bvh_cache_type, data);
if (bvh_cache_p) {
data->cached = bvhcache_find(
bvh_cache_p, bvh_cache_type, &data->tree, &lock_started, mesh_eval_mutex);
if (data->cached) {
BLI_assert(lock_started == false);
return data->tree;
}
}
switch (bvh_cache_type) {
case BVHTREE_FROM_EM_LOOSEVERTS: {
int mask_bits_act_len = -1;
const BitVector<> mask = bmverts_loose_map_get(em->bm, &mask_bits_act_len);
data->tree = bvhtree_from_editmesh_verts_create_tree(
0.0f, tree_type, 6, em, mask, mask_bits_act_len);
break;
}
case BVHTREE_FROM_EM_EDGES:
data->tree = bvhtree_from_editmesh_edges_create_tree(0.0f, tree_type, 6, em, {}, -1);
break;
case BVHTREE_FROM_EM_LOOPTRIS:
data->tree = bvhtree_from_editmesh_corner_tris_create_tree(0.0f, tree_type, 6, em, {}, -1);
break;
case BVHTREE_FROM_VERTS:
case BVHTREE_FROM_EDGES:
case BVHTREE_FROM_FACES:
case BVHTREE_FROM_CORNER_TRIS:
case BVHTREE_FROM_CORNER_TRIS_NO_HIDDEN:
case BVHTREE_FROM_LOOSEVERTS:
case BVHTREE_FROM_LOOSEEDGES:
case BVHTREE_MAX_ITEM:
BLI_assert(false);
break;
}
bvhtree_balance(data->tree, lock_started);
if (bvh_cache_p) {
/* Save on cache for later use */
// printf("BVHTree built and saved on cache\n");
BLI_assert(data->cached == false);
data->cached = true;
bvhcache_insert(*bvh_cache_p, data->tree, bvh_cache_type);
bvhcache_unlock(*bvh_cache_p, lock_started);
}
#ifndef NDEBUG
if (data->tree != nullptr) {
if (BLI_bvhtree_get_tree_type(data->tree) != tree_type) {
printf("tree_type %d obtained instead of %d\n",
BLI_bvhtree_get_tree_type(data->tree),
tree_type);
}
}
#endif
return data->tree;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Free Functions
* \{ */
void free_bvhtree_from_editmesh(BVHTreeFromEditMesh *data)
{
if (data->tree) {
if (!data->cached) {
BLI_bvhtree_free(data->tree);
}
memset(data, 0, sizeof(*data));
}
}
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
{
if (data->tree && !data->cached) {

View File

@@ -1618,7 +1618,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParam
material_index.finish();
}
void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *cd_mask_extra)
void BM_mesh_bm_to_me_compact(BMesh &bm,
Mesh &mesh,
const CustomData_MeshMasks *mask,
const bool add_mesh_attributes)
{
/* NOTE: The function is called from multiple threads with the same input BMesh and different
* mesh objects. */
@@ -1626,7 +1629,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
using namespace blender;
/* Must be an empty mesh. */
BLI_assert(mesh.verts_num == 0);
BLI_assert(cd_mask_extra == nullptr || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0);
/* Just in case, clear the derived geometry caches from the input mesh. */
BKE_mesh_runtime_clear_geometry(&mesh);
@@ -1638,6 +1640,8 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
mesh.runtime->deformed_only = true;
const bool use_threading = (mesh.faces_num + mesh.edges_num) > 1024;
/* In a first pass, update indices of BMesh elements and build tables for easy iteration later.
* Also check if some optional mesh attributes should be added in the next step. Since each
* domain has no effect on others, process the independent domains on separate threads. */
@@ -1651,13 +1655,14 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
bool need_sharp_edge = false;
bool need_sharp_face = false;
bool need_uv_seams = false;
Array<const BMVert *> vert_table;
Array<const BMEdge *> edge_table;
Array<const BMFace *> face_table;
Array<const BMLoop *> loop_table;
Vector<int> loop_layers_not_to_copy;
threading::parallel_invoke(
(mesh.faces_num + mesh.edges_num) > 1024,
use_threading,
[&]() {
vert_table.reinitialize(bm.totvert);
bm_vert_table_build(bm, vert_table, need_select_vert, need_hide_vert);
@@ -1684,22 +1689,16 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
});
bm.elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE | BM_LOOP);
/* Don't process shape-keys. We only feed them through the modifier stack as needed,
* e.g. for applying modifiers or the like. */
CustomData_MeshMasks mask = CD_MASK_DERIVEDMESH;
if (cd_mask_extra != nullptr) {
CustomData_MeshMasks_update(&mask, cd_mask_extra);
if (mask) {
CustomData_merge_layout(&bm.vdata, &mesh.vert_data, mask->vmask, CD_CONSTRUCT, mesh.verts_num);
CustomData_merge_layout(&bm.edata, &mesh.edge_data, mask->emask, CD_CONSTRUCT, mesh.edges_num);
CustomData_merge_layout(
&bm.ldata, &mesh.corner_data, mask->lmask, CD_CONSTRUCT, mesh.corners_num);
CustomData_merge_layout(&bm.pdata, &mesh.face_data, mask->pmask, CD_CONSTRUCT, mesh.faces_num);
}
mask.vmask &= ~CD_MASK_SHAPEKEY;
CustomData_merge_layout(&bm.vdata, &mesh.vert_data, mask.vmask, CD_CONSTRUCT, mesh.verts_num);
CustomData_merge_layout(&bm.edata, &mesh.edge_data, mask.emask, CD_CONSTRUCT, mesh.edges_num);
CustomData_merge_layout(
&bm.ldata, &mesh.corner_data, mask.lmask, CD_CONSTRUCT, mesh.corners_num);
CustomData_merge_layout(&bm.pdata, &mesh.face_data, mask.pmask, CD_CONSTRUCT, mesh.faces_num);
/* Add optional mesh attributes before parallel iteration. */
assert_bmesh_has_no_mesh_only_attributes(bm);
bke::MutableAttributeAccessor attrs = mesh.attributes_for_write();
bke::SpanAttributeWriter<bool> select_vert;
bke::SpanAttributeWriter<bool> hide_vert;
bke::SpanAttributeWriter<bool> select_edge;
@@ -1710,41 +1709,48 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
bke::SpanAttributeWriter<bool> hide_poly;
bke::SpanAttributeWriter<bool> sharp_face;
bke::SpanAttributeWriter<int> material_index;
if (need_select_vert) {
select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".select_vert", AttrDomain::Point);
}
if (need_hide_vert) {
hide_vert = attrs.lookup_or_add_for_write_only_span<bool>(".hide_vert", AttrDomain::Point);
}
if (need_select_edge) {
select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".select_edge", AttrDomain::Edge);
}
if (need_sharp_edge) {
sharp_edge = attrs.lookup_or_add_for_write_only_span<bool>("sharp_edge", AttrDomain::Edge);
}
if (need_uv_seams) {
uv_seams = attrs.lookup_or_add_for_write_only_span<bool>(".uv_seam", AttrDomain::Edge);
}
if (need_hide_edge) {
hide_edge = attrs.lookup_or_add_for_write_only_span<bool>(".hide_edge", AttrDomain::Edge);
}
if (need_select_poly) {
select_poly = attrs.lookup_or_add_for_write_only_span<bool>(".select_poly", AttrDomain::Face);
}
if (need_hide_poly) {
hide_poly = attrs.lookup_or_add_for_write_only_span<bool>(".hide_poly", AttrDomain::Face);
}
if (need_sharp_face) {
sharp_face = attrs.lookup_or_add_for_write_only_span<bool>("sharp_face", AttrDomain::Face);
}
if (need_material_index) {
material_index = attrs.lookup_or_add_for_write_only_span<int>("material_index",
if (add_mesh_attributes) {
bke::MutableAttributeAccessor attrs = mesh.attributes_for_write();
if (need_select_vert) {
select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".select_vert",
AttrDomain::Point);
}
if (need_hide_vert) {
hide_vert = attrs.lookup_or_add_for_write_only_span<bool>(".hide_vert", AttrDomain::Point);
}
if (need_select_edge) {
select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".select_edge",
AttrDomain::Edge);
}
if (need_sharp_edge) {
sharp_edge = attrs.lookup_or_add_for_write_only_span<bool>("sharp_edge", AttrDomain::Edge);
}
if (need_uv_seams) {
uv_seams = attrs.lookup_or_add_for_write_only_span<bool>(".uv_seam", AttrDomain::Edge);
}
if (need_hide_edge) {
hide_edge = attrs.lookup_or_add_for_write_only_span<bool>(".hide_edge", AttrDomain::Edge);
}
if (need_select_poly) {
select_poly = attrs.lookup_or_add_for_write_only_span<bool>(".select_poly",
AttrDomain::Face);
}
if (need_hide_poly) {
hide_poly = attrs.lookup_or_add_for_write_only_span<bool>(".hide_poly", AttrDomain::Face);
}
if (need_sharp_face) {
sharp_face = attrs.lookup_or_add_for_write_only_span<bool>("sharp_face", AttrDomain::Face);
}
if (need_material_index) {
material_index = attrs.lookup_or_add_for_write_only_span<int>("material_index",
AttrDomain::Face);
}
}
/* Loop over all elements in parallel, copying attributes and building the Mesh topology. */
threading::parallel_invoke(
(mesh.faces_num + mesh.edges_num) > 1024,
use_threading,
[&]() { bm_to_mesh_verts(bm, vert_table, mesh, select_vert.span, hide_vert.span); },
[&]() {
bm_to_mesh_edges(bm,
@@ -1763,6 +1769,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
hide_poly.span,
sharp_face.span,
material_index.span);
if (bm.act_face) {
mesh.act_face = BM_elem_index_get(bm.act_face);
}
},
[&]() {
bm_to_mesh_loops(bm, loop_table, mesh);
@@ -1771,14 +1780,29 @@ void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks
}
});
select_vert.finish();
hide_vert.finish();
select_edge.finish();
hide_edge.finish();
sharp_edge.finish();
uv_seams.finish();
select_poly.finish();
hide_poly.finish();
sharp_face.finish();
material_index.finish();
if (add_mesh_attributes) {
select_vert.finish();
hide_vert.finish();
select_edge.finish();
hide_edge.finish();
sharp_edge.finish();
uv_seams.finish();
select_poly.finish();
hide_poly.finish();
sharp_face.finish();
material_index.finish();
}
}
void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *cd_mask_extra)
{
/* Don't process shape-keys. We only feed them through the modifier stack as needed,
* e.g. for applying modifiers or the like. */
CustomData_MeshMasks mask = CD_MASK_DERIVEDMESH;
if (cd_mask_extra != nullptr) {
CustomData_MeshMasks_update(&mask, cd_mask_extra);
}
mask.vmask &= ~CD_MASK_SHAPEKEY;
BM_mesh_bm_to_me_compact(bm, mesh, &mask, true);
}

View File

@@ -90,3 +90,16 @@ void BM_mesh_bm_to_me(struct Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMe
* \note Was `cddm_from_bmesh_ex` in 2.7x, removed `MFace` support.
*/
void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *cd_mask_extra);
/**
* A version of #BM_mesh_bm_to_me_for_eval but copying data layers and Mesh attributes is optional.
* It also allows shape-keys but don't re-assigns shape-key indices.
*
* \param mask Custom data masks to control which layers are copied.
* If nullptr, no layer data is copied.
* \param add_mesh_attributes If true, adds mesh attributes during the conversion.
*/
void BM_mesh_bm_to_me_compact(BMesh &bm,
Mesh &mesh,
const CustomData_MeshMasks *mask,
bool add_mesh_attributes);

View File

@@ -29,6 +29,7 @@
#ifdef DEBUG_SNAP_TIME
# include "BLI_timeit.hh"
# include <iostream>
# if WIN32 and NDEBUG
# pragma optimize("t", on)
@@ -374,21 +375,17 @@ static ID *data_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_
return nullptr;
}
Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval);
Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval);
Mesh *editmesh_eval = (edit_mode_type == SNAP_GEOM_FINAL) ?
BKE_object_get_editmesh_eval_final(ob_eval) :
(edit_mode_type == SNAP_GEOM_CAGE) ?
BKE_object_get_editmesh_eval_cage(ob_eval) :
nullptr;
if ((edit_mode_type == SNAP_GEOM_FINAL) && editmesh_eval_final) {
if (editmesh_eval_final->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) {
if (editmesh_eval) {
if (editmesh_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) {
return nullptr;
}
me_eval = editmesh_eval_final;
use_hide = true;
}
else if ((edit_mode_type == SNAP_GEOM_CAGE) && editmesh_eval_cage) {
if (editmesh_eval_cage->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) {
return nullptr;
}
me_eval = editmesh_eval_cage;
me_eval = editmesh_eval;
use_hide = true;
}
}
@@ -836,39 +833,21 @@ void cb_snap_edge(void *userdata,
static eSnapMode snap_polygon(SnapObjectContext *sctx, eSnapMode snap_to_flag)
{
if (sctx->ret.ob->type != OB_MESH) {
if (sctx->ret.ob->type != OB_MESH || !sctx->ret.data || GS(sctx->ret.data->name) != ID_ME) {
return SCE_SNAP_TO_NONE;
}
if (sctx->ret.data && GS(sctx->ret.data->name) != ID_ME) {
return SCE_SNAP_TO_NONE;
}
if (sctx->ret.data) {
return snap_polygon_mesh(
sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, snap_to_flag, sctx->ret.index);
}
return snap_polygon_editmesh(
return snap_polygon_mesh(
sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, snap_to_flag, sctx->ret.index);
}
static eSnapMode snap_edge_points(SnapObjectContext *sctx, const float dist_px_sq_orig)
{
eSnapMode elem = SCE_SNAP_TO_EDGE;
if (sctx->ret.ob->type != OB_MESH) {
return elem;
if (sctx->ret.ob->type != OB_MESH || !sctx->ret.data || GS(sctx->ret.data->name) != ID_ME) {
return SCE_SNAP_TO_EDGE;
}
if (sctx->ret.data && GS(sctx->ret.data->name) != ID_ME) {
return elem;
}
if (sctx->ret.data) {
return snap_edge_points_mesh(
sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, dist_px_sq_orig, sctx->ret.index);
}
return snap_edge_points_editmesh(
return snap_edge_points_mesh(
sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, dist_px_sq_orig, sctx->ret.index);
}

View File

@@ -210,20 +210,6 @@ eSnapMode snap_object_editmesh(SnapObjectContext *sctx,
eSnapMode snap_to_flag,
bool use_hide);
eSnapMode snap_polygon_editmesh(SnapObjectContext *sctx,
Object *ob_eval,
const ID *id,
const blender::float4x4 &obmat,
eSnapMode snap_to_flag,
int face);
eSnapMode snap_edge_points_editmesh(SnapObjectContext *sctx,
Object *ob_eval,
const ID *id,
const blender::float4x4 &obmat,
float dist_px_sq_orig,
int edge);
/* transform_snap_object_mesh.cc */
eSnapMode snap_object_mesh(SnapObjectContext *sctx,

View File

@@ -6,19 +6,17 @@
* \ingroup edtransform
*/
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.h"
#include "BKE_attribute.hh"
#include "BKE_bvhutils.hh"
#include "BKE_editmesh.hh"
#include "BKE_global.hh"
#include "BKE_lib_id.hh"
#include "BKE_mesh.hh"
#include "BKE_object.hh"
#include "DEG_depsgraph_query.hh"
#include "ED_transform_snap_object_context.hh"
#include "ED_view3d.hh"
#include "transform_snap_object.hh"
@@ -28,27 +26,43 @@ using namespace blender;
/** \name Snap Object Data
* \{ */
static Mesh *get_mesh_ref(Object *ob_eval)
{
if (Mesh *me = BKE_object_get_editmesh_eval_final(ob_eval)) {
return me;
}
if (Mesh *me = BKE_object_get_editmesh_eval_cage(ob_eval)) {
return me;
}
return static_cast<Mesh *>(ob_eval->data);
}
struct SnapCache_EditMesh : public SnapObjectContext::SnapCache {
/* Loose Verts, Edges, Triangles. */
BVHTree *bvhtree[3];
bool cached[3];
/* Mesh created from the edited mesh. */
Mesh *mesh;
BMEditMesh *em;
/* Reference to pointers that change when the mesh is changed. It is used to detect updates. */
Mesh *mesh_ref;
bke::MeshRuntime *runtime_ref;
bke::EditMeshData *edit_data_ref;
/** Default callbacks to BVH nearest and ray-cast used only for triangles. */
BVHTree_NearestPointCallback nearest_callback;
BVHTree_RayCastCallback raycast_callback;
bool has_mesh_updated(Mesh *mesh)
{
if (mesh != this->mesh_ref || mesh->runtime != this->runtime_ref ||
mesh->runtime->edit_data.get() != this->edit_data_ref)
{
return true;
}
bke::MeshRuntime *mesh_runtime;
float min[3], max[3];
return false;
}
void clear()
{
for (int i = 0; i < ARRAY_SIZE(this->bvhtree); i++) {
if (!this->cached[i]) {
BLI_bvhtree_free(this->bvhtree[i]);
}
this->bvhtree[i] = nullptr;
if (this->mesh) {
BKE_id_free(nullptr, this->mesh);
}
}
@@ -58,98 +72,101 @@ struct SnapCache_EditMesh : public SnapObjectContext::SnapCache {
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("SnapData_EditMesh")
MEM_CXX_CLASS_ALLOC_FUNCS("SnapCache_EditMesh")
#endif
};
/**
* Calculate the minimum and maximum coordinates of the box that encompasses this mesh.
*/
static void snap_editmesh_minmax(SnapObjectContext *sctx,
BMesh *bm,
float r_min[3],
float r_max[3])
static Mesh *create_mesh(SnapObjectContext *sctx,
Object *ob_eval,
eSnapEditType /*edit_mode_type*/)
{
INIT_MINMAX(r_min, r_max);
BMIter iter;
BMVert *v;
Mesh *mesh = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr));
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
BMesh *bm = em->bm;
BM_mesh_bm_to_me_compact(*bm, *mesh, nullptr, false);
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (sctx->callbacks.edit_mesh.test_vert_fn &&
!sctx->callbacks.edit_mesh.test_vert_fn(v, sctx->callbacks.edit_mesh.user_data))
{
continue;
}
minmax_v3v3_v3(r_min, r_max, v->co);
}
}
bke::MutableAttributeAccessor attrs = mesh->attributes_for_write();
bke::SpanAttributeWriter<bool> hide_vert = attrs.lookup_or_add_for_write_only_span<bool>(
".hide_vert", bke::AttrDomain::Point);
bke::SpanAttributeWriter<bool> hide_edge = attrs.lookup_or_add_for_write_only_span<bool>(
".hide_edge", bke::AttrDomain::Edge);
bke::SpanAttributeWriter<bool> hide_poly = attrs.lookup_or_add_for_write_only_span<bool>(
".hide_poly", bke::AttrDomain::Face);
/* Searches for the #Mesh_Runtime associated with the object that is most likely to be updated due
* to changes in the `edit_mesh`. */
static blender::bke::MeshRuntime *snap_object_data_editmesh_runtime_get(Object *ob_eval)
{
Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval);
if (editmesh_eval_final) {
return editmesh_eval_final->runtime;
}
/* Loop over all elements in parallel to choose which elements will participate in the snap.
* Hidden elements are ignored for snapping. */
const bool use_threading = (mesh->faces_num + mesh->edges_num) > 1024;
threading::parallel_invoke(
use_threading,
[&]() {
BMIter iter;
BMVert *v;
int i;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
if (sctx->callbacks.edit_mesh.test_vert_fn) {
hide_vert.span[i] = !sctx->callbacks.edit_mesh.test_vert_fn(
v, sctx->callbacks.edit_mesh.user_data);
}
else {
hide_vert.span[i] = BM_elem_flag_test_bool(v, BM_ELEM_HIDDEN);
}
}
},
[&]() {
BMIter iter;
BMEdge *e;
int i;
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
if (sctx->callbacks.edit_mesh.test_edge_fn) {
hide_edge.span[i] = !sctx->callbacks.edit_mesh.test_edge_fn(
e, sctx->callbacks.edit_mesh.user_data);
}
else {
hide_edge.span[i] = BM_elem_flag_test_bool(e, BM_ELEM_HIDDEN);
}
}
},
[&]() {
BMIter iter;
BMFace *f;
int i;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
if (sctx->callbacks.edit_mesh.test_face_fn) {
hide_poly.span[i] = !sctx->callbacks.edit_mesh.test_face_fn(
f, sctx->callbacks.edit_mesh.user_data);
}
else {
hide_poly.span[i] = BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN);
}
}
});
Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval);
if (editmesh_eval_cage) {
return editmesh_eval_cage->runtime;
}
return ((Mesh *)ob_eval->data)->runtime;
hide_vert.finish();
hide_edge.finish();
hide_poly.finish();
return mesh;
}
static SnapCache_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em,
const bool create)
bool create)
{
SnapCache_EditMesh *em_cache = nullptr;
bool init = false;
Mesh *mesh_ref = (G.moving) ? /* WORKAROUND:
* Avoid updating while transforming. Do not check if the reference
* mesh has been updated. */
nullptr :
get_mesh_ref(ob_eval);
if (std::unique_ptr<SnapObjectContext::SnapCache> *em_cache_p = sctx->editmesh_caches.lookup_ptr(
ob_eval->runtime->data_orig))
{
em_cache = static_cast<SnapCache_EditMesh *>(em_cache_p->get());
bool is_dirty = false;
/* Check if the geometry has changed. */
if (em_cache->em != em) {
is_dirty = true;
}
else if (em_cache->mesh_runtime) {
if (em_cache->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
if (G.moving) {
/* WORKAROUND: avoid updating while transforming. */
BLI_assert(!em_cache->cached[0] && !em_cache->cached[1] && !em_cache->cached[2]);
em_cache->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
}
else {
is_dirty = true;
}
}
else if (em_cache->bvhtree[0] && em_cache->cached[0] &&
!bvhcache_has_tree(em_cache->mesh_runtime->bvh_cache, em_cache->bvhtree[0]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
else if (em_cache->bvhtree[1] && em_cache->cached[1] &&
!bvhcache_has_tree(em_cache->mesh_runtime->bvh_cache, em_cache->bvhtree[1]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
else if (em_cache->bvhtree[2] && em_cache->cached[2] &&
!bvhcache_has_tree(em_cache->mesh_runtime->bvh_cache, em_cache->bvhtree[2]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
}
if (is_dirty) {
/* Check if the geometry has changed. */
if (mesh_ref && em_cache->has_mesh_updated(mesh_ref)) {
em_cache->clear();
init = true;
}
@@ -162,54 +179,17 @@ static SnapCache_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx
}
if (init) {
/* Operators only update the editmesh looptris of the original mesh. */
BLI_assert(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
em_cache->em = em;
em_cache->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
snap_editmesh_minmax(sctx, em->bm, em_cache->min, em_cache->max);
em_cache->mesh = create_mesh(sctx, ob_eval, sctx->runtime.params.edit_mode_type);
if (mesh_ref) {
em_cache->mesh_ref = mesh_ref;
em_cache->runtime_ref = mesh_ref->runtime;
em_cache->edit_data_ref = mesh_ref->runtime->edit_data.get();
}
}
return em_cache;
}
static void snap_cache_tri_ensure(SnapCache_EditMesh *em_cache, SnapObjectContext *sctx)
{
if (em_cache->bvhtree[2] == nullptr) {
BVHTreeFromEditMesh treedata{};
BMEditMesh *em = em_cache->em;
if (sctx->callbacks.edit_mesh.test_face_fn) {
BMesh *bm = em->bm;
BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri);
blender::BitVector<> elem_mask(em->tottri);
int looptris_num_active = BM_iter_mesh_bitmap_from_filter_tessface(
bm,
elem_mask,
sctx->callbacks.edit_mesh.test_face_fn,
sctx->callbacks.edit_mesh.user_data);
bvhtree_from_editmesh_looptris_ex(&treedata, em, elem_mask, looptris_num_active, 0.0f, 4, 6);
}
else {
/* Only cache if BVH-tree is created without a mask.
* This helps keep a standardized BVH-tree in cache. */
BKE_bvhtree_from_editmesh_get(&treedata,
em,
4,
BVHTREE_FROM_EM_LOOPTRIS,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &em_cache->mesh_runtime->bvh_cache,
&em_cache->mesh_runtime->eval_mutex);
}
em_cache->bvhtree[2] = treedata.tree;
em_cache->cached[2] = treedata.cached;
em_cache->nearest_callback = treedata.nearest_callback;
em_cache->raycast_callback = treedata.raycast_callback;
}
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -241,7 +221,7 @@ static SnapCache_EditMesh *editmesh_snapdata_init(SnapObjectContext *sctx,
return nullptr;
}
SnapCache_EditMesh *em_cache = snap_object_data_editmesh_get(sctx, ob_eval, em, false);
SnapCache_EditMesh *em_cache = snap_object_data_editmesh_get(sctx, ob_eval, false);
if (em_cache != nullptr) {
return em_cache;
}
@@ -251,423 +231,7 @@ static SnapCache_EditMesh *editmesh_snapdata_init(SnapObjectContext *sctx,
return nullptr;
}
return snap_object_data_editmesh_get(sctx, ob_eval, em, true);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ray Cast Functions
* \{ */
/* Callback to ray-cast with back-face culling (#EditMesh). */
static void editmesh_looptris_raycast_backface_culling_cb(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
BMEditMesh *em = static_cast<BMEditMesh *>(userdata);
const BMLoop **ltri = const_cast<const BMLoop **>(em->looptris[index]);
const float *t0, *t1, *t2;
t0 = ltri[0]->v->co;
t1 = ltri[1]->v->co;
t2 = ltri[2]->v->co;
{
float dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2);
if (dist >= 0 && dist < hit->dist) {
float no[3];
if (raycast_tri_backface_culling_test(ray->direction, t0, t1, t2, no)) {
hit->index = index;
hit->dist = dist;
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
normalize_v3_v3(hit->no, no);
}
}
}
}
static bool raycastEditMesh(SnapCache_EditMesh *em_cache,
SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em,
const float4x4 &obmat,
const uint ob_index)
{
bool retval = false;
float4x4 imat = math::invert(obmat);
float3 ray_start_local = math::transform_point(imat, sctx->runtime.ray_start);
float3 ray_normal_local = math::transform_direction(imat, sctx->runtime.ray_dir);
float local_scale, local_depth, len_diff = 0.0f;
/* local scale in normal direction */
ray_normal_local = math::normalize_and_get_length(ray_normal_local, local_scale);
const bool is_in_front = sctx->runtime.params.use_occlusion_test &&
(ob_eval->dtx & OB_DRAW_IN_FRONT) != 0;
const float depth_max = is_in_front ? sctx->ret.ray_depth_max_in_front : sctx->ret.ray_depth_max;
local_depth = depth_max;
if (local_depth != BVH_RAYCAST_DIST_MAX) {
local_depth *= local_scale;
}
/* Test bounding box */
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, em_cache->min, em_cache->max, &len_diff, nullptr))
{
return retval;
}
/* We pass a temp ray_start, set from object's bounding-box, to avoid precision issues with
* very far away ray_start values (as returned in case of ortho view3d), see #50486, #38358. */
if (len_diff > 400.0f) {
len_diff -= local_scale; /* make temp start point a bit away from bounding-box hit point. */
madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff);
local_depth -= len_diff;
}
else {
len_diff = 0.0f;
}
snap_cache_tri_ensure(em_cache, sctx);
if (em_cache->bvhtree[2] == nullptr) {
return retval;
}
if (sctx->ret.hit_list) {
RayCastAll_Data data;
data.bvhdata = em;
data.raycast_callback = em_cache->raycast_callback;
data.obmat = &obmat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob_uuid = ob_index;
data.hit_list = sctx->ret.hit_list;
void *hit_last_prev = data.hit_list->last;
BLI_bvhtree_ray_cast_all(em_cache->bvhtree[2],
ray_start_local,
ray_normal_local,
0.0f,
depth_max,
raycast_all_cb,
&data);
retval = hit_last_prev != data.hit_list->last;
}
else {
BVHTreeRayHit hit{};
hit.index = -1;
hit.dist = local_depth;
if (BLI_bvhtree_ray_cast(em_cache->bvhtree[2],
ray_start_local,
ray_normal_local,
0.0f,
&hit,
sctx->runtime.params.use_backface_culling ?
editmesh_looptris_raycast_backface_culling_cb :
em_cache->raycast_callback,
em) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= depth_max) {
hit.index = BM_elem_index_get(em->looptris[hit.index][0]->f);
retval = true;
}
SnapData::register_result_raycast(sctx, ob_eval, nullptr, obmat, &hit, is_in_front);
}
}
return retval;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Surface Snap Functions
* \{ */
static bool nearest_world_editmesh(SnapCache_EditMesh *em_cache,
SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em,
const float4x4 &obmat)
{
snap_cache_tri_ensure(em_cache, sctx);
if (em_cache->bvhtree[2] == nullptr) {
return false;
}
BVHTreeNearest nearest{};
nearest.dist_sq = sctx->ret.dist_nearest_sq;
if (nearest_world_tree(
sctx, em_cache->bvhtree[2], em_cache->nearest_callback, obmat, em, &nearest))
{
SnapData::register_result(sctx, ob_eval, nullptr, obmat, &nearest);
return true;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Subclass for Snapping to Edges or Points of an EditMesh
* \{ */
class SnapData_EditMesh : public SnapData {
public:
BMesh *bm;
SnapData_EditMesh(SnapObjectContext *sctx, BMesh *bm, const float4x4 &obmat)
: SnapData(sctx, obmat), bm(bm){};
void get_vert_co(const int index, const float **r_co) override
{
BMVert *eve = BM_vert_at_index(this->bm, index);
*r_co = eve->co;
}
void get_edge_verts_index(const int index, int r_v_index[2]) override
{
BMEdge *eed = BM_edge_at_index(this->bm, index);
r_v_index[0] = BM_elem_index_get(eed->v1);
r_v_index[1] = BM_elem_index_get(eed->v2);
}
void copy_vert_no(const int index, float r_no[3]) override
{
BMVert *eve = BM_vert_at_index(this->bm, index);
copy_v3_v3(r_no, eve->no);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Object Snapping API
* \{ */
eSnapMode snap_polygon_editmesh(SnapObjectContext *sctx,
Object *ob_eval,
const ID * /*id*/,
const float4x4 &obmat,
eSnapMode snap_to_flag,
int face)
{
eSnapMode elem = SCE_SNAP_TO_NONE;
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
SnapData_EditMesh nearest2d(sctx, em->bm, obmat);
nearest2d.clip_planes_enable(sctx, ob_eval);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = sctx->ret.dist_px_sq;
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
BMFace *f = BM_face_at_index(em->bm, face);
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
if (snap_to_flag & SCE_SNAP_TO_EDGE) {
elem = SCE_SNAP_TO_EDGE;
BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_EDGE);
BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE);
do {
cb_snap_edge(&nearest2d,
BM_elem_index_get(l_iter->e),
&nearest2d.nearest_precalc,
reinterpret_cast<float(*)[4]>(nearest2d.clip_planes.data()),
nearest2d.clip_planes.size(),
&nearest);
} while ((l_iter = l_iter->next) != l_first);
}
else {
elem = SCE_SNAP_TO_EDGE_ENDPOINT;
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
do {
cb_snap_vert(&nearest2d,
BM_elem_index_get(l_iter->v),
&nearest2d.nearest_precalc,
reinterpret_cast<float(*)[4]>(nearest2d.clip_planes.data()),
nearest2d.clip_planes.size(),
&nearest);
} while ((l_iter = l_iter->next) != l_first);
}
if (nearest.index != -1) {
nearest2d.nearest_point = nearest;
nearest2d.register_result(sctx, ob_eval, nullptr);
return elem;
}
return SCE_SNAP_TO_NONE;
}
eSnapMode snap_edge_points_editmesh(SnapObjectContext *sctx,
Object *ob_eval,
const ID * /*id*/,
const float4x4 &obmat,
float dist_pex_sq_orig,
int edge)
{
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
SnapData_EditMesh nearest2d(sctx, em->bm, obmat);
eSnapMode elem = nearest2d.snap_edge_points_impl(sctx, edge, dist_pex_sq_orig);
if (nearest2d.nearest_point.index != -2) {
nearest2d.register_result(sctx, ob_eval, nullptr);
}
return elem;
}
static eSnapMode snapEditMesh(SnapCache_EditMesh *em_cache,
SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em,
const float4x4 &obmat,
eSnapMode snap_to_flag)
{
BLI_assert(snap_to_flag != SCE_SNAP_TO_FACE);
SnapData_EditMesh nearest2d(sctx, em->bm, obmat);
/* Was BKE_boundbox_ray_hit_check, see: cf6ca226fa58. */
if (!nearest2d.snap_boundbox(em_cache->min, em_cache->max)) {
return SCE_SNAP_TO_NONE;
}
if (snap_to_flag & SCE_SNAP_TO_POINT) {
BVHTreeFromEditMesh treedata{};
treedata.tree = em_cache->bvhtree[0];
if (treedata.tree == nullptr) {
if (sctx->callbacks.edit_mesh.test_vert_fn) {
auto test_looseverts_fn = [](BMElem *elem, void *user_data) {
SnapObjectContext *sctx_ = static_cast<SnapObjectContext *>(user_data);
BMVert *v = reinterpret_cast<BMVert *>(elem);
if (v->e && (!sctx_->callbacks.edit_mesh.test_edge_fn ||
sctx_->callbacks.edit_mesh.test_edge_fn(
v->e, sctx_->callbacks.edit_mesh.user_data)))
{
return false;
}
return sctx_->callbacks.edit_mesh.test_vert_fn(v, sctx_->callbacks.edit_mesh.user_data);
};
blender::BitVector<> verts_mask(em->bm->totvert);
const int verts_num_active = BM_iter_mesh_bitmap_from_filter(
BM_VERTS_OF_MESH, em->bm, verts_mask, test_looseverts_fn, sctx);
bvhtree_from_editmesh_verts_ex(&treedata, em, verts_mask, verts_num_active, 0.0f, 2, 6);
}
else {
BKE_bvhtree_from_editmesh_get(&treedata,
em,
2,
BVHTREE_FROM_EM_LOOSEVERTS,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &em_cache->mesh_runtime->bvh_cache,
&em_cache->mesh_runtime->eval_mutex);
}
em_cache->bvhtree[0] = treedata.tree;
em_cache->cached[0] = treedata.cached;
}
}
if (snap_to_flag & SNAP_TO_EDGE_ELEMENTS) {
BVHTreeFromEditMesh treedata{};
treedata.tree = em_cache->bvhtree[1];
if (treedata.tree == nullptr) {
if (sctx->callbacks.edit_mesh.test_edge_fn) {
blender::BitVector<> edges_mask(em->bm->totedge);
const int edges_num_active = BM_iter_mesh_bitmap_from_filter(
BM_EDGES_OF_MESH,
em->bm,
edges_mask,
(bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_edge_fn,
sctx->callbacks.edit_mesh.user_data);
bvhtree_from_editmesh_edges_ex(&treedata, em, edges_mask, edges_num_active, 0.0f, 2, 6);
}
else {
BKE_bvhtree_from_editmesh_get(&treedata,
em,
2,
BVHTREE_FROM_EM_EDGES,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &em_cache->mesh_runtime->bvh_cache,
&em_cache->mesh_runtime->eval_mutex);
}
em_cache->bvhtree[1] = treedata.tree;
em_cache->cached[1] = treedata.cached;
}
}
/* #XRAY_ENABLED can return false even with the XRAY flag enabled, this happens because the
* alpha is 1.0 in this case. But even with the alpha being 1.0, the edit mesh is still not
* occluded. */
const bool skip_occlusion_plane = XRAY_FLAG_ENABLED(sctx->runtime.v3d);
nearest2d.clip_planes_enable(sctx, ob_eval, skip_occlusion_plane);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = sctx->ret.dist_px_sq;
eSnapMode elem = SCE_SNAP_TO_POINT;
if (em_cache->bvhtree[0] && (snap_to_flag & SCE_SNAP_TO_POINT)) {
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
BLI_bvhtree_find_nearest_projected(em_cache->bvhtree[0],
nearest2d.pmat_local.ptr(),
sctx->runtime.win_size,
sctx->runtime.mval,
reinterpret_cast<float(*)[4]>(nearest2d.clip_planes.data()),
nearest2d.clip_planes.size(),
&nearest,
cb_snap_vert,
&nearest2d);
}
if (em_cache->bvhtree[1] && (snap_to_flag & SNAP_TO_EDGE_ELEMENTS)) {
int last_index = nearest.index;
nearest.index = -1;
BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT);
BM_mesh_elem_index_ensure(em->bm, BM_EDGE | BM_VERT);
BLI_bvhtree_find_nearest_projected(em_cache->bvhtree[1],
nearest2d.pmat_local.ptr(),
sctx->runtime.win_size,
sctx->runtime.mval,
reinterpret_cast<float(*)[4]>(nearest2d.clip_planes.data()),
nearest2d.clip_planes.size(),
&nearest,
cb_snap_edge,
&nearest2d);
if (nearest.index != -1) {
elem = SCE_SNAP_TO_EDGE;
}
else {
nearest.index = last_index;
}
}
if (nearest.index != -1) {
nearest2d.nearest_point = nearest;
nearest2d.register_result(sctx, ob_eval, nullptr);
return elem;
}
return SCE_SNAP_TO_NONE;
return snap_object_data_editmesh_get(sctx, ob_eval, true);
}
/** \} */
@@ -679,33 +243,9 @@ eSnapMode snap_object_editmesh(SnapObjectContext *sctx,
eSnapMode snap_to_flag,
bool /*use_hide*/)
{
eSnapMode elem = SCE_SNAP_TO_NONE;
SnapCache_EditMesh *em_cache = editmesh_snapdata_init(sctx, ob_eval, snap_to_flag);
if (em_cache == nullptr) {
return elem;
if (em_cache && em_cache->mesh) {
return snap_object_mesh(sctx, ob_eval, &em_cache->mesh->id, obmat, snap_to_flag, true);
}
BMEditMesh *em = em_cache->em;
eSnapMode snap_mode_used = snap_to_flag & editmesh_snap_mode_supported(em->bm);
if (snap_mode_used & (SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT)) {
elem = snapEditMesh(em_cache, sctx, ob_eval, em, obmat, snap_mode_used);
if (elem) {
return elem;
}
}
if (snap_mode_used & SCE_SNAP_TO_FACE) {
if (raycastEditMesh(em_cache, sctx, ob_eval, em, obmat, sctx->runtime.object_index++)) {
return SCE_SNAP_TO_FACE;
}
}
if (snap_mode_used & SCE_SNAP_INDIVIDUAL_NEAREST) {
if (nearest_world_editmesh(em_cache, sctx, ob_eval, em, obmat)) {
return SCE_SNAP_INDIVIDUAL_NEAREST;
}
}
return SCE_SNAP_TO_NONE;
}

View File

@@ -108,14 +108,12 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
/* Test bounding box */
if (ob_eval->data == me_eval) {
const Bounds<float3> bounds = *me_eval->bounds_min_max();
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, bounds.min, bounds.max, &len_diff, nullptr))
{
return retval;
}
const Bounds<float3> bounds = *me_eval->bounds_min_max();
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, bounds.min, bounds.max, &len_diff, nullptr))
{
return retval;
}
/* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with