diff options
Diffstat (limited to 'source/blender/editors/transform/transform_snap.c')
-rw-r--r-- | source/blender/editors/transform/transform_snap.c | 385 |
1 files changed, 260 insertions, 125 deletions
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 649217092aa..9a563aaf473 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -126,8 +126,12 @@ bool activeSnap(const TransInfo *t) ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP_INVERT); } -bool activeSnap_with_project(const TransInfo *t) +bool activeSnap_SnappingIndividual(const TransInfo *t) { + if (activeSnap(t) && t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) { + return true; + } + if (!t->tsnap.project) { return false; } @@ -143,6 +147,27 @@ bool activeSnap_with_project(const TransInfo *t) return true; } +bool activeSnap_SnappingAsGroup(const TransInfo *t) +{ + if (!activeSnap(t)) { + return false; + } + + if (t->tsnap.mode == SCE_SNAP_MODE_FACE_RAYCAST && t->tsnap.project) { + return false; + } + + if (t->tsnap.mode == SCE_SNAP_MODE_FACE_NEAREST) { + return false; + } + + if (doForceIncrementSnap(t)) { + return false; + } + + return true; +} + bool transformModeUseSnap(const TransInfo *t) { ToolSettings *ts = t->settings; @@ -343,93 +368,157 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event) return status; } -void applyProject(TransInfo *t) +static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td) { - if (!activeSnap_with_project(t)) { - return; + if (!(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST)) { + return false; + } + + float iloc[3], loc[3], no[3]; + float mval_fl[2]; + + copy_v3_v3(iloc, td->loc); + if (tc->use_local_mat) { + mul_m4_v3(tc->mat, iloc); + } + else if (t->options & CTX_OBJECT) { + BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); + copy_v3_v3(iloc, td->ob->obmat[3]); + } + + if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) != + V3D_PROJ_RET_OK) { + return false; + } + + eSnapMode hit = ED_transform_snap_object_project_view3d( + t->tsnap.object_context, + t->depsgraph, + t->region, + t->view, + SCE_SNAP_MODE_FACE_RAYCAST, + &(const struct SnapObjectParams){ + .snap_target_select = t->tsnap.target_select, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, + .use_occlusion_test = false, + .use_backface_culling = t->tsnap.use_backface_culling, + }, + NULL, + mval_fl, + NULL, + 0, + loc, + no); + if (hit != SCE_SNAP_MODE_FACE_RAYCAST) { + return false; } float tvec[3]; - int i; + sub_v3_v3v3(tvec, loc, iloc); - /* XXX FLICKER IN OBJECT MODE */ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *td = tc->data; - for (i = 0; i < tc->data_len; i++, td++) { - float iloc[3], loc[3], no[3]; - float mval_fl[2]; - if (td->flag & TD_SKIP) { - continue; - } + mul_m3_v3(td->smtx, tvec); - if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) { - continue; - } + add_v3_v3(td->loc, tvec); - copy_v3_v3(iloc, td->loc); - if (tc->use_local_mat) { - mul_m4_v3(tc->mat, iloc); - } - else if (t->options & CTX_OBJECT) { - BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); - copy_v3_v3(iloc, td->ob->obmat[3]); - } + if (t->tsnap.align && (t->options & CTX_OBJECT)) { + /* handle alignment as well */ + const float *original_normal; + float mat[3][3]; - if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) == - V3D_PROJ_RET_OK) { - eSnapMode hit = ED_transform_snap_object_project_view3d( - t->tsnap.object_context, - t->depsgraph, - t->region, - t->view, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_target_select = t->tsnap.target_select, - .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, - .use_occlusion_test = false, - .use_backface_culling = t->tsnap.use_backface_culling, - }, - mval_fl, - NULL, - 0, - loc, - no); - if (hit != SCE_SNAP_MODE_FACE) { - return; - } + /* In pose mode, we want to align normals with Y axis of bones. */ + original_normal = td->axismtx[2]; -#if 0 - if (tc->use_local_mat) { - mul_m4_v3(tc->imat, loc); - } -#endif + rotation_between_vecs_to_mat3(mat, original_normal, no); + + transform_data_ext_rotate(td, mat, true); + + /* TODO: support constraints for rotation too? see #ElementRotation. */ + } + return true; +} + +static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td) +{ + if (!(t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST)) { + return; + } - sub_v3_v3v3(tvec, loc, iloc); + float init_loc[3]; + float prev_loc[3]; + float snap_loc[3], snap_no[3]; - mul_m3_v3(td->smtx, tvec); + copy_v3_v3(init_loc, td->iloc); + copy_v3_v3(prev_loc, td->loc); + if (tc->use_local_mat) { + mul_m4_v3(tc->mat, init_loc); + mul_m4_v3(tc->mat, prev_loc); + } + else if (t->options & CTX_OBJECT) { + BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); + copy_v3_v3(init_loc, td->ob->obmat[3]); + } - add_v3_v3(td->loc, tvec); + eSnapMode hit = ED_transform_snap_object_project_view3d( + t->tsnap.object_context, + t->depsgraph, + t->region, + t->view, + SCE_SNAP_MODE_FACE_NEAREST, + &(const struct SnapObjectParams){ + .snap_target_select = t->tsnap.target_select, + .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, + .use_occlusion_test = false, + .use_backface_culling = false, + .face_nearest_steps = t->tsnap.face_nearest_steps, + .keep_on_same_target = t->tsnap.flag & SCE_SNAP_KEEP_ON_SAME_OBJECT, + }, + init_loc, + NULL, + prev_loc, + 0, + snap_loc, + snap_no); + + if (hit != SCE_SNAP_MODE_FACE_NEAREST) { + return; + } - if (t->tsnap.align && (t->options & CTX_OBJECT)) { - /* handle alignment as well */ - const float *original_normal; - float mat[3][3]; + float tvec[3]; + sub_v3_v3v3(tvec, snap_loc, prev_loc); + mul_m3_v3(td->smtx, tvec); + add_v3_v3(td->loc, tvec); - /* In pose mode, we want to align normals with Y axis of bones... */ - original_normal = td->axismtx[2]; + /* TODO: support snap alignment similar to #SCE_SNAP_MODE_FACE_RAYCAST? */ +} - rotation_between_vecs_to_mat3(mat, original_normal, no); +void applySnappingIndividual(TransInfo *t) +{ + if (!activeSnap_SnappingIndividual(t)) { + return; + } - transform_data_ext_rotate(td, mat, true); + /* XXX FLICKER IN OBJECT MODE */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_SKIP) { + continue; + } - /* TODO: support constraints for rotation too? see #ElementRotation. */ - } + if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) { + continue; } - } + /* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and + * fallback to face nearest ray-cast does not hit. */ + bool hit = applyFaceProject(t, tc, td); + if (!hit) { + applyFaceNearest(t, tc, td); + } #if 0 /* TODO: support this? */ - constraintTransLim(t, td); + constraintTransLim(t, td); #endif + } } } @@ -483,15 +572,9 @@ void applyGridAbsolute(TransInfo *t) } } -void applySnapping(TransInfo *t, float *vec) +void applySnappingAsGroup(TransInfo *t, float *vec) { - /* Each Trans Data already makes the snap to face */ - if (doForceIncrementSnap(t)) { - return; - } - - if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) { - /* A similar snap will be applied to each transdata in `applyProject`. */ + if (!activeSnap_SnappingAsGroup(t)) { return; } @@ -644,70 +727,76 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t) return SCE_SNAP_MODE_INCREMENT; } -static eSnapTargetSelect snap_select_type_get(TransInfo *t) +static eSnapTargetSelect snap_target_select_from_spacetype(TransInfo *t) { ViewLayer *view_layer = t->view_layer; Base *base_act = view_layer->basact; + + eSnapTargetSelect ret = SCE_SNAP_TARGET_ALL; + + bool use_snap_active = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_ACTIVE) == 0; + bool use_snap_edit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_EDITED) == 0; + bool use_snap_nonedit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_NONEDITED) == 0; + bool use_snap_selectable_only = (t->tsnap.target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0; + if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { + if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) { + /* Particles edit mode. */ + return ret; + } + + if (use_snap_selectable_only) { + ret |= SCE_SNAP_TARGET_ONLY_SELECTABLE; + } + if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { /* In "Edit Strokes" mode, * snap tool can perform snap to selected or active objects (see T49632) * TODO: perform self snap in gpencil_strokes. * * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ - return SCE_SNAP_TARGET_ALL; + return ret; } const int obedit_type = t->obedit_type; if (obedit_type != -1) { /* Edit mode */ - if (ELEM(obedit_type, - OB_MESH, - OB_ARMATURE, - OB_CURVES_LEGACY, - OB_SURF, - OB_LATTICE, - OB_MBALL)) { - /* Temporary limited to edit mode meshes, armature, curves, lattice and metaballs. */ - - if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) { - /* Exclude editmesh if using proportional edit */ - return SCE_SNAP_TARGET_NOT_EDITED; + if (obedit_type == OB_MESH) { + /* Editing a mesh */ + if ((t->flag & T_PROP_EDIT) != 0) { + /* Exclude editmesh when using proportional edit */ + ret |= SCE_SNAP_TARGET_NOT_EDITED; } - - if (!t->tsnap.snap_self) { - return SCE_SNAP_TARGET_NOT_ACTIVE; + if (!use_snap_active) { + ret |= SCE_SNAP_TARGET_NOT_ACTIVE; + } + if (!use_snap_edit) { + ret |= SCE_SNAP_TARGET_NOT_EDITED; + } + if (!use_snap_nonedit) { + ret |= SCE_SNAP_TARGET_NOT_NONEDITED; } - - return SCE_SNAP_TARGET_NOT_SELECTED; } - - return SCE_SNAP_TARGET_ALL; + else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) { + /* Temporary limited to edit mode armature, curves, surfaces, lattices, and metaballs. */ + ret |= SCE_SNAP_TARGET_NOT_SELECTED; + } } - - if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) { - /* Particles edit mode. */ - return SCE_SNAP_TARGET_ALL; + else { + /* Object or pose mode. */ + ret |= SCE_SNAP_TARGET_NOT_SELECTED | SCE_SNAP_TARGET_NOT_ACTIVE; } - - /* Object or pose mode. */ - return SCE_SNAP_TARGET_NOT_SELECTED; } - - if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) { - return SCE_SNAP_TARGET_NOT_SELECTED; + else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) { + ret |= SCE_SNAP_TARGET_NOT_SELECTED; } - return SCE_SNAP_TARGET_ALL; + return ret; } static void initSnappingMode(TransInfo *t) { - ToolSettings *ts = t->settings; - t->tsnap.mode = snap_mode_from_spacetype(t); - t->tsnap.target_select = snap_select_type_get(t); - - if ((t->spacetype != SPACE_VIEW3D) || !(ts->snap_mode & SCE_SNAP_MODE_FACE)) { + if ((t->spacetype != SPACE_VIEW3D) || !(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST)) { /* Force project off when not supported. */ t->tsnap.project = false; } @@ -724,7 +813,7 @@ static void initSnappingMode(TransInfo *t) t->tsnap.use_backface_culling = snap_use_backface_culling(t); t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0); - if (t->data_type == TC_MESH_VERTS) { + if (t->data_type == &TransConvertType_Mesh) { /* Ignore elements being transformed. */ ED_transform_snap_object_context_set_editmesh_callbacks( t->tsnap.object_context, @@ -753,9 +842,14 @@ static void initSnappingMode(TransInfo *t) void initSnapping(TransInfo *t, wmOperator *op) { + ToolSettings *ts = t->settings; + eSnapSourceSelect snap_source = ts->snap_target; + resetSnapping(t); + t->tsnap.mode = snap_mode_from_spacetype(t); t->tsnap.flag = snap_flag_from_spacetype(t); - eSnapSourceSelect snap_source = t->settings->snap_target; + t->tsnap.target_select = snap_target_select_from_spacetype(t); + t->tsnap.face_nearest_steps = max_ii(ts->snap_face_nearest_steps, 1); /* if snap property exists */ PropertyRNA *prop; @@ -764,11 +858,16 @@ void initSnapping(TransInfo *t, wmOperator *op) if (RNA_property_boolean_get(op->ptr, prop)) { t->modifiers |= MOD_SNAP; + if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) && + RNA_property_is_set(op->ptr, prop)) { + t->tsnap.mode = RNA_property_enum_get(op->ptr, prop); + } + + /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of + * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved + * geometry is snapped). */ if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) && RNA_property_is_set(op->ptr, prop)) { - /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid - * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is - * geometry to which moved geometry is snapped). */ snap_source = RNA_property_enum_get(op->ptr, prop); } @@ -791,9 +890,33 @@ void initSnapping(TransInfo *t, wmOperator *op) t->tsnap.project = RNA_property_boolean_get(op->ptr, prop); } + /* use_snap_self is misnamed and should be use_snap_active */ if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) && RNA_property_is_set(op->ptr, prop)) { - t->tsnap.snap_self = RNA_property_boolean_get(op->ptr, prop); + SET_FLAG_FROM_TEST(t->tsnap.target_select, + !RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_NOT_ACTIVE); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) && + RNA_property_is_set(op->ptr, prop)) { + SET_FLAG_FROM_TEST(t->tsnap.target_select, + !RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_NOT_EDITED); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) && + RNA_property_is_set(op->ptr, prop)) { + SET_FLAG_FROM_TEST(t->tsnap.target_select, + !RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_NOT_NONEDITED); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) && + RNA_property_is_set(op->ptr, prop)) { + SET_FLAG_FROM_TEST(t->tsnap.target_select, + RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_ONLY_SELECTABLE); } } } @@ -805,8 +928,19 @@ void initSnapping(TransInfo *t, wmOperator *op) t->tsnap.align = ((t->tsnap.flag & SCE_SNAP_ROTATE) != 0); t->tsnap.project = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0); - t->tsnap.snap_self = !((t->tsnap.flag & SCE_SNAP_NO_SELF) != 0); t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0); + SET_FLAG_FROM_TEST(t->tsnap.target_select, + (ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE), + SCE_SNAP_TARGET_NOT_ACTIVE); + SET_FLAG_FROM_TEST(t->tsnap.target_select, + !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), + SCE_SNAP_TARGET_NOT_EDITED); + SET_FLAG_FROM_TEST(t->tsnap.target_select, + !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED), + SCE_SNAP_TARGET_NOT_NONEDITED); + SET_FLAG_FROM_TEST(t->tsnap.target_select, + (ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE), + SCE_SNAP_TARGET_ONLY_SELECTABLE); } t->tsnap.source_select = snap_source; @@ -991,8 +1125,8 @@ static void snap_calc_view3d_fn(TransInfo *t, float *UNUSED(vec)) found = (snap_elem != SCE_SNAP_MODE_NONE); } if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) { - found = peelObjectsTransform( - t, mval, (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, loc, no, NULL); + bool use_peel = (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0; + found = peelObjectsTransform(t, mval, use_peel, loc, no, NULL); if (found) { snap_elem = SCE_SNAP_MODE_VOLUME; @@ -1026,7 +1160,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) objects, objects_len, t->mval, - t->tsnap.target_select == SCE_SNAP_TARGET_NOT_SELECTED, + t->tsnap.target_select & SCE_SNAP_TARGET_NOT_SELECTED, &dist_sq, t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; @@ -1118,7 +1252,7 @@ static void snap_target_grid_ensure(TransInfo *t) { /* Only need to calculate once. */ if ((t->tsnap.status & TARGET_GRID_INIT) == 0) { - if (t->data_type == TC_CURSOR_VIEW3D) { + if (t->data_type == &TransConvertType_Cursor3D) { /* Use a fallback when transforming the cursor. * In this case the center is _not_ derived from the cursor which is being transformed. */ copy_v3_v3(t->tsnap.snapTargetGrid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc); @@ -1321,9 +1455,10 @@ eSnapMode snapObjectsTransform( &(const struct SnapObjectParams){ .snap_target_select = t->tsnap.target_select, .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, - .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, + .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE_RAYCAST, .use_backface_culling = t->tsnap.use_backface_culling, }, + NULL, mval, target, dist_px, @@ -1423,7 +1558,7 @@ bool peelObjectsTransform(TransInfo *t, static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetSelect snap_target_select) { /* node is use for snapping only if a) snap mode matches and b) node is inside the view */ - return ((snap_target_select == SCE_SNAP_TARGET_NOT_SELECTED && !(node->flag & NODE_SELECT)) || + return (((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && !(node->flag & NODE_SELECT)) || (snap_target_select == SCE_SNAP_TARGET_ALL && !(node->flag & NODE_ACTIVE))) && (node->totr.xmin < v2d->cur.xmax && node->totr.xmax > v2d->cur.xmin && node->totr.ymin < v2d->cur.ymax && node->totr.ymax > v2d->cur.ymin); |