Cleanup: Split 'transform_snap_object.cc' into smaller files

This commit splits the `transform_snap_object.cc` file into 4 files:
- `transform_snap_object.hh`
- `transform_snap_object.cc`
- `transform_snap_object_editmesh.cc`
- `transform_snap_object_mesh.cc`

The `transform_snap_object.hh` header, in addition to sharing common
types, allows functions defined in each of these files to be shared
between them.

This makes the code easier to read and simplifies maintenance.

Pull Request: https://projects.blender.org/blender/blender/pulls/108949
This commit is contained in:
Germano Cavalcante
2023-06-14 20:08:08 +02:00
committed by Germano Cavalcante
parent f98e488311
commit 9265fa1eed
5 changed files with 1775 additions and 1446 deletions

View File

@@ -98,6 +98,8 @@ set(SRC
transform_snap.cc
transform_snap_animation.c
transform_snap_object.cc
transform_snap_object_editmesh.cc
transform_snap_object_mesh.cc
transform_snap_sequencer.c
transform.h
@@ -109,6 +111,7 @@ set(SRC
transform_mode.h
transform_orientations.h
transform_snap.h
transform_snap_object.hh
)
set(LIB

View File

@@ -6,27 +6,13 @@
* \ingroup edtransform
*/
#include <cstdlib>
#include "MEM_guardedalloc.h"
#include "BLI_bitmap.h"
#include "BLI_kdopbvh.h"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector.hh"
#include "BLI_utildefines.h"
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_view3d_types.h"
#include "BKE_armature.h"
#include "BKE_bvhutils.h"
@@ -34,126 +20,17 @@
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
#include "BKE_geometry_set.h"
#include "BKE_global.h"
#include "BKE_layer.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_object.h"
#include "BKE_tracking.h"
#include "DEG_depsgraph_query.h"
#include "ED_armature.h"
#include "ED_transform_snap_object_context.h"
#include "ED_view3d.h"
using blender::float3;
using blender::float4x4;
using blender::Map;
using blender::Span;
/* -------------------------------------------------------------------- */
/** \name Internal Data Types
* \{ */
#define MAX_CLIPPLANE_LEN 3
enum eViewProj {
VIEW_PROJ_NONE = -1,
VIEW_PROJ_ORTHO = 0,
VIEW_PROJ_PERSP = -1,
};
/** #SnapObjectContext.editmesh_caches */
struct SnapData_EditMesh {
/* Verts, Edges. */
BVHTree *bvhtree[2];
bool cached[2];
/* BVH tree from #BMEditMesh.looptris. */
BVHTreeFromEditMesh treedata_editmesh;
blender::bke::MeshRuntime *mesh_runtime;
float min[3], max[3];
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;
}
free_bvhtree_from_editmesh(&this->treedata_editmesh);
}
~SnapData_EditMesh()
{
this->clear();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("SnapData_EditMesh")
#endif
};
struct SnapObjectContext {
Scene *scene;
int flag;
Map<const BMEditMesh *, std::unique_ptr<SnapData_EditMesh>> editmesh_caches;
/* Filter data, returns true to check this value */
struct {
struct {
bool (*test_vert_fn)(BMVert *, void *user_data);
bool (*test_edge_fn)(BMEdge *, void *user_data);
bool (*test_face_fn)(BMFace *, void *user_data);
void *user_data;
} edit_mesh;
} callbacks;
struct {
Depsgraph *depsgraph;
const ARegion *region;
const View3D *v3d;
float mval[2];
float pmat[4][4]; /* perspective matrix */
float win_size[2]; /* win x and y */
enum eViewProj view_proj;
float clip_plane[MAX_CLIPPLANE_LEN][4];
short clip_plane_len;
eSnapMode snap_to_flag;
bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */
} runtime;
/* Output. */
struct {
/* Location of snapped point on target surface. */
float loc[3];
/* Normal of snapped point on target surface. */
float no[3];
/* Index of snapped element on target object (-1 when no valid index is found). */
int index;
/* Matrix of target object (may not be #Object.object_to_world with dupli-instances). */
float obmat[4][4];
/* List of #SnapObjectHitDepth (caller must free). */
ListBase *hit_list;
/* Snapped object. */
Object *ob;
/* Snapped data. */
ID *data;
float dist_sq;
bool is_edit;
} ret;
};
/** \} */
#include "transform_snap_object.hh"
/* -------------------------------------------------------------------- */
/** \name Utilities
@@ -212,184 +89,6 @@ static ID *data_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_
/** \} */
/* -------------------------------------------------------------------- */
/** \name Snap Object Data
* \{ */
/**
* 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])
{
INIT_MINMAX(r_min, r_max);
BMIter iter;
BMVert *v;
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);
}
}
static void snap_object_data_mesh_get(const Mesh *me_eval,
bool use_hide,
BVHTreeFromMesh *r_treedata)
{
const Span<float3> vert_positions = me_eval->vert_positions();
const blender::OffsetIndices polys = me_eval->polys();
const Span<int> corner_verts = me_eval->corner_verts();
/* The BVHTree from looptris is always required. */
BKE_bvhtree_from_mesh_get(
r_treedata, me_eval, use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI, 4);
BLI_assert(reinterpret_cast<const float3 *>(r_treedata->vert_positions) ==
vert_positions.data());
BLI_assert(r_treedata->corner_verts == corner_verts.data());
BLI_assert(!polys.data() || r_treedata->looptri);
BLI_assert(!r_treedata->tree || r_treedata->looptri);
UNUSED_VARS_NDEBUG(vert_positions, polys, corner_verts);
}
/* 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;
}
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;
}
static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em)
{
SnapData_EditMesh *sod;
bool init = false;
if (std::unique_ptr<SnapData_EditMesh> *sod_p = sctx->editmesh_caches.lookup_ptr(em)) {
sod = sod_p->get();
bool is_dirty = false;
/* Check if the geometry has changed. */
if (sod->treedata_editmesh.em != em) {
is_dirty = true;
}
else if (sod->mesh_runtime) {
if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
if (G.moving) {
/* WORKAROUND: avoid updating while transforming. */
BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]);
sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
}
else {
is_dirty = true;
}
}
else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
else if (sod->bvhtree[0] && sod->cached[0] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
else if (sod->bvhtree[1] && sod->cached[1] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
}
if (is_dirty) {
sod->clear();
init = true;
}
}
else {
std::unique_ptr<SnapData_EditMesh> sod_ptr = std::make_unique<SnapData_EditMesh>();
sod = sod_ptr.get();
sctx->editmesh_caches.add_new(em, std::move(sod_ptr));
init = true;
}
if (init) {
sod->treedata_editmesh.em = em;
sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
snap_editmesh_minmax(sctx, em->bm, sod->min, sod->max);
}
return sod;
}
static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em)
{
SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh;
if (treedata->tree == nullptr) {
/* Operators only update the editmesh looptris of the original mesh. */
BLI_assert(sod->treedata_editmesh.em ==
BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
em = sod->treedata_editmesh.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 looptri_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_looptri_ex(treedata, em, elem_mask, looptri_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_LOOPTRI,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
}
if (treedata->tree == nullptr) {
return nullptr;
}
return treedata;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Iterator
* \{ */
@@ -528,26 +227,6 @@ static eSnapMode iter_snap_objects(SnapObjectContext *sctx,
/* Store all ray-hits
* Support for storing all depths, not just the first (ray-cast 'all'). */
struct RayCastAll_Data {
void *bvhdata;
/* internal vars for adding depths */
BVHTree_RayCastCallback raycast_callback;
const float (*obmat)[4];
const float (*timat)[3];
float len_diff;
float local_scale;
Object *ob_eval;
uint ob_uuid;
/* output data */
ListBase *hit_list;
bool retval;
};
static SnapObjectHitDepth *hit_depth_create(const float depth,
const float co[3],
const float no[3],
@@ -586,7 +265,7 @@ static int hit_depth_cmp(const void *arg1, const void *arg2)
return val;
}
static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
RayCastAll_Data *data = static_cast<RayCastAll_Data *>(userdata);
data->raycast_callback(data->bvhdata, index, ray, hit);
@@ -610,350 +289,13 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
}
}
static bool raycast_tri_backface_culling_test(
bool raycast_tri_backface_culling_test(
const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3])
{
cross_tri_v3(no, v0, v1, v2);
return dot_v3v3(no, dir) < 0.0f;
}
/* Callback to ray-cast with back-face culling (#Mesh). */
static void mesh_looptri_raycast_backface_culling_cb(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata;
const float(*vert_positions)[3] = data->vert_positions;
const MLoopTri *lt = &data->looptri[index];
const float *vtri_co[3] = {
vert_positions[data->corner_verts[lt->tri[0]]],
vert_positions[data->corner_verts[lt->tri[1]]],
vert_positions[data->corner_verts[lt->tri[2]]],
};
float dist = bvhtree_ray_tri_intersection(ray, hit->dist, UNPACK3(vtri_co));
if (dist >= 0 && dist < hit->dist) {
float no[3];
if (raycast_tri_backface_culling_test(ray->direction, UNPACK3(vtri_co), no)) {
hit->index = index;
hit->dist = dist;
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
normalize_v3_v3(hit->no, no);
}
}
}
/* Callback to ray-cast with back-face culling (#EditMesh). */
static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata;
BMEditMesh *em = data->em;
const BMLoop **ltri = (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 raycastMesh(const SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
Object *ob_eval,
const Mesh *me_eval,
const float obmat[4][4],
const uint ob_index,
bool use_hide,
/* read/write args */
float *ray_depth,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index,
ListBase *r_hit_list)
{
bool retval = false;
if (me_eval->totpoly == 0) {
return retval;
}
float imat[4][4];
float ray_start_local[3], ray_normal_local[3];
float local_scale, local_depth, len_diff = 0.0f;
invert_m4_m4(imat, obmat);
copy_v3_v3(ray_start_local, ray_start);
copy_v3_v3(ray_normal_local, ray_dir);
mul_m4_v3(imat, ray_start_local);
mul_mat3_m4_v3(imat, ray_normal_local);
/* local scale in normal direction */
local_scale = normalize_v3(ray_normal_local);
local_depth = *ray_depth;
if (local_depth != BVH_RAYCAST_DIST_MAX) {
local_depth *= local_scale;
}
/* Test BoundBox */
if (ob_eval->data == me_eval) {
const BoundBox *bb = BKE_object_boundbox_get(ob_eval);
if (bb) {
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], &len_diff, nullptr))
{
return retval;
}
}
}
/* We pass a temp ray_start, set from object's boundbox, 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) {
/* Make temporary start point a bit away from bounding-box hit point. */
len_diff -= local_scale;
madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff);
local_depth -= len_diff;
}
else {
len_diff = 0.0f;
}
BVHTreeFromMesh treedata;
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
const blender::Span<int> looptri_polys = me_eval->looptri_polys();
if (treedata.tree == nullptr) {
return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
BLI_assert(treedata.raycast_callback != nullptr);
if (r_hit_list) {
RayCastAll_Data data;
data.bvhdata = &treedata;
data.raycast_callback = treedata.raycast_callback;
data.obmat = obmat;
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
BLI_bvhtree_ray_cast_all(
treedata.tree, ray_start_local, ray_normal_local, 0.0f, *ray_depth, raycast_all_cb, &data);
retval = data.retval;
}
else {
BVHTreeRayHit hit{};
hit.index = -1;
hit.dist = local_depth;
if (BLI_bvhtree_ray_cast(treedata.tree,
ray_start_local,
ray_normal_local,
0.0f,
&hit,
params->use_backface_culling ?
mesh_looptri_raycast_backface_culling_cb :
treedata.raycast_callback,
&treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
/* Back to world-space. */
mul_m4_v3(obmat, r_loc);
if (r_no) {
copy_v3_v3(r_no, hit.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
retval = true;
if (r_index) {
*r_index = looptri_polys[hit.index];
}
}
}
}
return retval;
}
static bool raycastEditMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
const uint ob_index,
/* read/write args */
float *ray_depth,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index,
ListBase *r_hit_list)
{
bool retval = false;
if (em->bm->totface == 0) {
return retval;
}
float imat[4][4];
float ray_start_local[3], ray_normal_local[3];
float local_scale, local_depth, len_diff = 0.0f;
invert_m4_m4(imat, obmat);
copy_v3_v3(ray_start_local, ray_start);
copy_v3_v3(ray_normal_local, ray_dir);
mul_m4_v3(imat, ray_start_local);
mul_mat3_m4_v3(imat, ray_normal_local);
/* local scale in normal direction */
local_scale = normalize_v3(ray_normal_local);
local_depth = *ray_depth;
if (local_depth != BVH_RAYCAST_DIST_MAX) {
local_depth *= local_scale;
}
SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, sod->min, sod->max, &len_diff, nullptr))
{
return retval;
}
/* We pass a temp ray_start, set from object's boundbox, 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 bbox hit point. */
madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff);
local_depth -= len_diff;
}
else {
len_diff = 0.0f;
}
BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
if (treedata == nullptr) {
return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
if (r_hit_list) {
RayCastAll_Data data;
data.bvhdata = treedata;
data.raycast_callback = treedata->raycast_callback;
data.obmat = obmat;
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
BLI_bvhtree_ray_cast_all(treedata->tree,
ray_start_local,
ray_normal_local,
0.0f,
*ray_depth,
raycast_all_cb,
&data);
retval = data.retval;
}
else {
BVHTreeRayHit hit{};
hit.index = -1;
hit.dist = local_depth;
if (BLI_bvhtree_ray_cast(treedata->tree,
ray_start_local,
ray_normal_local,
0.0f,
&hit,
params->use_backface_culling ?
editmesh_looptri_raycast_backface_culling_cb :
treedata->raycast_callback,
treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
/* Back to world-space. */
mul_m4_v3(obmat, r_loc);
if (r_no) {
copy_v3_v3(r_no, hit.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
retval = true;
if (r_index) {
em = sod->treedata_editmesh.em;
*r_index = BM_elem_index_get(em->looptris[hit.index][0]->f);
}
}
}
}
return retval;
}
struct RaycastObjUserData {
const float *ray_start;
const float *ray_dir;
@@ -1133,18 +475,18 @@ static void nearest_world_tree_co(BVHTree *tree,
}
}
static bool nearest_world_tree(SnapObjectContext * /*sctx*/,
const SnapObjectParams *params,
BVHTree *tree,
BVHTree_NearestPointCallback nearest_cb,
void *treedata,
const float (*obmat)[4],
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index)
bool nearest_world_tree(SnapObjectContext * /*sctx*/,
const SnapObjectParams *params,
BVHTree *tree,
BVHTree_NearestPointCallback nearest_cb,
void *treedata,
const float (*obmat)[4],
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index)
{
if (curr_co == nullptr || init_co == nullptr) {
/* No location to work with, so just return. */
@@ -1206,68 +548,6 @@ static bool nearest_world_tree(SnapObjectContext * /*sctx*/,
return true;
}
static bool nearest_world_mesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const Mesh *me_eval,
const float (*obmat)[4],
bool use_hide,
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index)
{
BVHTreeFromMesh treedata;
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
if (treedata.tree == nullptr) {
return false;
}
return nearest_world_tree(sctx,
params,
treedata.tree,
treedata.nearest_callback,
&treedata,
obmat,
init_co,
curr_co,
r_dist_sq,
r_loc,
r_no,
r_index);
}
static bool nearest_world_editmesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
BMEditMesh *em,
const float (*obmat)[4],
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index)
{
BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
if (treedata == nullptr) {
return false;
}
return nearest_world_tree(sctx,
params,
treedata->tree,
treedata->nearest_callback,
treedata,
obmat,
init_co,
curr_co,
r_dist_sq,
r_loc,
r_no,
r_index);
}
static eSnapMode nearest_world_object_fn(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
@@ -1369,12 +649,12 @@ static bool nearestWorldObjects(SnapObjectContext *sctx,
* \{ */
/* Test BoundBox */
static bool snap_bound_box_check_dist(const float min[3],
const float max[3],
const float lpmat[4][4],
const float win_size[2],
const float mval[2],
float dist_px_sq)
bool snap_bound_box_check_dist(const float min[3],
const float max[3],
const float lpmat[4][4],
const float win_size[2],
const float mval[2],
float dist_px_sq)
{
/* In vertex and edges you need to get the pixel distance from ray to BoundBox,
* see: #46099, #46816 */
@@ -1397,118 +677,6 @@ static bool snap_bound_box_check_dist(const float min[3],
/** \name Callbacks
* \{ */
struct Nearest2dUserData;
using Nearest2DGetVertCoCallback = void (*)(const int index,
const Nearest2dUserData *data,
const float **r_co);
using Nearest2DGetEdgeVertsCallback = void (*)(const int index,
const Nearest2dUserData *data,
int r_v_index[2]);
using Nearest2DGetTriVertsCallback = void (*)(const int index,
const Nearest2dUserData *data,
int r_v_index[3]);
/* Equal the previous one */
using Nearest2DGetTriEdgesCallback = void (*)(const int index,
const Nearest2dUserData *data,
int r_e_index[3]);
using Nearest2DCopyVertNoCallback = void (*)(const int index,
const Nearest2dUserData *data,
float r_no[3]);
struct Nearest2dUserData {
Nearest2DGetVertCoCallback get_vert_co;
Nearest2DGetEdgeVertsCallback get_edge_verts_index;
Nearest2DGetTriVertsCallback get_tri_verts_index;
Nearest2DGetTriEdgesCallback get_tri_edges_index;
Nearest2DCopyVertNoCallback copy_vert_no;
union {
struct {
BMesh *bm;
};
struct {
const blender::float3 *vert_positions;
const blender::float3 *vert_normals;
const blender::int2 *edges; /* only used for #BVHTreeFromMeshEdges */
const int *corner_verts;
const int *corner_edges;
const MLoopTri *looptris;
};
};
bool is_persp;
bool use_backface_culling;
};
static void cb_mvert_co_get(const int index, const Nearest2dUserData *data, const float **r_co)
{
*r_co = data->vert_positions[index];
}
static void cb_bvert_co_get(const int index, const Nearest2dUserData *data, const float **r_co)
{
BMVert *eve = BM_vert_at_index(data->bm, index);
*r_co = eve->co;
}
static void cb_mvert_no_copy(const int index, const Nearest2dUserData *data, float r_no[3])
{
copy_v3_v3(r_no, data->vert_normals[index]);
}
static void cb_bvert_no_copy(const int index, const Nearest2dUserData *data, float r_no[3])
{
BMVert *eve = BM_vert_at_index(data->bm, index);
copy_v3_v3(r_no, eve->no);
}
static void cb_medge_verts_get(const int index, const Nearest2dUserData *data, int r_v_index[2])
{
const blender::int2 &edge = data->edges[index];
r_v_index[0] = edge[0];
r_v_index[1] = edge[1];
}
static void cb_bedge_verts_get(const int index, const Nearest2dUserData *data, int r_v_index[2])
{
BMEdge *eed = BM_edge_at_index(data->bm, index);
r_v_index[0] = BM_elem_index_get(eed->v1);
r_v_index[1] = BM_elem_index_get(eed->v2);
}
static void cb_mlooptri_edges_get(const int index, const Nearest2dUserData *data, int r_v_index[3])
{
const blender::int2 *edges = data->edges;
const int *corner_verts = data->corner_verts;
const int *corner_edges = data->corner_edges;
const MLoopTri *lt = &data->looptris[index];
for (int j = 2, j_next = 0; j_next < 3; j = j_next++) {
const blender::int2 &edge = edges[corner_edges[lt->tri[j]]];
const int tri_edge[2] = {corner_verts[lt->tri[j]], corner_verts[lt->tri[j_next]]};
if (ELEM(edge[0], tri_edge[0], tri_edge[1]) && ELEM(edge[1], tri_edge[0], tri_edge[1])) {
// printf("real edge found\n");
r_v_index[j] = corner_edges[lt->tri[j]];
}
else {
r_v_index[j] = -1;
}
}
}
static void cb_mlooptri_verts_get(const int index, const Nearest2dUserData *data, int r_v_index[3])
{
const int *corner_verts = data->corner_verts;
const MLoopTri *looptri = &data->looptris[index];
r_v_index[0] = corner_verts[looptri->tri[0]];
r_v_index[1] = corner_verts[looptri->tri[1]];
r_v_index[2] = corner_verts[looptri->tri[2]];
}
static bool test_projected_vert_dist(const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
@@ -1569,12 +737,12 @@ static bool test_projected_edge_dist(const DistProjectedAABBPrecalc *precalc,
precalc, clip_plane, clip_plane_len, is_persp, near_co, dist_px_sq, r_co);
}
static void cb_snap_vert(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
void cb_snap_vert(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
@@ -1589,12 +757,12 @@ static void cb_snap_vert(void *userdata,
}
}
static void cb_snap_edge(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
void cb_snap_edge(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
@@ -1619,261 +787,50 @@ static void cb_snap_edge(void *userdata,
}
}
static void cb_snap_edge_verts(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
int vindex[2];
data->get_edge_verts_index(index, data, vindex);
for (int i = 2; i--;) {
if (vindex[i] == nearest->index) {
continue;
}
cb_snap_vert(userdata, vindex[i], precalc, clip_plane, clip_plane_len, nearest);
}
}
static void cb_snap_tri_edges(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
if (data->use_backface_culling) {
int vindex[3];
data->get_tri_verts_index(index, data, vindex);
const float *t0, *t1, *t2;
data->get_vert_co(vindex[0], data, &t0);
data->get_vert_co(vindex[1], data, &t1);
data->get_vert_co(vindex[2], data, &t2);
float dummy[3];
if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
return;
}
}
int eindex[3];
data->get_tri_edges_index(index, data, eindex);
for (int i = 3; i--;) {
if (eindex[i] != -1) {
if (eindex[i] == nearest->index) {
continue;
}
cb_snap_edge(userdata, eindex[i], precalc, clip_plane, clip_plane_len, nearest);
}
}
}
static void cb_snap_tri_verts(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
int vindex[3];
data->get_tri_verts_index(index, data, vindex);
if (data->use_backface_culling) {
const float *t0, *t1, *t2;
data->get_vert_co(vindex[0], data, &t0);
data->get_vert_co(vindex[1], data, &t1);
data->get_vert_co(vindex[2], data, &t2);
float dummy[3];
if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
return;
}
}
for (int i = 3; i--;) {
if (vindex[i] == nearest->index) {
continue;
}
cb_snap_vert(userdata, vindex[i], precalc, clip_plane, clip_plane_len, nearest);
}
}
static void nearest2d_data_init_mesh(const Mesh *mesh,
bool is_persp,
bool use_backface_culling,
Nearest2dUserData *r_nearest2d)
{
r_nearest2d->get_vert_co = cb_mvert_co_get;
r_nearest2d->get_edge_verts_index = cb_medge_verts_get;
r_nearest2d->copy_vert_no = cb_mvert_no_copy;
r_nearest2d->get_tri_verts_index = cb_mlooptri_verts_get;
r_nearest2d->get_tri_edges_index = cb_mlooptri_edges_get;
r_nearest2d->vert_positions = mesh->vert_positions().data();
r_nearest2d->vert_normals = mesh->vert_normals().data();
r_nearest2d->edges = mesh->edges().data();
r_nearest2d->corner_verts = mesh->corner_verts().data();
r_nearest2d->corner_edges = mesh->corner_edges().data();
r_nearest2d->looptris = mesh->looptris().data();
r_nearest2d->is_persp = is_persp;
r_nearest2d->use_backface_culling = use_backface_culling;
}
static void nearest2d_data_init_editmesh(SnapData_EditMesh *sod,
bool is_persp,
bool use_backface_culling,
Nearest2dUserData *r_nearest2d)
{
r_nearest2d->get_vert_co = cb_bvert_co_get;
r_nearest2d->get_edge_verts_index = cb_bedge_verts_get;
r_nearest2d->copy_vert_no = cb_bvert_no_copy;
r_nearest2d->get_tri_verts_index = nullptr;
r_nearest2d->get_tri_edges_index = nullptr;
r_nearest2d->bm = sod->treedata_editmesh.em->bm;
r_nearest2d->is_persp = is_persp;
r_nearest2d->use_backface_culling = use_backface_culling;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Object Snapping API
* \{ */
static eSnapMode snap_mesh_polygon(SnapObjectContext *sctx,
const SnapObjectParams *params,
/* read/write args */
float *dist_px)
static eSnapMode snap_polygon(SnapObjectContext *sctx,
const SnapObjectParams *params,
/* read/write args */
float *dist_px)
{
eSnapMode elem = SCE_SNAP_MODE_NONE;
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, sctx->ret.obmat);
DistProjectedAABBPrecalc neasrest_precalc;
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval);
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, sctx->ret.obmat);
for (int i = sctx->runtime.clip_plane_len; i--;) {
mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]);
}
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = square_f(*dist_px);
Nearest2dUserData nearest2d;
const Mesh *mesh = sctx->ret.data && GS(sctx->ret.data->name) == ID_ME ?
(const Mesh *)sctx->ret.data :
nullptr;
if (mesh) {
nearest2d_data_init_mesh(mesh,
sctx->runtime.view_proj == VIEW_PROJ_PERSP,
params->use_backface_culling,
&nearest2d);
const blender::IndexRange poly = mesh->polys()[sctx->ret.index];
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
elem = SCE_SNAP_MODE_EDGE;
BLI_assert(nearest2d.edges != nullptr);
const int *poly_edges = &nearest2d.corner_edges[poly.start()];
for (int i = poly.size(); i--;) {
cb_snap_edge(&nearest2d,
poly_edges[i],
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
}
}
else {
elem = SCE_SNAP_MODE_VERTEX;
const int *poly_verts = &nearest2d.corner_verts[poly.start()];
for (int i = poly.size(); i--;) {
cb_snap_vert(&nearest2d,
poly_verts[i],
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
}
}
return snap_polygon_mesh(sctx,
params,
mesh,
sctx->ret.obmat,
clip_planes_local,
dist_px,
sctx->ret.loc,
sctx->ret.no,
&sctx->ret.index);
}
else if (sctx->ret.is_edit) {
/* The object's #BMEditMesh was used to snap instead. */
std::unique_ptr<SnapData_EditMesh> &sod_editmesh = sctx->editmesh_caches.lookup(
BKE_editmesh_from_object(sctx->ret.ob));
BLI_assert(sod_editmesh.get() != nullptr);
nearest2d_data_init_editmesh(sod_editmesh.get(),
sctx->runtime.view_proj == VIEW_PROJ_PERSP,
params->use_backface_culling,
&nearest2d);
BMEditMesh *em = sod_editmesh->treedata_editmesh.em;
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
BMFace *f = BM_face_at_index(em->bm, sctx->ret.index);
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
elem = SCE_SNAP_MODE_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),
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
} while ((l_iter = l_iter->next) != l_first);
}
else {
elem = SCE_SNAP_MODE_VERTEX;
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),
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
} while ((l_iter = l_iter->next) != l_first);
}
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
copy_v3_v3(sctx->ret.loc, nearest.co);
mul_m4_v3(sctx->ret.obmat, sctx->ret.loc);
{
float imat[4][4];
invert_m4_m4(imat, sctx->ret.obmat);
copy_v3_v3(sctx->ret.no, nearest.no);
mul_transposed_mat3_m4_v3(imat, sctx->ret.no);
normalize_v3(sctx->ret.no);
}
sctx->ret.index = nearest.index;
return elem;
return snap_polygon_editmesh(sctx,
params,
BKE_editmesh_from_object(sctx->ret.ob),
sctx->ret.obmat,
clip_planes_local,
dist_px,
sctx->ret.loc,
sctx->ret.no,
&sctx->ret.index);
}
return SCE_SNAP_MODE_NONE;
@@ -1905,9 +862,7 @@ static eSnapMode snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
}
else if (sctx->ret.is_edit) {
/* The object's #BMEditMesh was used to snap instead. */
std::unique_ptr<SnapData_EditMesh> &sod_editmesh = sctx->editmesh_caches.lookup(
BKE_editmesh_from_object(sctx->ret.ob));
nearest2d_data_init_editmesh(sod_editmesh.get(),
nearest2d_data_init_editmesh(BKE_editmesh_from_object(sctx->ret.ob),
sctx->runtime.view_proj == VIEW_PROJ_PERSP,
params->use_backface_culling,
&nearest2d);
@@ -2524,350 +1479,6 @@ static eSnapMode snapCamera(const SnapObjectContext *sctx,
return SCE_SNAP_MODE_NONE;
}
static eSnapMode snapMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
const Mesh *me_eval,
const float obmat[4][4],
bool use_hide,
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
if (me_eval->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
return SCE_SNAP_MODE_NONE;
}
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
float dist_px_sq = square_f(*dist_px);
/* Test BoundBox */
if (ob_eval->data == me_eval) {
const BoundBox *bb = BKE_object_boundbox_get(ob_eval);
if (!snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq))
{
return SCE_SNAP_MODE_NONE;
}
}
BVHTreeFromMesh treedata, treedata_dummy;
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
BVHTree *bvhtree[2] = {nullptr};
bvhtree[0] = BKE_bvhtree_from_mesh_get(&treedata_dummy, me_eval, BVHTREE_FROM_LOOSEEDGES, 2);
BLI_assert(treedata_dummy.cached);
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) {
bvhtree[1] = BKE_bvhtree_from_mesh_get(&treedata_dummy, me_eval, BVHTREE_FROM_LOOSEVERTS, 2);
BLI_assert(treedata_dummy.cached);
}
Nearest2dUserData nearest2d;
nearest2d_data_init_mesh(me_eval,
sctx->runtime.view_proj == VIEW_PROJ_PERSP,
params->use_backface_culling,
&nearest2d);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = dist_px_sq;
int last_index = nearest.index;
eSnapMode elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, obmat);
for (int i = sctx->runtime.clip_plane_len; i--;) {
mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]);
}
if (bvhtree[1]) {
BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX);
/* snap to loose verts */
BLI_bvhtree_find_nearest_projected(bvhtree[1],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_vert,
&nearest2d);
last_index = nearest.index;
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
if (bvhtree[0]) {
/* Snap to loose edges. */
BLI_bvhtree_find_nearest_projected(bvhtree[0],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_edge,
&nearest2d);
}
if (treedata.tree) {
/* Snap to looptris. */
BLI_bvhtree_find_nearest_projected(treedata.tree,
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_tri_edges,
&nearest2d);
}
if (last_index != nearest.index) {
elem = SCE_SNAP_MODE_EDGE;
}
}
else {
BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX);
if (bvhtree[0]) {
/* Snap to loose edge verts. */
BLI_bvhtree_find_nearest_projected(bvhtree[0],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_edge_verts,
&nearest2d);
}
if (treedata.tree) {
/* Snap to looptri verts. */
BLI_bvhtree_find_nearest_projected(treedata.tree,
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_tri_verts,
&nearest2d);
}
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
copy_v3_v3(r_loc, nearest.co);
mul_m4_v3(obmat, r_loc);
if (r_no) {
float imat[4][4];
invert_m4_m4(imat, obmat);
copy_v3_v3(r_no, nearest.no);
mul_transposed_mat3_m4_v3(imat, r_no);
normalize_v3(r_no);
}
if (r_index) {
*r_index = nearest.index;
}
return elem;
}
return SCE_SNAP_MODE_NONE;
}
static eSnapMode snapEditMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
if (em->bm->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
}
else {
if (em->bm->totedge == 0) {
return SCE_SNAP_MODE_NONE;
}
}
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
float dist_px_sq = square_f(*dist_px);
SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
/* Was BKE_boundbox_ray_hit_check, see: cf6ca226fa58. */
if (!snap_bound_box_check_dist(
sod->min, sod->max, lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq))
{
return SCE_SNAP_MODE_NONE;
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) {
BVHTreeFromEditMesh treedata{};
treedata.tree = sod->bvhtree[0];
if (treedata.tree == nullptr) {
if (sctx->callbacks.edit_mesh.test_vert_fn) {
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,
(bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn,
sctx->callbacks.edit_mesh.user_data);
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_VERTS,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
sod->bvhtree[0] = treedata.tree;
sod->cached[0] = treedata.cached;
}
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
BVHTreeFromEditMesh treedata{};
treedata.tree = sod->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 : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
sod->bvhtree[1] = treedata.tree;
sod->cached[1] = treedata.cached;
}
}
Nearest2dUserData nearest2d;
nearest2d_data_init_editmesh(
sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, params->use_backface_culling, &nearest2d);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = dist_px_sq;
eSnapMode elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, obmat);
for (int i = sctx->runtime.clip_plane_len; i--;) {
mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]);
}
if (sod->bvhtree[0] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
BLI_bvhtree_find_nearest_projected(sod->bvhtree[0],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_vert,
&nearest2d);
}
if (sod->bvhtree[1] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE)) {
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(sod->bvhtree[1],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_edge,
&nearest2d);
if (nearest.index != -1) {
elem = SCE_SNAP_MODE_EDGE;
}
else {
nearest.index = last_index;
}
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
copy_v3_v3(r_loc, nearest.co);
mul_m4_v3(obmat, r_loc);
if (r_no) {
float imat[4][4];
invert_m4_m4(imat, obmat);
copy_v3_v3(r_no, nearest.no);
mul_transposed_mat3_m4_v3(imat, r_no);
normalize_v3(r_no);
}
if (r_index) {
*r_index = nearest.index;
}
return elem;
}
return SCE_SNAP_MODE_NONE;
}
struct SnapObjUserData {
/* read/write args */
float *dist_px;
@@ -3403,7 +2014,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
new_clipplane[3] += 0.01f;
/* Try to snap only to the polygon. */
elem_test = snap_mesh_polygon(sctx, params, &dist_px_tmp);
elem_test = snap_polygon(sctx, params, &dist_px_tmp);
if (elem_test) {
elem = elem_test;
}

View File

@@ -0,0 +1,335 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup editors
*/
#pragma once
#define MAX_CLIPPLANE_LEN 3
enum eViewProj {
VIEW_PROJ_NONE = -1,
VIEW_PROJ_ORTHO = 0,
VIEW_PROJ_PERSP = -1,
};
/* -------------------------------------------------------------------- */
/** \name Internal Data Types
* \{ */
/** #SnapObjectContext.editmesh_caches */
struct SnapData_EditMesh {
/* Verts, Edges. */
BVHTree *bvhtree[2];
bool cached[2];
/* BVH tree from #BMEditMesh.looptris. */
BVHTreeFromEditMesh treedata_editmesh;
blender::bke::MeshRuntime *mesh_runtime;
float min[3], max[3];
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;
}
free_bvhtree_from_editmesh(&this->treedata_editmesh);
}
~SnapData_EditMesh()
{
this->clear();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("SnapData_EditMesh")
#endif
};
/** \} */
struct SnapObjectContext {
struct Scene *scene;
int flag;
blender::Map<const BMEditMesh *, std::unique_ptr<SnapData_EditMesh>> editmesh_caches;
/* Filter data, returns true to check this value */
struct {
struct {
bool (*test_vert_fn)(BMVert *, void *user_data);
bool (*test_edge_fn)(BMEdge *, void *user_data);
bool (*test_face_fn)(BMFace *, void *user_data);
void *user_data;
} edit_mesh;
} callbacks;
struct {
Depsgraph *depsgraph;
const ARegion *region;
const View3D *v3d;
float mval[2];
float pmat[4][4]; /* perspective matrix */
float win_size[2]; /* win x and y */
enum eViewProj view_proj;
float clip_plane[MAX_CLIPPLANE_LEN][4];
short clip_plane_len;
eSnapMode snap_to_flag;
bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */
} runtime;
/* Output. */
struct {
/* Location of snapped point on target surface. */
float loc[3];
/* Normal of snapped point on target surface. */
float no[3];
/* Index of snapped element on target object (-1 when no valid index is found). */
int index;
/* Matrix of target object (may not be #Object.object_to_world with dupli-instances). */
float obmat[4][4];
/* List of #SnapObjectHitDepth (caller must free). */
ListBase *hit_list;
/* Snapped object. */
Object *ob;
/* Snapped data. */
ID *data;
float dist_sq;
bool is_edit;
} ret;
};
struct RayCastAll_Data {
void *bvhdata;
/* internal vars for adding depths */
BVHTree_RayCastCallback raycast_callback;
const float (*obmat)[4];
const float (*timat)[3];
float len_diff;
float local_scale;
Object *ob_eval;
uint ob_uuid;
/* output data */
ListBase *hit_list;
bool retval;
};
struct Nearest2dUserData;
using Nearest2DGetVertCoCallback = void (*)(const int index,
const Nearest2dUserData *data,
const float **r_co);
using Nearest2DGetEdgeVertsCallback = void (*)(const int index,
const Nearest2dUserData *data,
int r_v_index[2]);
using Nearest2DGetTriVertsCallback = void (*)(const int index,
const Nearest2dUserData *data,
int r_v_index[3]);
/* Equal the previous one */
using Nearest2DGetTriEdgesCallback = void (*)(const int index,
const Nearest2dUserData *data,
int r_e_index[3]);
using Nearest2DCopyVertNoCallback = void (*)(const int index,
const Nearest2dUserData *data,
float r_no[3]);
struct Nearest2dUserData {
Nearest2DGetVertCoCallback get_vert_co;
Nearest2DGetEdgeVertsCallback get_edge_verts_index;
Nearest2DGetTriVertsCallback get_tri_verts_index;
Nearest2DGetTriEdgesCallback get_tri_edges_index;
Nearest2DCopyVertNoCallback copy_vert_no;
union {
struct {
BMesh *bm;
};
struct {
const blender::float3 *vert_positions;
const blender::float3 *vert_normals;
const blender::int2 *edges; /* only used for #BVHTreeFromMeshEdges */
const int *corner_verts;
const int *corner_edges;
const MLoopTri *looptris;
};
};
bool is_persp;
bool use_backface_culling;
};
/* transform_snap_object.cc */
void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit);
bool raycast_tri_backface_culling_test(
const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3]);
bool snap_bound_box_check_dist(const float min[3],
const float max[3],
const float lpmat[4][4],
const float win_size[2],
const float mval[2],
float dist_px_sq);
void cb_snap_vert(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest);
void cb_snap_edge(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest);
bool nearest_world_tree(SnapObjectContext *sctx,
const struct SnapObjectParams *params,
BVHTree *tree,
BVHTree_NearestPointCallback nearest_cb,
void *treedata,
const float (*obmat)[4],
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index);
/* transform_snap_object_editmesh.cc */
bool raycastEditMesh(SnapObjectContext *sctx,
const struct SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
const uint ob_index,
/* read/write args */
float *ray_depth,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index,
ListBase *r_hit_list);
eSnapMode snap_polygon_editmesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
BMEditMesh *em,
const float obmat[4][4],
float clip_planes_local[MAX_CLIPPLANE_LEN][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index);
eSnapMode snapEditMesh(struct SnapObjectContext *sctx,
const struct SnapObjectParams *params,
struct Object *ob_eval,
struct BMEditMesh *em,
const float obmat[4][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index);
bool nearest_world_editmesh(struct SnapObjectContext *sctx,
const struct SnapObjectParams *params,
struct Object *ob_eval,
struct BMEditMesh *em,
const float (*obmat)[4],
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index);
void nearest2d_data_init_editmesh(struct BMEditMesh *em,
bool is_persp,
bool use_backface_culling,
struct Nearest2dUserData *r_nearest2d);
/* transform_snap_object_mesh.cc */
bool raycastMesh(const struct SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
struct Object *ob_eval,
const struct Mesh *me_eval,
const float obmat[4][4],
const uint ob_index,
bool use_hide,
/* read/write args */
float *ray_depth,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index,
struct ListBase *r_hit_list);
bool nearest_world_mesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const Mesh *me_eval,
const float (*obmat)[4],
bool use_hide,
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index);
void nearest2d_data_init_mesh(const Mesh *mesh,
bool is_persp,
bool use_backface_culling,
Nearest2dUserData *r_nearest2d);
eSnapMode snap_polygon_mesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const Mesh *mesh,
const float obmat[4][4],
float clip_planes_local[MAX_CLIPPLANE_LEN][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index);
eSnapMode snapMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
const Mesh *me_eval,
const float obmat[4][4],
bool use_hide,
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index);

View File

@@ -0,0 +1,704 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edtransform
*/
#include "BLI_math.h"
#include "BLI_math_matrix_types.hh"
#include "BKE_bvhutils.h"
#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_mesh.hh"
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "ED_transform_snap_object_context.h"
#include "transform_snap_object.hh"
/* -------------------------------------------------------------------- */
/** \name Snap Object Data
* \{ */
/**
* 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])
{
INIT_MINMAX(r_min, r_max);
BMIter iter;
BMVert *v;
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);
}
}
/* 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;
}
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;
}
static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em)
{
SnapData_EditMesh *sod;
bool init = false;
if (std::unique_ptr<SnapData_EditMesh> *sod_p = sctx->editmesh_caches.lookup_ptr(em)) {
sod = sod_p->get();
bool is_dirty = false;
/* Check if the geometry has changed. */
if (sod->treedata_editmesh.em != em) {
is_dirty = true;
}
else if (sod->mesh_runtime) {
if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
if (G.moving) {
/* WORKAROUND: avoid updating while transforming. */
BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]);
sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
}
else {
is_dirty = true;
}
}
else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
else if (sod->bvhtree[0] && sod->cached[0] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
else if (sod->bvhtree[1] && sod->cached[1] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1]))
{
/* The tree is owned by the EditMesh and may have been freed since we last used! */
is_dirty = true;
}
}
if (is_dirty) {
sod->clear();
init = true;
}
}
else {
std::unique_ptr<SnapData_EditMesh> sod_ptr = std::make_unique<SnapData_EditMesh>();
sod = sod_ptr.get();
sctx->editmesh_caches.add_new(em, std::move(sod_ptr));
init = true;
}
if (init) {
sod->treedata_editmesh.em = em;
sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
snap_editmesh_minmax(sctx, em->bm, sod->min, sod->max);
}
return sod;
}
static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em)
{
SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh;
if (treedata->tree == nullptr) {
/* Operators only update the editmesh looptris of the original mesh. */
BLI_assert(sod->treedata_editmesh.em ==
BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
em = sod->treedata_editmesh.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 looptri_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_looptri_ex(treedata, em, elem_mask, looptri_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_LOOPTRI,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
}
if (treedata->tree == nullptr) {
return nullptr;
}
return treedata;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ray Cast Functions
* \{ */
/* Callback to ray-cast with back-face culling (#EditMesh). */
static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata;
BMEditMesh *em = data->em;
const BMLoop **ltri = (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);
}
}
}
}
bool raycastEditMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
const uint ob_index,
/* read/write args */
float *ray_depth,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index,
ListBase *r_hit_list)
{
bool retval = false;
if (em->bm->totface == 0) {
return retval;
}
float imat[4][4];
float ray_start_local[3], ray_normal_local[3];
float local_scale, local_depth, len_diff = 0.0f;
invert_m4_m4(imat, obmat);
copy_v3_v3(ray_start_local, ray_start);
copy_v3_v3(ray_normal_local, ray_dir);
mul_m4_v3(imat, ray_start_local);
mul_mat3_m4_v3(imat, ray_normal_local);
/* local scale in normal direction */
local_scale = normalize_v3(ray_normal_local);
local_depth = *ray_depth;
if (local_depth != BVH_RAYCAST_DIST_MAX) {
local_depth *= local_scale;
}
SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, sod->min, sod->max, &len_diff, nullptr))
{
return retval;
}
/* We pass a temp ray_start, set from object's boundbox, 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 bbox hit point. */
madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff);
local_depth -= len_diff;
}
else {
len_diff = 0.0f;
}
BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
if (treedata == nullptr) {
return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
if (r_hit_list) {
RayCastAll_Data data;
data.bvhdata = treedata;
data.raycast_callback = treedata->raycast_callback;
data.obmat = obmat;
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
BLI_bvhtree_ray_cast_all(treedata->tree,
ray_start_local,
ray_normal_local,
0.0f,
*ray_depth,
raycast_all_cb,
&data);
retval = data.retval;
}
else {
BVHTreeRayHit hit{};
hit.index = -1;
hit.dist = local_depth;
if (BLI_bvhtree_ray_cast(treedata->tree,
ray_start_local,
ray_normal_local,
0.0f,
&hit,
params->use_backface_culling ?
editmesh_looptri_raycast_backface_culling_cb :
treedata->raycast_callback,
treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
/* Back to world-space. */
mul_m4_v3(obmat, r_loc);
if (r_no) {
copy_v3_v3(r_no, hit.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
retval = true;
if (r_index) {
em = sod->treedata_editmesh.em;
*r_index = BM_elem_index_get(em->looptris[hit.index][0]->f);
}
}
}
}
return retval;
}
/* -------------------------------------------------------------------- */
/** \name Surface Snap Functions
* \{ */
bool nearest_world_editmesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
BMEditMesh *em,
const float (*obmat)[4],
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index)
{
BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em);
if (treedata == nullptr) {
return false;
}
return nearest_world_tree(sctx,
params,
treedata->tree,
treedata->nearest_callback,
treedata,
obmat,
init_co,
curr_co,
r_dist_sq,
r_loc,
r_no,
r_index);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks
* \{ */
static void cb_bvert_co_get(const int index, const Nearest2dUserData *data, const float **r_co)
{
BMVert *eve = BM_vert_at_index(data->bm, index);
*r_co = eve->co;
}
static void cb_bvert_no_copy(const int index, const Nearest2dUserData *data, float r_no[3])
{
BMVert *eve = BM_vert_at_index(data->bm, index);
copy_v3_v3(r_no, eve->no);
}
static void cb_bedge_verts_get(const int index, const Nearest2dUserData *data, int r_v_index[2])
{
BMEdge *eed = BM_edge_at_index(data->bm, index);
r_v_index[0] = BM_elem_index_get(eed->v1);
r_v_index[1] = BM_elem_index_get(eed->v2);
}
void nearest2d_data_init_editmesh(BMEditMesh *em,
bool is_persp,
bool use_backface_culling,
Nearest2dUserData *r_nearest2d)
{
r_nearest2d->get_vert_co = cb_bvert_co_get;
r_nearest2d->get_edge_verts_index = cb_bedge_verts_get;
r_nearest2d->copy_vert_no = cb_bvert_no_copy;
r_nearest2d->get_tri_verts_index = nullptr;
r_nearest2d->get_tri_edges_index = nullptr;
r_nearest2d->bm = em->bm;
r_nearest2d->is_persp = is_persp;
r_nearest2d->use_backface_culling = use_backface_culling;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Object Snapping API
* \{ */
eSnapMode snap_polygon_editmesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
BMEditMesh *em,
const float obmat[4][4],
float clip_planes_local[MAX_CLIPPLANE_LEN][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index)
{
BLI_assert(sctx->editmesh_caches.lookup(em).get()->treedata_editmesh.em == em);
eSnapMode elem = SCE_SNAP_MODE_NONE;
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
DistProjectedAABBPrecalc neasrest_precalc;
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = square_f(*dist_px);
Nearest2dUserData nearest2d;
nearest2d_data_init_editmesh(
em, sctx->runtime.view_proj == VIEW_PROJ_PERSP, params->use_backface_culling, &nearest2d);
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
BMFace *f = BM_face_at_index(em->bm, sctx->ret.index);
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
elem = SCE_SNAP_MODE_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),
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
} while ((l_iter = l_iter->next) != l_first);
}
else {
elem = SCE_SNAP_MODE_VERTEX;
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),
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
} while ((l_iter = l_iter->next) != l_first);
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
mul_m4_v3(obmat, nearest.co);
copy_v3_v3(r_loc, nearest.co);
{
float imat[4][4];
invert_m4_m4(imat, obmat);
mul_transposed_mat3_m4_v3(imat, nearest.no);
normalize_v3(nearest.no);
copy_v3_v3(r_no, nearest.no);
}
*r_index = nearest.index;
return elem;
}
return SCE_SNAP_MODE_NONE;
}
eSnapMode snapEditMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
if (em->bm->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
}
else {
if (em->bm->totedge == 0) {
return SCE_SNAP_MODE_NONE;
}
}
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
float dist_px_sq = square_f(*dist_px);
SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
/* Was BKE_boundbox_ray_hit_check, see: cf6ca226fa58. */
if (!snap_bound_box_check_dist(
sod->min, sod->max, lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq))
{
return SCE_SNAP_MODE_NONE;
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) {
BVHTreeFromEditMesh treedata{};
treedata.tree = sod->bvhtree[0];
if (treedata.tree == nullptr) {
if (sctx->callbacks.edit_mesh.test_vert_fn) {
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,
(bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn,
sctx->callbacks.edit_mesh.user_data);
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_VERTS,
/* WORKAROUND: avoid updating while transforming. */
G.moving ? nullptr : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
sod->bvhtree[0] = treedata.tree;
sod->cached[0] = treedata.cached;
}
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
BVHTreeFromEditMesh treedata{};
treedata.tree = sod->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 : &sod->mesh_runtime->bvh_cache,
&sod->mesh_runtime->eval_mutex);
}
sod->bvhtree[1] = treedata.tree;
sod->cached[1] = treedata.cached;
}
}
Nearest2dUserData nearest2d;
nearest2d_data_init_editmesh(sod->treedata_editmesh.em,
sctx->runtime.view_proj == VIEW_PROJ_PERSP,
params->use_backface_culling,
&nearest2d);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = dist_px_sq;
eSnapMode elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, obmat);
for (int i = sctx->runtime.clip_plane_len; i--;) {
mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]);
}
if (sod->bvhtree[0] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
BLI_bvhtree_find_nearest_projected(sod->bvhtree[0],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_vert,
&nearest2d);
}
if (sod->bvhtree[1] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE)) {
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(sod->bvhtree[1],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_edge,
&nearest2d);
if (nearest.index != -1) {
elem = SCE_SNAP_MODE_EDGE;
}
else {
nearest.index = last_index;
}
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
copy_v3_v3(r_loc, nearest.co);
mul_m4_v3(obmat, r_loc);
if (r_no) {
float imat[4][4];
invert_m4_m4(imat, obmat);
copy_v3_v3(r_no, nearest.no);
mul_transposed_mat3_m4_v3(imat, r_no);
normalize_v3(r_no);
}
if (r_index) {
*r_index = nearest.index;
}
return elem;
}
return SCE_SNAP_MODE_NONE;
}
/** \} */

View File

@@ -0,0 +1,676 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edtransform
*/
#include "BLI_math.h"
#include "BLI_math_matrix_types.hh"
#include "BKE_bvhutils.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.hh"
#include "BKE_object.h"
#include "ED_transform_snap_object_context.h"
#include "transform_snap_object.hh"
using blender::float3;
using blender::Span;
/* -------------------------------------------------------------------- */
/** \name Snap Object Data
* \{ */
static void snap_object_data_mesh_get(const Mesh *me_eval,
bool use_hide,
BVHTreeFromMesh *r_treedata)
{
const Span<float3> vert_positions = me_eval->vert_positions();
const blender::OffsetIndices polys = me_eval->polys();
const Span<int> corner_verts = me_eval->corner_verts();
/* The BVHTree from looptris is always required. */
BKE_bvhtree_from_mesh_get(
r_treedata, me_eval, use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI, 4);
BLI_assert(reinterpret_cast<const float3 *>(r_treedata->vert_positions) ==
vert_positions.data());
BLI_assert(r_treedata->corner_verts == corner_verts.data());
BLI_assert(!polys.data() || r_treedata->looptri);
BLI_assert(!r_treedata->tree || r_treedata->looptri);
UNUSED_VARS_NDEBUG(vert_positions, polys, corner_verts);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ray Cast Functions
* \{ */
/* Store all ray-hits
* Support for storing all depths, not just the first (ray-cast 'all'). */
/* Callback to ray-cast with back-face culling (#Mesh). */
static void mesh_looptri_raycast_backface_culling_cb(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit *hit)
{
const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata;
const float(*vert_positions)[3] = data->vert_positions;
const MLoopTri *lt = &data->looptri[index];
const float *vtri_co[3] = {
vert_positions[data->corner_verts[lt->tri[0]]],
vert_positions[data->corner_verts[lt->tri[1]]],
vert_positions[data->corner_verts[lt->tri[2]]],
};
float dist = bvhtree_ray_tri_intersection(ray, hit->dist, UNPACK3(vtri_co));
if (dist >= 0 && dist < hit->dist) {
float no[3];
if (raycast_tri_backface_culling_test(ray->direction, UNPACK3(vtri_co), no)) {
hit->index = index;
hit->dist = dist;
madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
normalize_v3_v3(hit->no, no);
}
}
}
bool raycastMesh(const SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
Object *ob_eval,
const Mesh *me_eval,
const float obmat[4][4],
const uint ob_index,
bool use_hide,
/* read/write args */
float *ray_depth,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index,
ListBase *r_hit_list)
{
bool retval = false;
if (me_eval->totpoly == 0) {
return retval;
}
float imat[4][4];
float ray_start_local[3], ray_normal_local[3];
float local_scale, local_depth, len_diff = 0.0f;
invert_m4_m4(imat, obmat);
copy_v3_v3(ray_start_local, ray_start);
copy_v3_v3(ray_normal_local, ray_dir);
mul_m4_v3(imat, ray_start_local);
mul_mat3_m4_v3(imat, ray_normal_local);
/* local scale in normal direction */
local_scale = normalize_v3(ray_normal_local);
local_depth = *ray_depth;
if (local_depth != BVH_RAYCAST_DIST_MAX) {
local_depth *= local_scale;
}
/* Test BoundBox */
if (ob_eval->data == me_eval) {
const BoundBox *bb = BKE_object_boundbox_get(ob_eval);
if (bb) {
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], &len_diff, nullptr))
{
return retval;
}
}
}
/* We pass a temp ray_start, set from object's boundbox, 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) {
/* Make temporary start point a bit away from bounding-box hit point. */
len_diff -= local_scale;
madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff);
local_depth -= len_diff;
}
else {
len_diff = 0.0f;
}
BVHTreeFromMesh treedata;
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
const blender::Span<int> looptri_polys = me_eval->looptri_polys();
if (treedata.tree == nullptr) {
return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
BLI_assert(treedata.raycast_callback != nullptr);
if (r_hit_list) {
RayCastAll_Data data;
data.bvhdata = &treedata;
data.raycast_callback = treedata.raycast_callback;
data.obmat = obmat;
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
BLI_bvhtree_ray_cast_all(
treedata.tree, ray_start_local, ray_normal_local, 0.0f, *ray_depth, raycast_all_cb, &data);
retval = data.retval;
}
else {
BVHTreeRayHit hit{};
hit.index = -1;
hit.dist = local_depth;
if (BLI_bvhtree_ray_cast(treedata.tree,
ray_start_local,
ray_normal_local,
0.0f,
&hit,
params->use_backface_culling ?
mesh_looptri_raycast_backface_culling_cb :
treedata.raycast_callback,
&treedata) != -1)
{
hit.dist += len_diff;
hit.dist /= local_scale;
if (hit.dist <= *ray_depth) {
*ray_depth = hit.dist;
copy_v3_v3(r_loc, hit.co);
/* Back to world-space. */
mul_m4_v3(obmat, r_loc);
if (r_no) {
copy_v3_v3(r_no, hit.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
retval = true;
if (r_index) {
*r_index = looptri_polys[hit.index];
}
}
}
}
return retval;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Surface Snap Functions
* \{ */
bool nearest_world_mesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const Mesh *me_eval,
const float (*obmat)[4],
bool use_hide,
const float init_co[3],
const float curr_co[3],
float *r_dist_sq,
float *r_loc,
float *r_no,
int *r_index)
{
BVHTreeFromMesh treedata;
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
if (treedata.tree == nullptr) {
return false;
}
return nearest_world_tree(sctx,
params,
treedata.tree,
treedata.nearest_callback,
&treedata,
obmat,
init_co,
curr_co,
r_dist_sq,
r_loc,
r_no,
r_index);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callbacks
* \{ */
static void cb_snap_edge_verts(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
int vindex[2];
data->get_edge_verts_index(index, data, vindex);
for (int i = 2; i--;) {
if (vindex[i] == nearest->index) {
continue;
}
cb_snap_vert(userdata, vindex[i], precalc, clip_plane, clip_plane_len, nearest);
}
}
static void cb_snap_tri_edges(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
if (data->use_backface_culling) {
int vindex[3];
data->get_tri_verts_index(index, data, vindex);
const float *t0, *t1, *t2;
data->get_vert_co(vindex[0], data, &t0);
data->get_vert_co(vindex[1], data, &t1);
data->get_vert_co(vindex[2], data, &t2);
float dummy[3];
if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
return;
}
}
int eindex[3];
data->get_tri_edges_index(index, data, eindex);
for (int i = 3; i--;) {
if (eindex[i] != -1) {
if (eindex[i] == nearest->index) {
continue;
}
cb_snap_edge(userdata, eindex[i], precalc, clip_plane, clip_plane_len, nearest);
}
}
}
static void cb_snap_tri_verts(void *userdata,
int index,
const DistProjectedAABBPrecalc *precalc,
const float (*clip_plane)[4],
const int clip_plane_len,
BVHTreeNearest *nearest)
{
Nearest2dUserData *data = static_cast<Nearest2dUserData *>(userdata);
int vindex[3];
data->get_tri_verts_index(index, data, vindex);
if (data->use_backface_culling) {
const float *t0, *t1, *t2;
data->get_vert_co(vindex[0], data, &t0);
data->get_vert_co(vindex[1], data, &t1);
data->get_vert_co(vindex[2], data, &t2);
float dummy[3];
if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
return;
}
}
for (int i = 3; i--;) {
if (vindex[i] == nearest->index) {
continue;
}
cb_snap_vert(userdata, vindex[i], precalc, clip_plane, clip_plane_len, nearest);
}
}
static void cb_mvert_co_get(const int index, const Nearest2dUserData *data, const float **r_co)
{
*r_co = data->vert_positions[index];
}
static void cb_mvert_no_copy(const int index, const Nearest2dUserData *data, float r_no[3])
{
copy_v3_v3(r_no, data->vert_normals[index]);
}
static void cb_medge_verts_get(const int index, const Nearest2dUserData *data, int r_v_index[2])
{
const blender::int2 &edge = data->edges[index];
r_v_index[0] = edge[0];
r_v_index[1] = edge[1];
}
static void cb_mlooptri_edges_get(const int index, const Nearest2dUserData *data, int r_v_index[3])
{
const blender::int2 *edges = data->edges;
const int *corner_verts = data->corner_verts;
const int *corner_edges = data->corner_edges;
const MLoopTri *lt = &data->looptris[index];
for (int j = 2, j_next = 0; j_next < 3; j = j_next++) {
const blender::int2 &edge = edges[corner_edges[lt->tri[j]]];
const int tri_edge[2] = {corner_verts[lt->tri[j]], corner_verts[lt->tri[j_next]]};
if (ELEM(edge[0], tri_edge[0], tri_edge[1]) && ELEM(edge[1], tri_edge[0], tri_edge[1])) {
// printf("real edge found\n");
r_v_index[j] = corner_edges[lt->tri[j]];
}
else {
r_v_index[j] = -1;
}
}
}
static void cb_mlooptri_verts_get(const int index, const Nearest2dUserData *data, int r_v_index[3])
{
const int *corner_verts = data->corner_verts;
const MLoopTri *looptri = &data->looptris[index];
r_v_index[0] = corner_verts[looptri->tri[0]];
r_v_index[1] = corner_verts[looptri->tri[1]];
r_v_index[2] = corner_verts[looptri->tri[2]];
}
void nearest2d_data_init_mesh(const Mesh *mesh,
bool is_persp,
bool use_backface_culling,
Nearest2dUserData *r_nearest2d)
{
r_nearest2d->get_vert_co = cb_mvert_co_get;
r_nearest2d->get_edge_verts_index = cb_medge_verts_get;
r_nearest2d->copy_vert_no = cb_mvert_no_copy;
r_nearest2d->get_tri_verts_index = cb_mlooptri_verts_get;
r_nearest2d->get_tri_edges_index = cb_mlooptri_edges_get;
r_nearest2d->vert_positions = mesh->vert_positions().data();
r_nearest2d->vert_normals = mesh->vert_normals().data();
r_nearest2d->edges = mesh->edges().data();
r_nearest2d->corner_verts = mesh->corner_verts().data();
r_nearest2d->corner_edges = mesh->corner_edges().data();
r_nearest2d->looptris = mesh->looptris().data();
r_nearest2d->is_persp = is_persp;
r_nearest2d->use_backface_culling = use_backface_culling;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Object Snapping API
* \{ */
eSnapMode snap_polygon_mesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
const Mesh *mesh,
const float obmat[4][4],
float clip_planes_local[MAX_CLIPPLANE_LEN][4],
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index)
{
eSnapMode elem = SCE_SNAP_MODE_NONE;
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
DistProjectedAABBPrecalc neasrest_precalc;
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = square_f(*dist_px);
Nearest2dUserData nearest2d;
nearest2d_data_init_mesh(
mesh, sctx->runtime.view_proj == VIEW_PROJ_PERSP, params->use_backface_culling, &nearest2d);
const blender::IndexRange poly = mesh->polys()[sctx->ret.index];
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
elem = SCE_SNAP_MODE_EDGE;
BLI_assert(nearest2d.edges != nullptr);
const int *poly_edges = &nearest2d.corner_edges[poly.start()];
for (int i = poly.size(); i--;) {
cb_snap_edge(&nearest2d,
poly_edges[i],
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
}
}
else {
elem = SCE_SNAP_MODE_VERTEX;
const int *poly_verts = &nearest2d.corner_verts[poly.start()];
for (int i = poly.size(); i--;) {
cb_snap_vert(&nearest2d,
poly_verts[i],
&neasrest_precalc,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest);
}
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
mul_m4_v3(obmat, nearest.co);
copy_v3_v3(r_loc, nearest.co);
{
float imat[4][4];
invert_m4_m4(imat, obmat);
mul_transposed_mat3_m4_v3(imat, nearest.no);
normalize_v3(nearest.no);
copy_v3_v3(r_no, nearest.no);
}
*r_index = nearest.index;
return elem;
}
return SCE_SNAP_MODE_NONE;
}
eSnapMode snapMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
Object *ob_eval,
const Mesh *me_eval,
const float obmat[4][4],
bool use_hide,
/* read/write args */
float *dist_px,
/* return args */
float r_loc[3],
float r_no[3],
int *r_index)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE);
if (me_eval->totvert == 0) {
return SCE_SNAP_MODE_NONE;
}
if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
return SCE_SNAP_MODE_NONE;
}
float lpmat[4][4];
mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat);
float dist_px_sq = square_f(*dist_px);
/* Test BoundBox */
if (ob_eval->data == me_eval) {
const BoundBox *bb = BKE_object_boundbox_get(ob_eval);
if (!snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq))
{
return SCE_SNAP_MODE_NONE;
}
}
BVHTreeFromMesh treedata, treedata_dummy;
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
BVHTree *bvhtree[2] = {nullptr};
bvhtree[0] = BKE_bvhtree_from_mesh_get(&treedata_dummy, me_eval, BVHTREE_FROM_LOOSEEDGES, 2);
BLI_assert(treedata_dummy.cached);
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) {
bvhtree[1] = BKE_bvhtree_from_mesh_get(&treedata_dummy, me_eval, BVHTREE_FROM_LOOSEVERTS, 2);
BLI_assert(treedata_dummy.cached);
}
Nearest2dUserData nearest2d;
nearest2d_data_init_mesh(me_eval,
sctx->runtime.view_proj == VIEW_PROJ_PERSP,
params->use_backface_culling,
&nearest2d);
BVHTreeNearest nearest{};
nearest.index = -1;
nearest.dist_sq = dist_px_sq;
int last_index = nearest.index;
eSnapMode elem = SCE_SNAP_MODE_VERTEX;
float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4];
transpose_m4_m4(tobmat, obmat);
for (int i = sctx->runtime.clip_plane_len; i--;) {
mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]);
}
if (bvhtree[1]) {
BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX);
/* snap to loose verts */
BLI_bvhtree_find_nearest_projected(bvhtree[1],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_vert,
&nearest2d);
last_index = nearest.index;
}
if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) {
if (bvhtree[0]) {
/* Snap to loose edges. */
BLI_bvhtree_find_nearest_projected(bvhtree[0],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_edge,
&nearest2d);
}
if (treedata.tree) {
/* Snap to looptris. */
BLI_bvhtree_find_nearest_projected(treedata.tree,
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_tri_edges,
&nearest2d);
}
if (last_index != nearest.index) {
elem = SCE_SNAP_MODE_EDGE;
}
}
else {
BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX);
if (bvhtree[0]) {
/* Snap to loose edge verts. */
BLI_bvhtree_find_nearest_projected(bvhtree[0],
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_edge_verts,
&nearest2d);
}
if (treedata.tree) {
/* Snap to looptri verts. */
BLI_bvhtree_find_nearest_projected(treedata.tree,
lpmat,
sctx->runtime.win_size,
sctx->runtime.mval,
clip_planes_local,
sctx->runtime.clip_plane_len,
&nearest,
cb_snap_tri_verts,
&nearest2d);
}
}
if (nearest.index != -1) {
*dist_px = sqrtf(nearest.dist_sq);
copy_v3_v3(r_loc, nearest.co);
mul_m4_v3(obmat, r_loc);
if (r_no) {
float imat[4][4];
invert_m4_m4(imat, obmat);
copy_v3_v3(r_no, nearest.no);
mul_transposed_mat3_m4_v3(imat, r_no);
normalize_v3(r_no);
}
if (r_index) {
*r_index = nearest.index;
}
return elem;
}
return SCE_SNAP_MODE_NONE;
}
/** \} */