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:
committed by
Germano Cavalcante
parent
7ca46bb25d
commit
1c77779160
@@ -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_*`.
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user