Snap: Refactor Face Nearest approach

- Use C++ types for matrices and vectors;
- Deduplicate code with `SnapData::register_result`;

This commit also improves the way of detecting supported snap types.
This commit is contained in:
Germano Cavalcante
2023-06-27 12:49:37 -03:00
parent 229ae63c6e
commit 597f9abcfa
5 changed files with 126 additions and 124 deletions

View File

@@ -272,29 +272,38 @@ eSnapMode SnapData::snap_edge_points_impl(SnapObjectContext *sctx,
return elem;
}
void SnapData::register_result(SnapObjectContext *sctx, Object *ob_eval, const ID *id_eval)
void SnapData::register_result(SnapObjectContext *sctx,
Object *ob_eval,
const ID *id_eval,
const float4x4 &obmat,
BVHTreeNearest *r_nearest)
{
BLI_assert(this->nearest_point.index != -2);
BLI_assert(r_nearest->index != -2);
copy_v3_v3(sctx->ret.loc, this->nearest_point.co);
copy_v3_v3(sctx->ret.no, this->nearest_point.no);
sctx->ret.index = this->nearest_point.index;
copy_m4_m4(sctx->ret.obmat, this->obmat_.ptr());
copy_v3_v3(sctx->ret.loc, r_nearest->co);
copy_v3_v3(sctx->ret.no, r_nearest->no);
sctx->ret.index = r_nearest->index;
copy_m4_m4(sctx->ret.obmat, obmat.ptr());
sctx->ret.ob = ob_eval;
sctx->ret.data = id_eval;
sctx->ret.dist_px_sq = this->nearest_point.dist_sq;
sctx->ret.dist_px_sq = r_nearest->dist_sq;
/* Global space. */
mul_m4_v3(this->obmat_.ptr(), sctx->ret.loc);
mul_mat3_m4_v3(this->obmat_.ptr(), sctx->ret.no);
mul_m4_v3(obmat.ptr(), sctx->ret.loc);
mul_mat3_m4_v3(obmat.ptr(), sctx->ret.no);
normalize_v3(sctx->ret.no);
#ifdef DEBUG
/* Make sure this is only called once. */
this->nearest_point.index = -2;
r_nearest->index = -2;
#endif
}
void SnapData::register_result(SnapObjectContext *sctx, Object *ob_eval, const ID *id_eval)
{
this->register_result(sctx, ob_eval, id_eval, this->obmat_, &this->nearest_point);
}
/* -------------------------------------------------------------------- */
/** \name Utilities
* \{ */
@@ -605,89 +614,57 @@ static bool raycastObjects(SnapObjectContext *sctx)
static void nearest_world_tree_co(BVHTree *tree,
BVHTree_NearestPointCallback nearest_cb,
void *treedata,
float co[3],
float r_co[3],
float r_no[3],
int *r_index,
float *r_dist_sq)
const float3 &co,
BVHTreeNearest *r_nearest)
{
BVHTreeNearest nearest = {};
nearest.index = -1;
copy_v3_fl(nearest.co, FLT_MAX);
nearest.dist_sq = FLT_MAX;
r_nearest->index = -1;
copy_v3_fl(r_nearest->co, FLT_MAX);
r_nearest->dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(tree, co, &nearest, nearest_cb, treedata);
BLI_bvhtree_find_nearest(tree, co, r_nearest, nearest_cb, treedata);
if (r_co) {
copy_v3_v3(r_co, nearest.co);
}
if (r_no) {
copy_v3_v3(r_no, nearest.no);
}
if (r_index) {
*r_index = nearest.index;
}
if (r_dist_sq) {
float diff[3];
sub_v3_v3v3(diff, co, nearest.co);
*r_dist_sq = len_squared_v3(diff);
}
float diff[3];
sub_v3_v3v3(diff, co, r_nearest->co);
r_nearest->dist_sq = len_squared_v3(diff);
}
bool nearest_world_tree(SnapObjectContext *sctx,
BVHTree *tree,
BVHTree_NearestPointCallback nearest_cb,
const float3 &init_co,
const float3 &curr_co,
void *treedata,
const float (*obmat)[4])
BVHTreeNearest *r_nearest)
{
float imat[4][4];
invert_m4_m4(imat, obmat);
/* compute offset between init co and prev co in local space */
float init_co_local[3], curr_co_local[3];
float delta_local[3];
mul_v3_m4v3(init_co_local, imat, sctx->runtime.init_co);
mul_v3_m4v3(curr_co_local, imat, sctx->runtime.curr_co);
sub_v3_v3v3(delta_local, curr_co_local, init_co_local);
float dist_sq;
BVHTreeNearest nearest{};
if (sctx->runtime.params.keep_on_same_target) {
nearest_world_tree_co(
tree, nearest_cb, treedata, init_co_local, nullptr, nullptr, nullptr, &dist_sq);
nearest_world_tree_co(tree, nearest_cb, treedata, init_co, &nearest);
}
else {
/* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain
* the answer. We could return immediately after updating r_loc, r_no, r_index, but that would
* also complicate the code. Foregoing slight optimization for code clarity. */
nearest_world_tree_co(
tree, nearest_cb, treedata, curr_co_local, nullptr, nullptr, nullptr, &dist_sq);
nearest_world_tree_co(tree, nearest_cb, treedata, curr_co, &nearest);
}
if (sctx->ret.dist_px_sq <= dist_sq) {
if (r_nearest->dist_sq <= nearest.dist_sq) {
return false;
}
sctx->ret.dist_px_sq = dist_sq;
/* scale to make `snap_face_nearest_steps` steps */
/* Scale to make `snap_face_nearest_steps` steps. */
float step_scale_factor = 1.0f / max_ff(1.0f, float(sctx->runtime.params.face_nearest_steps));
mul_v3_fl(delta_local, step_scale_factor);
float co_local[3];
float no_local[3];
copy_v3_v3(co_local, init_co_local);
/* Compute offset between init co and prev co. */
float3 delta = (curr_co - init_co) * step_scale_factor;
float3 co = init_co;
for (int i = 0; i < sctx->runtime.params.face_nearest_steps; i++) {
add_v3_v3(co_local, delta_local);
nearest_world_tree_co(
tree, nearest_cb, treedata, co_local, co_local, no_local, &sctx->ret.index, nullptr);
co += delta;
nearest_world_tree_co(tree, nearest_cb, treedata, co, &nearest);
co = nearest.co;
}
mul_v3_m4v3(sctx->ret.loc, obmat, co_local);
copy_v3_v3(sctx->ret.no, no_local);
mul_mat3_m4_v3(obmat, sctx->ret.no);
normalize_v3(sctx->ret.no);
*r_nearest = nearest;
return true;
}
@@ -864,7 +841,7 @@ eSnapMode snap_object_center(SnapObjectContext *sctx,
if (nearest2d.snap_point(float3(0.0f))) {
nearest2d.register_result(sctx, ob_eval, static_cast<const ID *>(ob_eval->data));
return SCE_SNAP_TO_VERTEX;
return SCE_SNAP_TO_POINT;
}
return SCE_SNAP_TO_NONE;

View File

@@ -119,6 +119,11 @@ class SnapData {
bool snap_point(const blender::float3 &co, int index = -1);
bool snap_edge(const blender::float3 &va, const blender::float3 &vb, int edge_index = -1);
eSnapMode snap_edge_points_impl(SnapObjectContext *sctx, int edge_index, float dist_px_sq_orig);
static void register_result(SnapObjectContext *sctx,
Object *ob_eval,
const ID *id_eval,
const blender::float4x4 &obmat,
BVHTreeNearest *r_nearest);
void register_result(SnapObjectContext *sctx, Object *ob_eval, const ID *id_eval);
virtual void get_vert_co(const int /*index*/, const float ** /*r_co*/){};
@@ -152,8 +157,10 @@ void cb_snap_edge(void *userdata,
bool nearest_world_tree(SnapObjectContext *sctx,
BVHTree *tree,
BVHTree_NearestPointCallback nearest_cb,
const blender::float3 &init_co,
const blender::float3 &curr_co,
void *treedata,
const float (*obmat)[4]);
BVHTreeNearest *r_nearest);
eSnapMode snap_object_center(SnapObjectContext *sctx,
Object *ob_eval,

View File

@@ -108,7 +108,7 @@ eSnapMode snapCurve(SnapObjectContext *sctx, Object *ob_eval, const float obmat[
}
if (has_snap) {
nearest2d.register_result(sctx, ob_eval, &cu->id);
return SCE_SNAP_TO_VERTEX;
return SCE_SNAP_TO_POINT;
}
return SCE_SNAP_TO_NONE;
}

View File

@@ -7,7 +7,7 @@
*/
#include "BLI_math.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_matrix.hh"
#include "BKE_bvhutils.h"
#include "BKE_editmesh.h"
@@ -196,18 +196,18 @@ static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapCache_Edi
/** \name Snap Object Data
* \{ */
static eSnapMode editmesh_snap_mode_supported(BMEditMesh *em)
static eSnapMode editmesh_snap_mode_supported(BMesh *bm)
{
eSnapMode snap_mode_supported = SCE_SNAP_TO_NONE;
if (em->bm->totface) {
snap_mode_supported |= SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST;
if (bm->totface) {
snap_mode_supported |= SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST | SNAP_TO_EDGE_ELEMENTS |
SCE_SNAP_TO_POINT;
}
if (em->bm->totedge) {
snap_mode_supported |= SCE_SNAP_TO_EDGE | SCE_SNAP_TO_EDGE_MIDPOINT |
SCE_SNAP_TO_EDGE_PERPENDICULAR;
else if (bm->totedge) {
snap_mode_supported |= SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT;
}
if (em->bm->totvert) {
snap_mode_supported |= SCE_SNAP_TO_VERTEX;
else if (bm->totvert) {
snap_mode_supported |= SCE_SNAP_TO_POINT;
}
return snap_mode_supported;
}
@@ -226,7 +226,7 @@ static SnapCache_EditMesh *editmesh_snapdata_init(SnapObjectContext *sctx,
return em_cache;
}
eSnapMode snap_mode_used = snap_to_flag & editmesh_snap_mode_supported(em);
eSnapMode snap_mode_used = snap_to_flag & editmesh_snap_mode_supported(em->bm);
if (snap_mode_used == SCE_SNAP_TO_NONE) {
return nullptr;
}
@@ -389,15 +389,27 @@ static bool raycastEditMesh(SnapCache_EditMesh *em_cache,
static bool nearest_world_editmesh(SnapCache_EditMesh *em_cache,
SnapObjectContext *sctx,
Object *ob_eval,
BMEditMesh *em,
const float (*obmat)[4])
const float4x4 &obmat)
{
BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(em_cache, sctx, em);
if (treedata == nullptr) {
return false;
}
return nearest_world_tree(sctx, treedata->tree, treedata->nearest_callback, treedata, obmat);
float3 init_co = math::transform_point(obmat, float3(sctx->runtime.init_co));
float3 curr_co = math::transform_point(obmat, float3(sctx->runtime.curr_co));
BVHTreeNearest nearest{};
nearest.dist_sq = sctx->ret.dist_px_sq;
if (nearest_world_tree(
sctx, treedata->tree, treedata->nearest_callback, init_co, curr_co, treedata, &nearest))
{
SnapData::register_result(sctx, ob_eval, nullptr, obmat, &nearest);
return true;
}
return false;
}
/** \} */
@@ -664,9 +676,9 @@ eSnapMode snap_object_editmesh(SnapObjectContext *sctx,
BMEditMesh *em = em_cache->treedata_editmesh.em;
eSnapMode snap_mode_used = snap_to_flag & editmesh_snap_mode_supported(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_to_flag);
elem = snapEditMesh(em_cache, sctx, ob_eval, em, obmat, snap_mode_used);
if (elem) {
return elem;
}
@@ -679,7 +691,7 @@ eSnapMode snap_object_editmesh(SnapObjectContext *sctx,
}
if (snap_mode_used & SCE_SNAP_INDIVIDUAL_NEAREST) {
if (nearest_world_editmesh(em_cache, sctx, em, obmat)) {
if (nearest_world_editmesh(em_cache, sctx, ob_eval, em, float4x4(obmat))) {
return SCE_SNAP_INDIVIDUAL_NEAREST;
}
}

View File

@@ -7,7 +7,7 @@
*/
#include "BLI_math.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_matrix.hh"
#include "BKE_bvhutils.h"
#include "BKE_editmesh.h"
@@ -220,8 +220,9 @@ static bool raycastMesh(SnapObjectContext *sctx,
* \{ */
static bool nearest_world_mesh(SnapObjectContext *sctx,
Object *ob_eval,
const Mesh *me_eval,
const float (*obmat)[4],
const float4x4 obmat,
bool use_hide)
{
BVHTreeFromMesh treedata;
@@ -230,7 +231,18 @@ static bool nearest_world_mesh(SnapObjectContext *sctx,
return false;
}
return nearest_world_tree(sctx, treedata.tree, treedata.nearest_callback, &treedata, obmat);
float3 init_co = math::transform_point(obmat, float3(sctx->runtime.init_co));
float3 curr_co = math::transform_point(obmat, float3(sctx->runtime.curr_co));
BVHTreeNearest nearest{};
nearest.dist_sq = sctx->ret.dist_px_sq;
if (nearest_world_tree(
sctx, treedata.tree, treedata.nearest_callback, init_co, curr_co, &treedata, &nearest))
{
SnapData::register_result(sctx, ob_eval, &me_eval->id, obmat, &nearest);
return true;
}
return false;
}
/** \} */
@@ -467,20 +479,27 @@ eSnapMode snap_edge_points_mesh(SnapObjectContext *sctx,
return elem;
}
static eSnapMode mesh_snap_mode_supported(const Mesh *mesh)
{
eSnapMode snap_mode_supported = mesh->loose_verts().count ? SCE_SNAP_TO_POINT : SCE_SNAP_TO_NONE;
if (mesh->totpoly) {
snap_mode_supported |= SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST | SNAP_TO_EDGE_ELEMENTS;
}
else if (mesh->totedge) {
snap_mode_supported |= SNAP_TO_EDGE_ELEMENTS;
}
return snap_mode_supported;
}
static eSnapMode snapMesh(SnapObjectContext *sctx,
Object *ob_eval,
const Mesh *me_eval,
const float obmat[4][4],
bool use_hide)
bool use_hide,
eSnapMode snap_to)
{
BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_TO_FACE);
if (me_eval->totvert == 0) {
return SCE_SNAP_TO_NONE;
}
if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_TO_POINT)) {
return SCE_SNAP_TO_NONE;
}
BLI_assert(snap_to != SCE_SNAP_TO_FACE);
SnapData_Mesh nearest2d(sctx, me_eval, float4x4(obmat));
if (ob_eval->data == me_eval) {
@@ -490,13 +509,18 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
}
}
snap_to &= mesh_snap_mode_supported(me_eval) & (SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT);
if (snap_to == SCE_SNAP_TO_NONE) {
return SCE_SNAP_TO_NONE;
}
BVHTreeFromMesh treedata, treedata_dummy;
snap_object_data_mesh_get(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_TO_POINT) {
if (snap_to & SCE_SNAP_TO_POINT) {
bvhtree[1] = BKE_bvhtree_from_mesh_get(&treedata_dummy, me_eval, BVHTREE_FROM_LOOSEVERTS, 2);
BLI_assert(treedata_dummy.cached);
}
@@ -511,7 +535,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
eSnapMode elem = SCE_SNAP_TO_POINT;
if (bvhtree[1]) {
BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_TO_POINT);
BLI_assert(snap_to & SCE_SNAP_TO_POINT);
/* snap to loose verts */
BLI_bvhtree_find_nearest_projected(bvhtree[1],
nearest2d.pmat_local.ptr(),
@@ -526,7 +550,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
last_index = nearest.index;
}
if (sctx->runtime.snap_to_flag & (SNAP_TO_EDGE_ELEMENTS & ~SCE_SNAP_TO_EDGE_ENDPOINT)) {
if (snap_to & (SNAP_TO_EDGE_ELEMENTS & ~SCE_SNAP_TO_EDGE_ENDPOINT)) {
if (bvhtree[0]) {
/* Snap to loose edges. */
BLI_bvhtree_find_nearest_projected(
@@ -560,7 +584,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
}
}
else {
BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_TO_EDGE_ENDPOINT);
BLI_assert(snap_to & SCE_SNAP_TO_EDGE_ENDPOINT);
if (bvhtree[0]) {
/* Snap to loose edge verts. */
BLI_bvhtree_find_nearest_projected(
@@ -601,22 +625,6 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
/** \} */
static eSnapMode mesh_snap_mode_supported(const Mesh *mesh)
{
eSnapMode snap_mode_supported = SCE_SNAP_TO_NONE;
if (mesh->totpoly) {
snap_mode_supported |= SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST;
}
if (mesh->totedge) {
snap_mode_supported |= SCE_SNAP_TO_EDGE | SCE_SNAP_TO_EDGE_MIDPOINT |
SCE_SNAP_TO_EDGE_PERPENDICULAR;
}
if (mesh->totvert) {
snap_mode_supported |= SCE_SNAP_TO_VERTEX;
}
return snap_mode_supported;
}
eSnapMode snap_object_mesh(SnapObjectContext *sctx,
Object *ob_eval,
const ID *id,
@@ -625,25 +633,23 @@ eSnapMode snap_object_mesh(SnapObjectContext *sctx,
bool use_hide)
{
eSnapMode elem = SCE_SNAP_TO_NONE;
const Mesh *mesh_eval = reinterpret_cast<const Mesh *>(id);
eSnapMode snap_mode_used = snap_to_flag & mesh_snap_mode_supported(mesh_eval);
if (snap_mode_used & (SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT)) {
elem = snapMesh(sctx, ob_eval, mesh_eval, obmat, use_hide);
if (snap_to_flag & (SNAP_TO_EDGE_ELEMENTS | SCE_SNAP_TO_POINT)) {
elem = snapMesh(sctx, ob_eval, mesh_eval, obmat, use_hide, snap_to_flag);
if (elem) {
return elem;
}
}
if (snap_mode_used & SCE_SNAP_TO_FACE) {
if (snap_to_flag & SCE_SNAP_TO_FACE) {
if (raycastMesh(sctx, ob_eval, mesh_eval, obmat, sctx->runtime.object_index++, use_hide)) {
return SCE_SNAP_TO_FACE;
}
}
if (snap_mode_used & SCE_SNAP_INDIVIDUAL_NEAREST) {
if (nearest_world_mesh(sctx, mesh_eval, obmat, use_hide)) {
if (snap_to_flag & SCE_SNAP_INDIVIDUAL_NEAREST) {
if (nearest_world_mesh(sctx, ob_eval, mesh_eval, float4x4(obmat), use_hide)) {
return SCE_SNAP_INDIVIDUAL_NEAREST;
}
}