diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index f8164d1f646..985714d813f 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -11,9 +11,9 @@ height="640" id="svg2" sodipodi:version="0.32" - inkscape:version="1.0 (4035a4f, 2020-05-01)" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" version="1.0" - sodipodi:docname="blender_icons.svg" + sodipodi:docname="blender_icons.master.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" style="display:inline;enable-background:new" inkscape:export-filename="blender_icons.png" @@ -42,17 +42,17 @@ guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:window-width="1792" - inkscape:window-height="968" + inkscape:window-width="1618" + inkscape:window-height="846" id="namedview34335" showgrid="false" - inkscape:zoom="0.815521" - inkscape:cx="60.911776" - inkscape:cy="331.5525" - inkscape:window-x="-1" - inkscape:window-y="25" + inkscape:zoom="16" + inkscape:cx="18.1714" + inkscape:cy="166.10682" + inkscape:window-x="185" + inkscape:window-y="159" inkscape:window-maximized="0" - inkscape:current-layer="layer2" /> + inkscape:current-layer="layer8" /> + + + + + + + toolsettings->snap_mode = (1 << 1); /* SCE_SNAP_MODE_EDGE */ break; case 3: - scene->toolsettings->snap_mode = (1 << 2); /* SCE_SNAP_MODE_FACE */ + scene->toolsettings->snap_mode = (1 << 2); /* SCE_SNAP_MODE_FACE_RAYCAST */ break; case 4: scene->toolsettings->snap_mode = (1 << 3); /* SCE_SNAP_MODE_VOLUME */ diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index ea8653bd0f4..68df560a389 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -3164,6 +3164,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + /* Zero isn't a valid value, use for versioning. */ + if (tool_settings->snap_face_nearest_steps == 0) { + /* Minimum of snap steps for face nearest is 1. */ + tool_settings->snap_face_nearest_steps = 1; + /* Set snap to edited and non-edited as default. */ + tool_settings->snap_flag |= SCE_SNAP_TO_INCLUDE_EDITED | SCE_SNAP_TO_INCLUDE_NONEDITED; + } + } } if (!MAIN_VERSION_ATLEAST(bmain, 303, 4)) { diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 4c0df48f65b..24302aca59b 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5562,7 +5562,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) Curve *cu; float location[3]; const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) && - (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE)); + (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST)); Nurb *nu; BezTriple *bezt; @@ -5595,12 +5595,13 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) vc.depsgraph, vc.region, vc.v3d, - SCE_SNAP_MODE_FACE, + SCE_SNAP_MODE_FACE_RAYCAST, &(const struct SnapObjectParams){ .snap_target_select = (vc.obedit != NULL) ? SCE_SNAP_TARGET_NOT_ACTIVE : SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_FINAL, }, + NULL, mval, NULL, NULL, diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 1ce67185c1e..af1f09d7e25 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -278,12 +278,13 @@ static int gizmo_move_modal(bContext *C, CTX_data_ensure_evaluated_depsgraph(C), region, CTX_wm_view3d(C), - (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), + (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST), &(const struct SnapObjectParams){ .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_EDIT, .use_occlusion_test = true, }, + NULL, mval_fl, NULL, &dist_px, diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index d468906f127..c5a542c0bf3 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -345,7 +345,7 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) prop = RNA_def_enum_flag(gzt->srna, "snap_elements_force", rna_enum_snap_element_items, - SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE, + SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST, "Snap Elements", ""); RNA_def_property_enum_funcs_runtime(prop, diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 20353c21f93..db44d9af706 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -60,6 +60,10 @@ struct SnapObjectParams { bool use_occlusion_test : true; /* exclude back facing geometry from snapping */ bool use_backface_culling : true; + /* Break nearest face snapping into steps to improve transformations across U-shaped targets. */ + short face_nearest_steps; + /* Enable to force nearest face snapping to snap to target the source was initially near. */ + bool keep_on_same_target; }; typedef struct SnapObjectContext SnapObjectContext; @@ -114,12 +118,33 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, bool sort, struct ListBase *r_hit_list); +/** + * Perform snapping. + * + * Given a 2D region value, snap to vert/edge/face/grid. + * + * \param sctx: Snap context. + * \param snap_to: Target elements to snap source to. + * \param params: Addition snapping options. + * \param init_co: Initial world-space coordinate of source (optional). + * \param mval: Current transformed screen-space coordinate or mouse position (optional). + * \param prev_co: Current transformed world-space coordinate of source (optional). + * \param dist_px: Maximum distance to snap (in pixels). + * \param r_loc: Snapped world-space coordinate. + * \param r_no: Snapped world-space normal (optional). + * \param r_index: Index of snapped-to target element (optional). + * \param r_ob: Snapped-to target object (optional). + * \param r_obmat: Matrix of snapped-to target object (optional). + * \param r_face_nor: World-space normal of snapped-to target face (optional). + * \return Snapped-to element, #eSnapMode. + */ eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, - eSnapMode snap_to, + const eSnapMode snap_to, const struct SnapObjectParams *params, + const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, @@ -135,19 +160,23 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *s * Given a 2D region value, snap to vert/edge/face. * * \param sctx: Snap context. - * \param mval: Screenspace coordinate. - * \param prev_co: Coordinate for perpendicular point calculation (optional). + * \param snap_to: Target elements to snap source to. + * \param params: Addition snapping options. + * \param init_co: Initial world-space coordinate of source (optional). + * \param mval: Current transformed screen-space coordinate or mouse position (optional). + * \param prev_co: Current transformed world-space coordinate of source (optional). * \param dist_px: Maximum distance to snap (in pixels). - * \param r_loc: hit location. - * \param r_no: hit normal (optional). - * \return Snap success. + * \param r_loc: Snapped world-space coordinate. + * \param r_no: Snapped world-space normal (optional). + * \return Snapped-to element, #eSnapMode. */ eSnapMode ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, - eSnapMode snap_to, + const eSnapMode snap_to, const struct SnapObjectParams *params, + const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index f1c0acf43f7..09057fd846e 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -652,7 +652,7 @@ DEF_ICON(PARTICLE_TIP) DEF_ICON(PARTICLE_PATH) /* EDITING */ -DEF_ICON_BLANK(669) +DEF_ICON(SNAP_FACE_NEAREST) DEF_ICON(SNAP_FACE_CENTER) DEF_ICON(SNAP_PERPENDICULAR) DEF_ICON(SNAP_MIDPOINT) diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 8d63b1ea020..330008d92d1 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -702,7 +702,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source"); const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) && - (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE)); + (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST)); /* First calculate the center of transformation. */ zero_v3(center); diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 04030583f5c..83968955583 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1646,12 +1646,13 @@ void EDBM_project_snap_verts( depsgraph, region, CTX_wm_view3d(C), - SCE_SNAP_MODE_FACE, + SCE_SNAP_MODE_FACE_RAYCAST, &(const struct SnapObjectParams){ .snap_target_select = SCE_SNAP_TARGET_NOT_ACTIVE, .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, + NULL, mval, NULL, NULL, diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index a879a05d41a..4a1bd6ba945 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -597,9 +597,9 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene); data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE; const bool calc_plane_omat = v3d_cursor_snap_calc_plane(); - if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE)) { - data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; - snap_elements |= SCE_SNAP_MODE_FACE; + if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) { + data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST; + snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST; } snap_data->is_enabled = true; @@ -614,7 +614,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, snap_data->snap_elem = SCE_SNAP_MODE_NONE; return; } - snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST; } } #endif @@ -649,6 +649,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, .edit_mode_type = edit_mode_type, .use_occlusion_test = use_occlusion_test, }, + NULL, mval_fl, prev_co, &dist_px, @@ -744,7 +745,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { snap_elem_index[1] = index; } - else if (snap_elem == SCE_SNAP_MODE_FACE) { + else if (snap_elem == SCE_SNAP_MODE_FACE_RAYCAST) { snap_elem_index[2] = index; } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 5fbdbef676c..041663b4a00 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -908,12 +908,13 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, - SCE_SNAP_MODE_FACE, + SCE_SNAP_MODE_FACE_RAYCAST, &(const struct SnapObjectParams){ .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_FINAL, .use_occlusion_test = true, }, + NULL, mval_fl, NULL, &dist_px, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 9aae30c4a7e..96f242dba9f 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -357,11 +357,12 @@ static bool view3d_ruler_item_mousemove(const bContext *C, depsgraph, ruler_info->region, v3d, - SCE_SNAP_MODE_FACE, + SCE_SNAP_MODE_FACE_RAYCAST, &(const struct SnapObjectParams){ .snap_target_select = SCE_SNAP_TARGET_ALL, .edit_mode_type = SNAP_GEOM_CAGE, }, + NULL, mval_fl, NULL, &dist_px, diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 960d9a25ca7..d9e23b98c66 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1577,7 +1577,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) if (transformModeUseSnap(t)) { if (!(t->modifiers & MOD_SNAP) != !(t->tsnap.flag & SCE_SNAP)) { /* Type is #eSnapFlag, but type must match various snap attributes in #ToolSettings. */ - char *snap_flag_ptr; + short *snap_flag_ptr; wmMsgParams_RNA msg_key_params = {{0}}; RNA_pointer_create(&t->scene->id, &RNA_ToolSettings, ts, &msg_key_params.ptr); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index a35942aedec..76573c85d52 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -306,9 +306,9 @@ typedef struct TransSnap { eSnapTargetSelect target_select; bool align; bool project; - bool snap_self; bool peel; bool use_backface_culling; + short face_nearest_steps; eTSnap status; /* Snapped Element Type (currently for objects only). */ eSnapMode snapElem; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 0bb00032561..658901a6991 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -402,7 +402,7 @@ static void applyAxisConstraintVec(const TransInfo *t, if (activeSnap(t)) { if (validSnap(t)) { is_snap_to_edge = (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) != 0; - is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE) != 0; + is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE_RAYCAST) != 0; is_snap_to_point = !is_snap_to_edge && !is_snap_to_face; } else if (t->tsnap.snapElem & SCE_SNAP_MODE_GRID) { diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index e1b25acb21e..a031a6cd45b 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1192,7 +1192,7 @@ static void restoreBones(TransDataContainer *tc) void recalcData_edit_armature(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index 51acfb2a788..b9581aa1e31 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -418,7 +418,7 @@ void createTransCurveVerts(TransInfo *t) void recalcData_curve(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c index f02d4e94448..d3c34697237 100644 --- a/source/blender/editors/transform/transform_convert_lattice.c +++ b/source/blender/editors/transform/transform_convert_lattice.c @@ -101,7 +101,7 @@ void createTransLatticeVerts(TransInfo *t) void recalcData_lattice(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c index 7cba4f97886..5b2a1f8336d 100644 --- a/source/blender/editors/transform/transform_convert_mball.c +++ b/source/blender/editors/transform/transform_convert_mball.c @@ -122,7 +122,7 @@ void createTransMBallVerts(TransInfo *t) void recalcData_mball(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->data_len) { diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index d4b12142162..c8d943df3fa 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1940,7 +1940,7 @@ static void tc_mesh_partial_types_calc(TransInfo *t, struct PartialTypeState *r_ } /* With projection, transform isn't affine. */ - if (activeSnap_with_project(t)) { + if (activeSnap_SnappingIndividual(t)) { if (partial_for_looptri == PARTIAL_TYPE_GROUP) { partial_for_looptri = PARTIAL_TYPE_ALL; } @@ -2056,7 +2056,7 @@ void recalcData_mesh(TransInfo *t) bool is_canceling = t->state == TRANS_CANCEL; /* Apply corrections. */ if (!is_canceling) { - applyProject(t); + applySnappingIndividual(t); bool do_mirror = !(t->flag & T_NO_MIRROR); FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 5879a65eb4b..47e78656861 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -865,7 +865,7 @@ void recalcData_objects(TransInfo *t) bool motionpath_update = false; if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c index 1f58ec80f02..e5a66f8a1d2 100644 --- a/source/blender/editors/transform/transform_convert_object_texspace.c +++ b/source/blender/editors/transform/transform_convert_object_texspace.c @@ -90,7 +90,7 @@ void recalcData_texspace(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c index d7b0f378c8a..31e73288ad0 100644 --- a/source/blender/editors/transform/transform_convert_particle.c +++ b/source/blender/editors/transform/transform_convert_particle.c @@ -238,7 +238,7 @@ static void flushTransParticles(TransInfo *t) void recalcData_particles(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } flushTransParticles(t); } diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index 2327aa4e9c4..8d790b4699b 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -84,7 +84,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2])) transform_snap_increment(t, &angle); - applySnapping(t, &angle); + applySnappingAsGroup(t, &angle); applyNumInput(&t->num, &angle); diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index 9a732562709..5ca1fdf75c6 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -87,7 +87,7 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2])) } else { copy_v2_v2(values_final, t->values); - applySnapping(t, values_final); + applySnappingAsGroup(t, values_final); transform_convert_sequencer_channel_clamp(t, values_final); if (t->con.mode & CON_APPLY) { diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index b8e9a0d1a4d..a3c49d2362f 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1292,7 +1292,7 @@ static void edge_slide_snap_apply(TransInfo *t, float *value) side_index = t_snap >= t_mid; } - if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) { + if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) { float co_dir[3]; sub_v3_v3v3(co_dir, co_dest[side_index], co_orig); normalize_v3(co_dir); @@ -1444,7 +1444,7 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0] + t->values_modal_offset[0]; - applySnapping(t, &final); + applySnappingAsGroup(t, &final); if (!validSnap(t)) { transform_snap_increment(t, &final); } diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index ffae651e4aa..31d40486afc 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -105,7 +105,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) constraintNumInput(t, t->values_final); } - applySnapping(t, t->values_final); + applySnappingAsGroup(t, t->values_final); } size_to_mat3(mat, t->values_final); diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index f75c65448df..a7207b36578 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -305,7 +305,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) final = large_rotation_limit(final); } else { - applySnapping(t, &final); + applySnappingAsGroup(t, &final); if (!(activeSnap(t) && validSnap(t))) { transform_snap_increment(t, &final); } diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 8099449ec23..bdbb66b72f4 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -99,7 +99,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) constraintNumInput(t, t->values_final); } - applySnapping(t, t->values_final); + applySnappingAsGroup(t, t->values_final); } size_to_mat3(mat_final, t->values_final); diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 3c6b6ea4117..65690f9069d 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -470,7 +470,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) } t->tsnap.snapElem = SCE_SNAP_MODE_NONE; - applySnapping(t, global_dir); + applySnappingAsGroup(t, global_dir); transform_snap_grid(t, global_dir); if (t->con.mode & CON_APPLY) { diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 77c5707d814..674ffcf17a8 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -539,7 +539,7 @@ static void vert_slide_snap_apply(TransInfo *t, float *value) getSnapPoint(t, dvec); sub_v3_v3(dvec, t->tsnap.snapTarget); - if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE)) { + if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) { float co_dir[3]; sub_v3_v3v3(co_dir, co_curr_3d, co_orig_3d); normalize_v3(co_dir); @@ -568,7 +568,7 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) final = t->values[0] + t->values_modal_offset[0]; - applySnapping(t, &final); + applySnappingAsGroup(t, &final); if (!validSnap(t)) { transform_snap_increment(t, &final); } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 649217092aa..22d062a71dc 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,21 +368,139 @@ 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)) { + 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]; + sub_v3_v3v3(tvec, loc, iloc); + + mul_m3_v3(td->smtx, tvec); + + add_v3_v3(td->loc, tvec); + + if (t->tsnap.align && (t->options & CTX_OBJECT)) { + /* handle alignment as well */ + const float *original_normal; + float mat[3][3]; + + /* In pose mode, we want to align normals with Y axis of bones. */ + original_normal = td->axismtx[2]; + + 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; + } + + float init_loc[3]; + float prev_loc[3]; + float snap_loc[3], snap_no[3]; + + 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]); + } + + 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; } float tvec[3]; - int i; + sub_v3_v3v3(tvec, snap_loc, prev_loc); + mul_m3_v3(td->smtx, tvec); + add_v3_v3(td->loc, tvec); + + /* TODO: support snap alignment similar to #SCE_SNAP_MODE_FACE_RAYCAST? */ +} + +void applySnappingIndividual(TransInfo *t) +{ + if (!activeSnap_SnappingIndividual(t)) { + return; + } /* 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]; + for (int i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_SKIP) { continue; } @@ -366,70 +509,16 @@ void applyProject(TransInfo *t) continue; } - copy_v3_v3(iloc, td->loc); - if (tc->use_local_mat) { - mul_m4_v3(tc->mat, iloc); + /* 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); } - 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) { - 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; - } - -#if 0 - if (tc->use_local_mat) { - mul_m4_v3(tc->imat, loc); - } -#endif - - sub_v3_v3v3(tvec, loc, iloc); - - mul_m3_v3(td->smtx, tvec); - - add_v3_v3(td->loc, tvec); - - if (t->tsnap.align && (t->options & CTX_OBJECT)) { - /* handle alignment as well */ - const float *original_normal; - float mat[3][3]; - - /* In pose mode, we want to align normals with Y axis of bones... */ - original_normal = td->axismtx[2]; - - rotation_between_vecs_to_mat3(mat, original_normal, no); - - transform_data_ext_rotate(td, mat, true); - - /* TODO: support constraints for rotation too? see #ElementRotation. */ - } - } - } - #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; + } + else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) { + ret |= SCE_SNAP_TARGET_NOT_SELECTED; } - if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) { - return 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; } @@ -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]; @@ -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); diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 6db027df067..3672e76c778 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -40,15 +40,16 @@ float transform_snap_increment_get(const TransInfo *t); bool transform_snap_grid(TransInfo *t, float *val); bool activeSnap(const TransInfo *t); -bool activeSnap_with_project(const TransInfo *t); +bool activeSnap_SnappingIndividual(const TransInfo *t); +bool activeSnap_SnappingAsGroup(const TransInfo *t); bool validSnap(const TransInfo *t); void initSnapping(struct TransInfo *t, struct wmOperator *op); void freeSnapping(struct TransInfo *t); -void applyProject(TransInfo *t); +void applySnappingIndividual(TransInfo *t); void applyGridAbsolute(TransInfo *t); -void applySnapping(TransInfo *t, float *vec); +void applySnappingAsGroup(TransInfo *t, float *vec); void resetSnapping(TransInfo *t); eRedrawFlag handleSnapping(TransInfo *t, const struct wmEvent *event); void drawSnapping(const struct bContext *C, TransInfo *t); diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index bd3d29fac8a..8a1eca3a6f7 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -405,6 +405,62 @@ static SnapData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext *sctx, 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(sod->mesh_runtime->eval_mutex)); + } + } + if (treedata == nullptr || treedata->tree == nullptr) { + return nullptr; + } + + return treedata; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -419,16 +475,16 @@ using IterSnapObjsCallback = void (*)(SnapObjectContext *sctx, void *data); 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, - const bool is_in_object_mode) + const Base *base) { if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { 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; } @@ -436,25 +492,37 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx, return false; } - if (snap_select == SCE_SNAP_TARGET_NOT_ACTIVE) { - return base_act != base; - } + /* get base attributes */ + 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 (snap_select == SCE_SNAP_TARGET_NOT_EDITED) { - return base->object->mode != OB_MODE_EDIT; - } - - if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) { - if (is_in_object_mode) { - return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)); + 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; + } } - - /* What is selectable or not is part of the object and depends on the mode. */ - return true; } - if (snap_select == SCE_SNAP_TARGET_ONLY_SELECTABLE) { - return (base->flag & BASE_SELECTABLE) != 0; + if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_edited) { + return false; + } + + if ((snap_target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) && !is_selectable) { + return false; + } + + if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_in_object_mode && is_selected) { + /* What is selectable or not is part of the object and depends on the mode. */ + return false; } return true; @@ -470,11 +538,10 @@ static void iter_snap_objects(SnapObjectContext *sctx, { ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph); const eSnapTargetSelect snap_target_select = params->snap_target_select; - 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) { - 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; } @@ -850,40 +917,9 @@ static bool raycastEditMesh(SnapObjectContext *sctx, len_diff = 0.0f; } - BVHTreeFromEditMesh *treedata = &sod->treedata_editmesh; - - 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(sod->mesh_runtime->eval_mutex)); - } - - if (treedata->tree == nullptr) { - return retval; - } + BVHTreeFromEditMesh *treedata = snap_object_data_editmesh_treedata_get(sctx, ob_eval, em); + if (treedata == nullptr) { + return retval; } float timat[3][3]; /* transpose inverse matrix for normals */ @@ -1098,7 +1134,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, * \param r_loc: Hit location. * \param r_no: Hit normal (optional). * \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_obmat: Object matrix (may not be #Object.obmat with dupli-instances). * \param r_hit_list: List of #SnapObjectHitDepth (caller must free). @@ -1149,6 +1185,324 @@ static bool raycastObjects(SnapObjectContext *sctx, /** \} */ +/* -------------------------------------------------------------------- */ +/** \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. */ + 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(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 + * =================================== + * + * 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 * \{ */ @@ -1842,7 +2196,8 @@ static eSnapMode snapArmature(SnapObjectContext *sctx, { 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; } @@ -2328,7 +2683,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx, float r_no[3], 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) { return SCE_SNAP_MODE_NONE; } @@ -2506,9 +2861,9 @@ static eSnapMode snapEditMesh(SnapObjectContext *sctx, float r_no[3], 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) { return SCE_SNAP_MODE_NONE; } @@ -2811,7 +3166,7 @@ static void snap_obj_fn(SnapObjectContext *sctx, * \param r_loc: Hit location. * \param r_no: Hit normal (optional). * \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_obmat: Object matrix (may not be #Object.obmat with dupli-instances). */ @@ -3014,6 +3369,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont const View3D *v3d, const eSnapMode snap_to_flag, const SnapObjectParams *params, + const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, @@ -3045,11 +3401,36 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont 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]; if (!ED_view3d_win_to_ray_clipped_ex( depsgraph, region, v3d, mval, nullptr, ray_normal, ray_start, true)) { - return SCE_SNAP_MODE_NONE; + return retval; } float dummy_ray_depth = BVH_RAYCAST_DIST_MAX; @@ -3071,8 +3452,8 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont copy_v3_v3(r_face_nor, no); } - if ((snap_to_flag & SCE_SNAP_MODE_FACE)) { - retval = SCE_SNAP_MODE_FACE; + if ((snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST)) { + retval = SCE_SNAP_MODE_FACE_RAYCAST; copy_v3_v3(r_loc, loc); if (r_no) { @@ -3193,6 +3574,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, 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, @@ -3209,6 +3591,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, v3d, snap_to, params, + init_co, mval, prev_co, dist_px, @@ -3226,6 +3609,7 @@ eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, 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, @@ -3238,6 +3622,7 @@ eSnapMode ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, v3d, snap_to, params, + init_co, mval, prev_co, dist_px, diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 74db1d14bbc..6cc01d254ce 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -337,7 +337,9 @@ .snap_mode = SCE_SNAP_MODE_INCREMENT, \ .snap_node_mode = SCE_SNAP_MODE_GRID, \ .snap_uv_mode = SCE_SNAP_MODE_INCREMENT, \ + .snap_flag = SCE_SNAP_TO_INCLUDE_EDITED | SCE_SNAP_TO_INCLUDE_NONEDITED, \ .snap_transform_mode_flag = SCE_SNAP_TRANSFORM_MODE_TRANSLATE, \ + .snap_face_nearest_steps = 1, \ \ .curve_paint_settings = _DNA_DEFAULTS_CurvePaintSettings, \ \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 1c62a550e60..c45b06f8c85 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1496,19 +1496,24 @@ typedef struct ToolSettings { char transform_pivot_point; char transform_flag; /** Snap elements (per spacetype), #eSnapMode. */ - char snap_mode; + char _pad1[1]; + short snap_mode; char snap_node_mode; char snap_uv_mode; /** Generic flags (per spacetype), #eSnapFlag. */ - char snap_flag; - char snap_flag_node; - char snap_flag_seq; - char snap_uv_flag; + short snap_flag; + short snap_flag_node; + short snap_flag_seq; + short snap_uv_flag; /** Default snap source, #eSnapSourceSelect. */ - /* TODO(@gfxcoder): Rename `snap_target` to `snap_source_point`, because target is incorrect. */ + /* 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). */ char snap_target; /** Snap mask for transform modes, #eSnapTransformMode. */ char snap_transform_mode_flag; + /** Steps to break transformation into with face nearest snapping */ + short snap_face_nearest_steps; char proportional_edit, prop_mode; /** Proportional edit, object mode. */ @@ -2085,10 +2090,15 @@ typedef enum eSnapFlag { SCE_SNAP = (1 << 0), SCE_SNAP_ROTATE = (1 << 1), SCE_SNAP_PEEL_OBJECT = (1 << 2), - SCE_SNAP_PROJECT = (1 << 3), - SCE_SNAP_NO_SELF = (1 << 4), + SCE_SNAP_PROJECT = (1 << 3), /* Project individual elements instead of whole object. */ + SCE_SNAP_NOT_TO_ACTIVE = (1 << 4), /* Was `SCE_SNAP_NO_SELF`, but self should be active. */ SCE_SNAP_ABS_GRID = (1 << 5), SCE_SNAP_BACKFACE_CULLING = (1 << 6), + SCE_SNAP_KEEP_ON_SAME_OBJECT = (1 << 7), + /* see #eSnapTargetSelect */ + SCE_SNAP_TO_INCLUDE_EDITED = (1 << 8), + SCE_SNAP_TO_INCLUDE_NONEDITED = (1 << 9), + SCE_SNAP_TO_ONLY_SELECTABLE = (1 << 10), } eSnapFlag; /* Due to dependency conflicts with Cycles, header cannot directly include `BLI_utildefines.h`. */ /* TODO: move this macro to a more general place. */ @@ -2096,7 +2106,7 @@ typedef enum eSnapFlag { ENUM_OPERATORS(eSnapFlag, SCE_SNAP_BACKFACE_CULLING) #endif -/** #ToolSettings.snap_target and #TransSnap.source_select */ +/** See #ToolSettings.snap_target (to be renamed `snap_source`) and #TransSnap.source_select */ typedef enum eSnapSourceSelect { SCE_SNAP_SOURCE_CLOSEST = 0, SCE_SNAP_SOURCE_CENTER = 1, @@ -2104,13 +2114,15 @@ typedef enum eSnapSourceSelect { SCE_SNAP_SOURCE_ACTIVE = 3, } eSnapSourceSelect; -/** #TransSnap.target_select and #ToolSettings.snap_flag (SCE_SNAP_NO_SELF) */ +/** #TransSnap.target_select and #ToolSettings.snap_flag (#SCE_SNAP_NOT_TO_ACTIVE, + * #SCE_SNAP_TO_INCLUDE_EDITED, #SCE_SNAP_TO_INCLUDE_NONEDITED, #SCE_SNAP_TO_ONLY_SELECTABLE) */ typedef enum eSnapTargetSelect { SCE_SNAP_TARGET_ALL = 0, - SCE_SNAP_TARGET_NOT_SELECTED = 1, - SCE_SNAP_TARGET_NOT_ACTIVE = 2, - SCE_SNAP_TARGET_NOT_EDITED = 3, - SCE_SNAP_TARGET_ONLY_SELECTABLE = 4, + SCE_SNAP_TARGET_NOT_SELECTED = (1 << 0), + SCE_SNAP_TARGET_NOT_ACTIVE = (1 << 1), + SCE_SNAP_TARGET_NOT_EDITED = (1 << 2), + SCE_SNAP_TARGET_ONLY_SELECTABLE = (1 << 3), + SCE_SNAP_TARGET_NOT_NONEDITED = (1 << 4), } eSnapTargetSelect; /** #ToolSettings.snap_mode */ @@ -2118,19 +2130,21 @@ typedef enum eSnapMode { SCE_SNAP_MODE_NONE = 0, SCE_SNAP_MODE_VERTEX = (1 << 0), SCE_SNAP_MODE_EDGE = (1 << 1), - SCE_SNAP_MODE_FACE = (1 << 2), /* TODO(@gfxcoder): Rename to `SCE_SNAP_MODE_FACE_RAYCAST` - when other face snapping methods are added. */ + SCE_SNAP_MODE_FACE_RAYCAST = (1 << 2), SCE_SNAP_MODE_VOLUME = (1 << 3), SCE_SNAP_MODE_EDGE_MIDPOINT = (1 << 4), SCE_SNAP_MODE_EDGE_PERPENDICULAR = (1 << 5), - SCE_SNAP_MODE_GEOM = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT), + SCE_SNAP_MODE_FACE_NEAREST = (1 << 8), + + SCE_SNAP_MODE_GEOM = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST | + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT | + SCE_SNAP_MODE_FACE_NEAREST), /** #ToolSettings.snap_node_mode */ SCE_SNAP_MODE_NODE_X = (1 << 0), SCE_SNAP_MODE_NODE_Y = (1 << 1), - /* #ToolSettings.snap_mode and #ToolSettings.snap_node_mode and #ToolSettings.snap_uv_mode */ + /** #ToolSettings.snap_mode and #ToolSettings.snap_node_mode and #ToolSettings.snap_uv_mode */ SCE_SNAP_MODE_INCREMENT = (1 << 6), SCE_SNAP_MODE_GRID = (1 << 7), } eSnapMode; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index cc7df54e648..daf4c99845d 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -151,7 +151,16 @@ const EnumPropertyItem rna_enum_snap_element_items[] = { "Snap to increments of grid"}, {SCE_SNAP_MODE_VERTEX, "VERTEX", ICON_SNAP_VERTEX, "Vertex", "Snap to vertices"}, {SCE_SNAP_MODE_EDGE, "EDGE", ICON_SNAP_EDGE, "Edge", "Snap to edges"}, - {SCE_SNAP_MODE_FACE, "FACE", ICON_SNAP_FACE, "Face", "Snap to faces"}, + {SCE_SNAP_MODE_FACE_RAYCAST, + "FACE", /* TODO(@gfxcoder): replace with "FACE_RAYCAST" as "FACE" is not descriptive. */ + ICON_SNAP_FACE, + "Face Project", + "Snap by projecting onto faces"}, + {SCE_SNAP_MODE_FACE_NEAREST, + "FACE_NEAREST", + ICON_SNAP_FACE_NEAREST, + "Face Nearest", + "Snap to nearest point on faces"}, {SCE_SNAP_MODE_VOLUME, "VOLUME", ICON_SNAP_VOLUME, "Volume", "Snap to volume"}, {SCE_SNAP_MODE_EDGE_MIDPOINT, "EDGE_MIDPOINT", @@ -3301,6 +3310,21 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Snap Element", "Type of element to snap to"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "snap_face_nearest_steps", PROP_INT, PROP_FACTOR); + RNA_def_property_int_sdna(prop, NULL, "snap_face_nearest_steps"); + RNA_def_property_range(prop, 1, 100); + RNA_def_property_ui_text( + prop, + "Face Nearest Steps", + "Number of steps to break transformation into for face nearest snapping"); + + prop = RNA_def_property(srna, "use_snap_to_same_target", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_KEEP_ON_SAME_OBJECT); + RNA_def_property_ui_text( + prop, + "Snap to Same Target", + "Snap only to target that source was initially near (Face Nearest Only)"); + /* node editor uses own set of snap modes */ prop = RNA_def_property(srna, "snap_node_element", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_node_mode"); @@ -3323,9 +3347,9 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Absolute grid alignment while translating (based on the pivot center)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ - /* 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). */ + /* 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). */ prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "snap_target"); RNA_def_property_enum_items(prop, rna_enum_snap_source_items); @@ -3350,9 +3374,30 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Backface Culling", "Exclude back facing geometry from snapping"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + /* TODO(@gfxcoder): Rename `use_snap_self` to `use_snap_active`, because active is correct but + * self is not (breaks API). This only makes a difference when more than one mesh is edited. */ prop = RNA_def_property(srna, "use_snap_self", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NO_SELF); - RNA_def_property_ui_text(prop, "Project onto Self", "Snap onto itself (Edit Mode Only)"); + RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NOT_TO_ACTIVE); + RNA_def_property_ui_text( + prop, "Snap onto Active", "Snap onto itself only if enabled (Edit Mode Only)"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + + prop = RNA_def_property(srna, "use_snap_edit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_INCLUDE_EDITED); + RNA_def_property_ui_text( + prop, "Snap onto Edited", "Snap onto non-active objects in Edit Mode (Edit Mode Only)"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + + prop = RNA_def_property(srna, "use_snap_nonedit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_INCLUDE_NONEDITED); + RNA_def_property_ui_text( + prop, "Snap onto Non-edited", "Snap onto objects not in Edit Mode (Edit Mode Only)"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + + prop = RNA_def_property(srna, "use_snap_selectable", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_ONLY_SELECTABLE); + RNA_def_property_ui_text( + prop, "Snap onto Selectable Only", "Snap only onto objects that are selectable"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ prop = RNA_def_property(srna, "use_snap_translate", PROP_BOOLEAN, PROP_NONE);