Changeset View
Standalone View
source/blender/editors/transform/transform_snap_object.cc
| Show First 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Utilities | /** \name Utilities | ||||
| * \{ */ | * \{ */ | ||||
| /** | /** | ||||
| * Mesh used for snapping. | * Mesh used for snapping. | ||||
| * | * | ||||
| * - When the return value is null the `BKE_editmesh_from_object(ob_eval)` should be used. | * - When the return value is null the `BKE_editmesh_from_object(ob_eval)` should be used. | ||||
| * - In rare cases there is no evaluated mesh available and a null result doesn't imply an | * - In rare cases there is no evaluated mesh available and a null result doesn't imply an | ||||
mano-wii: Why is this necessary?
If I'm not mistaken, the edit_mesh of ob_eval and ob_orig should be the… | |||||
Done Inline Actionshonestly, I'm not sure if this is necessary, as I don't fully understand the underlying systems. I was only following what had been used in this code in the past, although it was used as a one-liner (BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));). maybe someone else can shine some light? gfxcoder: honestly, I'm not sure if this is necessary, as I don't fully understand the underlying systems. | |||||
| * edit-mesh, so callers need to account for a null edit-mesh too, see: T96536. | * edit-mesh, so callers need to account for a null edit-mesh too, see: T96536. | ||||
| */ | */ | ||||
| static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) | static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) | ||||
| { | { | ||||
| const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); | const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); | ||||
| bool use_hide = false; | bool use_hide = false; | ||||
| if (BKE_object_is_in_editmode(ob_eval)) { | if (BKE_object_is_in_editmode(ob_eval)) { | ||||
| if (edit_mode_type == SNAP_GEOM_EDIT) { | if (edit_mode_type == SNAP_GEOM_EDIT) { | ||||
| ▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | if (init) { | ||||
| sod->treedata_editmesh.em = em; | sod->treedata_editmesh.em = em; | ||||
| sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval); | sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval); | ||||
| snap_editmesh_minmax(sctx, em->bm, sod->min, sod->max); | snap_editmesh_minmax(sctx, em->bm, sod->min, sod->max); | ||||
| } | } | ||||
| return sod; | return sod; | ||||
| } | } | ||||
| static BVHTreeFromMesh *snap_object_data_mesh_treedata_get(SnapObjectContext *sctx, | |||||
| Object *ob_eval, | |||||
| const Mesh *me_eval, | |||||
| bool use_hide) | |||||
| { | |||||
| SnapData_Mesh *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide); | |||||
| return &sod->treedata_mesh; | |||||
| } | |||||
| static BVHTreeFromEditMesh *snap_object_data_editmesh_treedata_get(SnapObjectContext *sctx, | |||||
| Object *ob_eval, | |||||
| BMEditMesh *em) | |||||
| { | |||||
| SnapData_EditMesh *sod = snap_object_data_editmesh_get(sctx, ob_eval, em); | |||||
| BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh; | |||||
| if (treedata->tree == nullptr) { | |||||
| /* Operators only update the editmesh looptris of the original mesh. */ | |||||
| BLI_assert(sod->treedata_editmesh.em == | |||||
| BKE_editmesh_from_object(DEG_get_original_object(ob_eval))); | |||||
| em = sod->treedata_editmesh.em; | |||||
| if (sctx->callbacks.edit_mesh.test_face_fn) { | |||||
| BMesh *bm = em->bm; | |||||
| BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri); | |||||
| BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em->tottri, __func__); | |||||
| int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( | |||||
| bm, | |||||
| elem_mask, | |||||
| sctx->callbacks.edit_mesh.test_face_fn, | |||||
| sctx->callbacks.edit_mesh.user_data); | |||||
| bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6); | |||||
| MEM_freeN(elem_mask); | |||||
| } | |||||
| else { | |||||
| /* Only cache if BVH-tree is created without a mask. | |||||
| * This helps keep a standardized BVH-tree in cache. */ | |||||
| BKE_bvhtree_from_editmesh_get(treedata, | |||||
| em, | |||||
| 4, | |||||
| BVHTREE_FROM_EM_LOOPTRI, | |||||
| &sod->mesh_runtime->bvh_cache, | |||||
| static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex)); | |||||
| } | |||||
| } | |||||
| if (treedata == nullptr || treedata->tree == nullptr) { | |||||
| return nullptr; | |||||
| } | |||||
| return treedata; | |||||
| } | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Iterator | /** \name Iterator | ||||
| * \{ */ | * \{ */ | ||||
| using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx, | using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| Object *ob_eval, | Object *ob_eval, | ||||
| const float obmat[4][4], | const float obmat[4][4], | ||||
| bool is_object_active, | bool is_object_active, | ||||
| void *data); | void *data); | ||||
| static bool snap_object_is_snappable(const SnapObjectContext *sctx, | static bool snap_object_is_snappable(const SnapObjectContext *sctx, | ||||
| const eSnapTargetSelect snap_select, | const eSnapTargetSelect snap_target_select, | ||||
| const Base *base_act, | const Base *base_act, | ||||
| const Base *base, | const Base *base) | ||||
| const bool is_in_object_mode) | |||||
| { | { | ||||
| if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { | if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if ((snap_select == SCE_SNAP_TARGET_ALL) || (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) { | if ((snap_target_select == SCE_SNAP_TARGET_ALL) || | ||||
| (base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE)) { | |||||
| return true; | return true; | ||||
| } | } | ||||
| if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) { | if (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (snap_select == SCE_SNAP_TARGET_NOT_ACTIVE) { | /* get base attributes */ | ||||
| return base_act != base; | const bool is_active = (base_act == base); | ||||
| const bool is_selected = (base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL); | |||||
| const bool is_edited = (base->object->mode == OB_MODE_EDIT); | |||||
| const bool is_selectable = (base->flag & BASE_SELECTABLE); | |||||
| const bool is_in_object_mode = (base_act == NULL) || (base_act->object->mode == OB_MODE_OBJECT); | |||||
| if (is_edited) { | |||||
| if (is_active) { | |||||
| if (snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| else { | |||||
| if (snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) { | |||||
| return false; | |||||
| } | |||||
| } | } | ||||
| if (snap_select == SCE_SNAP_TARGET_NOT_EDITED) { | |||||
| return base->object->mode != OB_MODE_EDIT; | |||||
| } | } | ||||
| if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) { | if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) { | ||||
| if (is_in_object_mode) { | return false; | ||||
| return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)); | |||||
| } | } | ||||
| /* What is selectable or not is part of the object and depends on the mode. */ | if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) { | ||||
| return true; | return false; | ||||
| } | } | ||||
| if (snap_select == SCE_SNAP_TARGET_ONLY_SELECTABLE) { | if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_in_object_mode && is_selected) { | ||||
| return (base->flag & BASE_SELECTABLE) != 0; | /* What is selectable or not is part of the object and depends on the mode. */ | ||||
| return false; | |||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| /** | /** | ||||
| * Walks through all objects in the scene to create the list of objects to snap. | * Walks through all objects in the scene to create the list of objects to snap. | ||||
| */ | */ | ||||
| static void iter_snap_objects(SnapObjectContext *sctx, | static void iter_snap_objects(SnapObjectContext *sctx, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| IterSnapObjsCallback sob_callback, | IterSnapObjsCallback sob_callback, | ||||
| void *data) | void *data) | ||||
| { | { | ||||
| ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph); | ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph); | ||||
| const eSnapTargetSelect snap_target_select = params->snap_target_select; | const eSnapTargetSelect snap_target_select = params->snap_target_select; | ||||
| Base *base_act = view_layer->basact; | Base *base_act = view_layer->basact; | ||||
| const bool is_in_object_mode = !base_act || base_act->object->mode == OB_MODE_OBJECT; | |||||
| LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { | LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { | ||||
| if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base, is_in_object_mode)) { | if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const bool is_object_active = (base == base_act); | const bool is_object_active = (base == base_act); | ||||
| Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); | Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); | ||||
| if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { | if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { | ||||
| ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval); | ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval); | ||||
| LISTBASE_FOREACH (DupliObject *, dupli_ob, lb) { | LISTBASE_FOREACH (DupliObject *, dupli_ob, lb) { | ||||
| ▲ Show 20 Lines • Show All 359 Lines • ▼ Show 20 Lines | if (len_diff > 400.0f) { | ||||
| len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ | len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ | ||||
| madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff); | madd_v3_v3fl(ray_start_local, ray_normal_local, len_diff); | ||||
| local_depth -= len_diff; | local_depth -= len_diff; | ||||
| } | } | ||||
| else { | else { | ||||
| len_diff = 0.0f; | len_diff = 0.0f; | ||||
| } | } | ||||
| BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh; | BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em); | ||||
| if (treedata == nullptr) { | |||||
| if (treedata->tree == nullptr) { | |||||
| em = sod->treedata_editmesh.em; | |||||
| if (sctx->callbacks.edit_mesh.test_face_fn) { | |||||
| BMesh *bm = em->bm; | |||||
| BLI_assert(poly_to_tri_count(bm->totface, bm->totloop) == em->tottri); | |||||
| BLI_bitmap *elem_mask = BLI_BITMAP_NEW(em->tottri, __func__); | |||||
| int looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( | |||||
| bm, | |||||
| elem_mask, | |||||
| sctx->callbacks.edit_mesh.test_face_fn, | |||||
| sctx->callbacks.edit_mesh.user_data); | |||||
| bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6); | |||||
| MEM_freeN(elem_mask); | |||||
| } | |||||
| else { | |||||
| /* Only cache if bvhtree is created without a mask. | |||||
| * This helps keep a standardized bvhtree in cache. */ | |||||
| BKE_bvhtree_from_editmesh_get(treedata, | |||||
| em, | |||||
| 4, | |||||
| BVHTREE_FROM_EM_LOOPTRI, | |||||
| &sod->mesh_runtime->bvh_cache, | |||||
| static_cast<ThreadMutex *>(sod->mesh_runtime->eval_mutex)); | |||||
| } | |||||
| if (treedata->tree == nullptr) { | |||||
| return retval; | return retval; | ||||
| } | } | ||||
| } | |||||
| float timat[3][3]; /* transpose inverse matrix for normals */ | float timat[3][3]; /* transpose inverse matrix for normals */ | ||||
| transpose_m3_m4(timat, imat); | transpose_m3_m4(timat, imat); | ||||
| if (r_hit_list) { | if (r_hit_list) { | ||||
| RayCastAll_Data data; | RayCastAll_Data data; | ||||
| data.bvhdata = treedata; | data.bvhdata = treedata; | ||||
| ▲ Show 20 Lines • Show All 197 Lines • ▼ Show 20 Lines | |||||
| * elements deeper than this value will be ignored. | * elements deeper than this value will be ignored. | ||||
| * | * | ||||
| * Output Args | * Output Args | ||||
| * ----------- | * ----------- | ||||
| * | * | ||||
| * \param r_loc: Hit location. | * \param r_loc: Hit location. | ||||
| * \param r_no: Hit normal (optional). | * \param r_no: Hit normal (optional). | ||||
| * \param r_index: Hit index or -1 when no valid index is found. | * \param r_index: Hit index or -1 when no valid index is found. | ||||
| * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`). | * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE_RAYCAST`). | ||||
| * \param r_ob: Hit object. | * \param r_ob: Hit object. | ||||
| * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). | * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). | ||||
| * \param r_hit_list: List of #SnapObjectHitDepth (caller must free). | * \param r_hit_list: List of #SnapObjectHitDepth (caller must free). | ||||
| */ | */ | ||||
| static bool raycastObjects(SnapObjectContext *sctx, | static bool raycastObjects(SnapObjectContext *sctx, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| const float ray_start[3], | const float ray_start[3], | ||||
| const float ray_dir[3], | const float ray_dir[3], | ||||
| Show All 35 Lines | static bool raycastObjects(SnapObjectContext *sctx, | ||||
| iter_snap_objects(sctx, params, raycast_obj_fn, &data); | iter_snap_objects(sctx, params, raycast_obj_fn, &data); | ||||
| return data.ret; | return data.ret; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Surface Snap Funcs | |||||
| * \{ */ | |||||
| struct NearestWorldObjUserData { | |||||
| const float *init_co; | |||||
| const float *curr_co; | |||||
| /* return args */ | |||||
| float *r_loc; | |||||
| float *r_no; | |||||
| int *r_index; | |||||
| float r_dist_sq; | |||||
| Object **r_ob; | |||||
| float (*r_obmat)[4]; | |||||
| ListBase *r_hit_list; | |||||
| bool ret; | |||||
| }; | |||||
| static void nearest_world_tree_co(BVHTree *tree, | |||||
| BVHTree_NearestPointCallback nearest_cb, | |||||
| void *treedata, | |||||
| float co[3], | |||||
| float r_co[3], | |||||
| float r_no[3], | |||||
| int *r_index, | |||||
| float *r_dist_sq) | |||||
| { | |||||
| BVHTreeNearest nearest = {}; | |||||
| nearest.index = -1; | |||||
| copy_v3_fl(nearest.co, FLT_MAX); | |||||
| nearest.dist_sq = FLT_MAX; | |||||
| BLI_bvhtree_find_nearest(tree, co, &nearest, nearest_cb, treedata); | |||||
| if (r_co) { | |||||
| copy_v3_v3(r_co, nearest.co); | |||||
| } | |||||
| if (r_no) { | |||||
| copy_v3_v3(r_no, nearest.no); | |||||
| } | |||||
| if (r_index) { | |||||
| *r_index = nearest.index; | |||||
| } | |||||
| if (r_dist_sq) { | |||||
| float diff[3]; | |||||
| sub_v3_v3v3(diff, co, nearest.co); | |||||
| *r_dist_sq = len_squared_v3(diff); | |||||
| } | |||||
| } | |||||
| static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx), | |||||
| const struct SnapObjectParams *params, | |||||
| BVHTree *tree, | |||||
| BVHTree_NearestPointCallback nearest_cb, | |||||
| void *treedata, | |||||
| const float (*obmat)[4], | |||||
| const float init_co[3], | |||||
| const float curr_co[3], | |||||
| float *r_dist_sq, | |||||
| float *r_loc, | |||||
| float *r_no, | |||||
| int *r_index) | |||||
| { | |||||
| if (curr_co == nullptr || init_co == nullptr) { | |||||
| /* No location to work with, so just return. */ | |||||
Not Done Inline ActionsThis comment should be expanded as I'm not sure what to make of it. Is this something that ever happens in typical usage? If not, perhaps a BLI_assert_unreachable() can be used here so developers will be alerted if it ever happens. campbellbarton: This comment should be expanded as I'm not sure what to make of it.
Is this something that… | |||||
Done Inline ActionsI'm not sure that this should not happen, so I just left it as a return. updated comment gfxcoder: I'm not sure that this should not happen, so I just left it as a return. updated comment | |||||
| return false; | |||||
| } | |||||
| float imat[4][4]; | |||||
| invert_m4_m4(imat, obmat); | |||||
| float timat[3][3]; /* transpose inverse matrix for normals */ | |||||
| transpose_m3_m4(timat, imat); | |||||
| /* compute offset between init co and prev co in local space */ | |||||
| float init_co_local[3], curr_co_local[3]; | |||||
| float delta_local[3]; | |||||
| mul_v3_m4v3(init_co_local, imat, init_co); | |||||
| mul_v3_m4v3(curr_co_local, imat, curr_co); | |||||
| sub_v3_v3v3(delta_local, curr_co_local, init_co_local); | |||||
| float dist_sq; | |||||
| if (params->keep_on_same_target) { | |||||
| nearest_world_tree_co( | |||||
| tree, nearest_cb, treedata, init_co_local, nullptr, nullptr, nullptr, &dist_sq); | |||||
| } | |||||
| else { | |||||
| /* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain | |||||
| * the answer. We could return immediately after updating r_loc, r_no, r_index, but that would | |||||
| * also complicate the code. Foregoing slight optimization for code clarity. */ | |||||
| nearest_world_tree_co( | |||||
| tree, nearest_cb, treedata, curr_co_local, nullptr, nullptr, nullptr, &dist_sq); | |||||
| } | |||||
| if (*r_dist_sq <= dist_sq) { | |||||
| return false; | |||||
| } | |||||
| *r_dist_sq = dist_sq; | |||||
| /* scale to make `snap_face_nearest_steps` steps */ | |||||
| float step_scale_factor = 1.0f / max_ff(1.0f, (float)params->face_nearest_steps); | |||||
| mul_v3_fl(delta_local, step_scale_factor); | |||||
| float co_local[3]; | |||||
| float no_local[3]; | |||||
| int index; | |||||
| copy_v3_v3(co_local, init_co_local); | |||||
| for (int i = 0; i < params->face_nearest_steps; i++) { | |||||
| add_v3_v3(co_local, delta_local); | |||||
| nearest_world_tree_co( | |||||
| tree, nearest_cb, treedata, co_local, co_local, no_local, &index, nullptr); | |||||
| } | |||||
| mul_v3_m4v3(r_loc, obmat, co_local); | |||||
| if (r_no) { | |||||
| mul_v3_m3v3(r_no, timat, no_local); | |||||
| normalize_v3(r_no); | |||||
| } | |||||
| if (r_index) { | |||||
| *r_index = index; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool nearest_world_mesh(SnapObjectContext *sctx, | |||||
| const struct SnapObjectParams *params, | |||||
| Object *ob_eval, | |||||
| const Mesh *me_eval, | |||||
| const float (*obmat)[4], | |||||
| bool use_hide, | |||||
| const float init_co[3], | |||||
| const float curr_co[3], | |||||
| float *r_dist_sq, | |||||
| float *r_loc, | |||||
| float *r_no, | |||||
| int *r_index) | |||||
| { | |||||
| BVHTreeFromMesh *treedata = snap_object_data_mesh_treedata_get(sctx, ob_eval, me_eval, use_hide); | |||||
| if (treedata == nullptr || treedata->tree == nullptr) { | |||||
| return false; | |||||
| } | |||||
| return nearest_world_tree(sctx, | |||||
| params, | |||||
| treedata->tree, | |||||
| treedata->nearest_callback, | |||||
| treedata, | |||||
| obmat, | |||||
| init_co, | |||||
| curr_co, | |||||
| r_dist_sq, | |||||
| r_loc, | |||||
| r_no, | |||||
| r_index); | |||||
| } | |||||
| static bool nearest_world_editmesh(SnapObjectContext *sctx, | |||||
| const struct SnapObjectParams *params, | |||||
| Object *ob_eval, | |||||
| BMEditMesh *em, | |||||
| const float (*obmat)[4], | |||||
| const float init_co[3], | |||||
| const float curr_co[3], | |||||
| float *r_dist_sq, | |||||
| float *r_loc, | |||||
| float *r_no, | |||||
| int *r_index) | |||||
| { | |||||
| BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em); | |||||
| if (treedata == nullptr || treedata->tree == nullptr) { | |||||
| return false; | |||||
| } | |||||
| return nearest_world_tree(sctx, | |||||
| params, | |||||
| treedata->tree, | |||||
| treedata->nearest_callback, | |||||
| treedata, | |||||
| obmat, | |||||
| init_co, | |||||
| curr_co, | |||||
| r_dist_sq, | |||||
| r_loc, | |||||
| r_no, | |||||
| r_index); | |||||
| } | |||||
| static void nearest_world_object_fn(SnapObjectContext *sctx, | |||||
| const struct SnapObjectParams *params, | |||||
| Object *ob_eval, | |||||
| const float obmat[4][4], | |||||
| bool is_object_active, | |||||
| void *data) | |||||
| { | |||||
| struct NearestWorldObjUserData *dt = static_cast<NearestWorldObjUserData *>(data); | |||||
| bool retval = false; | |||||
| switch (ob_eval->type) { | |||||
| case OB_MESH: { | |||||
| const eSnapEditType edit_mode_type = params->edit_mode_type; | |||||
| bool use_hide = false; | |||||
| const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); | |||||
| if (me_eval) { | |||||
| retval = nearest_world_mesh(sctx, | |||||
| params, | |||||
| ob_eval, | |||||
| me_eval, | |||||
| obmat, | |||||
| use_hide, | |||||
| dt->init_co, | |||||
| dt->curr_co, | |||||
| &dt->r_dist_sq, | |||||
| dt->r_loc, | |||||
| dt->r_no, | |||||
| dt->r_index); | |||||
| } | |||||
| else { | |||||
| BMEditMesh *em = BKE_editmesh_from_object(ob_eval); | |||||
| BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), | |||||
| "Make sure there is only one pointer for looptris"); | |||||
| retval = nearest_world_editmesh(sctx, | |||||
| params, | |||||
| ob_eval, | |||||
| em, | |||||
| obmat, | |||||
| dt->init_co, | |||||
| dt->curr_co, | |||||
| &dt->r_dist_sq, | |||||
| dt->r_loc, | |||||
| dt->r_no, | |||||
| dt->r_index); | |||||
| } | |||||
| break; | |||||
| } | |||||
| case OB_CURVES_LEGACY: | |||||
| case OB_SURF: | |||||
| case OB_FONT: | |||||
| if (!is_object_active) { | |||||
| const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); | |||||
| if (me_eval) { | |||||
| retval = nearest_world_mesh(sctx, | |||||
| params, | |||||
| ob_eval, | |||||
| me_eval, | |||||
| obmat, | |||||
| false, | |||||
| dt->init_co, | |||||
| dt->curr_co, | |||||
| &dt->r_dist_sq, | |||||
| dt->r_loc, | |||||
| dt->r_no, | |||||
| dt->r_index); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| if (retval) { | |||||
| if (dt->r_ob) { | |||||
| *dt->r_ob = ob_eval; | |||||
| } | |||||
| if (dt->r_obmat) { | |||||
| copy_m4_m4(dt->r_obmat, obmat); | |||||
| } | |||||
| dt->ret = true; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Main Nearest World Surface Function | |||||
| * =================================== | |||||
Not Done Inline ActionsUnderline should match length. campbellbarton: Underline should match length. | |||||
Done Inline Actionsdone gfxcoder: done | |||||
| * | |||||
| * Walks through all objects in the scene to find the nearest location on target surface. | |||||
| * | |||||
| * \param sctx: Snap context to store data. | |||||
| * \param params: Settings for snapping. | |||||
| * \param init_co: Initial location of source point. | |||||
| * \param prev_co: Current location of source point after transformation but before snapping. | |||||
| * | |||||
| * Output Args | |||||
| * ----------- | |||||
| * | |||||
| * \param r_loc: Location of nearest point on target surface. | |||||
| * \param r_no: Normal of nearest point on target surface. | |||||
| * \param r_index: Index of nearest polygon on target surface. | |||||
| * \param r_ob: Nearest target object. | |||||
| * \param r_obmat: Nearest target matrix (may not be #Object.obmat with dupli-instances). | |||||
| */ | |||||
| static bool nearestWorldObjects(SnapObjectContext *sctx, | |||||
| const struct SnapObjectParams *params, | |||||
| const float init_co[3], | |||||
| const float curr_co[3], | |||||
| float *r_loc /* NOLINT */, | |||||
| float *r_no /* NOLINT */, | |||||
| int *r_index /* NOLINT */, | |||||
| Object **r_ob, | |||||
| float r_obmat[4][4]) | |||||
| { | |||||
| NearestWorldObjUserData data = {}; | |||||
| data.init_co = init_co; | |||||
| data.curr_co = curr_co; | |||||
| data.r_loc = r_loc; | |||||
| data.r_no = r_no; | |||||
| data.r_index = r_index; | |||||
| data.r_dist_sq = FLT_MAX; | |||||
| data.r_ob = r_ob; | |||||
| data.r_obmat = r_obmat; | |||||
| data.ret = false; | |||||
| iter_snap_objects(sctx, params, nearest_world_object_fn, &data); | |||||
| return data.ret; | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Snap Nearest utilities | /** \name Snap Nearest utilities | ||||
| * \{ */ | * \{ */ | ||||
| /* Test BoundBox */ | /* Test BoundBox */ | ||||
| static bool snap_bound_box_check_dist(const float min[3], | static bool snap_bound_box_check_dist(const float min[3], | ||||
| const float max[3], | const float max[3], | ||||
| const float lpmat[4][4], | const float lpmat[4][4], | ||||
| const float win_size[2], | const float win_size[2], | ||||
| ▲ Show 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | static eSnapMode snapArmature(SnapObjectContext *sctx, | ||||
| float *dist_px, | float *dist_px, | ||||
| /* return args */ | /* return args */ | ||||
| float r_loc[3], | float r_loc[3], | ||||
| float *UNUSED(r_no), | float *UNUSED(r_no), | ||||
| int *r_index) | int *r_index) | ||||
| { | { | ||||
| eSnapMode retval = SCE_SNAP_MODE_NONE; | eSnapMode retval = SCE_SNAP_MODE_NONE; | ||||
| if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ | if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE_RAYCAST) { | ||||
| /* Currently only edge and vert */ | |||||
| return retval; | return retval; | ||||
| } | } | ||||
| float lpmat[4][4], dist_px_sq = square_f(*dist_px); | float lpmat[4][4], dist_px_sq = square_f(*dist_px); | ||||
| mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); | mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); | ||||
| DistProjectedAABBPrecalc neasrest_precalc; | DistProjectedAABBPrecalc neasrest_precalc; | ||||
| dist_squared_to_projected_aabb_precalc( | dist_squared_to_projected_aabb_precalc( | ||||
| ▲ Show 20 Lines • Show All 469 Lines • ▼ Show 20 Lines | static eSnapMode snapMesh(SnapObjectContext *sctx, | ||||
| bool use_hide, | bool use_hide, | ||||
| /* read/write args */ | /* read/write args */ | ||||
| float *dist_px, | float *dist_px, | ||||
| /* return args */ | /* return args */ | ||||
| float r_loc[3], | float r_loc[3], | ||||
| float r_no[3], | float r_no[3], | ||||
| int *r_index) | int *r_index) | ||||
| { | { | ||||
| BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); | BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE_RAYCAST); | ||||
| if (me_eval->totvert == 0) { | if (me_eval->totvert == 0) { | ||||
| return SCE_SNAP_MODE_NONE; | return SCE_SNAP_MODE_NONE; | ||||
| } | } | ||||
| if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { | if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { | ||||
| return SCE_SNAP_MODE_NONE; | return SCE_SNAP_MODE_NONE; | ||||
| } | } | ||||
| float lpmat[4][4]; | float lpmat[4][4]; | ||||
| ▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | static eSnapMode snapEditMesh(SnapObjectContext *sctx, | ||||
| const float obmat[4][4], | const float obmat[4][4], | ||||
| /* read/write args */ | /* read/write args */ | ||||
| float *dist_px, | float *dist_px, | ||||
| /* return args */ | /* return args */ | ||||
| float r_loc[3], | float r_loc[3], | ||||
| float r_no[3], | float r_no[3], | ||||
| int *r_index) | int *r_index) | ||||
| { | { | ||||
| BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); | BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE_RAYCAST); | ||||
| if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { | if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE_RAYCAST) == SCE_SNAP_MODE_VERTEX) { | ||||
| if (em->bm->totvert == 0) { | if (em->bm->totvert == 0) { | ||||
| return SCE_SNAP_MODE_NONE; | return SCE_SNAP_MODE_NONE; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| if (em->bm->totedge == 0) { | if (em->bm->totedge == 0) { | ||||
| return SCE_SNAP_MODE_NONE; | return SCE_SNAP_MODE_NONE; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 286 Lines • ▼ Show 20 Lines | |||||
| * \param dist_px: Maximum threshold distance (in pixels). | * \param dist_px: Maximum threshold distance (in pixels). | ||||
| * | * | ||||
| * Output Args | * Output Args | ||||
| * ----------- | * ----------- | ||||
| * | * | ||||
| * \param r_loc: Hit location. | * \param r_loc: Hit location. | ||||
| * \param r_no: Hit normal (optional). | * \param r_no: Hit normal (optional). | ||||
| * \param r_index: Hit index or -1 when no valid index is found. | * \param r_index: Hit index or -1 when no valid index is found. | ||||
| * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`). | * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE_RAYCAST`). | ||||
| * \param r_ob: Hit object. | * \param r_ob: Hit object. | ||||
| * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). | * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). | ||||
| */ | */ | ||||
| static eSnapMode snapObjectsRay(SnapObjectContext *sctx, | static eSnapMode snapObjectsRay(SnapObjectContext *sctx, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| /* read/write args */ | /* read/write args */ | ||||
| /* Parameters below cannot be const, because they are assigned to a | /* Parameters below cannot be const, because they are assigned to a | ||||
| * non-const variable (readability-non-const-parameter). */ | * non-const variable (readability-non-const-parameter). */ | ||||
| ▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, | ||||
| bool sort, | bool sort, | ||||
| ListBase *r_hit_list) | ListBase *r_hit_list) | ||||
| { | { | ||||
| sctx->runtime.depsgraph = depsgraph; | sctx->runtime.depsgraph = depsgraph; | ||||
| sctx->runtime.v3d = v3d; | sctx->runtime.v3d = v3d; | ||||
| if (ray_depth == -1.0f) { | if (ray_depth == -1.0f) { | ||||
| ray_depth = BVH_RAYCAST_DIST_MAX; | ray_depth = BVH_RAYCAST_DIST_MAX; | ||||
| } | } | ||||
Not Done Inline ActionsWhy was this change needed? It seems like changes to ray projection shouldn't be necessary for this patch. If it's kept, the doc-string for ED_transform_snap_object_project_ray_all needs to be updated. campbellbarton: Why was this change needed? It seems like changes to ray projection shouldn't be necessary for… | |||||
Done Inline Actionsreverted. will apply as separate patch gfxcoder: reverted. will apply as separate patch | |||||
| #ifdef DEBUG | #ifdef DEBUG | ||||
| float ray_depth_prev = ray_depth; | float ray_depth_prev = ray_depth; | ||||
| #endif | #endif | ||||
| bool retval = raycastObjects(sctx, | bool retval = raycastObjects(sctx, | ||||
| params, | params, | ||||
| ray_start, | ray_start, | ||||
| ▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx, | static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx, | ||||
| Depsgraph *depsgraph, | Depsgraph *depsgraph, | ||||
| const ARegion *region, | const ARegion *region, | ||||
| const View3D *v3d, | const View3D *v3d, | ||||
| const eSnapMode snap_to_flag, | const eSnapMode snap_to_flag, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| const float init_co[3], | |||||
| const float mval[2], | const float mval[2], | ||||
| const float prev_co[3], | const float prev_co[3], | ||||
| float *dist_px, | float *dist_px, | ||||
| float r_loc[3], | float r_loc[3], | ||||
| float r_no[3], | float r_no[3], | ||||
| int *r_index, | int *r_index, | ||||
| Object **r_ob, | Object **r_ob, | ||||
| float r_obmat[4][4], | float r_obmat[4][4], | ||||
| Show All 15 Lines | static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectContext *sctx, | ||||
| float no[3] = {0.0f, 0.0f, 0.0f}; | float no[3] = {0.0f, 0.0f, 0.0f}; | ||||
| float obmat[4][4]; | float obmat[4][4]; | ||||
| int index = -1; | int index = -1; | ||||
| const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); | const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); | ||||
| bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d); | bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d); | ||||
| if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) { | /* Note: if both face raycast and face nearest are enabled, first find result of nearest, then | ||||
| * override with raycast. */ | |||||
| if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) { | |||||
| has_hit = nearestWorldObjects( | |||||
| sctx, params, init_co, prev_co, loc, no, &index, &ob_eval, obmat); | |||||
| if (has_hit) { | |||||
| retval = SCE_SNAP_MODE_FACE_NEAREST; | |||||
| copy_v3_v3(r_loc, loc); | |||||
| if (r_no) { | |||||
| copy_v3_v3(r_no, no); | |||||
| } | |||||
| if (r_ob) { | |||||
| *r_ob = ob_eval; | |||||
| } | |||||
| if (r_obmat) { | |||||
| copy_m4_m4(r_obmat, obmat); | |||||
| } | |||||
| if (r_index) { | |||||
| *r_index = index; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST || use_occlusion_test) { | |||||
| float ray_start[3], ray_normal[3]; | float ray_start[3], ray_normal[3]; | ||||
| if (!ED_view3d_win_to_ray_clipped_ex( | if (!ED_view3d_win_to_ray_clipped_ex( | ||||
| depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) { | depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) { | ||||
| return SCE_SNAP_MODE_NONE; | return retval; | ||||
| } | } | ||||
Not Done Inline ActionsThe code does all the tricky work of detecting the nearest face and then ignores it and returns SCE_SNAP_MODE_NONE. mano-wii: The code does all the tricky work of detecting the nearest face and then ignores it and returns… | |||||
Done Inline ActionsI've gone back and forth on which should be done first if both nearest face and project-to-face options are enabled, or how these two options interact, or whether they should just be mutually exclusive (like Increment/Grid snapping's absolute or relative option). maybe others (perhaps an artist) would have some insights? this particular return SCE_SNAP_MODE_NONE; is a bug, in that I forgot to change it when I recently moved the nearest face code above the project to face code. gfxcoder: I've gone back and forth on which should be done first if both nearest face and project-to-face… | |||||
| float dummy_ray_depth = BVH_RAYCAST_DIST_MAX; | float dummy_ray_depth = BVH_RAYCAST_DIST_MAX; | ||||
| has_hit = raycastObjects(sctx, | has_hit = raycastObjects(sctx, | ||||
| params, | params, | ||||
| ray_start, | ray_start, | ||||
| ray_normal, | ray_normal, | ||||
| &dummy_ray_depth, | &dummy_ray_depth, | ||||
| loc, | loc, | ||||
| no, | no, | ||||
| &index, | &index, | ||||
| &ob_eval, | &ob_eval, | ||||
| obmat, | obmat, | ||||
| nullptr); | nullptr); | ||||
| if (has_hit) { | if (has_hit) { | ||||
| if (r_face_nor) { | if (r_face_nor) { | ||||
| copy_v3_v3(r_face_nor, no); | copy_v3_v3(r_face_nor, no); | ||||
| } | } | ||||
| if ((snap_to_flag & SCE_SNAP_MODE_FACE)) { | if ((snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST)) { | ||||
| retval = SCE_SNAP_MODE_FACE; | retval = SCE_SNAP_MODE_FACE_RAYCAST; | ||||
| copy_v3_v3(r_loc, loc); | copy_v3_v3(r_loc, loc); | ||||
| if (r_no) { | if (r_no) { | ||||
| copy_v3_v3(r_no, no); | copy_v3_v3(r_no, no); | ||||
| } | } | ||||
| if (r_ob) { | if (r_ob) { | ||||
| *r_ob = ob_eval; | *r_ob = ob_eval; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, | eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, | ||||
| Depsgraph *depsgraph, | Depsgraph *depsgraph, | ||||
| const ARegion *region, | const ARegion *region, | ||||
| const View3D *v3d, | const View3D *v3d, | ||||
| const eSnapMode snap_to, | const eSnapMode snap_to, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| const float init_co[3], | |||||
| const float mval[2], | const float mval[2], | ||||
| const float prev_co[3], | const float prev_co[3], | ||||
| float *dist_px, | float *dist_px, | ||||
| float r_loc[3], | float r_loc[3], | ||||
| float r_no[3], | float r_no[3], | ||||
| int *r_index, | int *r_index, | ||||
| Object **r_ob, | Object **r_ob, | ||||
| float r_obmat[4][4], | float r_obmat[4][4], | ||||
| float r_face_nor[3]) | float r_face_nor[3]) | ||||
| { | { | ||||
| return transform_snap_context_project_view3d_mixed_impl(sctx, | return transform_snap_context_project_view3d_mixed_impl(sctx, | ||||
| depsgraph, | depsgraph, | ||||
| region, | region, | ||||
| v3d, | v3d, | ||||
| snap_to, | snap_to, | ||||
| params, | params, | ||||
| init_co, | |||||
| mval, | mval, | ||||
| prev_co, | prev_co, | ||||
| dist_px, | dist_px, | ||||
| r_loc, | r_loc, | ||||
| r_no, | r_no, | ||||
| r_index, | r_index, | ||||
| r_ob, | r_ob, | ||||
| r_obmat, | r_obmat, | ||||
| r_face_nor); | r_face_nor); | ||||
| } | } | ||||
| eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, | eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, | ||||
| Depsgraph *depsgraph, | Depsgraph *depsgraph, | ||||
| const ARegion *region, | const ARegion *region, | ||||
| const View3D *v3d, | const View3D *v3d, | ||||
| const eSnapMode snap_to, | const eSnapMode snap_to, | ||||
| const SnapObjectParams *params, | const SnapObjectParams *params, | ||||
| const float init_co[3], | |||||
| const float mval[2], | const float mval[2], | ||||
| const float prev_co[3], | const float prev_co[3], | ||||
| float *dist_px, | float *dist_px, | ||||
| float r_loc[3], | float r_loc[3], | ||||
| float r_no[3]) | float r_no[3]) | ||||
| { | { | ||||
| return ED_transform_snap_object_project_view3d_ex(sctx, | return ED_transform_snap_object_project_view3d_ex(sctx, | ||||
| depsgraph, | depsgraph, | ||||
| region, | region, | ||||
| v3d, | v3d, | ||||
| snap_to, | snap_to, | ||||
| params, | params, | ||||
| init_co, | |||||
| mval, | mval, | ||||
| prev_co, | prev_co, | ||||
| dist_px, | dist_px, | ||||
| r_loc, | r_loc, | ||||
| r_no, | r_no, | ||||
| nullptr, | nullptr, | ||||
| nullptr, | nullptr, | ||||
| nullptr, | nullptr, | ||||
| Show All 25 Lines | |||||
Why is this necessary?
If I'm not mistaken, the edit_mesh of ob_eval and ob_orig should be the same.
(I may have misunderstood the function update_mesh_edit_mode_pointers)