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