Blender V5.0
transform_snap_object.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "BLI_listbase.h"
12#include "BLI_math_matrix.h"
13#include "BLI_math_matrix.hh"
14#include "BLI_math_vector.h"
15
16#include "DNA_screen_types.h"
17
18#include "BKE_bvhutils.hh"
19#include "BKE_duplilist.hh"
21#include "BKE_layer.hh"
22#include "BKE_mesh.hh"
23#include "BKE_mesh_wrapper.hh"
24#include "BKE_object.hh"
25
27
29#include "ED_view3d.hh"
30
32
33#ifdef DEBUG_SNAP_TIME
34# include "BLI_timeit.hh"
35# include <iostream>
36
37# if WIN32 and NDEBUG
38# pragma optimize("t", on)
39# endif
40
41static int64_t total_count_ = 0;
42static blender::timeit::Nanoseconds duration_;
43#endif
44
45namespace blender::ed::transform {
46
47static float4 occlusion_plane_create(float3 ray_dir, float3 ray_co, float3 ray_no)
48{
49 float4 plane;
50 plane_from_point_normal_v3(plane, ray_co, ray_no);
51 if (dot_v3v3(ray_dir, plane) > 0.0f) {
52 /* The plane is facing the wrong direction. */
53 negate_v4(plane);
54 }
55
56 /* Small offset to simulate a kind of volume for edges and vertices. */
57 plane[3] += 0.01f;
58
59 return plane;
60}
61
63 const float (*clip_plane)[4],
64 const int clip_plane_len,
65 const bool is_persp,
66 const float *co,
67 BVHTreeNearest *nearest)
68{
69 if (!isect_point_planes_v3_negated(clip_plane, clip_plane_len, co)) {
70 return false;
71 }
72
73 float co2d[2] = {
74 (dot_m4_v3_row_x(precalc->pmat, co) + precalc->pmat[3][0]),
75 (dot_m4_v3_row_y(precalc->pmat, co) + precalc->pmat[3][1]),
76 };
77
78 if (is_persp) {
79 float w = mul_project_m4_v3_zfac(precalc->pmat, co);
80 mul_v2_fl(co2d, 1.0f / w);
81 }
82
83 const float dist_sq = len_squared_v2v2(precalc->mval, co2d);
84 if (dist_sq < nearest->dist_sq) {
85 copy_v3_v3(nearest->co, co);
86 nearest->dist_sq = dist_sq;
87 return true;
88 }
89 return false;
90}
91
93 const float (*clip_plane)[4],
94 const int clip_plane_len,
95 const bool is_persp,
96 const float va[3],
97 const float vb[3],
98 BVHTreeNearest *nearest)
99{
100 float near_co[3];
101 closest_ray_to_segment_v3(precalc->ray_origin, precalc->ray_direction, va, vb, near_co);
102 return test_projected_vert_dist(precalc, clip_plane, clip_plane_len, is_persp, near_co, nearest);
103}
104
106 : nearest_precalc(),
107 obmat_(obmat),
108 is_persp(sctx->runtime.rv3d ? sctx->runtime.rv3d->is_persp : false),
110{
111 if (sctx->runtime.rv3d) {
112 this->pmat_local = float4x4(sctx->runtime.rv3d->persmat) * obmat;
113 }
114 else {
115 this->pmat_local = obmat;
116 }
117
119 &this->nearest_precalc, this->pmat_local.ptr(), sctx->runtime.win_size, sctx->runtime.mval);
120
121 this->nearest_point.index = -2;
122 this->nearest_point.dist_sq = sctx->ret.dist_px_sq;
123 copy_v3_fl3(this->nearest_point.no, 0.0f, 0.0f, 1.0f);
124}
125
127 const Object *ob_eval,
128 bool skip_occlusion_plane)
129{
130 float4x4 tobmat = math::transpose(this->obmat_);
131 if (!skip_occlusion_plane) {
132 const bool is_in_front = (sctx->runtime.params.occlusion_test == SNAP_OCCLUSION_AS_SEEM) &&
133 ob_eval && (ob_eval->dtx & OB_DRAW_IN_FRONT) != 0;
134 if (!is_in_front && sctx->runtime.has_occlusion_plane) {
135 this->clip_planes.append(tobmat * sctx->runtime.occlusion_plane);
136 }
137 else if (sctx->runtime.has_occlusion_plane_in_front) {
138 this->clip_planes.append(tobmat * sctx->runtime.occlusion_plane_in_front);
139 }
140 }
141
142 for (float4 &plane : sctx->runtime.clip_planes) {
143 this->clip_planes.append(tobmat * plane);
144 }
145}
146
148{
149 /* In vertex and edges you need to get the pixel distance from ray to bounding box,
150 * see: #46099, #46816 */
151
152#ifdef TEST_CLIPPLANES_IN_BOUNDBOX
153 int isect_type = isect_aabb_planes_v3(
154 reinterpret_cast<const float (*)[4]>(this->clip_planes.data()),
155 this->clip_planes.size(),
156 min,
157 max);
158
159 if (isect_type == ISECT_AABB_PLANE_BEHIND_ANY) {
160 return false;
161 }
162#endif
163
164 bool dummy[3];
165 float bb_dist_px_sq = dist_squared_to_projected_aabb(&this->nearest_precalc, min, max, dummy);
166 if (bb_dist_px_sq > this->nearest_point.dist_sq) {
167 return false;
168 }
169
170 return true;
171}
172
173bool SnapData::snap_point(const float3 &co, int index)
174{
176 reinterpret_cast<const float (*)[4]>(this->clip_planes.data()),
177 this->clip_planes.size(),
178 this->is_persp,
179 co,
180 &this->nearest_point))
181 {
182 this->nearest_point.index = index;
183 return true;
184 }
185 return false;
186}
187
188bool SnapData::snap_edge(const float3 &va, const float3 &vb, int edge_index)
189{
191 reinterpret_cast<const float (*)[4]>(this->clip_planes.data()),
192 this->clip_planes.size(),
193 this->is_persp,
194 va,
195 vb,
196 &this->nearest_point))
197 {
198 this->nearest_point.index = edge_index;
199 sub_v3_v3v3(this->nearest_point.no, vb, va);
200 return true;
201 }
202 return false;
203}
204
206 int edge_index,
207 float dist_px_sq_orig)
208{
210
211 int vindex[2];
212 this->get_edge_verts_index(edge_index, vindex);
213
214 const float *v_pair[2];
215 this->get_vert_co(vindex[0], &v_pair[0]);
216 this->get_vert_co(vindex[1], &v_pair[1]);
217
218 float lambda;
219 if (!isect_ray_line_v3(this->nearest_precalc.ray_origin,
220 this->nearest_precalc.ray_direction,
221 v_pair[0],
222 v_pair[1],
223 &lambda))
224 {
225 /* Do nothing. */
226 }
227 else {
228 this->nearest_point.dist_sq = dist_px_sq_orig;
229
230 eSnapMode snap_to = sctx->runtime.snap_to_flag;
231 int e_mode_len = ((snap_to & SCE_SNAP_TO_EDGE) != 0) +
232 ((snap_to & SCE_SNAP_TO_EDGE_ENDPOINT) != 0) +
233 ((snap_to & SCE_SNAP_TO_EDGE_MIDPOINT) != 0);
234
235 float range = 1.0f / (2 * e_mode_len - 1);
236
237 if (snap_to & SCE_SNAP_TO_EDGE_MIDPOINT) {
238 range *= e_mode_len - 1;
239 if ((range) < lambda && lambda < (1.0f - range)) {
240 float vmid[3];
241 mid_v3_v3v3(vmid, v_pair[0], v_pair[1]);
242
243 if (this->snap_point(vmid, edge_index)) {
244 sub_v3_v3v3(this->nearest_point.no, v_pair[1], v_pair[0]);
246 }
247 }
248 }
249
250 if (snap_to & SCE_SNAP_TO_EDGE_PERPENDICULAR) {
251 float v_near[3], va_g[3], vb_g[3];
252
253 mul_v3_m4v3(va_g, this->obmat_.ptr(), v_pair[0]);
254 mul_v3_m4v3(vb_g, this->obmat_.ptr(), v_pair[1]);
255 float lambda_perp = line_point_factor_v3(sctx->runtime.curr_co, va_g, vb_g);
256
257 if (IN_RANGE(lambda_perp, 0.0f, 1.0f)) {
258 interp_v3_v3v3(v_near, v_pair[0], v_pair[1], lambda_perp);
259
260 if (this->snap_point(v_near, edge_index)) {
261 sub_v3_v3v3(this->nearest_point.no, v_pair[1], v_pair[0]);
263 }
264 }
265 }
266
267 /* Leave this one for last so it doesn't change the normal. */
268 if (snap_to & SCE_SNAP_TO_EDGE_ENDPOINT) {
269 if (lambda < (range) || (1.0f - range) < lambda) {
270 int v_id = lambda < 0.5f ? 0 : 1;
271
272 if (this->snap_point(v_pair[v_id], vindex[v_id])) {
274 this->copy_vert_no(vindex[v_id], this->nearest_point.no);
275 }
276 }
277 }
278 }
279
280 return elem;
281}
282
284 const Object *ob_eval,
285 const ID *id_eval,
286 const float4x4 &obmat,
287 BVHTreeNearest *r_nearest)
288{
289 BLI_assert(r_nearest->index != -2);
290
291 copy_v3_v3(sctx->ret.loc, r_nearest->co);
292 copy_v3_v3(sctx->ret.no, r_nearest->no);
293 sctx->ret.index = r_nearest->index;
294 sctx->ret.obmat = obmat;
295 sctx->ret.ob = ob_eval;
296 sctx->ret.data = id_eval;
297 sctx->ret.dist_px_sq = r_nearest->dist_sq;
298
299 /* Global space. */
300 sctx->ret.loc = math::transform_point(obmat, sctx->ret.loc);
301 sctx->ret.no = math::normalize(math::transform_direction(obmat, sctx->ret.no));
302
303#ifndef NDEBUG
304 /* Make sure this is only called once. */
305 r_nearest->index = -2;
306#endif
307}
308
309void SnapData::register_result(SnapObjectContext *sctx, const Object *ob_eval, const ID *id_eval)
310{
311 register_result(sctx, ob_eval, id_eval, this->obmat_, &this->nearest_point);
312}
313
315 const Object *ob_eval,
316 const ID *id_eval,
317 const blender::float4x4 &obmat,
318 const BVHTreeRayHit *hit,
319 const bool is_in_front)
320{
321 const float depth_max = is_in_front ? sctx->ret.ray_depth_max_in_front : sctx->ret.ray_depth_max;
322 if (hit->dist <= depth_max) {
323 float3 co = math::transform_point(obmat, float3(hit->co));
325
326 sctx->ret.loc = co;
327 sctx->ret.no = no;
328 sctx->ret.index = hit->index;
329 sctx->ret.obmat = obmat;
330 sctx->ret.ob = ob_eval;
331 sctx->ret.data = id_eval;
332 sctx->ret.ray_depth_max = std::min(hit->dist, sctx->ret.ray_depth_max);
333
334 if (is_in_front) {
336 sctx->runtime.ray_dir, co, no);
338 }
339 }
340}
341
342/* -------------------------------------------------------------------- */
345
353static const ID *data_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
354{
355 if (r_use_hide) {
356 *r_use_hide = false;
357 }
358
359 /* Get evaluated edit mesh when in mesh edit mode. */
360 if (ob_eval->type == OB_MESH && BKE_object_is_in_editmode(ob_eval)) {
361 if (edit_mode_type == SNAP_GEOM_EDIT) {
362 return nullptr;
363 }
364
365 const Mesh *editmesh_eval = (edit_mode_type == SNAP_GEOM_FINAL) ?
367 (edit_mode_type == SNAP_GEOM_CAGE) ?
369 nullptr;
370
371 if (editmesh_eval) {
372 if (editmesh_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) {
373 return nullptr;
374 }
375 if (*r_use_hide) {
376 *r_use_hide = true;
377 }
378 return &editmesh_eval->id;
379 }
380 }
381
382 /* For curves and surfaces in edit mode, use their original data when snapping.
383 * Only use the evaluated mesh when snapping to the final geometry. */
384 if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) && BKE_object_is_in_editmode(ob_eval) &&
385 edit_mode_type != SNAP_GEOM_FINAL)
386 {
387 return static_cast<const ID *>(ob_eval->data);
388 }
389
390 /* Get evaluated mesh including subdivision. This may come from a mesh object,
391 * or another object type that has modifiers producing a mesh. */
392 if (Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval)) {
393 return &mesh_eval->id;
394 }
395
396 return static_cast<const ID *>(ob_eval->data);
397}
398
403static const ID *data_for_snap_dupli(ID *ob_data)
404{
405 if (GS(ob_data->name) == ID_ME) {
406 Mesh *mesh = blender::id_cast<Mesh *>(ob_data);
407 return reinterpret_cast<const ID *>(BKE_mesh_wrapper_ensure_subdivision(mesh));
408 }
409 return ob_data;
410}
411
413
414/* -------------------------------------------------------------------- */
417
419 const Object *ob_eval,
420 const ID *ob_data,
421 const float4x4 &obmat,
422 bool is_object_active,
423 bool use_hide);
424
426 const eSnapTargetOP snap_target_select,
427 const Base *base_act,
428 const Base *base)
429{
430 if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
431 return false;
432 }
433
434 if (snap_target_select == SCE_SNAP_TARGET_ALL) {
435 return true;
436 }
437
439 return false;
440 }
441
442 /* Get attributes of potential target. */
443 const bool is_active = (base_act == base);
444 const bool is_selected = (base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL);
445 const bool is_edited = (base->object->mode == OB_MODE_EDIT);
446 const bool is_selectable = (base->flag & BASE_SELECTABLE);
447 /* Get attributes of state. */
448 const bool is_in_object_mode = (base_act == nullptr) ||
449 (base_act->object->mode == OB_MODE_OBJECT);
450
451 if (is_in_object_mode) {
452 /* Handle target selection options that make sense for object mode. */
453 if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_selected) {
454 /* What is selectable or not is part of the object and depends on the mode. */
455 return false;
456 }
457 }
458 else {
459 /* Handle target selection options that make sense for edit/pose mode. */
460 if ((snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) && is_active) {
461 return false;
462 }
463 if ((snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) && is_edited && !is_active) {
464 /* Base is edited, but not active. */
465 return false;
466 }
467 if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) {
468 return false;
469 }
470 }
471
472 if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) {
473 return false;
474 }
475
476 return true;
477}
478
483{
485 eSnapMode tmp;
486
489 const eSnapTargetOP snap_target_select = sctx->runtime.params.snap_target_select;
490 BKE_view_layer_synced_ensure(scene, view_layer);
491 Base *base_act = BKE_view_layer_active_base_get(view_layer);
492
493 DupliList duplilist;
495 if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) {
496 continue;
497 }
498
499 const bool is_object_active = (base == base_act);
500 Object *obj_eval = DEG_get_evaluated(sctx->runtime.depsgraph, base->object);
501 if (obj_eval->transflag & OB_DUPLI ||
503 {
504 object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval, nullptr, duplilist);
505 for (DupliObject &dupli_ob : duplilist) {
506 BLI_assert(DEG_is_evaluated(dupli_ob.ob));
507 const ID *ob_data = dupli_ob.ob_data ? data_for_snap_dupli(dupli_ob.ob_data) : nullptr;
508 if ((tmp = sob_callback(
509 sctx, dupli_ob.ob, ob_data, float4x4(dupli_ob.mat), is_object_active, false)) !=
511 {
512 ret = tmp;
513 }
514 }
515 duplilist.clear();
516 }
517
518 bool use_hide = false;
519 const ID *ob_data = data_for_snap(obj_eval, sctx->runtime.params.edit_mode_type, &use_hide);
520 if ((tmp = sob_callback(
521 sctx, obj_eval, ob_data, obj_eval->object_to_world(), is_object_active, use_hide)) !=
523 {
524 ret = tmp;
525 }
526 }
527 return ret;
528}
529
531
532/* -------------------------------------------------------------------- */
535
536/* Store all ray-hits
537 * Support for storing all depths, not just the first (ray-cast 'all'). */
538
539static SnapObjectHitDepth *hit_depth_create(const float depth, const float co[3], uint ob_uuid)
540{
541 SnapObjectHitDepth *hit = MEM_new<SnapObjectHitDepth>(__func__);
542
543 hit->depth = depth;
544 copy_v3_v3(hit->co, co);
545 hit->ob_uuid = ob_uuid;
546
547 return hit;
548}
549
550static int hit_depth_cmp(const void *arg1, const void *arg2)
551{
552 const SnapObjectHitDepth *h1 = static_cast<const SnapObjectHitDepth *>(arg1);
553 const SnapObjectHitDepth *h2 = static_cast<const SnapObjectHitDepth *>(arg2);
554 int val = 0;
555
556 if (h1->depth < h2->depth) {
557 val = -1;
558 }
559 else if (h1->depth > h2->depth) {
560 val = 1;
561 }
562
563 return val;
564}
565
566void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
567{
568 RayCastAll_Data *data = static_cast<RayCastAll_Data *>(userdata);
569 data->raycast_callback(data->bvhdata, index, ray, hit);
570 if (hit->index != -1) {
571 /* Get all values in world-space. */
572 float location[3];
573 float depth;
574
575 /* World-space location. */
576 mul_v3_m4v3(location, (float (*)[4])data->obmat, hit->co);
577 depth = (hit->dist + data->len_diff) / data->local_scale;
578
579 SnapObjectHitDepth *hit_item = hit_depth_create(depth, location, data->ob_uuid);
580 BLI_addtail(data->hit_list, hit_item);
581 }
582}
583
585 const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3])
586{
587 cross_tri_v3(no, v0, v1, v2);
588 return dot_v3v3(no, dir) < 0.0f;
589}
590
595 const Object *ob_eval,
596 const ID *ob_data,
597 const float4x4 &obmat,
598 bool /*is_object_active*/,
599 bool use_hide)
600{
601 bool retval = false;
602
603 if (ob_eval->visibility_flag & OB_HIDE_SURFACE_PICK) {
604 /* Do not snap it surface picking is disabled. */
605 return SCE_SNAP_TO_NONE;
606 }
607
608 if (ob_data == nullptr) {
610 ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE))
611 {
612 /* Do not hit objects that are in wire or bounding box display mode. */
613 return SCE_SNAP_TO_NONE;
614 }
615 if (ob_eval->type == OB_MESH) {
616 if (snap_object_editmesh(sctx, ob_eval, nullptr, obmat, SCE_SNAP_TO_FACE, use_hide)) {
617 retval = true;
618 }
619 }
620 else {
621 return SCE_SNAP_TO_NONE;
622 }
623 }
625 ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE))
626 {
627 /* Do not hit objects that are in wire or bounding box display mode. */
628 return SCE_SNAP_TO_NONE;
629 }
630 else if (GS(ob_data->name) != ID_ME) {
631 return SCE_SNAP_TO_NONE;
632 }
633 else if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) &&
636 {
637 /* The final Curves geometry is generated as a Mesh. Skip this Mesh if the target is not
638 * #SNAP_GEOM_FINAL. This also avoids creating and destroying BVH Trees too frequently while
639 * editing. */
640 return SCE_SNAP_TO_NONE;
641 }
642 else {
643 retval = snap_object_mesh(sctx, ob_eval, ob_data, obmat, SCE_SNAP_TO_FACE, use_hide);
644 }
645
646 if (retval) {
647 return SCE_SNAP_TO_FACE;
648 }
649 return SCE_SNAP_TO_NONE;
650}
651
667{
669}
670
672
673/* -------------------------------------------------------------------- */
676
679 void *treedata,
680 const float3 &co,
681 BVHTreeNearest *r_nearest)
682{
683 r_nearest->index = -1;
684 copy_v3_fl(r_nearest->co, FLT_MAX);
685 r_nearest->dist_sq = FLT_MAX;
686
687 BLI_bvhtree_find_nearest(tree, co, r_nearest, nearest_cb, treedata);
688}
689
691 const BVHTree *tree,
693 const blender::float4x4 &obmat,
694 void *treedata,
695 BVHTreeNearest *r_nearest)
696{
697 float4x4 imat = math::invert(obmat);
698 float3 init_co = math::transform_point(imat, sctx->runtime.init_co);
699 float3 curr_co = math::transform_point(imat, sctx->runtime.curr_co);
700
701 BVHTreeNearest nearest{};
702 float3 vec;
704 nearest_world_tree_co(tree, nearest_cb, treedata, init_co, &nearest);
705 vec = float3(nearest.co) - init_co;
706 }
707 else {
708 /* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain
709 * the answer. We could return immediately after updating r_loc, r_no, r_index, but that would
710 * also complicate the code. Foregoing slight optimization for code clarity. */
711 nearest_world_tree_co(tree, nearest_cb, treedata, curr_co, &nearest);
712 vec = float3(nearest.co) - curr_co;
713 }
714
715 float original_distance = math::length_squared(math::transform_direction(obmat, vec));
716 if (r_nearest->dist_sq <= original_distance) {
717 return false;
718 }
719
720 /* Scale to make `snap_face_nearest_steps` steps. */
721 float step_scale_factor = 1.0f / max_ff(1.0f, float(sctx->runtime.params.face_nearest_steps));
722
723 /* Compute offset between init co and prev co. */
724 float3 delta = (curr_co - init_co) * step_scale_factor;
725
726 float3 co = init_co;
727 for (int i = 0; i < sctx->runtime.params.face_nearest_steps; i++) {
728 co += delta;
729 nearest_world_tree_co(tree, nearest_cb, treedata, co, &nearest);
730 co = nearest.co;
731 }
732
733 *r_nearest = nearest;
735 r_nearest->dist_sq = original_distance;
736 }
737 else if (sctx->runtime.params.face_nearest_steps > 1) {
738 /* Recalculate the distance.
739 * When multiple steps are tested, we cannot depend on the distance calculated for
740 * `nearest.dist_sq`, as it reduces with each step. */
741 vec = co - curr_co;
743 }
744 return true;
745}
746
748 const Object *ob_eval,
749 const ID *ob_data,
750 const float4x4 &obmat,
751 bool /*is_object_active*/,
752 bool use_hide)
753{
755
756 if (ob_data == nullptr) {
757 if (ob_eval->type == OB_MESH) {
758 retval = snap_object_editmesh(
759 sctx, ob_eval, nullptr, obmat, SCE_SNAP_INDIVIDUAL_NEAREST, use_hide);
760 }
761 else {
762 return SCE_SNAP_TO_NONE;
763 }
764 }
765 else if (GS(ob_data->name) != ID_ME) {
766 return SCE_SNAP_TO_NONE;
767 }
768 else if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) &&
771 {
772 /* The final Curves geometry is generated as a Mesh. Skip this Mesh if the target is not
773 * #SNAP_GEOM_FINAL. This also avoids creating and destroying BVH Trees too frequently while
774 * editing. */
775 return SCE_SNAP_TO_NONE;
776 }
777 else {
778 retval = snap_object_mesh(
779 sctx, ob_eval, ob_data, obmat, SCE_SNAP_INDIVIDUAL_NEAREST, use_hide);
780 }
781
782 return retval;
783}
784
800
802
803/* -------------------------------------------------------------------- */
806
807void cb_snap_vert(void *userdata,
808 int index,
809 const DistProjectedAABBPrecalc *precalc,
810 const float (*clip_plane)[4],
811 const int clip_plane_len,
812 BVHTreeNearest *nearest)
813{
814 SnapData *data = static_cast<SnapData *>(userdata);
815
816 const float *co;
817 data->get_vert_co(index, &co);
818
819 if (test_projected_vert_dist(precalc, clip_plane, clip_plane_len, data->is_persp, co, nearest)) {
820 data->copy_vert_no(index, nearest->no);
821 nearest->index = index;
822 }
823}
824
825void cb_snap_edge(void *userdata,
826 int index,
827 const DistProjectedAABBPrecalc *precalc,
828 const float (*clip_plane)[4],
829 const int clip_plane_len,
830 BVHTreeNearest *nearest)
831{
832 SnapData *data = static_cast<SnapData *>(userdata);
833
834 int vindex[2];
835 data->get_edge_verts_index(index, vindex);
836
837 const float *v_pair[2];
838 data->get_vert_co(vindex[0], &v_pair[0]);
839 data->get_vert_co(vindex[1], &v_pair[1]);
840
842 precalc, clip_plane, clip_plane_len, data->is_persp, v_pair[0], v_pair[1], nearest))
843 {
844 sub_v3_v3v3(nearest->no, v_pair[1], v_pair[0]);
845 nearest->index = index;
846 }
847}
848
850
851/* -------------------------------------------------------------------- */
854
856{
857 if (!sctx->ret.data || GS(sctx->ret.data->name) != ID_ME) {
858 return SCE_SNAP_TO_NONE;
859 }
860
861 return snap_polygon_mesh(
862 sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, snap_to_flag, sctx->ret.index);
863}
864
865static eSnapMode snap_edge_points(SnapObjectContext *sctx, const float dist_px_sq_orig)
866{
867 if (!sctx->ret.data || GS(sctx->ret.data->name) != ID_ME) {
868 return SCE_SNAP_TO_EDGE;
869 }
870
872 sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, dist_px_sq_orig, sctx->ret.index);
873}
874
876 const Object *ob_eval,
877 const float4x4 &obmat,
878 eSnapMode snap_to_flag)
879{
880 /* May extend later (for now just snaps to empty or camera center). */
881
882 if (ob_eval->transflag & OB_DUPLI) {
883 return SCE_SNAP_TO_NONE;
884 }
885
886 /* For now only vertex supported. */
887 if ((snap_to_flag & SCE_SNAP_TO_POINT) == 0) {
888 return SCE_SNAP_TO_NONE;
889 }
890
891 SnapData nearest2d(sctx, obmat);
892
893 nearest2d.clip_planes_enable(sctx, ob_eval);
894
895 if (nearest2d.snap_point(float3(0.0f))) {
896 nearest2d.register_result(sctx, ob_eval, static_cast<const ID *>(ob_eval->data));
897 return SCE_SNAP_TO_POINT;
898 }
899
900 return SCE_SNAP_TO_NONE;
901}
902
907 const Object *ob_eval,
908 const ID *ob_data,
909 const float4x4 &obmat,
910 bool is_object_active,
911 bool use_hide)
912{
913 if (ob_data == nullptr && (ob_eval->type == OB_MESH)) {
915 sctx, ob_eval, nullptr, obmat, sctx->runtime.snap_to_flag, use_hide);
916 }
917
918 if (ob_data == nullptr) {
919 return snap_object_center(sctx, ob_eval, obmat, sctx->runtime.snap_to_flag);
920 }
921
922 if (ob_eval->dt == OB_BOUNDBOX) {
923 /* Do not snap to objects that are in bounding box display mode. */
924 return SCE_SNAP_TO_NONE;
925 }
926
927 if (GS(ob_data->name) == ID_ME) {
928 if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) &&
931 {
932 /* The final Curves geometry is generated as a Mesh. Skip this Mesh if the target is not
933 * #SNAP_GEOM_FINAL. */
934 return SCE_SNAP_TO_NONE;
935 }
936 return snap_object_mesh(sctx, ob_eval, ob_data, obmat, sctx->runtime.snap_to_flag, use_hide);
937 }
938
940 switch (ob_eval->type) {
941 case OB_MESH: {
942 break;
943 }
944 case OB_ARMATURE:
945 retval = snapArmature(sctx, ob_eval, obmat, is_object_active);
946 break;
947 case OB_CURVES_LEGACY:
948 case OB_SURF:
949 if (ob_eval->type == OB_CURVES_LEGACY || BKE_object_is_in_editmode(ob_eval)) {
950 retval = snapCurve(sctx, ob_eval, obmat);
951 }
952 break;
953 case OB_FONT: {
954 break;
955 }
956 case OB_EMPTY:
957 case OB_LAMP:
958 retval = snap_object_center(sctx, ob_eval, obmat, sctx->runtime.snap_to_flag);
959 break;
960 case OB_CAMERA:
961 retval = snapCamera(sctx, ob_eval, obmat, sctx->runtime.snap_to_flag);
962 break;
963 }
964
965 return retval;
966}
967
982{
983 return iter_snap_objects(sctx, snap_obj_fn);
984}
985
986static bool snap_grid(SnapObjectContext *sctx)
987{
988 SnapData nearest2d(sctx);
989 nearest2d.clip_planes_enable(sctx, nullptr, true);
990
991 /* Ignore the maximum pixel distance when snapping to grid.
992 * This avoids undesirable jumps of the element being snapped. */
993 nearest2d.nearest_point.dist_sq = FLT_MAX;
994
995 float grid_dist = sctx->grid.size;
996
997 if (sctx->grid.use_init_co) {
998 float3 co = math::round((sctx->runtime.init_co) / grid_dist) * grid_dist;
999 if (nearest2d.snap_point(co)) {
1000 nearest2d.register_result(sctx, nullptr, nullptr);
1001 return true;
1002 }
1003 }
1004
1005 float ray_dist;
1006 for (int i = 0; i < 4; i++) {
1008 sctx->runtime.ray_dir,
1009 sctx->grid.planes[i],
1010 &ray_dist,
1011 false) &&
1012 (ray_dist > 0.0f))
1013 {
1014 float3 co = math::round((sctx->runtime.ray_start + sctx->runtime.ray_dir * ray_dist) /
1015 grid_dist) *
1016 grid_dist;
1017
1018 if (nearest2d.snap_point(co)) {
1019 copy_v3_v3(nearest2d.nearest_point.no, sctx->grid.planes[i]);
1020 if (!sctx->runtime.rv3d->is_persp && RV3D_VIEW_IS_AXIS(sctx->runtime.rv3d->view)) {
1021 /* Project in #sctx->runtime.curr_co plane. */
1022 add_v3_v3(nearest2d.nearest_point.co,
1023 sctx->runtime.curr_co * float3(nearest2d.nearest_point.no));
1024 }
1025 nearest2d.register_result(sctx, nullptr, nullptr);
1026 return true;
1027 }
1028 }
1029 }
1030
1031 return false;
1032}
1033
1035
1036/* -------------------------------------------------------------------- */
1039
1041{
1042 SnapObjectContext *sctx = MEM_new<SnapObjectContext>(__func__);
1043
1044 sctx->scene = scene;
1045
1046 return sctx;
1047}
1048
1050{
1051 MEM_delete(sctx);
1052}
1053
1055 bool (*test_vert_fn)(BMVert *, void *user_data),
1056 bool (*test_edge_fn)(BMEdge *, void *user_data),
1057 bool (*test_face_fn)(BMFace *, void *user_data),
1058 void *user_data)
1059{
1060 bool is_cache_dirty = false;
1061 if (sctx->callbacks.edit_mesh.test_vert_fn != test_vert_fn) {
1062 sctx->callbacks.edit_mesh.test_vert_fn = test_vert_fn;
1063 is_cache_dirty = true;
1064 }
1065 if (sctx->callbacks.edit_mesh.test_edge_fn != test_edge_fn) {
1066 sctx->callbacks.edit_mesh.test_edge_fn = test_edge_fn;
1067 is_cache_dirty = true;
1068 }
1069 if (sctx->callbacks.edit_mesh.test_face_fn != test_face_fn) {
1070 sctx->callbacks.edit_mesh.test_face_fn = test_face_fn;
1071 is_cache_dirty = true;
1072 }
1073 if (sctx->callbacks.edit_mesh.user_data != user_data) {
1074 sctx->callbacks.edit_mesh.user_data = user_data;
1075 is_cache_dirty = true;
1076 }
1077
1078 if (is_cache_dirty) {
1079 sctx->editmesh_caches.clear();
1080 }
1081}
1082
1084 Depsgraph *depsgraph,
1085 const ARegion *region,
1086 const View3D *v3d,
1087 eSnapMode snap_to_flag,
1088 eSnapOcclusionTest occlusion_test,
1089 const SnapObjectParams *params,
1090 const float ray_start[3],
1091 const float ray_dir[3],
1092 const float ray_depth,
1093 const float mval[2],
1094 const float init_co[3],
1095 const float prev_co[3],
1096 const float dist_px_sq,
1097 ListBase *hit_list)
1098{
1099 if (snap_to_flag &
1101 {
1102 if (prev_co) {
1103 copy_v3_v3(sctx->runtime.curr_co, prev_co);
1104 if (init_co) {
1105 copy_v3_v3(sctx->runtime.init_co, init_co);
1106 }
1107 else {
1108 snap_to_flag &= ~SCE_SNAP_INDIVIDUAL_NEAREST;
1109 }
1110 }
1111 else {
1113 }
1114 }
1115
1116 if (snap_to_flag == SCE_SNAP_TO_NONE) {
1117 return false;
1118 }
1119
1120 sctx->runtime.depsgraph = depsgraph;
1121 sctx->runtime.rv3d = nullptr;
1122 sctx->runtime.v3d = v3d;
1123 sctx->runtime.snap_to_flag = snap_to_flag;
1124 sctx->runtime.params = *params;
1125 sctx->runtime.params.occlusion_test = occlusion_test;
1127 occlusion_test;
1128 sctx->runtime.has_occlusion_plane = false;
1130 sctx->runtime.object_index = 0;
1131
1132 copy_v3_v3(sctx->runtime.ray_start, ray_start);
1133 copy_v3_v3(sctx->runtime.ray_dir, ray_dir);
1134
1135 if (mval) {
1136 copy_v2_v2(sctx->runtime.mval, mval);
1137 }
1138
1139 if (region) {
1140 const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
1141 sctx->runtime.win_size[0] = region->winx;
1142 sctx->runtime.win_size[1] = region->winy;
1143
1144 sctx->runtime.clip_planes.resize(2);
1145
1147 nullptr,
1148 nullptr,
1149 nullptr,
1150 nullptr,
1151 sctx->runtime.clip_planes[0],
1152 sctx->runtime.clip_planes[1]);
1153
1154 if (rv3d->rflag & RV3D_CLIPPING) {
1155 sctx->runtime.clip_planes.extend_unchecked(reinterpret_cast<const float4 *>(rv3d->clip), 4);
1156 }
1157
1158 sctx->runtime.rv3d = rv3d;
1159
1161 sctx->grid.use_init_co = init_co != nullptr;
1162 if (params->grid_size) {
1163 sctx->grid.size = params->grid_size;
1164 }
1165 if (!compare_m4m4(sctx->grid.persmat.ptr(), rv3d->persmat, FLT_EPSILON)) {
1166 sctx->grid.persmat = float4x4(rv3d->persmat);
1167 if (params->grid_size == 0.0f) {
1169 sctx->scene, sctx->runtime.v3d, region, nullptr);
1170 }
1171
1172 if (!sctx->grid.use_init_co) {
1173 memset(sctx->grid.planes, 0, sizeof(sctx->grid.planes));
1174 sctx->grid.planes[0][2] = 1.0f;
1175 if (math::abs(sctx->runtime.ray_dir[0]) < math::abs(sctx->runtime.ray_dir[1])) {
1176 sctx->grid.planes[1][1] = 1.0f;
1177 sctx->grid.planes[2][0] = 1.0f;
1178 }
1179 else {
1180 sctx->grid.planes[1][0] = 1.0f;
1181 sctx->grid.planes[2][1] = 1.0f;
1182 }
1183
1185 sctx->grid.planes[3], sctx->runtime.curr_co, rv3d->viewinv[2]);
1186 }
1187 }
1188 }
1189 }
1190 sctx->runtime.hit_list = hit_list;
1191
1192 sctx->ret.ray_depth_max = sctx->ret.ray_depth_max_in_front = ray_depth;
1193 sctx->ret.index = -1;
1194 sctx->ret.ob = nullptr;
1195 sctx->ret.data = nullptr;
1196 sctx->ret.dist_px_sq = dist_px_sq;
1197
1198 return true;
1199}
1200
1202 Depsgraph *depsgraph,
1203 const View3D *v3d,
1204 const SnapObjectParams *params,
1205 const float ray_start[3],
1206 const float ray_normal[3],
1207 float *ray_depth,
1208 float r_loc[3],
1209 float r_no[3],
1210 int *r_index,
1211 const Object **r_ob,
1212 float r_obmat[4][4])
1213{
1215 depsgraph,
1216 nullptr,
1217 v3d,
1219 params->occlusion_test,
1220 params,
1221 ray_start,
1222 ray_normal,
1223 !ray_depth || *ray_depth == -1.0f ? BVH_RAYCAST_DIST_MAX :
1224 *ray_depth,
1225 nullptr,
1226 nullptr,
1227 nullptr,
1228 0,
1229 nullptr))
1230 {
1231 return false;
1232 }
1233
1234 if (raycastObjects(sctx)) {
1235 copy_v3_v3(r_loc, sctx->ret.loc);
1236 if (r_no) {
1237 copy_v3_v3(r_no, sctx->ret.no);
1238 }
1239 if (r_index) {
1240 *r_index = sctx->ret.index;
1241 }
1242 if (r_ob) {
1243 *r_ob = sctx->ret.ob;
1244 }
1245 if (r_obmat) {
1246 copy_m4_m4(r_obmat, sctx->ret.obmat.ptr());
1247 }
1248 if (ray_depth) {
1249 *ray_depth = sctx->ret.ray_depth_max;
1250 }
1251 return true;
1252 }
1253 return false;
1254}
1255
1257 Depsgraph *depsgraph,
1258 const View3D *v3d,
1259 const SnapObjectParams *params,
1260 const float ray_start[3],
1261 const float ray_normal[3],
1262 float ray_depth,
1263 bool sort,
1264 ListBase *r_hit_list)
1265{
1267 depsgraph,
1268 nullptr,
1269 v3d,
1271 params->occlusion_test,
1272 params,
1273 ray_start,
1274 ray_normal,
1275 ray_depth == -1.0f ? BVH_RAYCAST_DIST_MAX : ray_depth,
1276 nullptr,
1277 nullptr,
1278 nullptr,
1279 0,
1280 r_hit_list))
1281 {
1282 return false;
1283 }
1284
1285#ifndef NDEBUG
1286 float ray_depth_prev = sctx->ret.ray_depth_max;
1287#endif
1288 if (raycastObjects(sctx)) {
1289 if (sort) {
1290 BLI_listbase_sort(r_hit_list, hit_depth_cmp);
1291 }
1292 /* Meant to be read-only for 'all' hits, ensure it is. */
1293#ifndef NDEBUG
1294 BLI_assert(ray_depth_prev == sctx->ret.ray_depth_max);
1295#endif
1296 return true;
1297 }
1298 return false;
1299}
1300
1302 Depsgraph *depsgraph,
1303 const View3D *v3d,
1304 const SnapObjectParams *params,
1305 const float ray_start[3],
1306 const float ray_normal[3],
1307 float *ray_depth,
1308 float r_co[3],
1309 float r_no[3])
1310{
1311 return snap_object_project_ray_ex(sctx,
1312 depsgraph,
1313 v3d,
1314 params,
1315 ray_start,
1316 ray_normal,
1317 ray_depth,
1318 r_co,
1319 r_no,
1320 nullptr,
1321 nullptr,
1322 nullptr);
1323}
1324
1326 Depsgraph *depsgraph,
1327 const ARegion *region,
1328 const View3D *v3d,
1329 eSnapMode snap_to_flag,
1330 const SnapObjectParams *params,
1331 const float init_co[3],
1332 const float mval[2],
1333 const float prev_co[3],
1334 float *dist_px,
1335 float r_loc[3],
1336 float r_no[3],
1337 int *r_index,
1338 const Object **r_ob,
1339 float r_obmat[4][4],
1340 float r_face_nor[3])
1341{
1342 eSnapMode retval = SCE_SNAP_TO_NONE;
1343 float ray_depth_max = BVH_RAYCAST_DIST_MAX;
1344 bool use_occlusion_plane = false;
1345
1346 /* It is required `mval` to calculate the occlusion plane. */
1347 if (mval && (snap_to_flag & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID))) {
1348 if (params->occlusion_test == SNAP_OCCLUSION_AS_SEEM) {
1349 use_occlusion_plane = !XRAY_ENABLED(v3d);
1350 }
1351 else if (params->occlusion_test == SNAP_OCCLUSION_ALWAYS) {
1352 use_occlusion_plane = true;
1353 }
1354 }
1355
1356 if (use_occlusion_plane || (snap_to_flag & SCE_SNAP_TO_FACE) ||
1357 /* Snap to Grid requires `ray_start` and `ray_dir`. */
1358 (snap_to_flag & SCE_SNAP_TO_GRID))
1359 {
1360 /* Calculate the direction (`ray_dir`) and starting point (`ray_start`) of the ray from the
1361 * viewport to a 3D point under the mouse cursor (`mval`), taking into account potential view
1362 * clipping. */
1363
1364 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
1365 float3 ray_end;
1367 region,
1368 v3d,
1369 mval,
1370 false,
1371 nullptr,
1372 sctx->runtime.ray_dir,
1373 sctx->runtime.ray_start,
1374 ray_end);
1375
1376 if (rv3d->rflag & RV3D_CLIPPING) {
1378 sctx->runtime.ray_start, ray_end, rv3d->clip, 6, sctx->runtime.ray_start, ray_end))
1379 {
1380 ray_depth_max = math::dot(ray_end - sctx->runtime.ray_start, sctx->runtime.ray_dir);
1381 }
1382 else {
1383 snap_to_flag &= ~SCE_SNAP_TO_FACE;
1384 use_occlusion_plane = false;
1385 }
1386 }
1387 }
1388
1390 depsgraph,
1391 region,
1392 v3d,
1393 snap_to_flag,
1394 use_occlusion_plane ? params->occlusion_test :
1396 params,
1397 sctx->runtime.ray_start,
1398 sctx->runtime.ray_dir,
1399 ray_depth_max,
1400 mval,
1401 init_co,
1402 prev_co,
1403 dist_px ? square_f(*dist_px) : FLT_MAX,
1404 nullptr))
1405 {
1406 return retval;
1407 }
1408
1409#ifdef DEBUG_SNAP_TIME
1410 const timeit::TimePoint start_ = timeit::Clock::now();
1411#endif
1412
1413 snap_to_flag = sctx->runtime.snap_to_flag;
1414
1416
1417 bool has_hit = false;
1418
1419 /* NOTE: if both face ray-cast and face nearest are enabled, first find result of nearest, then
1420 * override with ray-cast. */
1421 if ((snap_to_flag & SCE_SNAP_INDIVIDUAL_NEAREST) && !has_hit) {
1422 has_hit = nearestWorldObjects(sctx);
1423
1424 if (has_hit) {
1426 }
1427 }
1428
1429 if (use_occlusion_plane || (snap_to_flag & SCE_SNAP_TO_FACE)) {
1430 has_hit = raycastObjects(sctx);
1431
1432 if (has_hit) {
1433 if (r_face_nor) {
1434 copy_v3_v3(r_face_nor, sctx->ret.no);
1435 }
1436
1437 if (snap_to_flag & SCE_SNAP_TO_FACE) {
1438 retval |= SCE_SNAP_TO_FACE;
1439 }
1440 }
1441 }
1442
1443 if (snap_to_flag & (SCE_SNAP_TO_POINT | SNAP_TO_EDGE_ELEMENTS)) {
1444 eSnapMode elem_test, elem = SCE_SNAP_TO_NONE;
1445
1446 /* Remove what has already been computed. */
1448
1449 SnapObjectContext::Output ret_bak{};
1450 if (!(sctx->runtime.snap_to_flag & SCE_SNAP_TO_EDGE) &&
1451 (sctx->runtime.snap_to_flag &
1453 {
1454 /* 'Snap to Edge' may occur even if it is not included among the selected snap types.
1455 * Save a backup to restore the previous result if needed. */
1456 ret_bak = sctx->ret;
1457 }
1458
1459 if (use_occlusion_plane && has_hit) {
1460 /* Compute the new clip_pane but do not add it yet. */
1461 BLI_ASSERT_UNIT_V3(sctx->ret.no);
1463 sctx->runtime.ray_dir, sctx->ret.loc, sctx->ret.no);
1464
1465 /* First, snap to the geometry of the polygon obtained via raycast.
1466 * This is necessary because the occlusion plane may "occlude" part of the polygon's
1467 * geometry. It also reduces the snap distance, optimizing the process.
1468 *
1469 * Note that if 'Snap to Edge Midpoint' or 'Snap to Edge Perpendicular' is selected, 'Snap to
1470 * Edge' will be returned instead.
1471 * This is because the same point can be tested in `snapObjectsRay` and fail this time due to
1472 * a mismatched snap distance, also resulting in snapping to the edge instead. */
1473 elem_test = snap_polygon(sctx, sctx->runtime.snap_to_flag);
1474 if (elem_test) {
1475 elem = elem_test;
1476 }
1477
1478 /* Add the new clip plane. */
1479 sctx->runtime.has_occlusion_plane = true;
1480 }
1481
1482 /* `snapObjectsRay` does 'Snap to Edge' instead of 'Snap to Edge Midpoint' or 'Snap to Edge
1483 * Perpendicular'. These points will be tested in the `snap_edge_points` function. */
1484 elem_test = snapObjectsRay(sctx);
1485 if (elem_test != SCE_SNAP_TO_NONE) {
1486 elem = elem_test;
1487 }
1488
1489 if (elem == SCE_SNAP_TO_EDGE) {
1490 if (snap_to_flag &
1492 {
1493 elem = snap_edge_points(sctx, square_f(*dist_px));
1494 }
1495
1496 if (!(elem & snap_to_flag)) {
1497 /* Restore the previous snap. */
1498 elem = SCE_SNAP_TO_NONE;
1499 sctx->ret = ret_bak;
1500 }
1501 }
1502
1503 if (elem != SCE_SNAP_TO_NONE) {
1504 retval = elem & snap_to_flag;
1505 }
1506 }
1507
1508 if ((retval == SCE_SNAP_TO_NONE) && (snap_to_flag & SCE_SNAP_TO_GRID)) {
1509 if (snap_grid(sctx)) {
1510 retval = SCE_SNAP_TO_GRID;
1511 }
1512 }
1513
1514 if (retval != SCE_SNAP_TO_NONE) {
1515 copy_v3_v3(r_loc, sctx->ret.loc);
1516 if (r_no) {
1517 copy_v3_v3(r_no, sctx->ret.no);
1518 }
1519 if (r_ob) {
1520 *r_ob = sctx->ret.ob;
1521 }
1522 if (r_obmat) {
1523 copy_m4_m4(r_obmat, sctx->ret.obmat.ptr());
1524 }
1525 if (r_index) {
1526 *r_index = sctx->ret.index;
1527 }
1528
1529 if (dist_px) {
1530 *dist_px = math::sqrt(sctx->ret.dist_px_sq);
1531 }
1532 }
1533
1534#ifdef DEBUG_SNAP_TIME
1535 duration_ += timeit::Clock::now() - start_;
1536 total_count_++;
1537#endif
1538
1539 return retval;
1540}
1541
1543 Depsgraph *depsgraph,
1544 const ARegion *region,
1545 const View3D *v3d,
1546 const eSnapMode snap_to,
1547 const SnapObjectParams *params,
1548 const float init_co[3],
1549 const float mval[2],
1550 const float prev_co[3],
1551 float *dist_px,
1552 float r_loc[3],
1553 float r_no[3])
1554{
1556 depsgraph,
1557 region,
1558 v3d,
1559 snap_to,
1560 params,
1561 init_co,
1562 mval,
1563 prev_co,
1564 dist_px,
1565 r_loc,
1566 r_no,
1567 nullptr,
1568 nullptr,
1569 nullptr,
1570 nullptr);
1571}
1572
1574 Depsgraph *depsgraph,
1575 const ARegion *region,
1576 const View3D *v3d,
1577 const SnapObjectParams *params,
1578 const float mval[2],
1579 float ray_depth,
1580 bool sort,
1581 ListBase *r_hit_list)
1582{
1583 float3 ray_start, ray_normal, ray_end;
1584 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
1585
1587 depsgraph, region, v3d, mval, false, nullptr, ray_normal, ray_start, ray_end))
1588 {
1589 return false;
1590 }
1591
1592 if ((rv3d->rflag & RV3D_CLIPPING) &&
1593 clip_segment_v3_plane_n(ray_start, ray_end, rv3d->clip, 6, ray_start, ray_end))
1594 {
1595 float ray_depth_max = math::dot(ray_end - ray_start, ray_normal);
1596 if ((ray_depth == -1.0f) || (ray_depth > ray_depth_max)) {
1597 ray_depth = ray_depth_max;
1598 }
1599 }
1600
1602 sctx, depsgraph, v3d, params, ray_start, ray_normal, ray_depth, sort, r_hit_list);
1603}
1604
1605#ifdef DEBUG_SNAP_TIME
1607{
1608 std::cout << "Average snapping time: ";
1609 std::cout << std::fixed << duration_.count() / 1.0e6 << " ms";
1610 std::cout << '\n';
1611
1612 duration_ = timeit::Nanoseconds::zero();
1613 total_count_ = 0;
1614}
1615#endif
1616
1618
1619} // namespace blender::ed::transform
blender::VectorList< DupliObject > DupliList
void object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob, blender::Set< const Object * > *include_objects, DupliList &r_duplilist)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
@ ME_WRAPPER_TYPE_BMESH
Mesh * BKE_mesh_wrapper_ensure_subdivision(Mesh *mesh)
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_editmesh_eval_cage(const Object *object)
bool BKE_object_is_in_editmode(const Object *ob)
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
const Mesh * BKE_object_get_editmesh_eval_final(const Object *object)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BVH_RAYCAST_DIST_MAX
void(*)(void *userdata, int index, const float co[3], BVHTreeNearest *nearest) BVHTree_NearestPointCallback
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void BLI_listbase_sort(ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
MINLINE float max_ff(float a, float b)
MINLINE float square_f(float a)
#define BLI_ASSERT_UNIT_V3(v)
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:217
bool clip_segment_v3_plane_n(const float p1[3], const float p2[3], const float plane_array[][4], int plane_num, float r_p1[3], float r_p2[3])
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, bool clip)
float dist_squared_to_projected_aabb(struct DistProjectedAABBPrecalc *data, const float bbmin[3], const float bbmax[3], bool r_axis_closest[3])
Definition math_geom.cc:858
bool isect_ray_line_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], float *r_lambda)
bool isect_point_planes_v3_negated(const float(*planes)[4], int totplane, const float p[3])
void cross_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:26
#define ISECT_AABB_PLANE_BEHIND_ANY
float closest_ray_to_segment_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], float r_close[3])
Definition math_geom.cc:409
void dist_squared_to_projected_aabb_precalc(struct DistProjectedAABBPrecalc *precalc, const float projmat[4][4], const float winsize[2], const float mval[2])
Definition math_geom.cc:806
int isect_aabb_planes_v3(const float(*planes)[4], int totplane, const float bbmin[3], const float bbmax[3])
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
void planes_from_projmat(const float mat[4][4], float left[4], float right[4], float bottom[4], float top[4], float near[4], float far[4])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool compare_m4m4(const float mat1[4][4], const float mat2[4][4], float limit)
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void copy_v3_fl3(float v[3], float x, float y, float z)
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], const float co[3]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE float dot_m4_v3_row_x(const float M[4][4], const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float dot_m4_v3_row_y(const float M[4][4], const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void negate_v4(float r[4])
unsigned int uint
#define IN_RANGE(a, b, c)
#define ELEM(...)
bool DEG_is_evaluated(const T *id)
ViewLayer * DEG_get_input_view_layer(const Depsgraph *graph)
Scene * DEG_get_input_scene(const Depsgraph *graph)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_ME
@ OB_WIRE
@ OB_BOUNDBOX
@ OB_MODE_EDIT
@ OB_MODE_OBJECT
@ OB_HIDE_SURFACE_PICK
@ OB_DRAW_IN_FRONT
@ OB_EMPTY
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_ARMATURE
@ OB_LAMP
@ OB_MESH
@ OB_CURVES_LEGACY
@ BA_WAS_SEL
@ BA_SNAP_FIX_DEPS_FIASCO
@ OB_DUPLI
#define BASE_SELECTED(v3d, base)
#define SCE_SNAP_TO_GEOM
eSnapTargetOP
@ SCE_SNAP_TARGET_NOT_ACTIVE
@ SCE_SNAP_TARGET_NOT_NONEDITED
@ SCE_SNAP_TARGET_ONLY_SELECTABLE
@ SCE_SNAP_TARGET_ALL
@ SCE_SNAP_TARGET_NOT_SELECTED
@ SCE_SNAP_TARGET_NOT_EDITED
#define BASE_SELECTABLE(v3d, base)
@ SCE_SNAP_INDIVIDUAL_NEAREST
@ SCE_SNAP_TO_EDGE
@ SCE_SNAP_TO_FACE
@ SCE_SNAP_TO_EDGE_ENDPOINT
@ SCE_SNAP_TO_GRID
@ SCE_SNAP_TO_EDGE_MIDPOINT
@ SCE_SNAP_TO_EDGE_PERPENDICULAR
@ SCE_SNAP_TO_POINT
@ SCE_SNAP_TO_NONE
#define BASE_VISIBLE(v3d, base)
#define RV3D_VIEW_IS_AXIS(view)
@ RV3D_CLIPPING
#define ED_transform_snap_object_time_average_print()
#define XRAY_ENABLED(v3d)
float ED_view3d_grid_view_scale(const Scene *scene, const View3D *v3d, const ARegion *region, const char **r_grid_unit)
bool ED_view3d_win_to_ray_clipped_ex(Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], const bool do_clip_planes, float r_ray_co[3], float r_ray_normal[3], float r_ray_start[3], float r_ray_end[3])
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
BPy_StructRNA * depsgraph
long long int int64_t
static DBVT_INLINE btDbvtNode * sort(btDbvtNode *n, btDbvtNode *&r)
Definition btDbvt.cpp:418
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
virtual void get_edge_verts_index(const int, int[2])
void clip_planes_enable(SnapObjectContext *sctx, const Object *ob_eval, bool skip_occlusion_plane=false)
Vector< float4, MAX_CLIPPLANE_LEN+1 > clip_planes
bool snap_edge(const float3 &va, const float3 &vb, int edge_index=-1)
virtual void copy_vert_no(const int, float[3])
SnapData(SnapObjectContext *sctx, const float4x4 &obmat=float4x4::identity())
virtual void get_vert_co(const int, const float **)
static void register_result_raycast(SnapObjectContext *sctx, const Object *ob_eval, const ID *id_eval, const float4x4 &obmat, const BVHTreeRayHit *hit, const bool is_in_front)
eSnapMode snap_edge_points_impl(SnapObjectContext *sctx, int edge_index, float dist_px_sq_orig)
bool snap_point(const float3 &co, int index=-1)
static void register_result(SnapObjectContext *sctx, const Object *ob_eval, const ID *id_eval, const float4x4 &obmat, BVHTreeNearest *r_nearest)
bool snap_boundbox(const float3 &min, const float3 &max)
KDTree_3d * tree
#define GS(x)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bool object_has_geometry_set_instances(const Object &object)
static eSnapMode snapObjectsRay(SnapObjectContext *sctx)
bool nearest_world_tree(SnapObjectContext *sctx, const BVHTree *tree, BVHTree_NearestPointCallback nearest_cb, const blender::float4x4 &obmat, void *treedata, BVHTreeNearest *r_nearest)
static eSnapMode iter_snap_objects(SnapObjectContext *sctx, IterSnapObjsCallback sob_callback)
eSnapMode snap_object_project_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const eSnapMode snap_to, const SnapObjectParams *params, const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3], int *r_index, const Object **r_ob, float r_obmat[4][4], float r_face_nor[3])
bool snap_object_project_ray_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, const View3D *v3d, const SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, float r_loc[3], float r_no[3], int *r_index, const Object **r_ob, float r_obmat[4][4])
void snap_object_context_destroy(SnapObjectContext *sctx)
void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
static bool test_projected_edge_dist(const DistProjectedAABBPrecalc *precalc, const float(*clip_plane)[4], const int clip_plane_len, const bool is_persp, const float va[3], const float vb[3], BVHTreeNearest *nearest)
eSnapMode snapArmature(SnapObjectContext *sctx, const Object *ob_eval, const float4x4 &obmat, bool is_object_active)
eSnapMode snapCamera(SnapObjectContext *sctx, const Object *object, const float4x4 &obmat, eSnapMode snap_to_flag)
eSnapMode(*)(SnapObjectContext *sctx, const Object *ob_eval, const ID *ob_data, const float4x4 &obmat, bool is_object_active, bool use_hide) IterSnapObjsCallback
static bool snap_object_is_snappable(const SnapObjectContext *sctx, const eSnapTargetOP snap_target_select, const Base *base_act, const Base *base)
static SnapObjectHitDepth * hit_depth_create(const float depth, const float co[3], uint ob_uuid)
static const ID * data_for_snap_dupli(ID *ob_data)
eSnapMode snap_object_mesh(SnapObjectContext *sctx, const Object *ob_eval, const ID *id, const float4x4 &obmat, eSnapMode snap_to_flag, bool skip_hidden, bool is_editmesh=false)
static float4 occlusion_plane_create(float3 ray_dir, float3 ray_co, float3 ray_no)
bool snap_object_project_ray_all(SnapObjectContext *sctx, Depsgraph *depsgraph, const View3D *v3d, const SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float ray_depth, bool sort, ListBase *r_hit_list)
static bool test_projected_vert_dist(const DistProjectedAABBPrecalc *precalc, const float(*clip_plane)[4], const int clip_plane_len, const bool is_persp, const float *co, BVHTreeNearest *nearest)
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])
eSnapMode snapCurve(SnapObjectContext *sctx, const Object *ob_eval, const float4x4 &obmat)
eSnapMode snap_edge_points_mesh(SnapObjectContext *sctx, const Object *ob_eval, const ID *id, const float4x4 &obmat, float dist_px_sq_orig, int edge_index)
static bool snap_object_context_runtime_init(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, eSnapMode snap_to_flag, eSnapOcclusionTest occlusion_test, const SnapObjectParams *params, const float ray_start[3], const float ray_dir[3], const float ray_depth, const float mval[2], const float init_co[3], const float prev_co[3], const float dist_px_sq, ListBase *hit_list)
void cb_snap_vert(void *userdata, int index, const DistProjectedAABBPrecalc *precalc, const float(*clip_plane)[4], const int clip_plane_len, BVHTreeNearest *nearest)
static bool raycastObjects(SnapObjectContext *sctx)
eSnapMode snap_object_project_view3d(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const eSnapMode snap_to, const SnapObjectParams *params, const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3])
static eSnapMode snap_polygon(SnapObjectContext *sctx, eSnapMode snap_to_flag)
static eSnapMode snap_edge_points(SnapObjectContext *sctx, const float dist_px_sq_orig)
static void nearest_world_tree_co(const BVHTree *tree, BVHTree_NearestPointCallback nearest_cb, void *treedata, const float3 &co, BVHTreeNearest *r_nearest)
void cb_snap_edge(void *userdata, int index, const DistProjectedAABBPrecalc *precalc, const float(*clip_plane)[4], const int clip_plane_len, BVHTreeNearest *nearest)
eSnapMode snap_object_editmesh(SnapObjectContext *sctx, const Object *ob_eval, const ID *id, const float4x4 &obmat, eSnapMode snap_to_flag, bool use_hide)
static eSnapMode nearest_world_object_fn(SnapObjectContext *sctx, const Object *ob_eval, const ID *ob_data, const float4x4 &obmat, bool, bool use_hide)
SnapObjectContext * snap_object_context_create(Scene *scene, int flag)
void snap_object_context_set_editmesh_callbacks(SnapObjectContext *sctx, 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)
static eSnapMode raycast_obj_fn(SnapObjectContext *sctx, const Object *ob_eval, const ID *ob_data, const float4x4 &obmat, bool, bool use_hide)
static eSnapMode snap_obj_fn(SnapObjectContext *sctx, const Object *ob_eval, const ID *ob_data, const float4x4 &obmat, bool is_object_active, bool use_hide)
bool snap_object_project_ray(SnapObjectContext *sctx, Depsgraph *depsgraph, const View3D *v3d, const SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, float r_co[3], float r_no[3])
static int hit_depth_cmp(const void *arg1, const void *arg2)
static const ID * data_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
static bool nearestWorldObjects(SnapObjectContext *sctx)
bool object_project_all_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const SnapObjectParams *params, const float mval[2], float ray_depth, bool sort, ListBase *r_hit_list)
eSnapMode snap_polygon_mesh(SnapObjectContext *sctx, const Object *ob_eval, const ID *id, const float4x4 &obmat, eSnapMode snap_to_flag, int face_index)
static bool snap_grid(SnapObjectContext *sctx)
eSnapMode snap_object_center(SnapObjectContext *sctx, const Object *ob_eval, const float4x4 &obmat, eSnapMode snap_to_flag)
T length_squared(const VecBase< T, Size > &a)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
T sqrt(const T &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T abs(const T &a)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
T round(const T &a)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
std::chrono::nanoseconds Nanoseconds
Definition BLI_timeit.hh:21
Clock::time_point TimePoint
Definition BLI_timeit.hh:20
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 3 > float3
return ret
#define FLT_MAX
Definition stdcycles.h:14
void * regiondata
short flag
struct Object * object
short flag_legacy
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
MeshRuntimeHandle * runtime
short transflag
short visibility_flag
float persmat[4][4]
float clip[6][4]
float viewinv[4][4]
const c_style_mat & ptr() const
struct blender::ed::transform::SnapObjectContext::@170374026073064374202114033260227063176045253050 runtime
struct blender::ed::transform::SnapObjectContext::@261131034374131144046074012251042111150012050144 grid
struct blender::ed::transform::SnapObjectContext::Output ret
bool(* test_edge_fn)(BMEdge *, void *user_data)
bool(* test_face_fn)(BMFace *, void *user_data)
bool(* test_vert_fn)(BMVert *, void *user_data)
struct blender::ed::transform::SnapObjectContext::@057303224201065302154130247374246316157207360104 callbacks
Map< const ID *, std::unique_ptr< SnapCache > > editmesh_caches
struct blender::ed::transform::SnapObjectContext::@057303224201065302154130247374246316157207360104::@052144141244060360354325332176274005342203076127 edit_mesh
Vector< float4, MAX_CLIPPLANE_LEN > clip_planes
i
Definition text_draw.cc:230
#define SNAP_TO_EDGE_ELEMENTS