Blender V4.5
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_object.hh"
24
26
28#include "ED_view3d.hh"
29
31
32#ifdef DEBUG_SNAP_TIME
33# include "BLI_timeit.hh"
34# include <iostream>
35
36# if WIN32 and NDEBUG
37# pragma optimize("t", on)
38# endif
39
40static int64_t total_count_ = 0;
41static blender::timeit::Nanoseconds duration_;
42#endif
43
44namespace blender::ed::transform {
45
46static float4 occlusion_plane_create(float3 ray_dir, float3 ray_co, float3 ray_no)
47{
48 float4 plane;
49 plane_from_point_normal_v3(plane, ray_co, ray_no);
50 if (dot_v3v3(ray_dir, plane) > 0.0f) {
51 /* The plane is facing the wrong direction. */
52 negate_v4(plane);
53 }
54
55 /* Small offset to simulate a kind of volume for edges and vertices. */
56 plane[3] += 0.01f;
57
58 return plane;
59}
60
62 const float (*clip_plane)[4],
63 const int clip_plane_len,
64 const bool is_persp,
65 const float *co,
66 BVHTreeNearest *nearest)
67{
68 if (!isect_point_planes_v3_negated(clip_plane, clip_plane_len, co)) {
69 return false;
70 }
71
72 float co2d[2] = {
73 (dot_m4_v3_row_x(precalc->pmat, co) + precalc->pmat[3][0]),
74 (dot_m4_v3_row_y(precalc->pmat, co) + precalc->pmat[3][1]),
75 };
76
77 if (is_persp) {
78 float w = mul_project_m4_v3_zfac(precalc->pmat, co);
79 mul_v2_fl(co2d, 1.0f / w);
80 }
81
82 const float dist_sq = len_squared_v2v2(precalc->mval, co2d);
83 if (dist_sq < nearest->dist_sq) {
84 copy_v3_v3(nearest->co, co);
85 nearest->dist_sq = dist_sq;
86 return true;
87 }
88 return false;
89}
90
92 const float (*clip_plane)[4],
93 const int clip_plane_len,
94 const bool is_persp,
95 const float va[3],
96 const float vb[3],
97 BVHTreeNearest *nearest)
98{
99 float near_co[3];
100 closest_ray_to_segment_v3(precalc->ray_origin, precalc->ray_direction, va, vb, near_co);
101 return test_projected_vert_dist(precalc, clip_plane, clip_plane_len, is_persp, near_co, nearest);
102}
103
105 : nearest_precalc(),
106 obmat_(obmat),
107 is_persp(sctx->runtime.rv3d ? sctx->runtime.rv3d->is_persp : false),
109{
110 if (sctx->runtime.rv3d) {
111 this->pmat_local = float4x4(sctx->runtime.rv3d->persmat) * obmat;
112 }
113 else {
114 this->pmat_local = obmat;
115 }
116
118 &this->nearest_precalc, this->pmat_local.ptr(), sctx->runtime.win_size, sctx->runtime.mval);
119
120 this->nearest_point.index = -2;
121 this->nearest_point.dist_sq = sctx->ret.dist_px_sq;
122 copy_v3_fl3(this->nearest_point.no, 0.0f, 0.0f, 1.0f);
123}
124
126 const Object *ob_eval,
127 bool skip_occlusion_plane)
128{
129 float4x4 tobmat = math::transpose(this->obmat_);
130 if (!skip_occlusion_plane) {
131 const bool is_in_front = (sctx->runtime.params.occlusion_test == SNAP_OCCLUSION_AS_SEEM) &&
132 ob_eval && (ob_eval->dtx & OB_DRAW_IN_FRONT) != 0;
133 if (!is_in_front && sctx->runtime.has_occlusion_plane) {
134 this->clip_planes.append(tobmat * sctx->runtime.occlusion_plane);
135 }
136 else if (sctx->runtime.has_occlusion_plane_in_front) {
137 this->clip_planes.append(tobmat * sctx->runtime.occlusion_plane_in_front);
138 }
139 }
140
141 for (float4 &plane : sctx->runtime.clip_planes) {
142 this->clip_planes.append(tobmat * plane);
143 }
144}
145
147{
148 /* In vertex and edges you need to get the pixel distance from ray to bounding box,
149 * see: #46099, #46816 */
150
151#ifdef TEST_CLIPPLANES_IN_BOUNDBOX
152 int isect_type = isect_aabb_planes_v3(
153 reinterpret_cast<const float(*)[4]>(this->clip_planes.data()),
154 this->clip_planes.size(),
155 min,
156 max);
157
158 if (isect_type == ISECT_AABB_PLANE_BEHIND_ANY) {
159 return false;
160 }
161#endif
162
163 bool dummy[3];
164 float bb_dist_px_sq = dist_squared_to_projected_aabb(&this->nearest_precalc, min, max, dummy);
165 if (bb_dist_px_sq > this->nearest_point.dist_sq) {
166 return false;
167 }
168
169 return true;
170}
171
172bool SnapData::snap_point(const float3 &co, int index)
173{
175 reinterpret_cast<const float(*)[4]>(this->clip_planes.data()),
176 this->clip_planes.size(),
177 this->is_persp,
178 co,
179 &this->nearest_point))
180 {
181 this->nearest_point.index = index;
182 return true;
183 }
184 return false;
185}
186
187bool SnapData::snap_edge(const float3 &va, const float3 &vb, int edge_index)
188{
190 reinterpret_cast<const float(*)[4]>(this->clip_planes.data()),
191 this->clip_planes.size(),
192 this->is_persp,
193 va,
194 vb,
195 &this->nearest_point))
196 {
197 this->nearest_point.index = edge_index;
198 sub_v3_v3v3(this->nearest_point.no, vb, va);
199 return true;
200 }
201 return false;
202}
203
205 int edge_index,
206 float dist_px_sq_orig)
207{
209
210 int vindex[2];
211 this->get_edge_verts_index(edge_index, vindex);
212
213 const float *v_pair[2];
214 this->get_vert_co(vindex[0], &v_pair[0]);
215 this->get_vert_co(vindex[1], &v_pair[1]);
216
217 float lambda;
218 if (!isect_ray_line_v3(this->nearest_precalc.ray_origin,
219 this->nearest_precalc.ray_direction,
220 v_pair[0],
221 v_pair[1],
222 &lambda))
223 {
224 /* Do nothing. */
225 }
226 else {
227 this->nearest_point.dist_sq = dist_px_sq_orig;
228
229 eSnapMode snap_to = sctx->runtime.snap_to_flag;
230 int e_mode_len = ((snap_to & SCE_SNAP_TO_EDGE) != 0) +
231 ((snap_to & SCE_SNAP_TO_EDGE_ENDPOINT) != 0) +
232 ((snap_to & SCE_SNAP_TO_EDGE_MIDPOINT) != 0);
233
234 float range = 1.0f / (2 * e_mode_len - 1);
235
236 if (snap_to & SCE_SNAP_TO_EDGE_MIDPOINT) {
237 range *= e_mode_len - 1;
238 if ((range) < lambda && lambda < (1.0f - range)) {
239 float vmid[3];
240 mid_v3_v3v3(vmid, v_pair[0], v_pair[1]);
241
242 if (this->snap_point(vmid, edge_index)) {
243 sub_v3_v3v3(this->nearest_point.no, v_pair[1], v_pair[0]);
245 }
246 }
247 }
248
249 if (snap_to & SCE_SNAP_TO_EDGE_PERPENDICULAR) {
250 float v_near[3], va_g[3], vb_g[3];
251
252 mul_v3_m4v3(va_g, this->obmat_.ptr(), v_pair[0]);
253 mul_v3_m4v3(vb_g, this->obmat_.ptr(), v_pair[1]);
254 float lambda_perp = line_point_factor_v3(sctx->runtime.curr_co, va_g, vb_g);
255
256 if (IN_RANGE(lambda_perp, 0.0f, 1.0f)) {
257 interp_v3_v3v3(v_near, v_pair[0], v_pair[1], lambda_perp);
258
259 if (this->snap_point(v_near, edge_index)) {
260 sub_v3_v3v3(this->nearest_point.no, v_pair[1], v_pair[0]);
262 }
263 }
264 }
265
266 /* Leave this one for last so it doesn't change the normal. */
267 if (snap_to & SCE_SNAP_TO_EDGE_ENDPOINT) {
268 if (lambda < (range) || (1.0f - range) < lambda) {
269 int v_id = lambda < 0.5f ? 0 : 1;
270
271 if (this->snap_point(v_pair[v_id], vindex[v_id])) {
273 this->copy_vert_no(vindex[v_id], this->nearest_point.no);
274 }
275 }
276 }
277 }
278
279 return elem;
280}
281
283 const Object *ob_eval,
284 const ID *id_eval,
285 const float4x4 &obmat,
286 BVHTreeNearest *r_nearest)
287{
288 BLI_assert(r_nearest->index != -2);
289
290 copy_v3_v3(sctx->ret.loc, r_nearest->co);
291 copy_v3_v3(sctx->ret.no, r_nearest->no);
292 sctx->ret.index = r_nearest->index;
293 sctx->ret.obmat = obmat;
294 sctx->ret.ob = ob_eval;
295 sctx->ret.data = id_eval;
296 sctx->ret.dist_px_sq = r_nearest->dist_sq;
297
298 /* Global space. */
299 sctx->ret.loc = math::transform_point(obmat, sctx->ret.loc);
300 sctx->ret.no = math::normalize(math::transform_direction(obmat, sctx->ret.no));
301
302#ifndef NDEBUG
303 /* Make sure this is only called once. */
304 r_nearest->index = -2;
305#endif
306}
307
308void SnapData::register_result(SnapObjectContext *sctx, const Object *ob_eval, const ID *id_eval)
309{
310 register_result(sctx, ob_eval, id_eval, this->obmat_, &this->nearest_point);
311}
312
314 const Object *ob_eval,
315 const ID *id_eval,
316 const blender::float4x4 &obmat,
317 const BVHTreeRayHit *hit,
318 const bool is_in_front)
319{
320 const float depth_max = is_in_front ? sctx->ret.ray_depth_max_in_front : sctx->ret.ray_depth_max;
321 if (hit->dist <= depth_max) {
322 float3 co = math::transform_point(obmat, float3(hit->co));
324
325 sctx->ret.loc = co;
326 sctx->ret.no = no;
327 sctx->ret.index = hit->index;
328 sctx->ret.obmat = obmat;
329 sctx->ret.ob = ob_eval;
330 sctx->ret.data = id_eval;
331 sctx->ret.ray_depth_max = std::min(hit->dist, sctx->ret.ray_depth_max);
332
333 if (is_in_front) {
335 sctx->runtime.ray_dir, co, no);
337 }
338 }
339}
340
341/* -------------------------------------------------------------------- */
344
352static ID *data_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
353{
354 bool use_hide = false;
355
356 switch (ob_eval->type) {
357 case OB_MESH: {
358 const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
359 if (BKE_object_is_in_editmode(ob_eval)) {
360 if (edit_mode_type == SNAP_GEOM_EDIT) {
361 return nullptr;
362 }
363
364 const Mesh *editmesh_eval = (edit_mode_type == SNAP_GEOM_FINAL) ?
366 (edit_mode_type == SNAP_GEOM_CAGE) ?
368 nullptr;
369
370 if (editmesh_eval) {
371 if (editmesh_eval->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) {
372 return nullptr;
373 }
374 mesh_eval = editmesh_eval;
375 use_hide = true;
376 }
377 }
378 if (r_use_hide) {
379 *r_use_hide = use_hide;
380 }
381 return (ID *)mesh_eval;
382 }
383 default:
384 break;
385 }
386 if (r_use_hide) {
387 *r_use_hide = use_hide;
388 }
389 return (ID *)ob_eval->data;
390}
391
393
394/* -------------------------------------------------------------------- */
397
399 const Object *ob_eval,
400 const ID *ob_data,
401 const float4x4 &obmat,
402 bool is_object_active,
403 bool use_hide);
404
406 const eSnapTargetOP snap_target_select,
407 const Base *base_act,
408 const Base *base)
409{
410 if (!BASE_VISIBLE(sctx->runtime.v3d, base)) {
411 return false;
412 }
413
414 if ((snap_target_select == SCE_SNAP_TARGET_ALL) ||
416 {
417 return true;
418 }
419
421 return false;
422 }
423
424 /* Get attributes of potential target. */
425 const bool is_active = (base_act == base);
426 const bool is_selected = (base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL);
427 const bool is_edited = (base->object->mode == OB_MODE_EDIT);
428 const bool is_selectable = (base->flag & BASE_SELECTABLE);
429 /* Get attributes of state. */
430 const bool is_in_object_mode = (base_act == nullptr) ||
431 (base_act->object->mode == OB_MODE_OBJECT);
432
433 if (is_in_object_mode) {
434 /* Handle target selection options that make sense for object mode. */
435 if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_selected) {
436 /* What is selectable or not is part of the object and depends on the mode. */
437 return false;
438 }
439 }
440 else {
441 /* Handle target selection options that make sense for edit/pose mode. */
442 if ((snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) && is_active) {
443 return false;
444 }
445 if ((snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) && is_edited && !is_active) {
446 /* Base is edited, but not active. */
447 return false;
448 }
449 if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) {
450 return false;
451 }
452 }
453
454 if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) {
455 return false;
456 }
457
458 return true;
459}
460
465{
467 eSnapMode tmp;
468
471 const eSnapTargetOP snap_target_select = sctx->runtime.params.snap_target_select;
472 BKE_view_layer_synced_ensure(scene, view_layer);
473 Base *base_act = BKE_view_layer_active_base_get(view_layer);
474
476 if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) {
477 continue;
478 }
479
480 const bool is_object_active = (base == base_act);
481 Object *obj_eval = DEG_get_evaluated(sctx->runtime.depsgraph, base->object);
482 if (obj_eval->transflag & OB_DUPLI ||
484 {
485 ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval);
486 LISTBASE_FOREACH (DupliObject *, dupli_ob, lb) {
487 BLI_assert(DEG_is_evaluated(dupli_ob->ob));
488 if ((tmp = sob_callback(sctx,
489 dupli_ob->ob,
490 dupli_ob->ob_data,
491 float4x4(dupli_ob->mat),
492 is_object_active,
493 false)) != SCE_SNAP_TO_NONE)
494 {
495 ret = tmp;
496 }
497 }
499 }
500
501 bool use_hide = false;
502 ID *ob_data = data_for_snap(obj_eval, sctx->runtime.params.edit_mode_type, &use_hide);
503 if ((tmp = sob_callback(
504 sctx, obj_eval, ob_data, obj_eval->object_to_world(), is_object_active, use_hide)) !=
506 {
507 ret = tmp;
508 }
509 }
510 return ret;
511}
512
514
515/* -------------------------------------------------------------------- */
518
519/* Store all ray-hits
520 * Support for storing all depths, not just the first (ray-cast 'all'). */
521
522static SnapObjectHitDepth *hit_depth_create(const float depth, const float co[3], uint ob_uuid)
523{
524 SnapObjectHitDepth *hit = MEM_new<SnapObjectHitDepth>(__func__);
525
526 hit->depth = depth;
527 copy_v3_v3(hit->co, co);
528 hit->ob_uuid = ob_uuid;
529
530 return hit;
531}
532
533static int hit_depth_cmp(const void *arg1, const void *arg2)
534{
535 const SnapObjectHitDepth *h1 = static_cast<const SnapObjectHitDepth *>(arg1);
536 const SnapObjectHitDepth *h2 = static_cast<const SnapObjectHitDepth *>(arg2);
537 int val = 0;
538
539 if (h1->depth < h2->depth) {
540 val = -1;
541 }
542 else if (h1->depth > h2->depth) {
543 val = 1;
544 }
545
546 return val;
547}
548
549void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
550{
551 RayCastAll_Data *data = static_cast<RayCastAll_Data *>(userdata);
552 data->raycast_callback(data->bvhdata, index, ray, hit);
553 if (hit->index != -1) {
554 /* Get all values in world-space. */
555 float location[3];
556 float depth;
557
558 /* World-space location. */
559 mul_v3_m4v3(location, (float(*)[4])data->obmat, hit->co);
560 depth = (hit->dist + data->len_diff) / data->local_scale;
561
562 SnapObjectHitDepth *hit_item = hit_depth_create(depth, location, data->ob_uuid);
563 BLI_addtail(data->hit_list, hit_item);
564 }
565}
566
568 const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3])
569{
570 cross_tri_v3(no, v0, v1, v2);
571 return dot_v3v3(no, dir) < 0.0f;
572}
573
578 const Object *ob_eval,
579 const ID *ob_data,
580 const float4x4 &obmat,
581 bool /*is_object_active*/,
582 bool use_hide)
583{
584 bool retval = false;
585
586 if (ob_data == nullptr) {
588 ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE))
589 {
590 /* Do not hit objects that are in wire or bounding box display mode. */
591 return SCE_SNAP_TO_NONE;
592 }
593 if (ob_eval->type == OB_MESH) {
594 if (snap_object_editmesh(sctx, ob_eval, nullptr, obmat, SCE_SNAP_TO_FACE, use_hide)) {
595 retval = true;
596 }
597 }
598 else {
599 return SCE_SNAP_TO_NONE;
600 }
601 }
603 ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE))
604 {
605 /* Do not hit objects that are in wire or bounding box display mode. */
606 return SCE_SNAP_TO_NONE;
607 }
608 else if (GS(ob_data->name) != ID_ME) {
609 return SCE_SNAP_TO_NONE;
610 }
611 else if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) &&
614 {
615 /* The final Curves geometry is generated as a Mesh. Skip this Mesh if the target is not
616 * #SNAP_GEOM_FINAL. This also avoids creating and destroying BVH Trees too frequently while
617 * editing. */
618 return SCE_SNAP_TO_NONE;
619 }
620 else {
621 retval = snap_object_mesh(sctx, ob_eval, ob_data, obmat, SCE_SNAP_TO_FACE, use_hide);
622 }
623
624 if (retval) {
625 return SCE_SNAP_TO_FACE;
626 }
627 return SCE_SNAP_TO_NONE;
628}
629
645{
647}
648
650
651/* -------------------------------------------------------------------- */
654
657 void *treedata,
658 const float3 &co,
659 BVHTreeNearest *r_nearest)
660{
661 r_nearest->index = -1;
662 copy_v3_fl(r_nearest->co, FLT_MAX);
663 r_nearest->dist_sq = FLT_MAX;
664
665 BLI_bvhtree_find_nearest(tree, co, r_nearest, nearest_cb, treedata);
666}
667
669 const BVHTree *tree,
671 const blender::float4x4 &obmat,
672 void *treedata,
673 BVHTreeNearest *r_nearest)
674{
675 float4x4 imat = math::invert(obmat);
676 float3 init_co = math::transform_point(imat, sctx->runtime.init_co);
677 float3 curr_co = math::transform_point(imat, sctx->runtime.curr_co);
678
679 BVHTreeNearest nearest{};
680 float3 vec;
682 nearest_world_tree_co(tree, nearest_cb, treedata, init_co, &nearest);
683 vec = float3(nearest.co) - init_co;
684 }
685 else {
686 /* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain
687 * the answer. We could return immediately after updating r_loc, r_no, r_index, but that would
688 * also complicate the code. Foregoing slight optimization for code clarity. */
689 nearest_world_tree_co(tree, nearest_cb, treedata, curr_co, &nearest);
690 vec = float3(nearest.co) - curr_co;
691 }
692
693 float original_distance = math::length_squared(math::transform_direction(obmat, vec));
694 if (r_nearest->dist_sq <= original_distance) {
695 return false;
696 }
697
698 /* Scale to make `snap_face_nearest_steps` steps. */
699 float step_scale_factor = 1.0f / max_ff(1.0f, float(sctx->runtime.params.face_nearest_steps));
700
701 /* Compute offset between init co and prev co. */
702 float3 delta = (curr_co - init_co) * step_scale_factor;
703
704 float3 co = init_co;
705 for (int i = 0; i < sctx->runtime.params.face_nearest_steps; i++) {
706 co += delta;
707 nearest_world_tree_co(tree, nearest_cb, treedata, co, &nearest);
708 co = nearest.co;
709 }
710
711 *r_nearest = nearest;
713 r_nearest->dist_sq = original_distance;
714 }
715 else if (sctx->runtime.params.face_nearest_steps > 1) {
716 /* Recalculate the distance.
717 * When multiple steps are tested, we cannot depend on the distance calculated for
718 * `nearest.dist_sq`, as it reduces with each step. */
719 vec = co - curr_co;
721 }
722 return true;
723}
724
726 const Object *ob_eval,
727 const ID *ob_data,
728 const float4x4 &obmat,
729 bool /*is_object_active*/,
730 bool use_hide)
731{
733
734 if (ob_data == nullptr) {
735 if (ob_eval->type == OB_MESH) {
736 retval = snap_object_editmesh(
737 sctx, ob_eval, nullptr, obmat, SCE_SNAP_INDIVIDUAL_NEAREST, use_hide);
738 }
739 else {
740 return SCE_SNAP_TO_NONE;
741 }
742 }
743 else if (GS(ob_data->name) != ID_ME) {
744 return SCE_SNAP_TO_NONE;
745 }
746 else if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) &&
749 {
750 /* The final Curves geometry is generated as a Mesh. Skip this Mesh if the target is not
751 * #SNAP_GEOM_FINAL. This also avoids creating and destroying BVH Trees too frequently while
752 * editing. */
753 return SCE_SNAP_TO_NONE;
754 }
755 else {
756 retval = snap_object_mesh(
757 sctx, ob_eval, ob_data, obmat, SCE_SNAP_INDIVIDUAL_NEAREST, use_hide);
758 }
759
760 return retval;
761}
762
778
780
781/* -------------------------------------------------------------------- */
784
785void cb_snap_vert(void *userdata,
786 int index,
787 const DistProjectedAABBPrecalc *precalc,
788 const float (*clip_plane)[4],
789 const int clip_plane_len,
790 BVHTreeNearest *nearest)
791{
792 SnapData *data = static_cast<SnapData *>(userdata);
793
794 const float *co;
795 data->get_vert_co(index, &co);
796
797 if (test_projected_vert_dist(precalc, clip_plane, clip_plane_len, data->is_persp, co, nearest)) {
798 data->copy_vert_no(index, nearest->no);
799 nearest->index = index;
800 }
801}
802
803void cb_snap_edge(void *userdata,
804 int index,
805 const DistProjectedAABBPrecalc *precalc,
806 const float (*clip_plane)[4],
807 const int clip_plane_len,
808 BVHTreeNearest *nearest)
809{
810 SnapData *data = static_cast<SnapData *>(userdata);
811
812 int vindex[2];
813 data->get_edge_verts_index(index, vindex);
814
815 const float *v_pair[2];
816 data->get_vert_co(vindex[0], &v_pair[0]);
817 data->get_vert_co(vindex[1], &v_pair[1]);
818
820 precalc, clip_plane, clip_plane_len, data->is_persp, v_pair[0], v_pair[1], nearest))
821 {
822 sub_v3_v3v3(nearest->no, v_pair[1], v_pair[0]);
823 nearest->index = index;
824 }
825}
826
828
829/* -------------------------------------------------------------------- */
832
834{
835 if (!sctx->ret.data || GS(sctx->ret.data->name) != ID_ME) {
836 return SCE_SNAP_TO_NONE;
837 }
838
839 return snap_polygon_mesh(
840 sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, snap_to_flag, sctx->ret.index);
841}
842
843static eSnapMode snap_edge_points(SnapObjectContext *sctx, const float dist_px_sq_orig)
844{
845 if (!sctx->ret.data || GS(sctx->ret.data->name) != ID_ME) {
846 return SCE_SNAP_TO_EDGE;
847 }
848
850 sctx, sctx->ret.ob, sctx->ret.data, sctx->ret.obmat, dist_px_sq_orig, sctx->ret.index);
851}
852
854 const Object *ob_eval,
855 const float4x4 &obmat,
856 eSnapMode snap_to_flag)
857{
858 /* May extend later (for now just snaps to empty or camera center). */
859
860 if (ob_eval->transflag & OB_DUPLI) {
861 return SCE_SNAP_TO_NONE;
862 }
863
864 /* For now only vertex supported. */
865 if ((snap_to_flag & SCE_SNAP_TO_POINT) == 0) {
866 return SCE_SNAP_TO_NONE;
867 }
868
869 SnapData nearest2d(sctx, obmat);
870
871 nearest2d.clip_planes_enable(sctx, ob_eval);
872
873 if (nearest2d.snap_point(float3(0.0f))) {
874 nearest2d.register_result(sctx, ob_eval, static_cast<const ID *>(ob_eval->data));
875 return SCE_SNAP_TO_POINT;
876 }
877
878 return SCE_SNAP_TO_NONE;
879}
880
885 const Object *ob_eval,
886 const ID *ob_data,
887 const float4x4 &obmat,
888 bool is_object_active,
889 bool use_hide)
890{
891 if (ob_data == nullptr && (ob_eval->type == OB_MESH)) {
893 sctx, ob_eval, nullptr, obmat, sctx->runtime.snap_to_flag, use_hide);
894 }
895
896 if (ob_data == nullptr) {
897 return snap_object_center(sctx, ob_eval, obmat, sctx->runtime.snap_to_flag);
898 }
899
900 if (ob_eval->dt == OB_BOUNDBOX) {
901 /* Do not snap to objects that are in bounding box display mode. */
902 return SCE_SNAP_TO_NONE;
903 }
904
905 if (GS(ob_data->name) == ID_ME) {
906 if (ELEM(ob_eval->type, OB_CURVES_LEGACY, OB_SURF) &&
908 {
909 /* The final Curves geometry is generated as a Mesh. Skip this Mesh if the target is not
910 * #SNAP_GEOM_FINAL. */
911 return SCE_SNAP_TO_NONE;
912 }
913 return snap_object_mesh(sctx, ob_eval, ob_data, obmat, sctx->runtime.snap_to_flag, use_hide);
914 }
915
917 switch (ob_eval->type) {
918 case OB_MESH: {
919 break;
920 }
921 case OB_ARMATURE:
922 retval = snapArmature(sctx, ob_eval, obmat, is_object_active);
923 break;
924 case OB_CURVES_LEGACY:
925 case OB_SURF:
926 if (ob_eval->type == OB_CURVES_LEGACY || BKE_object_is_in_editmode(ob_eval)) {
927 retval = snapCurve(sctx, ob_eval, obmat);
928 }
929 break;
930 case OB_FONT: {
931 break;
932 }
933 case OB_EMPTY:
934 case OB_LAMP:
935 retval = snap_object_center(sctx, ob_eval, obmat, sctx->runtime.snap_to_flag);
936 break;
937 case OB_CAMERA:
938 retval = snapCamera(sctx, ob_eval, obmat, sctx->runtime.snap_to_flag);
939 break;
940 }
941
942 return retval;
943}
944
959{
960 return iter_snap_objects(sctx, snap_obj_fn);
961}
962
963static bool snap_grid(SnapObjectContext *sctx)
964{
965 SnapData nearest2d(sctx);
966 nearest2d.clip_planes_enable(sctx, nullptr, true);
967
968 /* Ignore the maximum pixel distance when snapping to grid.
969 * This avoids undesirable jumps of the element being snapped. */
970 nearest2d.nearest_point.dist_sq = FLT_MAX;
971
972 float grid_dist = sctx->grid.size;
973
974 if (sctx->grid.use_init_co) {
975 float3 co = math::round((sctx->runtime.init_co) / grid_dist) * grid_dist;
976 if (nearest2d.snap_point(co)) {
977 nearest2d.register_result(sctx, nullptr, nullptr);
978 return true;
979 }
980 }
981
982 float ray_dist;
983 for (int i = 0; i < 4; i++) {
985 sctx->runtime.ray_dir,
986 sctx->grid.planes[i],
987 &ray_dist,
988 false) &&
989 (ray_dist > 0.0f))
990 {
991 float3 co = math::round((sctx->runtime.ray_start + sctx->runtime.ray_dir * ray_dist) /
992 grid_dist) *
993 grid_dist;
994
995 if (nearest2d.snap_point(co)) {
996 copy_v3_v3(nearest2d.nearest_point.no, sctx->grid.planes[i]);
997 if (!sctx->runtime.rv3d->is_persp && RV3D_VIEW_IS_AXIS(sctx->runtime.rv3d->view)) {
998 /* Project in #sctx->runtime.curr_co plane. */
999 add_v3_v3(nearest2d.nearest_point.co,
1000 sctx->runtime.curr_co * float3(nearest2d.nearest_point.no));
1001 }
1002 nearest2d.register_result(sctx, nullptr, nullptr);
1003 return true;
1004 }
1005 }
1006 }
1007
1008 return false;
1009}
1010
1012
1013/* -------------------------------------------------------------------- */
1016
1018{
1019 SnapObjectContext *sctx = MEM_new<SnapObjectContext>(__func__);
1020
1021 sctx->scene = scene;
1022
1023 return sctx;
1024}
1025
1027{
1028 MEM_delete(sctx);
1029}
1030
1032 bool (*test_vert_fn)(BMVert *, void *user_data),
1033 bool (*test_edge_fn)(BMEdge *, void *user_data),
1034 bool (*test_face_fn)(BMFace *, void *user_data),
1035 void *user_data)
1036{
1037 bool is_cache_dirty = false;
1038 if (sctx->callbacks.edit_mesh.test_vert_fn != test_vert_fn) {
1039 sctx->callbacks.edit_mesh.test_vert_fn = test_vert_fn;
1040 is_cache_dirty = true;
1041 }
1042 if (sctx->callbacks.edit_mesh.test_edge_fn != test_edge_fn) {
1043 sctx->callbacks.edit_mesh.test_edge_fn = test_edge_fn;
1044 is_cache_dirty = true;
1045 }
1046 if (sctx->callbacks.edit_mesh.test_face_fn != test_face_fn) {
1047 sctx->callbacks.edit_mesh.test_face_fn = test_face_fn;
1048 is_cache_dirty = true;
1049 }
1050 if (sctx->callbacks.edit_mesh.user_data != user_data) {
1051 sctx->callbacks.edit_mesh.user_data = user_data;
1052 is_cache_dirty = true;
1053 }
1054
1055 if (is_cache_dirty) {
1056 sctx->editmesh_caches.clear();
1057 }
1058}
1059
1061 Depsgraph *depsgraph,
1062 const ARegion *region,
1063 const View3D *v3d,
1064 eSnapMode snap_to_flag,
1065 eSnapOcclusionTest occlusion_test,
1066 const SnapObjectParams *params,
1067 const float ray_start[3],
1068 const float ray_dir[3],
1069 const float ray_depth,
1070 const float mval[2],
1071 const float init_co[3],
1072 const float prev_co[3],
1073 const float dist_px_sq,
1074 ListBase *hit_list)
1075{
1076 if (snap_to_flag &
1078 {
1079 if (prev_co) {
1080 copy_v3_v3(sctx->runtime.curr_co, prev_co);
1081 if (init_co) {
1082 copy_v3_v3(sctx->runtime.init_co, init_co);
1083 }
1084 else {
1085 snap_to_flag &= ~SCE_SNAP_INDIVIDUAL_NEAREST;
1086 }
1087 }
1088 else {
1090 }
1091 }
1092
1093 if (snap_to_flag == SCE_SNAP_TO_NONE) {
1094 return false;
1095 }
1096
1097 sctx->runtime.depsgraph = depsgraph;
1098 sctx->runtime.rv3d = nullptr;
1099 sctx->runtime.v3d = v3d;
1100 sctx->runtime.snap_to_flag = snap_to_flag;
1101 sctx->runtime.params = *params;
1102 sctx->runtime.params.occlusion_test = occlusion_test;
1104 occlusion_test;
1105 sctx->runtime.has_occlusion_plane = false;
1107 sctx->runtime.object_index = 0;
1108
1109 copy_v3_v3(sctx->runtime.ray_start, ray_start);
1110 copy_v3_v3(sctx->runtime.ray_dir, ray_dir);
1111
1112 if (mval) {
1113 copy_v2_v2(sctx->runtime.mval, mval);
1114 }
1115
1116 if (region) {
1117 const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
1118 sctx->runtime.win_size[0] = region->winx;
1119 sctx->runtime.win_size[1] = region->winy;
1120
1121 sctx->runtime.clip_planes.resize(2);
1122
1124 nullptr,
1125 nullptr,
1126 nullptr,
1127 nullptr,
1128 sctx->runtime.clip_planes[0],
1129 sctx->runtime.clip_planes[1]);
1130
1131 if (rv3d->rflag & RV3D_CLIPPING) {
1132 sctx->runtime.clip_planes.extend_unchecked(reinterpret_cast<const float4 *>(rv3d->clip), 4);
1133 }
1134
1135 sctx->runtime.rv3d = rv3d;
1136
1138 sctx->grid.use_init_co = init_co != nullptr;
1139 if (params->grid_size) {
1140 sctx->grid.size = params->grid_size;
1141 }
1142 if (!compare_m4m4(sctx->grid.persmat.ptr(), rv3d->persmat, FLT_EPSILON)) {
1143 sctx->grid.persmat = float4x4(rv3d->persmat);
1144 if (params->grid_size == 0.0f) {
1146 sctx->scene, sctx->runtime.v3d, region, nullptr);
1147 }
1148
1149 if (!sctx->grid.use_init_co) {
1150 memset(sctx->grid.planes, 0, sizeof(sctx->grid.planes));
1151 sctx->grid.planes[0][2] = 1.0f;
1152 if (math::abs(sctx->runtime.ray_dir[0]) < math::abs(sctx->runtime.ray_dir[1])) {
1153 sctx->grid.planes[1][1] = 1.0f;
1154 sctx->grid.planes[2][0] = 1.0f;
1155 }
1156 else {
1157 sctx->grid.planes[1][0] = 1.0f;
1158 sctx->grid.planes[2][1] = 1.0f;
1159 }
1160
1162 sctx->grid.planes[3], sctx->runtime.curr_co, rv3d->viewinv[2]);
1163 }
1164 }
1165 }
1166 }
1167
1168 sctx->ret.ray_depth_max = sctx->ret.ray_depth_max_in_front = ray_depth;
1169 sctx->ret.index = -1;
1170 sctx->ret.hit_list = hit_list;
1171 sctx->ret.ob = nullptr;
1172 sctx->ret.data = nullptr;
1173 sctx->ret.dist_px_sq = dist_px_sq;
1174
1175 return true;
1176}
1177
1179 Depsgraph *depsgraph,
1180 const View3D *v3d,
1181 const SnapObjectParams *params,
1182 const float ray_start[3],
1183 const float ray_normal[3],
1184 float *ray_depth,
1185 float r_loc[3],
1186 float r_no[3],
1187 int *r_index,
1188 const Object **r_ob,
1189 float r_obmat[4][4])
1190{
1192 depsgraph,
1193 nullptr,
1194 v3d,
1196 params->occlusion_test,
1197 params,
1198 ray_start,
1199 ray_normal,
1200 !ray_depth || *ray_depth == -1.0f ? BVH_RAYCAST_DIST_MAX :
1201 *ray_depth,
1202 nullptr,
1203 nullptr,
1204 nullptr,
1205 0,
1206 nullptr))
1207 {
1208 return false;
1209 }
1210
1211 if (raycastObjects(sctx)) {
1212 copy_v3_v3(r_loc, sctx->ret.loc);
1213 if (r_no) {
1214 copy_v3_v3(r_no, sctx->ret.no);
1215 }
1216 if (r_index) {
1217 *r_index = sctx->ret.index;
1218 }
1219 if (r_ob) {
1220 *r_ob = sctx->ret.ob;
1221 }
1222 if (r_obmat) {
1223 copy_m4_m4(r_obmat, sctx->ret.obmat.ptr());
1224 }
1225 if (ray_depth) {
1226 *ray_depth = sctx->ret.ray_depth_max;
1227 }
1228 return true;
1229 }
1230 return false;
1231}
1232
1234 Depsgraph *depsgraph,
1235 const View3D *v3d,
1236 const SnapObjectParams *params,
1237 const float ray_start[3],
1238 const float ray_normal[3],
1239 float ray_depth,
1240 bool sort,
1241 ListBase *r_hit_list)
1242{
1244 depsgraph,
1245 nullptr,
1246 v3d,
1248 params->occlusion_test,
1249 params,
1250 ray_start,
1251 ray_normal,
1252 ray_depth == -1.0f ? BVH_RAYCAST_DIST_MAX : ray_depth,
1253 nullptr,
1254 nullptr,
1255 nullptr,
1256 0,
1257 r_hit_list))
1258 {
1259 return false;
1260 }
1261
1262#ifndef NDEBUG
1263 float ray_depth_prev = sctx->ret.ray_depth_max;
1264#endif
1265 if (raycastObjects(sctx)) {
1266 if (sort) {
1267 BLI_listbase_sort(r_hit_list, hit_depth_cmp);
1268 }
1269 /* Meant to be read-only for 'all' hits, ensure it is. */
1270#ifndef NDEBUG
1271 BLI_assert(ray_depth_prev == sctx->ret.ray_depth_max);
1272#endif
1273 return true;
1274 }
1275 return false;
1276}
1277
1279 Depsgraph *depsgraph,
1280 const View3D *v3d,
1281 const SnapObjectParams *params,
1282 const float ray_start[3],
1283 const float ray_normal[3],
1284 float *ray_depth,
1285 float r_co[3],
1286 float r_no[3])
1287{
1288 return snap_object_project_ray_ex(sctx,
1289 depsgraph,
1290 v3d,
1291 params,
1292 ray_start,
1293 ray_normal,
1294 ray_depth,
1295 r_co,
1296 r_no,
1297 nullptr,
1298 nullptr,
1299 nullptr);
1300}
1301
1303 Depsgraph *depsgraph,
1304 const ARegion *region,
1305 const View3D *v3d,
1306 eSnapMode snap_to_flag,
1307 const SnapObjectParams *params,
1308 const float init_co[3],
1309 const float mval[2],
1310 const float prev_co[3],
1311 float *dist_px,
1312 float r_loc[3],
1313 float r_no[3],
1314 int *r_index,
1315 const Object **r_ob,
1316 float r_obmat[4][4],
1317 float r_face_nor[3])
1318{
1319 eSnapMode retval = SCE_SNAP_TO_NONE;
1320 float ray_depth_max = BVH_RAYCAST_DIST_MAX;
1321 bool use_occlusion_plane = false;
1322
1323 /* It is required `mval` to calculate the occlusion plane. */
1324 if (mval && (snap_to_flag & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID))) {
1325 if (params->occlusion_test == SNAP_OCCLUSION_AS_SEEM) {
1326 use_occlusion_plane = !XRAY_ENABLED(v3d);
1327 }
1328 else if (params->occlusion_test == SNAP_OCCLUSION_ALWAYS) {
1329 use_occlusion_plane = true;
1330 }
1331 }
1332
1333 if (use_occlusion_plane || (snap_to_flag & SCE_SNAP_TO_FACE) ||
1334 /* Snap to Grid requires `ray_start` and `ray_dir`. */
1335 (snap_to_flag & SCE_SNAP_TO_GRID))
1336 {
1337 /* Calculate the direction (`ray_dir`) and starting point (`ray_start`) of the ray from the
1338 * viewport to a 3D point under the mouse cursor (`mval`), taking into account potential view
1339 * clipping. */
1340
1341 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
1342 float3 ray_end;
1344 region,
1345 v3d,
1346 mval,
1347 false,
1348 nullptr,
1349 sctx->runtime.ray_dir,
1350 sctx->runtime.ray_start,
1351 ray_end);
1352
1353 if (rv3d->rflag & RV3D_CLIPPING) {
1355 sctx->runtime.ray_start, ray_end, rv3d->clip, 6, sctx->runtime.ray_start, ray_end))
1356 {
1357 ray_depth_max = math::dot(ray_end - sctx->runtime.ray_start, sctx->runtime.ray_dir);
1358 }
1359 else {
1360 snap_to_flag &= ~SCE_SNAP_TO_FACE;
1361 use_occlusion_plane = false;
1362 }
1363 }
1364 }
1365
1367 depsgraph,
1368 region,
1369 v3d,
1370 snap_to_flag,
1371 use_occlusion_plane ? params->occlusion_test :
1373 params,
1374 sctx->runtime.ray_start,
1375 sctx->runtime.ray_dir,
1376 ray_depth_max,
1377 mval,
1378 init_co,
1379 prev_co,
1380 dist_px ? square_f(*dist_px) : FLT_MAX,
1381 nullptr))
1382 {
1383 return retval;
1384 }
1385
1386#ifdef DEBUG_SNAP_TIME
1387 const timeit::TimePoint start_ = timeit::Clock::now();
1388#endif
1389
1390 snap_to_flag = sctx->runtime.snap_to_flag;
1391
1393
1394 bool has_hit = false;
1395
1396 /* NOTE: if both face ray-cast and face nearest are enabled, first find result of nearest, then
1397 * override with ray-cast. */
1398 if ((snap_to_flag & SCE_SNAP_INDIVIDUAL_NEAREST) && !has_hit) {
1399 has_hit = nearestWorldObjects(sctx);
1400
1401 if (has_hit) {
1403 }
1404 }
1405
1406 if (use_occlusion_plane || (snap_to_flag & SCE_SNAP_TO_FACE)) {
1407 has_hit = raycastObjects(sctx);
1408
1409 if (has_hit) {
1410 if (r_face_nor) {
1411 copy_v3_v3(r_face_nor, sctx->ret.no);
1412 }
1413
1414 if (snap_to_flag & SCE_SNAP_TO_FACE) {
1415 retval |= SCE_SNAP_TO_FACE;
1416 }
1417 }
1418 }
1419
1420 if (snap_to_flag & (SCE_SNAP_TO_POINT | SNAP_TO_EDGE_ELEMENTS)) {
1421 eSnapMode elem_test, elem = SCE_SNAP_TO_NONE;
1422
1423 /* Remove what has already been computed. */
1425
1426 if (use_occlusion_plane && has_hit) {
1427 /* Compute the new clip_pane but do not add it yet. */
1428 BLI_ASSERT_UNIT_V3(sctx->ret.no);
1430 sctx->runtime.ray_dir, sctx->ret.loc, sctx->ret.no);
1431
1432 /* First, snap to the geometry of the polygon obtained via raycast.
1433 * This is necessary because the occlusion plane may "occlude" part of the polygon's
1434 * geometry. It also reduces the snap distance, optimizing the process.
1435 *
1436 * Note that if 'Snap to Edge Midpoint' or 'Snap to Edge Perpendicular' is selected, 'Snap to
1437 * Edge' will be returned instead.
1438 * This is because the same point can be tested in `snapObjectsRay` and fail this time due to
1439 * a mismatched snap distance, also resulting in snapping to the edge instead. */
1440 elem_test = snap_polygon(sctx, sctx->runtime.snap_to_flag);
1441 if (elem_test) {
1442 elem = elem_test;
1443 }
1444
1445 /* Add the new clip plane. */
1446 sctx->runtime.has_occlusion_plane = true;
1447 }
1448
1449 /* `snapObjectsRay` does 'Snap to Edge' instead of 'Snap to Edge Midpoint' or 'Snap to Edge
1450 * Perpendicular'. These points will be tested in the `snap_edge_points` function. */
1451 elem_test = snapObjectsRay(sctx);
1452 if (elem_test != SCE_SNAP_TO_NONE) {
1453 elem = elem_test;
1454 }
1455
1456 if ((elem == SCE_SNAP_TO_EDGE) &&
1457 (snap_to_flag &
1459 {
1460 elem = snap_edge_points(sctx, square_f(*dist_px));
1461 }
1462
1463 if (elem != SCE_SNAP_TO_NONE) {
1464 retval = elem & snap_to_flag;
1465 }
1466 }
1467
1468 if ((retval == SCE_SNAP_TO_NONE) && (snap_to_flag & SCE_SNAP_TO_GRID)) {
1469 if (snap_grid(sctx)) {
1470 retval = SCE_SNAP_TO_GRID;
1471 }
1472 }
1473
1474 if (retval != SCE_SNAP_TO_NONE) {
1475 copy_v3_v3(r_loc, sctx->ret.loc);
1476 if (r_no) {
1477 copy_v3_v3(r_no, sctx->ret.no);
1478 }
1479 if (r_ob) {
1480 *r_ob = sctx->ret.ob;
1481 }
1482 if (r_obmat) {
1483 copy_m4_m4(r_obmat, sctx->ret.obmat.ptr());
1484 }
1485 if (r_index) {
1486 *r_index = sctx->ret.index;
1487 }
1488
1489 if (dist_px) {
1490 *dist_px = math::sqrt(sctx->ret.dist_px_sq);
1491 }
1492 }
1493
1494#ifdef DEBUG_SNAP_TIME
1495 duration_ += timeit::Clock::now() - start_;
1496 total_count_++;
1497#endif
1498
1499 return retval;
1500}
1501
1503 Depsgraph *depsgraph,
1504 const ARegion *region,
1505 const View3D *v3d,
1506 const eSnapMode snap_to,
1507 const SnapObjectParams *params,
1508 const float init_co[3],
1509 const float mval[2],
1510 const float prev_co[3],
1511 float *dist_px,
1512 float r_loc[3],
1513 float r_no[3])
1514{
1516 depsgraph,
1517 region,
1518 v3d,
1519 snap_to,
1520 params,
1521 init_co,
1522 mval,
1523 prev_co,
1524 dist_px,
1525 r_loc,
1526 r_no,
1527 nullptr,
1528 nullptr,
1529 nullptr,
1530 nullptr);
1531}
1532
1534 Depsgraph *depsgraph,
1535 const ARegion *region,
1536 const View3D *v3d,
1537 const SnapObjectParams *params,
1538 const float mval[2],
1539 float ray_depth,
1540 bool sort,
1541 ListBase *r_hit_list)
1542{
1543 float3 ray_start, ray_normal, ray_end;
1544 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
1545
1547 depsgraph, region, v3d, mval, false, nullptr, ray_normal, ray_start, ray_end))
1548 {
1549 return false;
1550 }
1551
1552 if ((rv3d->rflag & RV3D_CLIPPING) &&
1553 clip_segment_v3_plane_n(ray_start, ray_end, rv3d->clip, 6, ray_start, ray_end))
1554 {
1555 float ray_depth_max = math::dot(ray_end - ray_start, ray_normal);
1556 if ((ray_depth == -1.0f) || (ray_depth > ray_depth_max)) {
1557 ray_depth = ray_depth_max;
1558 }
1559 }
1560
1562 sctx, depsgraph, v3d, params, ray_start, ray_normal, ray_depth, sort, r_hit_list);
1563}
1564
1565#ifdef DEBUG_SNAP_TIME
1567{
1568 std::cout << "Average snapping time: ";
1569 std::cout << std::fixed << duration_.count() / 1.0e6 << " ms";
1570 std::cout << '\n';
1571
1572 duration_ = timeit::Nanoseconds::zero();
1573 total_count_ = 0;
1574}
1575#endif
1576
1578
1579} // namespace blender::ed::transform
void free_object_duplilist(ListBase *lb)
ListBase * object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob, blender::Set< const Object * > *include_objects=nullptr)
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
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
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
void(*)(void *userdata, int index, const float co[3], BVHTreeNearest *nearest) BVHTree_NearestPointCallback
#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_DUPLI
@ OB_EMPTY
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_ARMATURE
@ OB_LAMP
@ OB_MESH
@ OB_CURVES_LEGACY
@ OB_DRAW_IN_FRONT
@ BA_WAS_SEL
@ BA_TRANSFORM_LOCKED_IN_PLACE
@ BA_SNAP_FIX_DEPS_FIASCO
#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 this
#define GS(a)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bool object_has_geometry_set_instances(const Object &object)
eSnapMode(*)(SnapObjectContext *sctx, const Object *ob_eval, const ID *ob_data, const float4x4 &obmat, bool is_object_active, bool use_hide) IterSnapObjsCallback
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)
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)
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 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)
static ID * data_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
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:404
char name[66]
Definition DNA_ID.h:415
MeshRuntimeHandle * runtime
short transflag
float persmat[4][4]
float clip[6][4]
float viewinv[4][4]
const c_style_mat & ptr() const
struct blender::ed::transform::SnapObjectContext::@274174034202226355225361265035000052335134256331::@101325324177307207354241331175336004040234247152 edit_mesh
struct blender::ed::transform::SnapObjectContext::@274174034202226355225361265035000052335134256331 callbacks
struct blender::ed::transform::SnapObjectContext::@030342217121254226117135200143045314224065016366 runtime
struct blender::ed::transform::SnapObjectContext::@270155162206021211055035123254365201020344241223 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::@037021255062237271074027012112350205353065203016 grid
Map< const ID *, std::unique_ptr< SnapCache > > editmesh_caches
Vector< float4, MAX_CLIPPLANE_LEN > clip_planes
i
Definition text_draw.cc:230
#define SNAP_TO_EDGE_ELEMENTS