diff options
Diffstat (limited to 'source/blender/editors')
298 files changed, 11151 insertions, 5581 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 9eeabf2d05e..729e8533d50 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -3123,7 +3123,7 @@ static bAnimChannelType ACF_DSSIMULATION = { /* TODO: just get this from RNA? */ static int acf_dsgpencil_icon(bAnimListElem *UNUSED(ale)) { - return ICON_GREASEPENCIL; + return ICON_OUTLINER_DATA_GREASEPENCIL; } /* Get the appropriate flag(s) for the setting when it is valid. */ @@ -4747,13 +4747,13 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi RNA_id_pointer_create(id, &id_ptr); /* Get NLA context for value remapping */ - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - (float)CFRA); + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( + depsgraph, (float)scene->r.cfra); NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context( &nla_cache, &id_ptr, adt, &anim_eval_context); /* get current frame and apply NLA-mapping to it (if applicable) */ - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); /* Get flags for keyframing. */ flag = ANIM_get_keyframing_flags(scene, true); @@ -4803,8 +4803,8 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi RNA_id_pointer_create((ID *)key, &id_ptr); /* Get NLA context for value remapping */ - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - (float)CFRA); + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( + depsgraph, (float)scene->r.cfra); NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context( &nla_cache, &id_ptr, key->adt, &anim_eval_context); @@ -4872,7 +4872,7 @@ static void achannel_setting_slider_nla_curve_cb(bContext *C, float cfra; /* get current frame - *no* NLA mapping should be done */ - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; /* get flags for keyframing */ flag = ANIM_get_keyframing_flags(scene, true); diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index b223a1493fd..8464f280c29 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2033,7 +2033,7 @@ static void setflag_anim_channels(bAnimContext *ac, if ((ac->spacetype == SPACE_GRAPH) && (ac->regiontype != RGN_TYPE_CHANNELS)) { /* graph editor (case 2) */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_CURVE_VISIBLE | - ANIMFILTER_NODUPLIS); + ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); } else { /* standard case */ diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index d80eac2422e..ff53ad42e84 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -356,7 +356,7 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) if (ale->update & ANIM_UPDATE_HANDLES) { ale->update &= ~ANIM_UPDATE_HANDLES; if (fcu) { - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } } diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index ee1522c7b76..d9dcbf1d57e 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -120,9 +120,9 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformThemeColorShadeAlpha(TH_BACK, -25, -100); - if (SFRA < EFRA) { - immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax); - immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + if (scene->r.sfra < scene->r.efra) { + immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)scene->r.sfra, v2d->cur.ymax); + immRectf(pos, (float)scene->r.efra, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); } else { immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); @@ -135,11 +135,11 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d) immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, (float)SFRA, v2d->cur.ymin); - immVertex2f(pos, (float)SFRA, v2d->cur.ymax); + immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymin); + immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymax); - immVertex2f(pos, (float)EFRA, v2d->cur.ymin); - immVertex2f(pos, (float)EFRA, v2d->cur.ymax); + immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymin); + immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymax); immEnd(); immUnbindProgram(); @@ -530,7 +530,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ bool donenext = false, doneprev = false; int nextcount = 0, prevcount = 0; - cfranext = cfraprev = (float)(CFRA); + cfranext = cfraprev = (float)(scene->r.cfra); /* seed up dummy dopesheet context with flags to perform necessary filtering */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { @@ -559,7 +559,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ aknext = ED_keylist_find_next(keylist, cfranext); if (aknext) { - if (CFRA == (int)aknext->cfra) { + if (scene->r.cfra == (int)aknext->cfra) { /* make this the new starting point for the search and ignore */ cfranext = aknext->cfra; } @@ -577,7 +577,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ akprev = ED_keylist_find_prev(keylist, cfraprev); if (akprev) { - if (CFRA == (int)akprev->cfra) { + if (scene->r.cfra == (int)akprev->cfra) { /* make this the new starting point for the search */ } else { @@ -599,14 +599,14 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ *r_prevfra = cfraprev; } else { - *r_prevfra = CFRA - (cfranext - CFRA); + *r_prevfra = scene->r.cfra - (cfranext - scene->r.cfra); } if (donenext) { *r_nextfra = cfranext; } else { - *r_nextfra = CFRA + (CFRA - cfraprev); + *r_nextfra = scene->r.cfra + (scene->r.cfra - cfraprev); } return true; diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index a75944fa2f2..d9eeed94868 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1807,11 +1807,13 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, ListBase tmp_data = {NULL, NULL}; size_t tmp_items = 0; - /* add gpencil animation channels */ - BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) { - tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + if (!(filter_mode & ANIMFILTER_FCURVESONLY)) { + /* add gpencil animation channels */ + BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) { + tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + } + END_ANIMFILTER_SUBCHANNELS; } - END_ANIMFILTER_SUBCHANNELS; /* did we find anything? */ if (tmp_items) { @@ -1925,6 +1927,9 @@ static size_t animdata_filter_ds_gpencil( tmp_items += animfilter_block_data(ac, &tmp_data, ads, &gpd->id, filter_mode); /* add Grease Pencil layers */ + if (!(filter_mode & ANIMFILTER_FCURVESONLY)) { + tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + } /* TODO: do these need a separate expander? * XXX: what order should these go in? */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index ad06b185132..3608140a29d 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -104,7 +104,7 @@ int ED_markers_post_apply_transform( ListBase *markers, Scene *scene, int mode, float value, char side) { TimeMarker *marker; - float cfra = (float)CFRA; + float cfra = (float)scene->r.cfra; int changed_tot = 0; /* sanity check - no markers, or locked markers */ @@ -1497,8 +1497,8 @@ static void ED_markers_select_leftright(bAnimContext *ac, } LISTBASE_FOREACH (TimeMarker *, marker, markers) { - if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= CFRA) || - (mode == MARKERS_LRSEL_RIGHT && marker->frame >= CFRA)) { + if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= scene->r.cfra) || + (mode == MARKERS_LRSEL_RIGHT && marker->frame >= scene->r.cfra)) { marker->flag |= SELECT; } } @@ -1588,12 +1588,13 @@ static void MARKER_OT_delete(wmOperatorType *ot) ot->idname = "MARKER_OT_delete"; /* api callbacks */ - ot->invoke = WM_operator_confirm; + ot->invoke = WM_operator_confirm_or_exec; ot->exec = ed_marker_delete_exec; ot->poll = ed_markers_poll_selected_no_locked_markers; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + WM_operator_properties_confirm_or_exec(ot); } /** \} */ @@ -1754,11 +1755,11 @@ static int ed_marker_camera_bind_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - marker = ED_markers_find_nearest_marker(markers, CFRA); - if ((marker == NULL) || (marker->frame != CFRA)) { + marker = ED_markers_find_nearest_marker(markers, scene->r.cfra); + if ((marker == NULL) || (marker->frame != scene->r.cfra)) { marker = MEM_callocN(sizeof(TimeMarker), "Camera TimeMarker"); marker->flag = SELECT; - marker->frame = CFRA; + marker->frame = scene->r.cfra; BLI_addtail(markers, marker); /* deselect all others, so that the user can then move it without problems */ diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index b15bd3db678..23c1d68b4d6 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -391,7 +391,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, return; } - const int cfra = CFRA; + const int cfra = scene->r.cfra; int sfra = INT_MAX, efra = INT_MIN; switch (range) { case ANIMVIZ_CALC_RANGE_CURRENT_FRAME: @@ -485,7 +485,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, sfra, efra, efra - sfra + 1); - for (CFRA = sfra; CFRA <= efra; CFRA++) { + for (scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) { if (range == ANIMVIZ_CALC_RANGE_CURRENT_FRAME) { /* For current frame, only update tagged. */ BKE_scene_graph_update_tagged(depsgraph, bmain); @@ -496,14 +496,14 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, } /* perform baking for targets */ - motionpaths_calc_bake_targets(targets, CFRA); + motionpaths_calc_bake_targets(targets, scene->r.cfra); } /* reset original environment */ /* NOTE: We don't always need to reevaluate the main scene, as the depsgraph * may be a temporary one that works on a subset of the data. * We always have to restore the current frame though. */ - CFRA = cfra; + scene->r.cfra = cfra; if (range != ANIMVIZ_CALC_RANGE_CURRENT_FRAME && restore) { motionpaths_calc_update_scene(depsgraph); } diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index f3972cb45df..c7e755fb6df 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -142,14 +142,14 @@ static void change_frame_apply(bContext *C, wmOperator *op) /* set the new frame number */ if (scene->r.flag & SCER_SHOW_SUBFRAME) { - CFRA = (int)frame; - SUBFRA = frame - (int)frame; + scene->r.cfra = (int)frame; + scene->r.subframe = frame - (int)frame; } else { - CFRA = round_fl_to_int(frame); - SUBFRA = 0.0f; + scene->r.cfra = round_fl_to_int(frame); + scene->r.subframe = 0.0f; } - FRAMENUMBER_MIN_CLAMP(CFRA); + FRAMENUMBER_MIN_CLAMP(scene->r.cfra); /* do updates */ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); @@ -382,7 +382,7 @@ static int anim_set_sfra_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - frame = CFRA; + frame = scene->r.cfra; /* if Preview Range is defined, set the 'start' frame for that */ if (PRVRANGEON) { @@ -437,7 +437,7 @@ static int anim_set_efra_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - frame = CFRA; + frame = scene->r.cfra; /* if Preview Range is defined, set the 'end' frame for that */ if (PRVRANGEON) { diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index c6f68228807..effedd4307d 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -124,7 +124,7 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[], insert_vert_fcurve( fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF); fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } } diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 6f31472907b..d2f0ee622c4 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -1020,7 +1020,7 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *c /* adding or removing the Cycles modifier requires an update to handles */ if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) { - calchandles_fcurve(curve); + BKE_fcurve_handles_recalc(curve); } /* did we succeed? */ diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index f8277cf6a85..706db498a82 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -444,7 +444,7 @@ void ANIM_animdata_keyframe_callback(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -831,7 +831,7 @@ static short snap_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt) { const Scene *scene = ked->scene; if (bezt->f2 & SELECT) { - bezt->vec[1][0] = (float)CFRA; + bezt->vec[1][0] = (float)scene->r.cfra; } return 0; } @@ -929,7 +929,7 @@ static short mirror_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt) const Scene *scene = ked->scene; if (bezt->f2 & SELECT) { - mirror_bezier_xaxis_ex(bezt, CFRA); + mirror_bezier_xaxis_ex(bezt, scene->r.cfra); } return 0; diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 00e2f221117..7723c221a40 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -47,77 +47,6 @@ /* **************************************************** */ -void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc) -{ - /* sanity check */ - if (fcu == NULL) { - return; - } - - /* verify the index: - * 1) cannot be greater than the number of available keyframes - * 2) negative indices are for specifying a value from the end of the array - */ - if (abs(index) >= fcu->totvert) { - return; - } - if (index < 0) { - index += fcu->totvert; - } - - /* Delete this keyframe */ - memmove( - &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); - fcu->totvert--; - - if (fcu->totvert == 0) { - MEM_SAFE_FREE(fcu->bezt); - } - - /* recalc handles - only if it won't cause problems */ - if (do_recalc) { - calchandles_fcurve(fcu); - } -} - -bool delete_fcurve_keys(FCurve *fcu) -{ - bool changed = false; - - if (fcu->bezt == NULL) { /* ignore baked curves */ - return false; - } - - /* Delete selected BezTriples */ - for (int i = 0; i < fcu->totvert; i++) { - if (fcu->bezt[i].f2 & SELECT) { - if (i == fcu->active_keyframe_index) { - BKE_fcurve_active_keyframe_set(fcu, NULL); - } - memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); - fcu->totvert--; - i--; - changed = true; - } - } - - /* Free the array of BezTriples if there are not keyframes */ - if (fcu->totvert == 0) { - clear_fcurve_keys(fcu); - } - - return changed; -} - -void clear_fcurve_keys(FCurve *fcu) -{ - MEM_SAFE_FREE(fcu->bezt); - - fcu->totvert = 0; -} - -/* ---------------- */ - bool duplicate_fcurve_keys(FCurve *fcu) { bool changed = false; @@ -282,7 +211,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo } if (fcu->bezt->vec[1][1] == default_value) { - clear_fcurve_keys(fcu); + BKE_fcurve_delete_keys_all(fcu); /* check if curve is really unused and if it is, return signal for deletion */ if (BKE_fcurve_is_empty(fcu)) { @@ -679,7 +608,7 @@ void smooth_fcurve(FCurve *fcu) } /* recalculate handles */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* ---------------- */ @@ -762,7 +691,7 @@ void sample_fcurve(FCurve *fcu) } /* recalculate channel's handles? */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* **************************************************** */ @@ -921,7 +850,7 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) } /* in case 'relative' paste method is used */ - animcopy_cfra = CFRA; + animcopy_cfra = scene->r.cfra; /* everything went fine */ return 0; @@ -1121,7 +1050,7 @@ static void paste_animedit_keys_fcurve( case KEYFRAME_PASTE_MERGE_OVER: /* remove all keys */ - clear_fcurve_keys(fcu); + BKE_fcurve_delete_keys_all(fcu); break; case KEYFRAME_PASTE_MERGE_OVER_RANGE: @@ -1148,7 +1077,7 @@ static void paste_animedit_keys_fcurve( } /* remove frames in the range */ - delete_fcurve_keys(fcu); + BKE_fcurve_delete_keys_selected(fcu); } break; } @@ -1182,7 +1111,7 @@ static void paste_animedit_keys_fcurve( } /* recalculate F-Curve's handles? */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = { @@ -1217,11 +1146,11 @@ const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = { {0, NULL, 0, NULL, NULL}, }; -short paste_animedit_keys(bAnimContext *ac, - ListBase *anim_data, - const eKeyPasteOffset offset_mode, - const eKeyMergeMode merge_mode, - bool flip) +eKeyPasteError paste_animedit_keys(bAnimContext *ac, + ListBase *anim_data, + const eKeyPasteOffset offset_mode, + const eKeyMergeMode merge_mode, + bool flip) { bAnimListElem *ale; @@ -1235,25 +1164,23 @@ short paste_animedit_keys(bAnimContext *ac, /* check if buffer is empty */ if (BLI_listbase_is_empty(&animcopybuf)) { - BKE_report(ac->reports, RPT_ERROR, "No animation data in buffer to paste"); - return -1; + return KEYFRAME_PASTE_NOTHING_TO_PASTE; } if (BLI_listbase_is_empty(anim_data)) { - BKE_report(ac->reports, RPT_ERROR, "No selected F-Curves to paste into"); - return -1; + return KEYFRAME_PASTE_NOWHERE_TO_PASTE; } /* methods of offset */ switch (offset_mode) { case KEYFRAME_PASTE_OFFSET_CFRA_START: - offset = (float)(CFRA - animcopy_firstframe); + offset = (float)(scene->r.cfra - animcopy_firstframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_END: - offset = (float)(CFRA - animcopy_lastframe); + offset = (float)(scene->r.cfra - animcopy_lastframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: - offset = (float)(CFRA - animcopy_cfra); + offset = (float)(scene->r.cfra - animcopy_cfra); break; case KEYFRAME_PASTE_OFFSET_NONE: offset = 0.0f; @@ -1335,7 +1262,7 @@ short paste_animedit_keys(bAnimContext *ac, ANIM_animdata_update(ac, anim_data); - return 0; + return KEYFRAME_PASTE_OK; } /* **************************************************** */ diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index aa99a4e50c3..9084b9bb214 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -639,7 +639,7 @@ int insert_vert_fcurve( * - we may calculate twice (due to auto-handle needing to be calculated twice) */ if ((flag & INSERTKEY_FAST) == 0) { - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* return the index at which the keyframe was added */ @@ -1282,10 +1282,12 @@ static bool insert_keyframe_value(ReportList *reports, /* delete keyframe immediately before/after newly added */ switch (insert_mode) { case KEYNEEDED_DELPREV: - delete_fcurve_key(fcu, fcu->totvert - 2, 1); + BKE_fcurve_delete_key(fcu, fcu->totvert - 2); + BKE_fcurve_handles_recalc(fcu); break; case KEYNEEDED_DELNEXT: - delete_fcurve_key(fcu, 1, 1); + BKE_fcurve_delete_key(fcu, 1); + BKE_fcurve_handles_recalc(fcu); break; } @@ -1683,7 +1685,8 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra) i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); /* Only delete curve too if it won't be doing anything anymore */ if (BKE_fcurve_is_empty(fcu)) { @@ -1947,7 +1950,8 @@ static int insert_key_exec(bContext *C, wmOperator *op) Object *obedit = CTX_data_edit_object(C); bool ob_edit_mode = false; - float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ + float cfra = (float) + scene->r.cfra; /* XXX for now, don't bother about all the yucky offset crap */ int num_channels; const bool confirm = op->flag & OP_IS_INVOKE; @@ -2168,7 +2172,8 @@ static int delete_key_exec(bContext *C, wmOperator *op) static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *ks) { Scene *scene = CTX_data_scene(C); - float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ + float cfra = (float) + scene->r.cfra; /* XXX for now, don't bother about all the yucky offset crap */ int num_channels; const bool confirm = op->flag & OP_IS_INVOKE; @@ -2344,7 +2349,7 @@ void ANIM_OT_keyframe_clear_v3d(wmOperatorType *ot) static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - float cfra = (float)CFRA; + float cfra = (float)scene->r.cfra; int selected_objects_len = 0; int selected_objects_success_len = 0; @@ -2494,7 +2499,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) char *path; uiBut *but; const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( - CTX_data_depsgraph_pointer(C), (float)CFRA); + CTX_data_depsgraph_pointer(C), (float)scene->r.cfra); bool changed = false; int index; const bool all = RNA_boolean_get(op->ptr, "all"); @@ -2663,7 +2668,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) PropertyRNA *prop = NULL; Main *bmain = CTX_data_main(C); char *path; - float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ + float cfra = (float) + scene->r.cfra; /* XXX for now, don't bother about all the yucky offset crap */ bool changed = false; int index; const bool all = RNA_boolean_get(op->ptr, "all"); @@ -2706,7 +2712,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); changed = true; } } @@ -2835,7 +2842,7 @@ void ANIM_OT_keyframe_clear_button(wmOperatorType *ot) bool autokeyframe_cfra_can_key(const Scene *scene, ID *id) { - float cfra = (float)CFRA; /* XXX for now, this will do */ + float cfra = (float)scene->r.cfra; /* XXX for now, this will do */ /* only filter if auto-key mode requires this */ if (IS_AUTOKEY_ON(scene) == 0) { @@ -3065,7 +3072,7 @@ bool ED_autokeyframe_object(bContext *C, Scene *scene, Object *ob, KeyingSet *ks * 3) Free the extra info. */ ANIM_relative_keyingset_add_source(&dsources, &ob->id, NULL, NULL); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra); BLI_freelistN(&dsources); return true; @@ -3085,7 +3092,7 @@ bool ED_autokeyframe_pchan( * 3) Free the extra info. */ ANIM_relative_keyingset_add_source(&dsources, &ob->id, &RNA_PoseBone, pchan); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra); BLI_freelistN(&dsources); return true; diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index dd43e3e6613..2071f056f9e 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -422,7 +422,7 @@ static void updateDuplicateActionConstraintSettings( float mat[4][4]; bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan}; - BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon); + BKE_constraint_custom_object_space_init(&cob, curcon); unit_m4(mat); bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget); @@ -576,7 +576,7 @@ static void updateDuplicateLocRotConstraintSettings(Object *ob, unit_m4(local_mat); bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan}; - BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon); + BKE_constraint_custom_object_space_init(&cob, curcon); BKE_constraint_mat_convertspace( ob, pchan, &cob, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false); @@ -631,7 +631,7 @@ static void updateDuplicateTransformConstraintSettings(Object *ob, float target_mat[4][4], own_mat[4][4], imat[4][4]; bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan}; - BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon); + BKE_constraint_custom_object_space_init(&cob, curcon); unit_m4(own_mat); BKE_constraint_mat_convertspace( diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 1f02e24666d..4f329dbe449 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -13,6 +13,7 @@ #include "MEM_guardedalloc.h" #include "DNA_armature_types.h" +#include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" @@ -281,6 +282,17 @@ void ED_armature_bone_rename(Main *bmain, } } + /* fix camera focus */ + if (ob->type == OB_CAMERA) { + Camera *cam = (Camera *)ob->data; + if ((cam->dof.focus_object != NULL) && (cam->dof.focus_object->data == arm)) { + if (STREQ(cam->dof.focus_subtarget, oldname)) { + BLI_strncpy(cam->dof.focus_subtarget, newname, MAXBONENAME); + DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE); + } + } + } + /* fix grease pencil modifiers and vertex groups */ if (ob->type == OB_GPENCIL) { diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index ea54c3014ca..ff187a52154 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -24,6 +24,7 @@ #include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_fcurve.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -448,7 +449,7 @@ static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *U ICON_NONE, "POSELIB_OT_pose_add", "frame", - CFRA); + scene->r.cfra); /* Replace existing - sub-menu. */ uiItemMenuF( @@ -615,7 +616,8 @@ static int poselib_remove_exec(bContext *C, wmOperator *op) for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* check if remove */ if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) { - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); break; } } @@ -1113,7 +1115,7 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData /* perform actual auto-keying now */ if (autokey) { /* insert keyframes for all relevant bones in one go */ - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra); BLI_freelistN(&dsources); } @@ -1578,7 +1580,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con case EVT_PADMINUS: if (pld->searchstr[0]) { /* searching... */ - poselib_preview_handle_search(pld, event->type, event->ascii); + poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event)); } else { /* view manipulation (see above) */ @@ -1589,7 +1591,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con /* otherwise, assume that searching might be able to handle it */ default: - poselib_preview_handle_search(pld, event->type, event->ascii); + poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event)); break; } diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 9ee289145c4..d866062cec0 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -134,7 +134,7 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd) } /* Perform actual auto-keying. */ - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra); BLI_freelistN(&dsources); /* send notifiers for this */ diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 0efa32ec63a..38c99c2ef60 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -2061,12 +2061,12 @@ static int pose_propagate_exec(bContext *C, wmOperator *op) if (mode == POSE_PROPAGATE_SMART_HOLDS) { /* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting * from the keyframe that occurs after the current frame. */ - modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA); + modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)scene->r.cfra); } /* Go through propagating pose to keyframes, curve by curve. */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { - pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData); + pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)scene->r.cfra, modeData); } } diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index f0b0218d7e0..cfc6b0b6b6e 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -1201,7 +1201,7 @@ static int pose_clear_transform_generic_exec(bContext *C, KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); /* insert keyframes */ - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)scene->r.cfra); /* now recalculate paths */ if (ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { @@ -1343,8 +1343,8 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - (float)CFRA); + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( + depsgraph, (float)scene->r.cfra); const bool only_select = RNA_boolean_get(op->ptr, "only_selected"); FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) { 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/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index ba5a7409ba7..ee6376ca95f 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -87,6 +87,8 @@ static const char *get_surf_defname(int type) return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle"); case CU_PRIM_PATCH: return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch"); + case CU_PRIM_TUBE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCylinder"); case CU_PRIM_SPHERE: return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere"); case CU_PRIM_DONUT: diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 6946c09e6f1..7632f1b1e64 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -1162,7 +1162,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) curve_draw_event_add_first(op, event); } } - else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + else if (ISMOUSE_MOTION(event->type)) { if (cdd->state == CURVE_DRAW_PAINTING) { const float mval_fl[2] = {UNPACK2(event->mval)}; if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) { diff --git a/source/blender/editors/curve/editcurve_pen.c b/source/blender/editors/curve/editcurve_pen.c index 729ad46877a..27f4e4fca61 100644 --- a/source/blender/editors/curve/editcurve_pen.c +++ b/source/blender/editors/curve/editcurve_pen.c @@ -1622,7 +1622,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event) } } - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (ISMOUSE_MOTION(event->type)) { /* Check if dragging */ if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) { cpd->dragging = true; diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 611dbb2e80c..ceed12dcff1 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -59,7 +59,7 @@ static int kill_selection(Object *obedit, int ins); /** \name Internal Utilities * \{ */ -static char32_t findaccent(char32_t char1, uint code) +static char32_t findaccent(char32_t char1, const char code) { char32_t new = 0; @@ -1638,12 +1638,11 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) Object *obedit = CTX_data_edit_object(C); Curve *cu = obedit->data; EditFont *ef = cu->editfont; - static int accentcode = 0; - uintptr_t ascii = event->ascii; + static bool accentcode = false; const bool alt = event->modifier & KM_ALT; const bool shift = event->modifier & KM_SHIFT; const bool ctrl = event->modifier & KM_CTRL; - int event_type = event->type, event_val = event->val; + char32_t insert_char_override = 0; char32_t inserted_text[2] = {0}; if (RNA_struct_property_is_set(op->ptr, "text")) { @@ -1652,48 +1651,47 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (RNA_struct_property_is_set(op->ptr, "accent")) { if (ef->len != 0 && ef->pos > 0) { - accentcode = 1; + accentcode = true; } return OPERATOR_FINISHED; } - /* tab should exit editmode, but we allow it to be typed using modifier keys */ - if (event_type == EVT_TABKEY) { - if ((alt || ctrl || shift) == 0) { - return OPERATOR_PASS_THROUGH; - } - - ascii = 9; - } - - if (event_type == EVT_BACKSPACEKEY) { + if (event->type == EVT_BACKSPACEKEY) { if (alt && ef->len != 0 && ef->pos > 0) { - accentcode = 1; + accentcode = true; } return OPERATOR_PASS_THROUGH; } - if (event_val && (ascii || event->utf8_buf[0])) { - /* handle case like TAB (== 9) */ - if ((ascii > 31 && ascii < 254 && ascii != 127) || (ELEM(ascii, 13, 10)) || (ascii == 8) || - (event->utf8_buf[0])) { + /* Tab typically exit edit-mode, but we allow it to be typed using modifier keys. */ + if (event->type == EVT_TABKEY) { + if ((alt || ctrl || shift) == 0) { + return OPERATOR_PASS_THROUGH; + } + insert_char_override = '\t'; + } + if (insert_char_override || event->utf8_buf[0]) { + if (insert_char_override) { + /* Handle case like TAB ('\t'). */ + inserted_text[0] = insert_char_override; + insert_into_textbuf(obedit, insert_char_override); + text_update_edited(C, obedit, FO_EDIT); + } + else { + BLI_assert(event->utf8_buf[0]); if (accentcode) { if (ef->pos > 0) { - inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], ascii); + inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], + BLI_str_utf8_as_unicode(event->utf8_buf)); ef->textbuf[ef->pos - 1] = inserted_text[0]; } - accentcode = 0; + accentcode = false; } else if (event->utf8_buf[0]) { inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf); - ascii = inserted_text[0]; - insert_into_textbuf(obedit, ascii); - accentcode = 0; - } - else if (ascii) { - insert_into_textbuf(obedit, ascii); - accentcode = 0; + insert_into_textbuf(obedit, inserted_text[0]); + accentcode = false; } else { BLI_assert(0); @@ -1702,11 +1700,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) kill_selection(obedit, 1); text_update_edited(C, obedit, FO_EDIT); } - else { - inserted_text[0] = ascii; - insert_into_textbuf(obedit, ascii); - text_update_edited(C, obedit, FO_EDIT); - } } else { return OPERATOR_PASS_THROUGH; @@ -1720,11 +1713,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_string_set(op->ptr, "text", inserted_utf8); } - /* reset property? */ - if (event_val == 0) { - accentcode = 0; - } - return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index 3c31e8014ff..303d2fb71dc 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -8,6 +8,7 @@ set(INC ../../depsgraph ../../functions ../../geometry + ../../gpu ../../makesdna ../../makesrna ../../windowmanager @@ -27,5 +28,17 @@ set(LIB bf_blenlib ) +if(WITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() +endif() + blender_add_lib(bf_editor_curves "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") add_dependencies(bf_editor_curves bf_rna) diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 552ef1d96c8..79916253207 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -6,12 +6,96 @@ #include "BLI_rand.hh" +#include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "ED_curves.h" +#include "ED_node.h" +#include "ED_object.h" + +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" namespace blender::ed::curves { +static bool has_surface_deformation_node(const bNodeTree &ntree) +{ + LISTBASE_FOREACH (const bNode *, node, &ntree.nodes) { + if (node->type == GEO_NODE_DEFORM_CURVES_ON_SURFACE) { + return true; + } + if (node->type == NODE_GROUP) { + if (node->id != nullptr) { + if (has_surface_deformation_node(*reinterpret_cast<const bNodeTree *>(node->id))) { + return true; + } + } + } + } + return false; +} + +static bool has_surface_deformation_node(const Object &curves_ob) +{ + LISTBASE_FOREACH (const ModifierData *, md, &curves_ob.modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + if (nmd->node_group == nullptr) { + continue; + } + if (has_surface_deformation_node(*nmd->node_group)) { + return true; + } + } + return false; +} + +void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob) +{ + if (has_surface_deformation_node(curves_ob)) { + return; + } + + Main *bmain = CTX_data_main(&C); + Scene *scene = CTX_data_scene(&C); + + ModifierData *md = ED_object_modifier_add( + nullptr, bmain, scene, &curves_ob, "Surface Deform", eModifierType_Nodes); + NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md); + nmd.node_group = ntreeAddTree(bmain, "Surface Deform", "GeometryNodeTree"); + + bNodeTree *ntree = nmd.node_group; + ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry"); + ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT); + bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT); + bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE); + + ED_node_tree_propagate_change(&C, bmain, nmd.node_group); + + nodeAddLink(ntree, + group_input, + static_cast<bNodeSocket *>(group_input->outputs.first), + deform_node, + nodeFindSocket(deform_node, SOCK_IN, "Curves")); + nodeAddLink(ntree, + deform_node, + nodeFindSocket(deform_node, SOCK_OUT, "Curves"), + group_output, + static_cast<bNodeSocket *>(group_output->inputs.first)); + + group_input->locx = -200; + group_output->locx = 200; + deform_node->locx = 0; + + ED_node_tree_propagate_change(&C, bmain, nmd.node_group); +} + bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int points_per_curve) { bke::CurvesGeometry curves(points_per_curve * curves_size, curves_size); diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 25bcba6cfb3..740e53b59f8 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -25,6 +25,7 @@ #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_mesh_runtime.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -72,7 +73,7 @@ static bool object_has_editable_curves(const Main &bmain, const Object &object) return true; } -static VectorSet<Curves *> get_unique_editable_curves(const bContext &C) +VectorSet<Curves *> get_unique_editable_curves(const bContext &C) { VectorSet<Curves *> unique_curves; @@ -93,6 +94,47 @@ static VectorSet<Curves *> get_unique_editable_curves(const bContext &C) return unique_curves; } +static bool curves_poll_impl(bContext *C, const bool check_editable, const bool check_surface) +{ + Object *object = CTX_data_active_object(C); + if (object == nullptr || object->type != OB_CURVES) { + return false; + } + if (check_editable) { + if (!ED_operator_object_active_editable_ex(C, object)) { + return false; + } + } + if (check_surface) { + Curves &curves = *static_cast<Curves *>(object->data); + if (curves.surface == nullptr || curves.surface->type != OB_MESH) { + CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set"); + return false; + } + } + return true; +} + +bool editable_curves_with_surface_poll(bContext *C) +{ + return curves_poll_impl(C, true, true); +} + +bool curves_with_surface_poll(bContext *C) +{ + return curves_poll_impl(C, false, true); +} + +bool editable_curves_poll(bContext *C) +{ + return curves_poll_impl(C, false, false); +} + +bool curves_poll(bContext *C) +{ + return curves_poll_impl(C, false, false); +} + using bke::CurvesGeometry; namespace convert_to_particle_system { @@ -243,17 +285,14 @@ static void try_convert_single_object(Object &curves_ob, } /* Prepare transformation matrices. */ - const float4x4 curves_to_world_mat = curves_ob.obmat; - const float4x4 surface_to_world_mat = surface_ob.obmat; - const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; + const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob}; for (const int new_hair_i : IndexRange(hair_num)) { const int curve_i = new_hair_i; const IndexRange points = curves.points_for_curve(curve_i); const float3 &root_pos_cu = positions_cu[points.first()]; - const float3 root_pos_su = curves_to_surface_mat * root_pos_cu; + const float3 root_pos_su = transforms.curves_to_surface * root_pos_cu; BVHTreeNearest nearest; nearest.dist_sq = FLT_MAX; @@ -293,7 +332,7 @@ static void try_convert_single_object(Object &curves_ob, for (const int key_i : hair_keys.index_range()) { const float3 &key_pos_cu = positions_cu[points[key_i]]; - const float3 key_pos_su = curves_to_surface_mat * key_pos_cu; + const float3 key_pos_su = transforms.curves_to_surface * key_pos_cu; const float3 key_pos_ha = surface_to_hair_mat * key_pos_su; HairKey &key = hair_keys[key_i]; @@ -339,16 +378,6 @@ static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static bool curves_convert_to_particle_system_poll(bContext *C) -{ - Object *ob = CTX_data_active_object(C); - if (ob == nullptr || ob->type != OB_CURVES) { - return false; - } - Curves &curves = *static_cast<Curves *>(ob->data); - return curves.surface != nullptr; -} - } // namespace convert_to_particle_system static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot) @@ -357,7 +386,7 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot) ot->idname = "CURVES_OT_convert_to_particle_system"; ot->description = "Add a new or update an existing hair particle system on the surface object"; - ot->poll = convert_to_particle_system::curves_convert_to_particle_system_poll; + ot->poll = curves_with_surface_poll; ot->exec = convert_to_particle_system::curves_convert_to_particle_system_exec; ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; @@ -467,7 +496,6 @@ static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNU } Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name); - ob_new->dtx |= OB_DRAWBOUNDOX; /* TODO: Remove once there is actual drawing. */ Curves *curves_id = static_cast<Curves *>(ob_new->data); BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false); bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval); @@ -504,174 +532,169 @@ enum class AttachMode { Deform, }; -static bool snap_curves_to_surface_poll(bContext *C) +static void snap_curves_to_surface_exec_object(Object &curves_ob, + const Object &surface_ob, + const AttachMode attach_mode, + bool *r_invalid_uvs, + bool *r_missing_uvs) { - Object *ob = CTX_data_active_object(C); - if (ob == nullptr || ob->type != OB_CURVES) { - return false; - } - if (!ED_operator_object_active_editable_ex(C, ob)) { - return false; + Curves &curves_id = *static_cast<Curves *>(curves_ob.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + + Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data); + + VArraySpan<float2> surface_uv_map; + if (curves_id.surface_uv_map != nullptr) { + const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh); + surface_uv_map = surface_attributes + .lookup(curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2) + .typed<float2>(); } - Curves &curves = *static_cast<Curves *>(ob->data); - if (curves.surface == nullptr) { - return false; + + MutableSpan<float3> positions_cu = curves.positions_for_write(); + MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); + + const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh), + BKE_mesh_runtime_looptri_len(&surface_mesh)}; + + const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob}; + + switch (attach_mode) { + case AttachMode::Nearest: { + BVHTreeFromMesh surface_bvh; + BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange points = curves.points_for_curve(curve_i); + const int first_point_i = points.first(); + const float3 old_first_point_pos_cu = positions_cu[first_point_i]; + const float3 old_first_point_pos_su = transforms.curves_to_surface * + old_first_point_pos_cu; + + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest(surface_bvh.tree, + old_first_point_pos_su, + &nearest, + surface_bvh.nearest_callback, + &surface_bvh); + const int looptri_index = nearest.index; + if (looptri_index == -1) { + continue; + } + + const float3 new_first_point_pos_su = nearest.co; + const float3 new_first_point_pos_cu = transforms.surface_to_curves * + new_first_point_pos_su; + const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu; + + for (float3 &pos_cu : positions_cu.slice(points)) { + pos_cu += pos_diff_cu; + } + + if (!surface_uv_map.is_empty()) { + const MLoopTri &looptri = surface_looptris[looptri_index]; + const int corner0 = looptri.tri[0]; + const int corner1 = looptri.tri[1]; + const int corner2 = looptri.tri[2]; + const float2 &uv0 = surface_uv_map[corner0]; + const float2 &uv1 = surface_uv_map[corner1]; + const float2 &uv2 = surface_uv_map[corner2]; + const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co; + const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co; + const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su); + const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2); + surface_uv_coords[curve_i] = uv; + } + } + }); + break; + } + case AttachMode::Deform: { + if (surface_uv_map.is_empty()) { + *r_missing_uvs = true; + break; + } + using geometry::ReverseUVSampler; + ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris}; + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange points = curves.points_for_curve(curve_i); + const int first_point_i = points.first(); + const float3 old_first_point_pos_cu = positions_cu[first_point_i]; + + const float2 uv = surface_uv_coords[curve_i]; + ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv); + if (lookup_result.type != ReverseUVSampler::ResultType::Ok) { + *r_invalid_uvs = true; + continue; + } + + const MLoopTri &looptri = *lookup_result.looptri; + const float3 &bary_coords = lookup_result.bary_weights; + + const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co; + const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co; + const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co; + + float3 new_first_point_pos_su; + interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords); + const float3 new_first_point_pos_cu = transforms.surface_to_curves * + new_first_point_pos_su; + + const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu; + for (float3 &pos_cu : positions_cu.slice(points)) { + pos_cu += pos_diff_cu; + } + } + }); + break; + } } - return true; + + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); } static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) { const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode")); - std::atomic<bool> found_invalid_uv = false; + bool found_invalid_uvs = false; + bool found_missing_uvs = false; CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { if (curves_ob->type != OB_CURVES) { continue; } Curves &curves_id = *static_cast<Curves *>(curves_ob->data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); if (curves_id.surface == nullptr) { continue; } - Object &surface_ob = *curves_id.surface; - if (surface_ob.type != OB_MESH) { + if (curves_id.surface->type != OB_MESH) { continue; } - Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data); - - MeshComponent surface_mesh_component; - surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly); - - VArray_Span<float2> surface_uv_map; - if (curves_id.surface_uv_map != nullptr) { - surface_uv_map = surface_mesh_component - .attribute_try_get_for_read( - curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2) - .typed<float2>(); - } - - MutableSpan<float3> positions_cu = curves.positions_for_write(); - MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); - - const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh), - BKE_mesh_runtime_looptri_len(&surface_mesh)}; - - const float4x4 curves_to_world_mat = curves_ob->obmat; - const float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); - const float4x4 surface_to_world_mat = surface_ob.obmat; - const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; - const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat; - - switch (attach_mode) { - case AttachMode::Nearest: { - BVHTreeFromMesh surface_bvh; - BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); - - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { - for (const int curve_i : curves_range) { - const IndexRange points = curves.points_for_curve(curve_i); - const int first_point_i = points.first(); - const float3 old_first_point_pos_cu = positions_cu[first_point_i]; - const float3 old_first_point_pos_su = curves_to_surface_mat * old_first_point_pos_cu; - - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest(surface_bvh.tree, - old_first_point_pos_su, - &nearest, - surface_bvh.nearest_callback, - &surface_bvh); - const int looptri_index = nearest.index; - if (looptri_index == -1) { - continue; - } - - const float3 new_first_point_pos_su = nearest.co; - const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su; - const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu; - - for (float3 &pos_cu : positions_cu.slice(points)) { - pos_cu += pos_diff_cu; - } - - if (!surface_uv_map.is_empty()) { - const MLoopTri &looptri = surface_looptris[looptri_index]; - const int corner0 = looptri.tri[0]; - const int corner1 = looptri.tri[1]; - const int corner2 = looptri.tri[2]; - const float2 &uv0 = surface_uv_map[corner0]; - const float2 &uv1 = surface_uv_map[corner1]; - const float2 &uv2 = surface_uv_map[corner2]; - const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co; - const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co; - const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co; - float3 bary_coords; - interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su); - const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2); - surface_uv_coords[curve_i] = uv; - } - } - }); - break; - } - case AttachMode::Deform: { - if (surface_uv_map.is_empty()) { - BKE_report(op->reports, - RPT_ERROR, - "Curves do not have attachment information that can be used for deformation"); - break; - } - using geometry::ReverseUVSampler; - ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris}; - - threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { - for (const int curve_i : curves_range) { - const IndexRange points = curves.points_for_curve(curve_i); - const int first_point_i = points.first(); - const float3 old_first_point_pos_cu = positions_cu[first_point_i]; - - const float2 uv = surface_uv_coords[curve_i]; - ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv); - if (lookup_result.type != ReverseUVSampler::ResultType::Ok) { - found_invalid_uv = true; - continue; - } - - const MLoopTri &looptri = *lookup_result.looptri; - const float3 &bary_coords = lookup_result.bary_weights; - - const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co; - const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co; - const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co; - - float3 new_first_point_pos_su; - interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords); - const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su; - - const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu; - for (float3 &pos_cu : positions_cu.slice(points)) { - pos_cu += pos_diff_cu; - } - } - }); - break; - } - } - - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + snap_curves_to_surface_exec_object( + *curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs); } CTX_DATA_END; - if (found_invalid_uv) { + if (found_missing_uvs) { + BKE_report(op->reports, + RPT_ERROR, + "Curves do not have attachment information that can be used for deformation"); + } + if (found_invalid_uvs) { BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface"); } - WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); + /* Refresh the entire window to also clear eventual modifier and nodes editor warnings.*/ + WM_event_add_notifier(C, NC_WINDOW, nullptr); return OPERATOR_FINISHED; } @@ -686,7 +709,7 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot) ot->idname = "CURVES_OT_snap_curves_to_surface"; ot->description = "Move curves so that the first point is exactly on the surface mesh"; - ot->poll = snap_curves_to_surface_poll; + ot->poll = editable_curves_with_surface_poll; ot->exec = snap_curves_to_surface_exec; ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; @@ -715,21 +738,6 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot) "How to find the point on the surface to attach to"); } -static bool selection_poll(bContext *C) -{ - const Object *object = CTX_data_active_object(C); - if (object == nullptr) { - return false; - } - if (object->type != OB_CURVES) { - return false; - } - if (!BKE_id_is_editable(CTX_data_main(C), static_cast<const ID *>(object->data))) { - return false; - } - return true; -} - namespace set_selection_domain { static int curves_set_selection_domain_exec(bContext *C, wmOperator *op) @@ -745,21 +753,20 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op) curves_id->selection_domain = domain; curves_id->flag |= CV_SCULPT_SELECTION_ENABLED; - CurveComponent component; - component.replace(curves_id, GeometryOwnershipType::Editable); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) { VArray<float> curve_selection = curves.adapt_domain( curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); curve_selection.materialize(curves.selection_curve_float_for_write()); - component.attribute_try_delete(".selection_point_float"); + attributes.remove(".selection_point_float"); } else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) { VArray<float> point_selection = curves.adapt_domain( curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); point_selection.materialize(curves.selection_point_float_for_write()); - component.attribute_try_delete(".selection_curve_float"); + attributes.remove(".selection_curve_float"); } /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic @@ -784,7 +791,7 @@ static void CURVES_OT_set_selection_domain(wmOperatorType *ot) ot->description = "Change the mode used for selection masking in curves sculpt mode"; ot->exec = set_selection_domain::curves_set_selection_domain_exec; - ot->poll = selection_poll; + ot->poll = editable_curves_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -820,13 +827,11 @@ static void CURVES_OT_disable_selection(wmOperatorType *ot) ot->description = "Disable the drawing of influence of selection in sculpt mode"; ot->exec = disable_selection::curves_disable_selection_exec; - ot->poll = selection_poll; + ot->poll = editable_curves_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -namespace select_all { - static bool varray_contains_nonzero(const VArray<float> &data) { bool contains_nonzero = false; @@ -841,6 +846,19 @@ static bool varray_contains_nonzero(const VArray<float> &data) return contains_nonzero; } +bool has_anything_selected(const Curves &curves_id) +{ + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: + return varray_contains_nonzero(curves.selection_point_float()); + case ATTR_DOMAIN_CURVE: + return varray_contains_nonzero(curves.selection_curve_float()); + } + BLI_assert_unreachable(); + return false; +} + static bool any_point_selected(const CurvesGeometry &curves) { return varray_contains_nonzero(curves.selection_point_float()); @@ -856,6 +874,8 @@ static bool any_point_selected(const Span<Curves *> curves_ids) return false; } +namespace select_all { + static void invert_selection(MutableSpan<float> selection) { threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { @@ -876,25 +896,14 @@ static int select_all_exec(bContext *C, wmOperator *op) } for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); if (action == SEL_SELECT) { - /* The optimization to avoid storing the selection when everything is selected causes too - * many problems at the moment, since there is no proper visualization yet. Keep the code but - * disable it for now. */ -#if 0 - CurveComponent component; - component.replace(curves_id, GeometryOwnershipType::Editable); - component.attribute_try_delete(".selection_point_float"); - component.attribute_try_delete(".selection_curve_float"); -#else - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); - MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? - curves.selection_point_float_for_write() : - curves.selection_curve_float_for_write(); - selection.fill(1.0f); -#endif + /* As an optimization, just remove the selection attributes when everything is selected. */ + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + attributes.remove(".selection_point_float"); + attributes.remove(".selection_curve_float"); } else { - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? curves.selection_point_float_for_write() : curves.selection_curve_float_for_write(); @@ -924,13 +933,95 @@ static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot) ot->description = "(De)select all control points"; ot->exec = select_all::select_all_exec; - ot->poll = selection_poll; + ot->poll = editable_curves_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; WM_operator_properties_select_all(ot); } +namespace surface_set { + +static bool surface_set_poll(bContext *C) +{ + const Object *object = CTX_data_active_object(C); + if (object == nullptr) { + return false; + } + if (object->type != OB_MESH) { + return false; + } + return true; +} + +static int surface_set_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + Object &new_surface_ob = *CTX_data_active_object(C); + + Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data); + const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.ldata, + CD_MLOOPUV); + + CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) { + if (selected_ob->type != OB_CURVES) { + continue; + } + Object &curves_ob = *selected_ob; + Curves &curves_id = *static_cast<Curves *>(curves_ob.data); + + MEM_SAFE_FREE(curves_id.surface_uv_map); + if (new_uv_map_name != nullptr) { + curves_id.surface_uv_map = BLI_strdup(new_uv_map_name); + } + + bool missing_uvs; + bool invalid_uvs; + snap_curves_to_surface::snap_curves_to_surface_exec_object( + curves_ob, + new_surface_ob, + snap_curves_to_surface::AttachMode::Nearest, + &invalid_uvs, + &missing_uvs); + + /* Add deformation modifier if necessary. */ + blender::ed::curves::ensure_surface_deformation_node_exists(*C, curves_ob); + + curves_id.surface = &new_surface_ob; + ED_object_parent_set( + op->reports, C, scene, &curves_ob, &new_surface_ob, PAR_OBJECT, false, true, nullptr); + + DEG_id_tag_update(&curves_ob.id, ID_RECALC_TRANSFORM); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id); + + /* Required for deformation. */ + new_surface_ob.modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION; + DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY); + } + CTX_DATA_END; + + DEG_relations_tag_update(bmain); + + return OPERATOR_FINISHED; +} + +} // namespace surface_set + +static void CURVES_OT_surface_set(wmOperatorType *ot) +{ + ot->name = "Set Curves Surface Object"; + ot->idname = __func__; + ot->description = + "Use the active object as surface for selected curves objects and set it as the parent"; + + ot->exec = surface_set::surface_set_exec; + ot->poll = surface_set::surface_set_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + } // namespace blender::ed::curves void ED_operatortypes_curves() @@ -942,4 +1033,5 @@ void ED_operatortypes_curves() WM_operatortype_append(CURVES_OT_set_selection_domain); WM_operatortype_append(SCULPT_CURVES_OT_select_all); WM_operatortype_append(CURVES_OT_disable_selection); + WM_operatortype_append(CURVES_OT_surface_set); } diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index c7e782b7b89..a6a9b2fcd26 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -282,8 +282,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) RNA_enum_get(op->ptr, "mode")); Mesh *mesh = reinterpret_cast<Mesh *>(ob_data); - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); /* General conversion steps are always the same: * 1. Convert old data to right domain and data type. @@ -301,33 +300,33 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - GVArray src_varray = mesh_component.attribute_get_for_read(name, dst_domain, dst_type); + GVArray src_varray = attributes.lookup_or_default(name, dst_domain, dst_type); const CPPType &cpp_type = src_varray.type(); void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__); src_varray.materialize_to_uninitialized(new_data); - mesh_component.attribute_try_delete(name); - mesh_component.attribute_try_create(name, dst_domain, dst_type, AttributeInitMove(new_data)); + attributes.remove(name); + attributes.add(name, dst_domain, dst_type, blender::bke::AttributeInitMove(new_data)); break; } case ConvertAttributeMode::UVMap: { MLoopUV *dst_uvs = static_cast<MLoopUV *>( MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopUV), __func__)); - VArray<float2> src_varray = mesh_component.attribute_get_for_read<float2>( + VArray<float2> src_varray = attributes.lookup_or_default<float2>( name, ATTR_DOMAIN_CORNER, {0.0f, 0.0f}); for (const int i : IndexRange(mesh->totloop)) { copy_v2_v2(dst_uvs[i].uv, src_varray[i]); } - mesh_component.attribute_try_delete(name); + attributes.remove(name); CustomData_add_layer_named( &mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str()); break; } case ConvertAttributeMode::VertexGroup: { Array<float> src_weights(mesh->totvert); - VArray<float> src_varray = mesh_component.attribute_get_for_read<float>( + VArray<float> src_varray = attributes.lookup_or_default<float>( name, ATTR_DOMAIN_POINT, 0.0f); src_varray.materialize(src_weights); - mesh_component.attribute_try_delete(name); + attributes.remove(name); bDeformGroup *defgroup = BKE_object_defgroup_new(ob, name.c_str()); const int defgroup_index = BLI_findindex(BKE_id_defgroup_list_get(&mesh->id), defgroup); @@ -406,7 +405,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) prop = RNA_def_float_color( ot->srna, "color", 4, nullptr, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); - RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); + RNA_def_property_subtype(prop, PROP_COLOR); RNA_def_property_float_array_default(prop, default_color); } @@ -652,15 +651,16 @@ bool ED_geometry_attribute_convert(Mesh *mesh, return false; } - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - GVArray src_varray = mesh_component.attribute_get_for_read(name, new_domain, new_type); + blender::bke::MutableAttributeAccessor attributes = blender::bke::mesh_attributes_for_write( + *mesh); + + GVArray src_varray = attributes.lookup_or_default(name, new_domain, new_type); const CPPType &cpp_type = src_varray.type(); void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__); src_varray.materialize_to_uninitialized(new_data); - mesh_component.attribute_try_delete(name); - mesh_component.attribute_try_create(name, new_domain, new_type, AttributeInitMove(new_data)); + attributes.remove(name); + attributes.add(name, new_domain, new_type, blender::bke::AttributeInitMove(new_data)); int *active_index = BKE_id_attributes_active_index_p(&mesh->id); if (*active_index > 0) { 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/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index f720f261ad5..ae09aea28d3 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -841,7 +841,8 @@ void ED_annotation_draw_2dimage(const bContext *C) } /* draw it! */ - annotation_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, area->spacetype); + annotation_draw_data_all( + scene, gpd, offsx, offsy, sizex, sizey, scene->r.cfra, dflag, area->spacetype); } void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) @@ -877,7 +878,7 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) } annotation_draw_data_all( - scene, gpd, 0, 0, region->winx, region->winy, CFRA, dflag, area->spacetype); + scene, gpd, 0, 0, region->winx, region->winy, scene->r.cfra, dflag, area->spacetype); } void ED_annotation_draw_view3d( @@ -928,7 +929,8 @@ void ED_annotation_draw_view3d( } /* draw it! */ - annotation_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + annotation_draw_data_all( + scene, gpd, offsx, offsy, winx, winy, scene->r.cfra, dflag, v3d->spacetype); } void ED_annotation_draw_ex( diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 8c393cc4f3f..d08d56a354a 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1568,7 +1568,7 @@ static void annotation_paint_initstroke(tGPsdata *p, add_frame_mode = GP_GETFRAME_ADD_NEW; } - p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_frame_get(p->gpl, scene->r.cfra, add_frame_mode); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; @@ -2062,11 +2062,18 @@ static void annotation_draw_apply_event( PointerRNA itemptr; float mousef[2]; - /* convert from window-space to area-space mouse coordinates - * add any x,y override position for fake events - */ - p->mval[0] = (float)event->mval[0] - x; - p->mval[1] = (float)event->mval[1] - y; + /* Convert from window-space to area-space mouse coordinates + * add any x,y override position for fake events. */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + /* The first run may be a drag event, see: T99368. */ + WM_event_drag_start_mval_fl(event, p->region, p->mval); + p->mval[0] -= x; + p->mval[1] -= y; + } + else { + p->mval[0] = (float)event->mval[0] - x; + p->mval[1] = (float)event->mval[1] - y; + } /* Key to toggle stabilization. */ if ((event->modifier & KM_SHIFT) && (p->paintmode == GP_PAINTMODE_DRAW)) { @@ -2634,7 +2641,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* handle mode-specific events */ if (p->status == GP_STATUS_PAINTING) { /* handle painting mouse-movements? */ - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { + if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { /* handle drawing event */ if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { annotation_add_missing_events(C, op, event, p); diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 949043e4be1..8a98dcb57fc 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -214,6 +214,18 @@ void ED_gpencil_layer_frames_select_region(KeyframeEditData *ked, } } +void ED_gpencil_set_active_channel(bGPdata *gpd, bGPDlayer *gpl) +{ + gpl->flag |= GP_LAYER_SELECT; + + /* Update other layer status. */ + if (BKE_gpencil_layer_active_get(gpd) != gpl) { + BKE_gpencil_layer_active_set(gpd, gpl); + BKE_gpencil_layer_autolock_set(gpd, false); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } +} + /* ***************************************** */ /* Frame Editing Tools */ @@ -316,8 +328,13 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - /* assume that each of these is a GP layer */ for (ale = anim_data.first; ale; ale = ale->next) { + /* This function only deals with grease pencil layer frames. + * This check is needed in the case of a call from the main dopesheet. */ + if (ale->type != ANIMTYPE_GPLAYER) { + continue; + } + ListBase copied_frames = {NULL, NULL}; bGPDlayer *gpl = (bGPDlayer *)ale->data; @@ -354,19 +371,13 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) } /* in case 'relative' paste method is used */ - gpencil_anim_copy_cfra = CFRA; + gpencil_anim_copy_cfra = scene->r.cfra; /* clean up */ ANIM_animdata_freelist(&anim_data); - /* check if anything ended up in the buffer */ - if (ELEM(NULL, gpencil_anim_copybuf.first, gpencil_anim_copybuf.last)) { - BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); - return false; - } - /* report success */ - return true; + return !BLI_listbase_is_empty(&gpencil_anim_copybuf); } bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) @@ -381,7 +392,6 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* check if buffer is empty */ if (BLI_listbase_is_empty(&gpencil_anim_copybuf)) { - BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste"); return false; } @@ -393,13 +403,13 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* methods of offset (eKeyPasteOffset) */ switch (offset_mode) { case KEYFRAME_PASTE_OFFSET_CFRA_START: - offset = (CFRA - gpencil_anim_copy_firstframe); + offset = (scene->r.cfra - gpencil_anim_copy_firstframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_END: - offset = (CFRA - gpencil_anim_copy_lastframe); + offset = (scene->r.cfra - gpencil_anim_copy_lastframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: - offset = (CFRA - gpencil_anim_copy_cfra); + offset = (scene->r.cfra - gpencil_anim_copy_cfra); break; case KEYFRAME_PASTE_OFFSET_NONE: offset = 0; @@ -414,6 +424,11 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* from selected channels */ for (ale = anim_data.first; ale; ale = ale->next) { + /* only deal with GPlayers (case of calls from general dopesheet) */ + if (ale->type != ANIMTYPE_GPLAYER) { + continue; + } + bGPDlayer *gpld = (bGPDlayer *)ale->data; bGPDlayer *gpls = NULL; bGPDframe *gpfs, *gpf; @@ -503,7 +518,7 @@ static bool gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *scene) static bool gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene) { if (gpf->flag & GP_FRAME_SELECT) { - gpf->framenum = (int)CFRA; + gpf->framenum = (int)scene->r.cfra; } return false; } @@ -545,8 +560,8 @@ static bool gpencil_frame_mirror_cframe(bGPDframe *gpf, Scene *scene) int diff; if (gpf->flag & GP_FRAME_SELECT) { - diff = CFRA - gpf->framenum; - gpf->framenum = CFRA + diff; + diff = scene->r.cfra - gpf->framenum; + gpf->framenum = scene->r.cfra + diff; } return false; diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c index 2f22fad53e7..e8e6d328804 100644 --- a/source/blender/editors/gpencil/gpencil_add_blank.c +++ b/source/blender/editors/gpencil/gpencil_add_blank.c @@ -76,7 +76,7 @@ void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4])) bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); /* frames */ - BKE_gpencil_frame_addnew(layer, CFRA); + BKE_gpencil_frame_addnew(layer, scene->r.cfra); /* update depsgraph */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index 65ee732f6a9..bc046e89d21 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -844,8 +844,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) /* frames */ /* NOTE: No need to check for existing, as this will take care of it for us */ - bGPDframe *frameFills = BKE_gpencil_frame_addnew(Fills, CFRA); - bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, CFRA); + bGPDframe *frameFills = BKE_gpencil_frame_addnew(Fills, scene->r.cfra); + bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, scene->r.cfra); /* generate strokes */ gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false); diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index bc5fe9b5cfb..e24964c4832 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -211,8 +211,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ - bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA); - bGPDframe *frame_lines = BKE_gpencil_frame_addnew(lines, CFRA); + bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, scene->r.cfra); + bGPDframe *frame_lines = BKE_gpencil_frame_addnew(lines, scene->r.cfra); UNUSED_VARS(frame_color); /* generate stroke */ diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc index 66f53bea326..e480852a9bb 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.cc +++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc @@ -265,7 +265,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op } /* Move scene to new frame. */ - CFRA = i; + scene->r.cfra = i; BKE_scene_graph_update_for_newframe(depsgraph); /* Loop all objects in the list. */ @@ -285,7 +285,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op /* Apply time modifier. */ int remap_cfra = BKE_gpencil_time_modifier_cfra( - depsgraph, scene, elem->ob, gpl_src, CFRA, false); + depsgraph, scene, elem->ob, gpl_src, scene->r.cfra, false); /* Duplicate frame. */ bGPDframe *gpf_src = BKE_gpencil_layer_frame_get( gpl_src, remap_cfra, GP_GETFRAME_USE_PREV); @@ -293,7 +293,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op continue; } bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true); - gpf_dst->framenum = CFRA + frame_offset; + gpf_dst->framenum = scene->r.cfra + frame_offset; gpf_dst->flag &= ~GP_FRAME_SELECT; BLI_addtail(&gpl_dst->frames, gpf_dst); @@ -337,7 +337,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op } } /* Return scene frame state and DB to original state. */ - CFRA = oldframe; + scene->r.cfra = oldframe; BKE_scene_graph_update_for_newframe(depsgraph); /* Free memory. */ diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 0601d009bf7..e02a82f4555 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -588,7 +588,7 @@ static void gpencil_stroke_path_animation(bContext *C, } /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1271,7 +1271,7 @@ static void gpencil_layer_to_curve(bContext *C, Collection *collection = CTX_data_collection(C); Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV); bGPDstroke *prev_gps = NULL; Object *ob; Curve *cu; @@ -1414,7 +1414,7 @@ static bool gpencil_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, int i; bool valid = true; - if (!gpl || !(gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) || + if (!gpl || !(gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV)) || !(gps = gpf->strokes.first)) { return false; } @@ -1481,7 +1481,7 @@ static bool gpencil_convert_poll(bContext *C) * and if we are not in edit mode! */ return ((area && area->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_active_get(gpd)) && - (gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) && + (gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV)) && (gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd))); } @@ -1811,7 +1811,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op) /* Add layer and frame. */ bGPdata *gpd = (bGPdata *)ob->data; bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false); - bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA); + bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, scene->r.cfra); done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask); if (done) { diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 6843c42d2d0..b7ac73b9692 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -226,7 +226,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); /* Add a new frame to make it visible in Dopesheet. */ if (gpl != NULL) { - gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); + gpl->actframe = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW); } } } @@ -646,12 +646,12 @@ static int gpencil_frame_duplicate_exec(bContext *C, wmOperator *op) } if (mode == 0) { - BKE_gpencil_frame_addcopy(gpl_active, CFRA); + BKE_gpencil_frame_addcopy(gpl_active, scene->r.cfra); } else { LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->flag & GP_LAYER_LOCKED) == 0) { - BKE_gpencil_frame_addcopy(gpl, CFRA); + BKE_gpencil_frame_addcopy(gpl, scene->r.cfra); } } } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 5028baf1589..71cf9b1fafd 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1718,7 +1718,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op) * we are obliged to add a new frame if one * doesn't exist already */ - gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); + gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW); if (gpf) { /* Create new stroke */ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true); @@ -1971,7 +1971,7 @@ static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); - int cfra = CFRA; + int cfra = scene->r.cfra; bGPDlayer *active_gpl = BKE_gpencil_layer_active_get(gpd); @@ -2075,7 +2075,7 @@ static int gpencil_actframe_delete_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { @@ -2150,7 +2150,7 @@ static int gpencil_actframe_delete_all_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { /* try to get the "active" frame - but only if it actually occurs on this frame */ - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV); if (gpf == NULL) { continue; @@ -3709,35 +3709,44 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + bool changed = false; - /* read all selected strokes */ + /* Read all selected strokes. */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { continue; } + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + continue; + } - if (is_curve_edit) { - BKE_report(op->reports, RPT_ERROR, "Not implemented!"); - } - else { - /* Flip stroke. */ - BKE_gpencil_stroke_flip(gps); + if (is_curve_edit) { + BKE_report(op->reports, RPT_ERROR, "Not implemented!"); + } + else { + /* Flip stroke. */ + BKE_gpencil_stroke_flip(gps); + changed = true; + } + } } - - changed = true; + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } } @@ -3818,7 +3827,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) /* update frame to get the new location of objects */ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) { cfra_prv = gpf->framenum; - CFRA = gpf->framenum; + scene->r.cfra = gpf->framenum; BKE_scene_graph_update_for_newframe(depsgraph); } @@ -3846,7 +3855,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* return frame state and DB to original state */ - CFRA = oldframe; + scene->r.cfra = oldframe; BKE_scene_graph_update_for_newframe(depsgraph); if (sctx != NULL) { diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 3f06dbfdbb3..5305c764b3a 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1748,7 +1748,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) tgpf->v3d = tgpf->area->spacedata.first; tgpf->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); tgpf->win = CTX_wm_window(C); - tgpf->active_cfra = CFRA; + tgpf->active_cfra = scene->r.cfra; tgpf->reports = op->reports; /* Setup space conversions. */ @@ -2222,8 +2222,8 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Hash of selected frames. */ GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64); - /* If not multi-frame and there is no frame in CFRA for the active layer, create - * a new frame. */ + /* If not multi-frame and there is no frame in scene->r.cfra for the active layer, + * create a new frame. */ if (!is_multiedit) { tgpf->gpf = BKE_gpencil_layer_frame_get( tgpf->gpl, diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 0039dbae674..e7a4f2fe2dc 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -483,10 +483,10 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); tgpil->gpl = gpl; - bGPDframe *gpf = gpencil_get_previous_keyframe(gpl, CFRA); + bGPDframe *gpf = gpencil_get_previous_keyframe(gpl, scene->r.cfra); tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpf, true); - gpf = gpencil_get_next_keyframe(gpl, CFRA); + gpf = gpencil_get_next_keyframe(gpl, scene->r.cfra); tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpf, true); BLI_addtail(&tgpi->ilayers, tgpil); @@ -750,7 +750,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent tGPDinterpolate *tgpi = NULL; /* Cannot interpolate if not between 2 frames. */ - int cfra = CFRA; + int cfra = scene->r.cfra; bGPDframe *gpf_prv = gpencil_get_previous_keyframe(gpl, cfra); bGPDframe *gpf_next = gpencil_get_next_keyframe(gpl, cfra); if (ELEM(NULL, gpf_prv, gpf_next)) { @@ -1221,7 +1221,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) GP_SpaceConversion gsc; gpencil_point_conversion_init(C, &gsc); - int cfra = CFRA; + int cfra = scene->r.cfra; GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; const int step = RNA_int_get(op->ptr, "step"); diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index 06343dcad43..8ff3f20cef3 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -113,7 +113,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo else { add_frame_mode = GP_GETFRAME_ADD_NEW; } - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, add_frame_mode); /* stroke */ bGPDstroke *gps = BKE_gpencil_stroke_new(MAX2(ob->actcol - 1, 0), totpoints, brush->size); diff --git a/source/blender/editors/gpencil/gpencil_mesh.cc b/source/blender/editors/gpencil/gpencil_mesh.cc index aee00d4ede3..b27e1c75746 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.cc +++ b/source/blender/editors/gpencil/gpencil_mesh.cc @@ -283,7 +283,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) } /* Move scene to new frame. */ - CFRA = i; + scene->r.cfra = i; BKE_scene_graph_update_for_newframe(depsgraph); /* Loop all objects in the list. */ @@ -325,7 +325,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) } /* Return scene frame state and DB to original state. */ - CFRA = oldframe; + scene->r.cfra = oldframe; BKE_scene_graph_update_for_newframe(depsgraph); /* Remove unused materials. */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 7c7f532f087..13ea5179b23 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -2166,7 +2166,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, if (gpl->actframe && gpl->actframe->strokes.first) { if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { short frame_mode = IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_COPY : GP_GETFRAME_USE_PREV; - gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, frame_mode); + gpl->actframe = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, frame_mode); } has_layer_to_erase = true; break; @@ -2204,7 +2204,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, bool need_tag = p->gpl->actframe == NULL; bGPDframe *actframe = p->gpl->actframe; - p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_frame_get(p->gpl, scene->r.cfra, add_frame_mode); /* Only if there wasn't an active frame, need update. */ if (need_tag) { DEG_id_tag_update(&p->gpd->id, ID_RECALC_GEOMETRY); @@ -3755,7 +3755,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* handle mode-specific events */ if (p->status == GP_STATUS_PAINTING) { /* handle painting mouse-movements? */ - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { + if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { /* handle drawing event */ bool is_speed_guide = ((guide->use_guide) && (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index b57b8145749..befff611d58 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -292,7 +292,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; Brush *brush = tgpi->brush; - int cfra = CFRA; + int cfra = scene->r.cfra; bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); @@ -1195,7 +1195,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) tgpi->orign_type = RNA_enum_get(op->ptr, "type"); /* set current frame number */ - tgpi->cframe = CFRA; + tgpi->cframe = scene->r.cfra; /* set GP datablock */ tgpi->gpd = gpd; diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index f25d5937ac4..e27cd255217 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -1013,7 +1013,7 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso) gpl = CTX_data_active_gpencil_layer(C); } bGPDframe *gpf = BKE_gpencil_layer_frame_get( - gpl, CFRA, IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_NEW : GP_GETFRAME_USE_PREV); + gpl, scene->r.cfra, IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_NEW : GP_GETFRAME_USE_PREV); if (gpf == NULL) { continue; } @@ -1336,7 +1336,7 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso bGPdata *gpd = gso->gpd; Scene *scene = gso->scene; - int cfra = CFRA; + int cfra = scene->r.cfra; /* only try to add a new frame if this is the first stroke, or the frame has changed */ if ((gpd == NULL) || (cfra == gso->cfra)) { @@ -1518,7 +1518,8 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, /* To each point individually... */ pt = &gps->points[i]; - if ((pt->runtime.pt_orig == NULL) && (tool != GPSCULPT_TOOL_GRAB)) { + if ((i != gps->totpoints - 2) && (pt->runtime.pt_orig == NULL) && + (tool != GPSCULPT_TOOL_GRAB)) { continue; } pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index e903d11a1e0..a19265720e8 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -759,7 +759,7 @@ static bool gpencil_select_same_layer(bContext *C) bool changed = false; CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV); bGPDstroke *gps; bool found = false; diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index 24a0dab24c9..f6e88e05d46 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -295,7 +295,7 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op) job->base_active = CTX_data_active_base(C); job->ob_active = job->base_active->object; job->image = (Image *)job->ob_active->data; - job->frame_target = CFRA; + job->frame_target = scene->r.cfra; job->use_current_frame = RNA_boolean_get(op->ptr, "use_current_frame"); /* Create a new grease pencil object or reuse selected. */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index da40eef87fd..cc3c68abc55 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -324,11 +324,15 @@ typedef enum eAnimFilter_Flags { /** duplicate entries for animation data attached to multi-user blocks must not occur */ ANIMFILTER_NODUPLIS = (1 << 11), + /** avoid channel that does not have any F-curve data */ + ANIMFILTER_FCURVESONLY = (1 << 12), + /** for checking if we should keep some collapsed channel around (internal use only!) */ ANIMFILTER_TMP_PEEK = (1 << 30), /** Ignore ONLYSEL flag from #bDopeSheet.filterflag (internal use only!) */ ANIMFILTER_TMP_IGNORE_ONLYSEL = (1u << 31), + } eAnimFilter_Flags; /** \} */ @@ -1042,6 +1046,8 @@ void ED_keymap_anim(struct wmKeyConfig *keyconf); void ED_operatormacros_graph(void); /* space_action */ void ED_operatormacros_action(void); +/* space_nla*/ +void ED_operatormacros_nla(void); /** \} */ diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 9233b65b2ce..00831ff7cc3 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -6,6 +6,8 @@ #pragma once +struct bContext; + #ifdef __cplusplus extern "C" { #endif @@ -19,10 +21,19 @@ void ED_operatortypes_curves(void); #ifdef __cplusplus # include "BKE_curves.hh" +# include "BLI_vector_set.hh" namespace blender::ed::curves { bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve); +bool has_anything_selected(const Curves &curves_id); +VectorSet<Curves *> get_unique_editable_curves(const bContext &C); +void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob); -} +bool editable_curves_with_surface_poll(bContext *C); +bool curves_with_surface_poll(bContext *C); +bool editable_curves_poll(bContext *C); +bool curves_poll(bContext *C); + +} // namespace blender::ed::curves #endif diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h index 8aab1533e25..b1c0b649d2b 100644 --- a/source/blender/editors/include/ED_curves_sculpt.h +++ b/source/blender/editors/include/ED_curves_sculpt.h @@ -10,8 +10,33 @@ extern "C" { #endif +struct Curves; + void ED_operatortypes_sculpt_curves(void); #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +# include "BLI_index_mask.hh" +# include "BLI_vector.hh" + +namespace blender::ed::sculpt_paint { + +/** + * Find curves that have any point selected (a selection factor greater than zero), + * or curves that have their own selection factor greater than zero. + */ +IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices); + +/** + * Find points that are selected (a selection factor greater than zero), + * or points in curves with a selection factor greater than zero). + */ +IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices); + +} // namespace blender::ed::sculpt_paint + +#endif diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 0943636a452..b6488d6da56 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -239,7 +239,7 @@ void ED_annotation_draw_ex( /* ----------- Grease-Pencil AnimEdit API ------------------ */ /** - * Loops over the gp-frames for a gp-layer, and applies the given callback. + * Loops over the GP-frames for a GP-layer, and applies the given callback. */ bool ED_gpencil_layer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, @@ -281,6 +281,11 @@ void ED_gpencil_select_frames(struct bGPDlayer *gpl, short select_mode); void ED_gpencil_select_frame(struct bGPDlayer *gpl, int selx, short select_mode); /** + * Set the layer's channel as active + */ +void ED_gpencil_set_active_channel(struct bGPdata *gpd, struct bGPDlayer *gpl); + +/** * Delete selected frames. */ bool ED_gpencil_layer_frames_delete(struct bGPDlayer *gpl); diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 163797d395d..1d63e01c84b 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -229,6 +229,16 @@ typedef enum eKeyMergeMode { KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL, } eKeyMergeMode; +/* Possible errors occurring while pasting keys. */ +typedef enum eKeyPasteError { + /* No errors occurred */ + KEYFRAME_PASTE_OK, + /* Nothing was copied */ + KEYFRAME_PASTE_NOTHING_TO_PASTE, + /* No F-curves was selected to paste into*/ + KEYFRAME_PASTE_NOWHERE_TO_PASTE +} eKeyPasteError; + /** \} */ /* -------------------------------------------------------------------- */ @@ -378,9 +388,6 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con /* ************************************************ */ /* Destructive Editing API (keyframes_general.c) */ -void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc); -bool delete_fcurve_keys(struct FCurve *fcu); -void clear_fcurve_keys(struct FCurve *fcu); bool duplicate_fcurve_keys(struct FCurve *fcu); float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr); @@ -416,11 +423,11 @@ void sample_fcurve(struct FCurve *fcu); void ANIM_fcurves_copybuf_free(void); short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data); -short paste_animedit_keys(struct bAnimContext *ac, - ListBase *anim_data, - eKeyPasteOffset offset_mode, - eKeyMergeMode merge_mode, - bool flip); +eKeyPasteError paste_animedit_keys(struct bAnimContext *ac, + ListBase *anim_data, + eKeyPasteOffset offset_mode, + eKeyMergeMode merge_mode, + bool flip); /* ************************************************ */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 30a98129ee6..b73b62d5a9d 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -137,7 +137,6 @@ void EDBM_update_extern(struct Mesh *me, bool do_tessellation, bool is_destructi */ struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const struct Scene *scene, - bool face_selected, bool uv_selected, bool use_winding, bool do_islands); @@ -425,6 +424,9 @@ void paintvert_select_ungrouped(struct Object *ob, bool extend, bool flush_flags void paintvert_flush_flags(struct Object *ob); void paintvert_tag_select_update(struct bContext *C, struct Object *ob); +void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected); +void paintvert_reveal(struct bContext *C, struct Object *ob, bool select); + /* mirrtopo */ typedef struct MirrTopoStore_t { intptr_t *index_lookup; diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h index ba5834fd508..048424cdee1 100644 --- a/source/blender/editors/include/ED_paint.h +++ b/source/blender/editors/include/ED_paint.h @@ -22,6 +22,7 @@ struct UndoType; struct bContext; struct wmKeyConfig; struct wmOperator; +typedef struct PaintTileMap PaintTileMap; /* paint_ops.c */ @@ -76,7 +77,7 @@ void ED_image_undo_restore(struct UndoStep *us); /** Export for ED_undo_sys. */ void ED_image_undosys_type(struct UndoType *ut); -void *ED_image_paint_tile_find(struct ListBase *paint_tiles, +void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map, struct Image *image, struct ImBuf *ibuf, struct ImageUser *iuser, @@ -84,7 +85,7 @@ void *ED_image_paint_tile_find(struct ListBase *paint_tiles, int y_tile, unsigned short **r_mask, bool validate); -void *ED_image_paint_tile_push(struct ListBase *paint_tiles, +void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map, struct Image *image, struct ImBuf *ibuf, struct ImBuf **tmpibuf, @@ -98,7 +99,7 @@ void *ED_image_paint_tile_push(struct ListBase *paint_tiles, void ED_image_paint_tile_lock_init(void); void ED_image_paint_tile_lock_end(void); -struct ListBase *ED_image_paint_tile_list_get(void); +struct PaintTileMap *ED_image_paint_tile_map_get(void); #define ED_IMAGE_UNDO_TILE_BITS 6 #define ED_IMAGE_UNDO_TILE_SIZE (1 << ED_IMAGE_UNDO_TILE_BITS) 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_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh new file mode 100644 index 00000000000..dfddace8899 --- /dev/null +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editorui + * + * Base class for all views (UIs to display data sets) and view items, supporting common features. + * https://wiki.blender.org/wiki/Source/Interface/Views + * + * One of the most important responsibilities of the base class is managing reconstruction, + * enabling state that is persistent over reconstructions/redraws. Other features: + * - Renaming + * - Custom context menus + * - Notifier listening + * - Drag controllers (dragging view items) + * - Drop controllers (dropping onto/into view items) + */ + +#pragma once + +#include <array> +#include <memory> + +#include "DNA_defs.h" + +#include "BLI_span.hh" +#include "BLI_string_ref.hh" + +struct bContext; +struct uiBlock; +struct uiBut; +struct uiLayout; +struct uiViewItemHandle; +struct wmDrag; +struct wmNotifier; + +namespace blender::ui { + +class AbstractViewItem; +class AbstractViewItemDropController; +class AbstractViewItemDragController; + +class AbstractView { + friend class AbstractViewItem; + + bool is_reconstructed_ = false; + /** + * Only one item can be renamed at a time. So rather than giving each item an own rename buffer + * (which just adds unused memory in most cases), have one here that is managed by the view. + * + * This fixed-size buffer is needed because that's what the rename button requires. In future we + * may be able to bind the button to a `std::string` or similar. + */ + std::unique_ptr<std::array<char, MAX_NAME>> rename_buffer_; + + public: + virtual ~AbstractView() = default; + + /** Listen to a notifier, returning true if a redraw is needed. */ + virtual bool listen(const wmNotifier &) const; + + /** + * Makes \a item valid for display in this view. Behavior is undefined for items not registered + * with this. + */ + void register_item(AbstractViewItem &item); + + /** Only one item can be renamed at a time. */ + bool is_renaming() const; + /** \return If renaming was started successfully. */ + bool begin_renaming(); + void end_renaming(); + Span<char> get_rename_buffer() const; + MutableSpan<char> get_rename_buffer(); + + protected: + AbstractView() = default; + + virtual void update_children_from_old(const AbstractView &old_view) = 0; + + /** + * Match the view and its items against an earlier version of itself (if any) and copy the old UI + * state (e.g. collapsed, active, selected, renaming, etc.) to the new one. See + * #AbstractViewItem.update_from_old(). + * After this, reconstruction is complete (see #is_reconstructed()). + */ + void update_from_old(uiBlock &new_block); + /** + * Check if the view is fully (re-)constructed. That means, both the build function and + * #update_from_old() have finished. + */ + bool is_reconstructed() const; +}; + +class AbstractViewItem { + friend class AbstractView; + friend class ViewItemAPIWrapper; + + protected: + /** + * The view this item is a part of, and was registered for using #AbstractView::register_item(). + * If this wasn't done, the behavior of items is undefined. + */ + AbstractView *view_ = nullptr; + bool is_active_ = false; + bool is_renaming_ = false; + + public: + virtual ~AbstractViewItem() = default; + + virtual void build_context_menu(bContext &C, uiLayout &column) const; + + /** + * Queries if the view item supports renaming in principle. Renaming may still fail, e.g. if + * another item is already being renamed. + */ + virtual bool supports_renaming() const; + /** + * Try renaming the item, or the data it represents. Can assume + * #AbstractViewItem::supports_renaming() returned true. Sub-classes that override this should + * usually call this, unless they have a custom #AbstractViewItem.matches() implementation. + * + * \return True if the renaming was successful. + */ + virtual bool rename(StringRefNull new_name); + /** + * Get the string that should be used for renaming, typically the item's label. This string will + * not be modified, but if the renaming is canceled, the value will be reset to this. + */ + virtual StringRef get_rename_string() const; + + /** + * If an item wants to support being dragged, it has to return a drag controller here. + * That is an object implementing #AbstractViewItemDragController. + */ + virtual std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const; + /** + * If an item wants to support dropping data into it, it has to return a drop controller here. + * That is an object implementing #AbstractViewItemDropController. + * + * \note This drop controller may be requested for each event. The view doesn't keep a drop + * controller around currently. So it can not contain persistent state. + */ + virtual std::unique_ptr<AbstractViewItemDropController> create_drop_controller() const; + + /** Get the view this item is registered for using #AbstractView::register_item(). */ + AbstractView &get_view() const; + + /** + * Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_active() const; + + bool is_renaming() const; + void begin_renaming(); + void end_renaming(); + void rename_apply(); + + template<typename ToType = AbstractViewItem> + static ToType *from_item_handle(uiViewItemHandle *handle); + + protected: + AbstractViewItem() = default; + + /** + * Compare this item's identity to \a other to check if they represent the same data. + * Implementations can assume that the types match already (caller must check). + * + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, + * renaming, etc.). + */ + virtual bool matches(const AbstractViewItem &other) const = 0; + + /** + * Copy persistent state (e.g. active, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. + * + * \note Always call the base class implementation when overriding this! + */ + virtual void update_from_old(const AbstractViewItem &old); + + /** + * Add a text button for renaming the item to \a block. This must be used for the built-in + * renaming to work. This button is meant to appear temporarily. It is removed when renaming is + * done. + */ + void add_rename_button(uiBlock &block); +}; + +template<typename ToType> ToType *AbstractViewItem::from_item_handle(uiViewItemHandle *handle) +{ + static_assert(std::is_base_of<AbstractViewItem, ToType>::value, + "Type must derive from and implement the AbstractViewItem interface"); + + return dynamic_cast<ToType *>(reinterpret_cast<AbstractViewItem *>(handle)); +} + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +/** + * Class to enable dragging a view item. An item can return a drop controller for itself by + * implementing #AbstractViewItem::create_drag_controller(). + */ +class AbstractViewItemDragController { + protected: + AbstractView &view_; + + public: + AbstractViewItemDragController(AbstractView &view); + virtual ~AbstractViewItemDragController() = default; + + virtual int get_drag_type() const = 0; + virtual void *create_drag_data() const = 0; + virtual void on_drag_start(); + + /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast` + * exception if the view is not of the requested type. */ + template<class ViewType> inline ViewType &get_view() const; +}; + +/** + * Class to define the behavior when dropping something onto/into a view item, plus the behavior + * when dragging over this item. An item can return a drop controller for itself via a custom + * implementation of #AbstractViewItem::create_drop_controller(). + */ +class AbstractViewItemDropController { + protected: + AbstractView &view_; + + public: + AbstractViewItemDropController(AbstractView &view); + virtual ~AbstractViewItemDropController() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the item this controller is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this item. Shouldn't be done too aggressively, e.g. + * don't set this if the drag-type can't be dropped here; only if it can + * but there's another reason it can't be dropped. + * Can assume this is a non-null pointer. + */ + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; + /** + * Custom text to display when dragging over a view item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. + */ + virtual std::string drop_tooltip(const wmDrag &drag) const = 0; + /** + * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this + * controller is for. + */ + virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; + + /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast` + * exception if the view is not of the requested type. */ + template<class ViewType> inline ViewType &get_view() const; +}; + +template<class ViewType> ViewType &AbstractViewItemDragController::get_view() const +{ + static_assert(std::is_base_of<AbstractView, ViewType>::value, + "Type must derive from and implement the ui::AbstractView interface"); + return dynamic_cast<ViewType &>(view_); +} + +template<class ViewType> ViewType &AbstractViewItemDropController::get_view() const +{ + static_assert(std::is_base_of<AbstractView, ViewType>::value, + "Type must derive from and implement the ui::AbstractView interface"); + return dynamic_cast<ViewType &>(view_); +} + +/** \} */ + +} // namespace blender::ui diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh index 6f553f4fad1..402c0c8512f 100644 --- a/source/blender/editors/include/UI_grid_view.hh +++ b/source/blender/editors/include/UI_grid_view.hh @@ -13,12 +13,13 @@ #include "BLI_map.hh" #include "BLI_vector.hh" +#include "UI_abstract_view.hh" #include "UI_resources.h" struct bContext; struct PreviewImage; struct uiBlock; -struct uiButGridTile; +struct uiButViewItem; struct uiLayout; struct View2D; struct wmNotifier; @@ -31,43 +32,29 @@ class AbstractGridView; /** \name Grid-View Item Type * \{ */ -class AbstractGridViewItem { +class AbstractGridViewItem : public AbstractViewItem { friend class AbstractGridView; friend class GridViewLayoutBuilder; - const AbstractGridView *view_; - - bool is_active_ = false; - protected: /** Reference to a string that uniquely identifies this item in the view. */ StringRef identifier_{}; - /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */ - uiButGridTile *grid_tile_but_ = nullptr; + /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */ + uiButViewItem *view_item_but_ = nullptr; public: virtual ~AbstractGridViewItem() = default; virtual void build_grid_tile(uiLayout &layout) const = 0; - /** - * Compare this item's identifier to \a other to check if they represent the same data. - * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, - * renaming, etc.). - */ - bool matches(const AbstractGridViewItem &other) const; - const AbstractGridView &get_view() const; - /** - * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we - * can't be sure about the item state. - */ - bool is_active() const; - protected: AbstractGridViewItem(StringRef identifier); + /** See AbstractViewItem::matches(). */ + virtual bool matches(const AbstractViewItem &other) const override; + /** Called when the item's state changes from inactive to active. */ virtual void on_activate(); /** @@ -77,13 +64,6 @@ class AbstractGridViewItem { virtual std::optional<bool> should_be_active() const; /** - * Copy persistent state (e.g. active, selection, etc.) from a matching item of - * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. - */ - virtual void update_from_old(const AbstractGridViewItem &old); - - /** * Activates this item, deactivates other items, and calls the * #AbstractGridViewItem::on_activate() function. * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the @@ -111,7 +91,7 @@ struct GridViewStyle { int tile_height = 0; }; -class AbstractGridView { +class AbstractGridView : public AbstractView { friend class AbstractGridViewItem; friend class GridViewBuilder; friend class GridViewLayoutBuilder; @@ -122,7 +102,6 @@ class AbstractGridView { * #update_from_old(). */ Map<StringRef, AbstractGridViewItem *> item_map_; GridViewStyle style_; - bool is_reconstructed_ = false; public: AbstractGridView(); @@ -131,9 +110,6 @@ class AbstractGridView { using ItemIterFn = FunctionRef<void(AbstractGridViewItem &)>; void foreach_item(ItemIterFn iter_fn) const; - /** Listen to a notifier, returning true if a redraw is needed. */ - virtual bool listen(const wmNotifier &) const; - /** * Convenience wrapper constructing the item by forwarding given arguments to the constructor of * the type (\a ItemT). @@ -154,19 +130,8 @@ class AbstractGridView { protected: virtual void build_items() = 0; - /** - * Check if the view is fully (re-)constructed. That means, both #build_items() and - * #update_from_old() have finished. - */ - bool is_reconstructed() const; - private: - /** - * Match the grid-view against an earlier version of itself (if any) and copy the old UI state - * (e.g. active, selected, renaming, etc.) to the new one. See - * #AbstractGridViewItem.update_from_old(). - */ - void update_from_old(uiBlock &new_block); + void update_children_from_old(const AbstractView &old_view) override; AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const; /** 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/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b2ec2102ddd..a8d25b75036 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -72,14 +72,10 @@ typedef struct uiBut uiBut; typedef struct uiButExtraOpIcon uiButExtraOpIcon; typedef struct uiLayout uiLayout; typedef struct uiPopupBlockHandle uiPopupBlockHandle; -/* C handle for C++ #ui::AbstractTreeView type. */ -typedef struct uiTreeViewHandle uiTreeViewHandle; -/* C handle for C++ #ui::AbstractTreeViewItem type. */ -typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; -/* C handle for C++ #ui::AbstractGridView type. */ -typedef struct uiGridViewHandle uiGridViewHandle; -/* C handle for C++ #ui::AbstractGridViewItem type. */ -typedef struct uiGridViewItemHandle uiGridViewItemHandle; +/* C handle for C++ #ui::AbstractView type. */ +typedef struct uiViewHandle uiViewHandle; +/* C handle for C++ #ui::AbstractViewItem type. */ +typedef struct uiViewItemHandle uiViewItemHandle; /* Defines */ @@ -393,10 +389,8 @@ typedef enum { /** Resize handle (resize uilist). */ UI_BTYPE_GRIP = 57 << 9, UI_BTYPE_DECORATOR = 58 << 9, - /* An item in a tree view. Parent items may be collapsible. */ - UI_BTYPE_TREEROW = 59 << 9, - /* An item in a grid view. */ - UI_BTYPE_GRID_TILE = 60 << 9, + /* An item a view (see #ui::AbstractViewItem). */ + UI_BTYPE_VIEW_ITEM = 59 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -1687,8 +1681,6 @@ int UI_search_items_find_index(uiSearchItems *items, const char *name); */ void UI_but_hint_drawstr_set(uiBut *but, const char *string); -void UI_but_treerow_indentation_set(uiBut *but, int indentation); - void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]); void UI_but_number_step_size_set(uiBut *but, float step_size); @@ -3203,54 +3195,44 @@ void UI_interface_tag_script_reload(void); void UI_block_views_listen(const uiBlock *block, const struct wmRegionListenerParams *listener_params); -bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle); -bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); -bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b); -bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); +bool UI_view_item_is_active(const uiViewItemHandle *item_handle); +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle); /** - * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't - * support dragging, i.e. it won't create a drag-controller upon request. - * \return True if dragging started successfully, otherwise false. - */ -bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_); -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, - const struct wmDrag *drag, - const char **r_disabled_hint); -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); -/** - * Let a tree-view item handle a drop event. - * \return True if the drop was handled by the tree-view item. - */ -bool UI_tree_view_item_drop_handle(struct bContext *C, - const uiTreeViewItemHandle *item_, - const struct ListBase *drags); -/** - * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around - * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed, + * Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around + * #AbstractViewItem::supports_renaming(). This also checks if there is another item being renamed, * and returns false if so. */ -bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); -void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle); +void UI_view_item_begin_rename(uiViewItemHandle *item_handle); -void UI_tree_view_item_context_menu_build(struct bContext *C, - const uiTreeViewItemHandle *item, - uiLayout *column); +void UI_view_item_context_menu_build(struct bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column); /** - * \param xy: Coordinate to find a tree-row item at, in window space. + * Attempt to start dragging \a item_. This will not work if the view item doesn't + * support dragging, i.e. if it won't create a drag-controller upon request. + * \return True if dragging started successfully, otherwise false. */ -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, - const int xy[2]) ATTR_NONNULL(1, 2); -uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); - +bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_); +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const struct wmDrag *drag, + const char **r_disabled_hint); +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item, const struct wmDrag *drag); /** - * Listen to \a notifier, returning true if the region should redraw. + * Let a view item handle a drop event. + * \return True if the drop was handled by the view item. */ -bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view, const wmNotifier *notifier); +bool UI_view_item_drop_handle(struct bContext *C, + const uiViewItemHandle *item_, + const struct ListBase *drags); + /** - * Listen to \a notifier, returning true if the region should redraw. + * \param xy: Coordinate to find a view item at, in window space. */ -bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view, const wmNotifier *notifier); +uiViewItemHandle *UI_region_views_find_item_at(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(); +uiViewItemHandle *UI_region_views_find_active_item(const struct ARegion *region); #ifdef __cplusplus } diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 3dc56b01993..82bfdd7e212 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -46,7 +46,7 @@ void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path); void attribute_search_add_items( StringRefNull str, - bool is_output, + bool can_create_attribute, Span<const nodes::geometry_nodes_eval_log::GeometryAttributeInfo *> infos, uiSearchItems *items, bool is_first); @@ -54,12 +54,12 @@ void attribute_search_add_items( } // namespace blender::ui /** - * Override this for all available tree types. + * Override this for all available view types. */ blender::ui::AbstractGridView *UI_block_add_view( uiBlock &block, blender::StringRef idname, - std::unique_ptr<blender::ui::AbstractGridView> tree_view); + std::unique_ptr<blender::ui::AbstractGridView> grid_view); blender::ui::AbstractTreeView *UI_block_add_view( uiBlock &block, blender::StringRef idname, diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 1aeb13ca5cc..872a6085060 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -9,7 +9,6 @@ #pragma once -#include <array> #include <functional> #include <memory> #include <string> @@ -19,23 +18,19 @@ #include "BLI_function_ref.hh" #include "BLI_vector.hh" +#include "UI_abstract_view.hh" #include "UI_resources.h" struct bContext; struct uiBlock; struct uiBut; -struct uiButTreeRow; +struct uiButViewItem; struct uiLayout; -struct wmDrag; -struct wmEvent; -struct wmNotifier; namespace blender::ui { class AbstractTreeView; class AbstractTreeViewItem; -class AbstractTreeViewItemDropController; -class AbstractTreeViewItemDragController; /* ---------------------------------------------------------------------- */ /** \name Tree-View Item Container @@ -112,45 +107,20 @@ using TreeViewOrItem = TreeViewItemContainer; /** \name Tree-View Base Class * \{ */ -class AbstractTreeView : public TreeViewItemContainer { +class AbstractTreeView : public AbstractView, public TreeViewItemContainer { friend class AbstractTreeViewItem; friend class TreeViewBuilder; - /** - * Only one item can be renamed at a time. So the tree is informed about the renaming state to - * enforce that. - */ - std::unique_ptr<std::array<char, MAX_NAME>> rename_buffer_; - - bool is_reconstructed_ = false; - public: virtual ~AbstractTreeView() = default; void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; - /** Listen to a notifier, returning true if a redraw is needed. */ - virtual bool listen(const wmNotifier &) const; - - /** Only one item can be renamed at a time. */ - bool is_renaming() const; - protected: virtual void build_tree() = 0; - /** - * Check if the tree is fully (re-)constructed. That means, both #build_tree() and - * #update_from_old() have finished. - */ - bool is_reconstructed() const; - private: - /** - * Match the tree-view against an earlier version of itself (if any) and copy the old UI state - * (e.g. collapsed, active, selected, renaming, etc.) to the new one. See - * #AbstractTreeViewItem.update_from_old(). - */ - void update_from_old(uiBlock &new_block); + void update_children_from_old(const AbstractView &old_view) override; static void update_children_from_old_recursive(const TreeViewOrItem &new_items, const TreeViewOrItem &old_items); static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, @@ -177,7 +147,7 @@ class AbstractTreeView : public TreeViewItemContainer { * It also stores state information that needs to be persistent over redraws, like the collapsed * state. */ -class AbstractTreeViewItem : public TreeViewItemContainer { +class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContainer { friend class AbstractTreeView; friend class TreeViewLayoutBuilder; /* Higher-level API. */ @@ -185,20 +155,17 @@ class AbstractTreeViewItem : public TreeViewItemContainer { private: bool is_open_ = false; - bool is_active_ = false; - bool is_renaming_ = false; protected: /** This label is used as the default way to identifying an item within its parent. */ std::string label_{}; - /** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */ - uiButTreeRow *tree_row_but_ = nullptr; + /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */ + uiButViewItem *view_item_but_ = nullptr; public: virtual ~AbstractTreeViewItem() = default; virtual void build_row(uiLayout &row) = 0; - virtual void build_context_menu(bContext &C, uiLayout &column) const; AbstractTreeView &get_tree_view() const; @@ -210,11 +177,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * can't be sure about the item state. */ bool is_collapsed() const; - /** - * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we - * can't be sure about the item state. - */ - bool is_active() const; protected: /** @@ -227,31 +189,21 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual std::optional<bool> should_be_active() const; - /** - * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if - * another item is already being renamed. - */ - virtual bool supports_renaming() const; - /** - * Try renaming the item, or the data it represents. Can assume - * #AbstractTreeViewItem::supports_renaming() returned true. Sub-classes that override this - * should usually call this, unless they have a custom #AbstractTreeViewItem.matches(). - * - * \return True if the renaming was successful. - */ - virtual bool rename(StringRefNull new_name); + /** See AbstractViewItem::get_rename_string(). */ + virtual StringRef get_rename_string() const override; + /** See AbstractViewItem::rename(). */ + virtual bool rename(StringRefNull new_name) override; /** * Return whether the item can be collapsed. Used to disable collapsing for items with children. */ virtual bool supports_collapsing() const; - /** - * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of - * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. - */ - virtual void update_from_old(const AbstractTreeViewItem &old); + /** See #AbstractViewItem::matches(). */ + virtual bool matches(const AbstractViewItem &other) const override; + + /** See #AbstractViewItem::update_from_old(). */ + virtual void update_from_old(const AbstractViewItem &old) override; /** * Compare this item to \a other to check if they represent the same data. @@ -259,22 +211,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * open/closed, active, etc.). Items are only matched if their parents also match. * By default this just matches the item's label (if the parents match!). If that isn't * good enough for a sub-class, that can override it. - */ - virtual bool matches(const AbstractTreeViewItem &other) const; - - /** - * If an item wants to support being dragged, it has to return a drag controller here. - * That is an object implementing #AbstractTreeViewItemDragController. - */ - virtual std::unique_ptr<AbstractTreeViewItemDragController> create_drag_controller() const; - /** - * If an item wants to support dropping data into it, it has to return a drop controller here. - * That is an object implementing #AbstractTreeViewItemDropController. * - * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop - * controller around currently. So it can not contain persistent state. + * TODO #matches_single() is a rather temporary name, used to indicate that this only compares + * the item itself, not the parents. Item matching is expected to change quite a bit anyway. */ - virtual std::unique_ptr<AbstractTreeViewItemDropController> create_drop_controller() const; + virtual bool matches_single(const AbstractTreeViewItem &other) const; /** * Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() @@ -292,29 +233,24 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ bool is_hovered() const; bool is_collapsible() const; - bool is_renaming() const; void ensure_parents_uncollapsed(); - uiButTreeRow *tree_row_button(); + uiButViewItem *view_item_button(); private: - static void rename_button_fn(bContext *, void *, char *); - static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but); static void tree_row_click_fn(struct bContext *, void *, void *); static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *); static bool is_collapse_chevron_but(const uiBut *but); /** See #AbstractTreeView::change_state_delayed() */ void change_state_delayed(); - void end_renaming(); void add_treerow_button(uiBlock &block); void add_indent(uiLayout &row) const; void add_collapse_chevron(uiBlock &block) const; void add_rename_button(uiLayout &row); - bool matches_including_parents(const AbstractTreeViewItem &other) const; bool has_active_child() const; int count_parents() const; }; @@ -322,69 +258,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Drag 'n Drop - * \{ */ - -/** - * Class to enable dragging a tree-item. An item can return a drop controller for itself via a - * custom implementation of #AbstractTreeViewItem::create_drag_controller(). - */ -class AbstractTreeViewItemDragController { - protected: - AbstractTreeView &tree_view_; - - public: - AbstractTreeViewItemDragController(AbstractTreeView &tree_view); - virtual ~AbstractTreeViewItemDragController() = default; - - virtual int get_drag_type() const = 0; - virtual void *create_drag_data() const = 0; - virtual void on_drag_start(); - - template<class TreeViewType> inline TreeViewType &tree_view() const; -}; - -/** - * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this - * item. An item can return a drop controller for itself via a custom implementation of - * #AbstractTreeViewItem::create_drop_controller(). - */ -class AbstractTreeViewItemDropController { - protected: - AbstractTreeView &tree_view_; - - public: - AbstractTreeViewItemDropController(AbstractTreeView &tree_view); - virtual ~AbstractTreeViewItemDropController() = default; - - /** - * Check if the data dragged with \a drag can be dropped on the item this controller is for. - * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping - * isn't possible on this item. Shouldn't be done too aggressively, e.g. - * don't set this if the drag-type can't be dropped here; only if it can - * but there's another reason it can't be dropped. - * Can assume this is a non-null pointer. - */ - virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; - /** - * Custom text to display when dragging over a tree item. Should explain what happens when - * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() - * returns true, so the implementing override doesn't have to check that again. - * The returned value must be a translated string. - */ - virtual std::string drop_tooltip(const wmDrag &drag) const = 0; - /** - * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this - * controller is for. - */ - virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; - - template<class TreeViewType> inline TreeViewType &tree_view() const; -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ /** \name Predefined Tree-View Item Types * * Common, Basic Tree-View Item Types. @@ -455,18 +328,4 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...))); } -template<class TreeViewType> TreeViewType &AbstractTreeViewItemDragController::tree_view() const -{ - static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value, - "Type must derive from and implement the AbstractTreeView interface"); - return static_cast<TreeViewType &>(tree_view_); -} - -template<class TreeViewType> TreeViewType &AbstractTreeViewItemDropController::tree_view() const -{ - static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value, - "Type must derive from and implement the AbstractTreeView interface"); - return static_cast<TreeViewType &>(tree_view_); -} - } // namespace blender::ui diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 5c4eb254462..e508c96b4f1 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -309,6 +309,12 @@ float UI_view2d_view_to_region_y(const struct View2D *v2d, float y); bool UI_view2d_view_to_region_clip( const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL(); +bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, + const float xy_a[2], + const float xy_b[2], + int r_region_a[2], + int r_region_b[2]) ATTR_NONNULL(); + /** * Convert from 2d-view space to screen/region space * diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 2a1852bd6e7..07c3bfdafbf 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(INC + . ../include ../../blenfont ../../blenkernel @@ -25,7 +26,13 @@ set(INC ) set(SRC - grid_view.cc + eyedroppers/interface_eyedropper.c + eyedroppers/eyedropper_color.c + eyedroppers/eyedropper_colorband.c + eyedroppers/eyedropper_datablock.c + eyedroppers/eyedropper_depth.c + eyedroppers/eyedropper_driver.c + eyedroppers/eyedropper_gpencil_color.c interface.cc interface_align.c interface_anim.c @@ -35,13 +42,6 @@ set(SRC interface_drag.cc interface_draw.c interface_dropboxes.cc - interface_eyedropper.c - interface_eyedropper_color.c - interface_eyedropper_colorband.c - interface_eyedropper_datablock.c - interface_eyedropper_depth.c - interface_eyedropper_driver.c - interface_eyedropper_gpencil_color.c interface_handlers.c interface_icons.c interface_icons_event.c @@ -67,17 +67,20 @@ set(SRC interface_templates.c interface_undo.c interface_utils.cc - interface_view.cc interface_widgets.c resources.c - tree_view.cc view2d.cc view2d_draw.cc view2d_edge_pan.cc view2d_gizmo_navigate.cc view2d_ops.cc + views/abstract_view.cc + views/abstract_view_item.cc + views/grid_view.cc + views/interface_view.cc + views/tree_view.cc - interface_eyedropper_intern.h + eyedroppers/eyedropper_intern.h interface_intern.h interface_regions_intern.h ) diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c index c015a60de89..9c430afd5f0 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c @@ -50,7 +50,7 @@ #include "RE_pipeline.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" typedef struct Eyedropper { struct ColorManagedDisplay *display; @@ -479,7 +479,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } } - else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + else if (ISMOUSE_MOTION(event->type)) { if (eye->accum_start) { /* button is pressed so keep sampling */ eyedropper_color_sample(C, eye, event->xy); diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c index a69c36fefbd..3f63a8020ed 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c @@ -35,7 +35,7 @@ #include "interface_intern.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" typedef struct Colorband_RNAUpdateCb { PointerRNA ptr; diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c index 01b958576b6..1710d0faa4d 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c @@ -38,7 +38,7 @@ #include "ED_space_api.h" #include "ED_view3d.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" /** diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c index 3c6f127582a..3fb5a74944b 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c @@ -38,7 +38,7 @@ #include "ED_space_api.h" #include "ED_view3d.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" /** diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c index 0cacb55c60c..a9314df44a5 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c @@ -32,7 +32,7 @@ #include "ED_keyframing.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" typedef struct DriverDropper { @@ -83,14 +83,14 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve if (but == NULL) { return; } - /* Get paths for src... */ + /* Get paths for the source. */ PointerRNA *target_ptr = &but->rnapoin; PropertyRNA *target_prop = but->rnaprop; const int target_index = but->rnaindex; char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop); - /* ... and destination */ + /* Get paths for the destination. */ char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop); /* Now create driver(s) */ diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c index f3c70e6a96a..c3879fe8bbd 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c @@ -46,7 +46,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" typedef struct EyedropperGPencil { diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h index 76316a85807..946f2145d1d 100644 --- a/source/blender/editors/interface/interface_eyedropper_intern.h +++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h @@ -3,7 +3,7 @@ /** \file * \ingroup edinterface * - * Share between interface_eyedropper_*.c files. + * Share between `interface/eyedropper/` files. */ #pragma once @@ -35,7 +35,7 @@ void datadropper_win_area_find(const struct bContext *C, * * Special check for image or nodes where we MAY have HDR pixels which don't display. * - * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking. + * \note Exposed by 'eyedropper_intern.h' for use with color band picking. */ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c index eaec1e249b7..c6fb8f0ab68 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c @@ -21,7 +21,7 @@ #include "interface_intern.h" -#include "interface_eyedropper_intern.h" /* own include */ +#include "eyedropper_intern.h" /* own include */ /* -------------------------------------------------------------------- */ /* Keymap diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 3f623566807..2f9e69137ed 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -769,20 +769,11 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) return false; } - if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) { - uiButTreeRow *but_treerow = (uiButTreeRow *)but; - uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut; - if (!but_treerow->tree_item || !oldbut_treerow->tree_item || - !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) { - return false; - } - } - - if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) { - uiButGridTile *but_gridtile = (uiButGridTile *)but; - uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut; - if (!but_gridtile->view_item || !oldbut_gridtile->view_item || - !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) { + if ((but->type == UI_BTYPE_VIEW_ITEM) && (oldbut->type == UI_BTYPE_VIEW_ITEM)) { + uiButViewItem *but_item = (uiButViewItem *)but; + uiButViewItem *oldbut_item = (uiButViewItem *)oldbut; + if (!but_item->view_item || !oldbut_item->view_item || + !UI_view_item_matches(but_item->view_item, oldbut_item->view_item)) { return false; } } @@ -907,16 +898,10 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) progress_oldbut->progress = progress_but->progress; break; } - case UI_BTYPE_TREEROW: { - uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut; - uiButTreeRow *treerow_newbut = (uiButTreeRow *)but; - SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); - break; - } - case UI_BTYPE_GRID_TILE: { - uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut; - uiButGridTile *gridtile_newbut = (uiButGridTile *)but; - SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item); + case UI_BTYPE_VIEW_ITEM: { + uiButViewItem *view_item_oldbut = (uiButViewItem *)oldbut; + uiButViewItem *view_item_newbut = (uiButViewItem *)but; + SWAP(uiViewItemHandle *, view_item_newbut->view_item, view_item_oldbut->view_item); break; } default: @@ -1013,7 +998,7 @@ static bool ui_but_update_from_old_block(const bContext *C, /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item * button which we also want to keep highlighted then. */ - if (ui_but_is_view_item(but)) { + if (but->type == UI_BTYPE_VIEW_ITEM) { flag_copy |= UI_ACTIVE; } @@ -2245,21 +2230,12 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } } break; - case UI_BTYPE_TREEROW: { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - - is_push = -1; - if (tree_row_but->tree_item) { - is_push = UI_tree_view_item_is_active(tree_row_but->tree_item); - } - break; - } - case UI_BTYPE_GRID_TILE: { - uiButGridTile *grid_tile_but = (uiButGridTile *)but; + case UI_BTYPE_VIEW_ITEM: { + const uiButViewItem *view_item_but = (const uiButViewItem *)but; is_push = -1; - if (grid_tile_but->view_item) { - is_push = UI_grid_view_item_is_active(grid_tile_but->view_item); + if (view_item_but->view_item) { + is_push = UI_view_item_is_active(view_item_but->view_item); } break; } @@ -4011,17 +3987,13 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButCurveProfile); alloc_str = "uiButCurveProfile"; break; - case UI_BTYPE_TREEROW: - alloc_size = sizeof(uiButTreeRow); - alloc_str = "uiButTreeRow"; - break; case UI_BTYPE_HOTKEY_EVENT: alloc_size = sizeof(uiButHotkeyEvent); alloc_str = "uiButHotkeyEvent"; break; - case UI_BTYPE_GRID_TILE: - alloc_size = sizeof(uiButGridTile); - alloc_str = "uiButGridTile"; + case UI_BTYPE_VIEW_ITEM: + alloc_size = sizeof(uiButViewItem); + alloc_str = "uiButViewItem"; break; default: alloc_size = sizeof(uiBut); @@ -4214,7 +4186,6 @@ static uiBut *ui_def_but(uiBlock *block, UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, - UI_BTYPE_TREEROW, UI_BTYPE_POPOVER)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -6469,15 +6440,6 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, return but; } -void UI_but_treerow_indentation_set(uiBut *but, int indentation) -{ - uiButTreeRow *but_row = (uiButTreeRow *)but; - BLI_assert(but->type == UI_BTYPE_TREEROW); - - but_row->indentation = indentation; - BLI_assert(indentation >= 0); -} - void UI_but_hint_drawstr_set(uiBut *but, const char *string) { ui_but_add_shortcut(but, string, false); @@ -6623,7 +6585,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) MenuType *mt = UI_but_menutype_get(but); if (mt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(mt->label); + tmp = BLI_strdup(CTX_TIP_(mt->translation_context, mt->label)); } else { /* Not all menus are from Python. */ @@ -6653,7 +6615,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) PanelType *pt = UI_but_paneltype_get(but); if (pt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(pt->label); + tmp = BLI_strdup(CTX_TIP_(pt->translation_context, pt->label)); } else { /* Not all panels are from Python. */ diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index e58298cdaee..518fe65ee09 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -927,11 +927,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev { const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); - uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy); - if (treerow_but) { - BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); - UI_tree_view_item_context_menu_build( - C, treerow_but->tree_item, uiLayoutColumn(layout, false)); + uiButViewItem *view_item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, + event->xy); + if (view_item_but) { + BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); + UI_view_item_context_menu_build(C, view_item_but->view_item, uiLayoutColumn(layout, false)); uiItemS(layout); } } diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 4c68870b2c7..1db3db32411 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -122,7 +122,7 @@ bool ui_but_drag_is_draggable(const uiBut *but) void ui_but_drag_start(bContext *C, uiBut *but) { - wmDrag *drag = WM_event_start_drag(C, + wmDrag *drag = WM_drag_data_create(C, but->icon, but->dragtype, but->dragpoin, @@ -136,6 +136,8 @@ void ui_but_drag_start(bContext *C, uiBut *but) WM_event_drag_image(drag, but->imb, but->imb_scale); } + WM_event_start_prepared_drag(C, drag); + /* Special feature for assets: We add another drag item that supports multiple assets. It * gets the assets from context. */ if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 9d3c1372b15..b72d8d2c238 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -22,15 +22,14 @@ #include "UI_interface.h" /* -------------------------------------------------------------------- */ -/** \name Tree View Drag/Drop Callbacks +/** \name View Drag/Drop Callbacks * \{ */ -static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, - event->xy); - if (!hovered_tree_item) { + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); + if (!hovered_item) { return false; } @@ -39,21 +38,21 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve } drag->drop_state.free_disabled_info = false; - return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->drop_state.disabled_info); + return UI_view_item_can_drop(hovered_item, drag, &drag->drop_state.disabled_info); } -static char *ui_tree_view_drop_tooltip(bContext *C, - wmDrag *drag, - const int xy[2], - wmDropBox *UNUSED(drop)) +static char *ui_view_drop_tooltip(bContext *C, + wmDrag *drag, + const int xy[2], + wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy); - if (!hovered_tree_item) { + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy); + if (!hovered_item) { return nullptr; } - return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); + return UI_view_item_drop_tooltip(hovered_item, drag); } /** \} */ @@ -140,12 +139,7 @@ void ED_dropboxes_ui() { ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); - WM_dropbox_add(lb, - "UI_OT_tree_view_drop", - ui_tree_view_drop_poll, - nullptr, - nullptr, - ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, "UI_OT_view_drop", ui_view_drop_poll, nullptr, nullptr, ui_view_drop_tooltip); WM_dropbox_add(lb, "UI_OT_drop_name", ui_drop_name_poll, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 7c00c4f1875..80fd0cbe16e 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1151,7 +1151,10 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } -static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +static void ui_apply_but_VIEW_ITEM(bContext *C, + uiBlock *block, + uiBut *but, + uiHandleButtonData *data) { if (data->apply_through_extra_icon) { /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons. @@ -2128,10 +2131,10 @@ static bool ui_but_drag_init(bContext *C, return false; } } - else if (but->type == UI_BTYPE_TREEROW) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - if (tree_row_but->tree_item) { - UI_tree_view_item_drag_start(C, tree_row_but->tree_item); + else if (but->type == UI_BTYPE_VIEW_ITEM) { + const uiButViewItem *view_item_but = (uiButViewItem *)but; + if (view_item_but->view_item) { + UI_view_item_drag_start(C, view_item_but->view_item); } } else { @@ -2289,11 +2292,8 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; - case UI_BTYPE_GRID_TILE: - ui_apply_but_ROW(C, block, but, data); - break; - case UI_BTYPE_TREEROW: - ui_apply_but_TREEROW(C, block, but, data); + case UI_BTYPE_VIEW_ITEM: + ui_apply_but_VIEW_ITEM(C, block, but, data); break; case UI_BTYPE_LISTROW: ui_apply_but_LISTROW(C, block, but, data); @@ -3172,19 +3172,11 @@ static bool ui_textedit_insert_buf(uiBut *but, return changed; } -static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, char ascii) +static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii) { + BLI_assert(isascii(ascii)); const char buf[2] = {ascii, '\0'}; - - if (UI_but_is_utf8(but) && (BLI_str_utf8_size(buf) == -1)) { - printf( - "%s: entering invalid ascii char into an ascii key (%d)\n", __func__, (int)(uchar)ascii); - - return false; - } - - /* in some cases we want to allow invalid utf8 chars */ - return ui_textedit_insert_buf(but, data, buf, 1); + return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1); } static void ui_textedit_move(uiBut *but, @@ -3897,30 +3889,27 @@ static void ui_do_but_textedit( } } - if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) + if ((event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) #ifdef WITH_INPUT_IME && !is_ime_composing && !WM_event_is_ime_switch(event) #endif ) { - char ascii = event->ascii; + char utf8_buf_override[2] = {'\0', '\0'}; const char *utf8_buf = event->utf8_buf; /* Exception that's useful for number buttons, some keyboard * numpads have a comma instead of a period. */ if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */ - if (event->type == EVT_PADPERIOD && ascii == ',') { - ascii = '.'; - utf8_buf = NULL; /* force ascii fallback */ + if ((event->type == EVT_PADPERIOD) && (utf8_buf[0] == ',')) { + utf8_buf_override[0] = '.'; + utf8_buf = utf8_buf_override; } } - if (utf8_buf && utf8_buf[0]) { + if (utf8_buf[0]) { const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); BLI_assert(utf8_buf_len != -1); - changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len); - } - else { - changed = ui_textedit_insert_ascii(but, data, ascii); + changed = ui_textedit_insert_buf(but, data, utf8_buf, utf8_buf_len); } retval = WM_UI_HANDLER_BREAK; @@ -3952,6 +3941,9 @@ static void ui_do_but_textedit( else if (event->type == WM_IME_COMPOSITE_END) { changed = true; } +#else + /* Prevent the function from being unused. */ + (void)ui_textedit_insert_ascii; #endif if (changed) { @@ -4517,7 +4509,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, } } else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) { - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (ISMOUSE_MOTION(event->type)) { return WM_UI_HANDLER_CONTINUE; } if (event->type == EVT_UNKNOWNKEY) { @@ -4583,7 +4575,7 @@ static int ui_do_but_KEYEVT(bContext *C, } } else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) { - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (ISMOUSE_MOTION(event->type)) { return WM_UI_HANDLER_CONTINUE; } @@ -4772,53 +4764,13 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_TREEROW(bContext *C, - uiBut *but, - uiHandleButtonData *data, - const wmEvent *event) -{ - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW); - - if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (event->type == LEFTMOUSE) { - switch (event->val) { - case KM_PRESS: - /* Extra icons have priority, don't mess with them. */ - if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { - return WM_UI_HANDLER_BREAK; - } - button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->xy[0]; - data->dragstarty = event->xy[1]; - return WM_UI_HANDLER_CONTINUE; - - case KM_CLICK: - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - - case KM_DBL_CLICK: - data->cancel = true; - UI_tree_view_item_begin_rename(tree_row_but->tree_item); - ED_region_tag_redraw(CTX_wm_region(C)); - return WM_UI_HANDLER_BREAK; - } - } - } - else if (data->state == BUTTON_STATE_WAIT_DRAG) { - /* Let "default" button handling take care of the drag logic. */ - return ui_do_but_EXIT(C, but, data, event); - } - - return WM_UI_HANDLER_CONTINUE; -} - -static int ui_do_but_GRIDTILE(bContext *C, - uiBut *but, - uiHandleButtonData *data, - const wmEvent *event) +static int ui_do_but_VIEW_ITEM(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) { - BLI_assert(but->type == UI_BTYPE_GRID_TILE); + uiButViewItem *view_item_but = (uiButViewItem *)but; + BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE) { @@ -4839,8 +4791,7 @@ static int ui_do_but_GRIDTILE(bContext *C, case KM_DBL_CLICK: data->cancel = true; - // uiButGridTile *grid_tile_but = (uiButGridTile *)but; - // UI_tree_view_item_begin_rename(grid_tile_but->tree_item); + UI_view_item_begin_rename(view_item_but->view_item); ED_region_tag_redraw(CTX_wm_region(C)); return WM_UI_HANDLER_BREAK; } @@ -7988,7 +7939,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * * to spawn the context menu should also activate the item. This makes it clear which item * will be operated on. * Apply the button immediately, so context menu polls get the right active item. */ - if (ELEM(but->type, UI_BTYPE_TREEROW)) { + if (ELEM(but->type, UI_BTYPE_VIEW_ITEM)) { ui_apply_but(C, but->block, but, but->active, true); } @@ -8053,11 +8004,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_ROW: retval = ui_do_but_TOG(C, but, data, event); break; - case UI_BTYPE_GRID_TILE: - retval = ui_do_but_GRIDTILE(C, but, data, event); - break; - case UI_BTYPE_TREEROW: - retval = ui_do_but_TREEROW(C, but, data, event); + case UI_BTYPE_VIEW_ITEM: + retval = ui_do_but_VIEW_ITEM(C, but, data, event); break; case UI_BTYPE_SCROLL: retval = ui_do_but_SCROLL(C, block, but, data, event); @@ -8149,7 +8097,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * #ifdef USE_DRAG_MULTINUM data = but->active; if (data) { - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || + if (ISMOUSE_MOTION(event->type) || /* if we started dragging, progress on any event */ (data->multi_data.init == BUTTON_MULTI_INIT_SETUP)) { if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) && @@ -9487,7 +9435,7 @@ static int ui_list_activate_hovered_row(bContext *C, } /* Simulate click on listrow button itself (which may be overlapped by another button). Also - * calls the custom activate operator (ui_list->custom_activate_opname). */ + * calls the custom activate operator (#uiListDyn::custom_activate_optype). */ UI_but_execute(C, region, listrow); ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype; @@ -9558,13 +9506,13 @@ static void ui_list_activate_row_from_index( uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox); if (new_active_row) { /* Preferred way to update the active item, also calls the custom activate operator - * (#uiList.custom_activate_opname). */ + * (#uiListDyn::custom_activate_optype). */ UI_but_execute(C, region, new_active_row); } else { /* A bit ugly, set the active index in RNA directly. That's because a button that's * scrolled away in the list box isn't created at all. - * The custom activate operator (#uiList.custom_activate_opname) is not called in this case + * The custom activate operator (#uiListDyn::custom_activate_optype) is not called in this case * (which may need the row button context). */ RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index); RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); @@ -9733,7 +9681,7 @@ static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *regio } LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (ui_but_is_view_item(but)) { + if (but->type == UI_BTYPE_VIEW_ITEM) { but->flag &= ~UI_ACTIVE; has_view_item = true; } @@ -9760,7 +9708,7 @@ static int ui_handle_view_item_event(bContext *C, ARegion *region, uiBut *view_but) { - BLI_assert(ui_but_is_view_item(view_but)); + BLI_assert(view_but->type == UI_BTYPE_VIEW_ITEM); if (event->type == LEFTMOUSE) { /* Will free active button if there already is one. */ ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER); @@ -10148,8 +10096,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock /* Pass, needed to click-exit outside of non-floating menus. */ ui_region_auto_open_clear(but->active->region); } - else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) && - ISMOUSE(event->type)) { + else if (ISMOUSE_BUTTON(event->type)) { if (!ui_but_contains_point_px(but, but->active->region, event->xy)) { but = NULL; } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 791e51b81a6..03b9d03a6e3 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -343,20 +343,12 @@ typedef struct uiButProgressbar { float progress; } uiButProgressbar; -/** Derived struct for #UI_BTYPE_TREEROW. */ -typedef struct uiButTreeRow { +typedef struct uiButViewItem { uiBut but; - uiTreeViewItemHandle *tree_item; - int indentation; -} uiButTreeRow; - -/** Derived struct for #UI_BTYPE_GRID_TILE. */ -typedef struct uiButGridTile { - uiBut but; - - uiGridViewItemHandle *view_item; -} uiButGridTile; + /* C-Handle to the view item this button was created for. */ + uiViewItemHandle *view_item; +} uiButViewItem; /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { @@ -1372,7 +1364,6 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but); bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT; -bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT; /** * Can we mouse over the button or is it hidden/disabled/layout. * \note ctrl is kind of a hack currently, @@ -1406,9 +1397,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); -uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) - ATTR_NONNULL(1, 2); -uiBut *ui_tree_row_find_active(const struct ARegion *region); +uiBut *ui_view_item_find_active(const struct ARegion *region); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); /** @@ -1543,12 +1532,11 @@ void ui_interface_tag_script_reload_queries(void); /* interface_view.cc */ void ui_block_free_views(struct uiBlock *block); -uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(const uiBlock *new_block, - const uiTreeViewHandle *new_view); -uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block( - const uiBlock *new_block, const uiGridViewHandle *new_view_handle); -uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, - const uiTreeViewItemHandle *new_item_handle); +uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiViewHandle *new_view); + +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle); /* interface_templates.c */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index aafb56119ae..ddd805f6010 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -2054,40 +2054,39 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \name UI Tree-View Drop Operator * \{ */ -static bool ui_tree_view_drop_poll(bContext *C) +static bool ui_view_drop_poll(bContext *C) { const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, win->eventstate->xy); + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy); - return hovered_tree_item != NULL; + return hovered_item != NULL; } -static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { if (event->custom != EVT_DATA_DRAGDROP) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy); + uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); - if (!UI_tree_view_item_drop_handle(C, hovered_tree_item, event->customdata)) { + if (!UI_view_item_drop_handle(C, hovered_item, event->customdata)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } return OPERATOR_FINISHED; } -static void UI_OT_tree_view_drop(wmOperatorType *ot) +static void UI_OT_view_drop(wmOperatorType *ot) { - ot->name = "Tree View drop"; - ot->idname = "UI_OT_tree_view_drop"; - ot->description = "Drag and drop items onto a tree item"; + ot->name = "View drop"; + ot->idname = "UI_OT_view_drop"; + ot->description = "Drag and drop items onto a data-set item"; - ot->invoke = ui_tree_view_drop_invoke; - ot->poll = ui_tree_view_drop_poll; + ot->invoke = ui_view_drop_invoke; + ot->poll = ui_view_drop_poll; ot->flag = OPTYPE_INTERNAL; } @@ -2095,43 +2094,42 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name UI Tree-View Item Rename Operator +/** \name UI View Item Rename Operator * - * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to - * context menus for example, tree-view API users don't have to implement their own renaming - * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() - * override. + * General purpose renaming operator for views. Thanks to this, to add a rename button to context + * menus for example, view API users don't have to implement their own renaming operators with the + * same logic as they already have for their #ui::AbstractViewItem::rename() override. * * \{ */ -static bool ui_tree_view_item_rename_poll(bContext *C) +static bool ui_view_item_rename_poll(bContext *C) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); - return active_item != NULL && UI_tree_view_item_can_rename(active_item); + const uiViewItemHandle *active_item = UI_region_views_find_active_item(region); + return active_item != NULL && UI_view_item_can_rename(active_item); } -static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) +static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) { ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + uiViewItemHandle *active_item = UI_region_views_find_active_item(region); - UI_tree_view_item_begin_rename(active_item); + UI_view_item_begin_rename(active_item); ED_region_tag_redraw(region); return OPERATOR_FINISHED; } -static void UI_OT_tree_view_item_rename(wmOperatorType *ot) +static void UI_OT_view_item_rename(wmOperatorType *ot) { - ot->name = "Rename Tree-View Item"; - ot->idname = "UI_OT_tree_view_item_rename"; - ot->description = "Rename the active item in the tree"; + ot->name = "Rename View Item"; + ot->idname = "UI_OT_view_item_rename"; + ot->description = "Rename the active item in the data-set view"; - ot->exec = ui_tree_view_item_rename_exec; - ot->poll = ui_tree_view_item_rename_poll; + ot->exec = ui_view_item_rename_exec; + ot->poll = ui_view_item_rename_poll; /* Could get a custom tooltip via the `get_description()` callback and another overridable - * function of the tree-view. */ + * function of the view. */ ot->flag = OPTYPE_INTERNAL; } @@ -2235,8 +2233,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_list_start_filter); - WM_operatortype_append(UI_OT_tree_view_drop); - WM_operatortype_append(UI_OT_tree_view_item_rename); + WM_operatortype_append(UI_OT_view_drop); + WM_operatortype_append(UI_OT_view_item_rename); /* external */ WM_operatortype_append(UI_OT_eyedropper_color); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 87bfb7ca0f7..d4a9a4ca4cd 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2318,7 +2318,7 @@ int ui_handler_panel_region(bContext *C, const uiBut *active_but) { /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */ - if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (ISMOUSE_MOTION(event->type)) { return WM_UI_HANDLER_CONTINUE; } diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 71cf60985df..f084f3e06cb 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -54,13 +54,7 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_TOGGLE_N, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, - UI_BTYPE_ROW, - UI_BTYPE_TREEROW); -} - -bool ui_but_is_view_item(const uiBut *but) -{ - return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE); + UI_BTYPE_ROW); } bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip) @@ -462,14 +456,9 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut return ui_but_find(region, ui_but_is_listrow_at_index, &data); } -static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) -{ - return but->type == UI_BTYPE_TREEROW; -} - static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata)) { - return ui_but_is_view_item(but); + return but->type == UI_BTYPE_VIEW_ITEM; } uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) @@ -477,24 +466,19 @@ uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr); } -uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) -{ - return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr); -} - -static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) +static bool ui_but_is_active_view_item(const uiBut *but, const void *UNUSED(customdata)) { - if (!ui_but_is_treerow(but, customdata)) { + if (but->type != UI_BTYPE_VIEW_ITEM) { return false; } - const uiButTreeRow *treerow_but = (const uiButTreeRow *)but; - return UI_tree_view_item_is_active(treerow_but->tree_item); + const uiButViewItem *view_item_but = (const uiButViewItem *)but; + return UI_view_item_is_active(view_item_but->view_item); } -uiBut *ui_tree_row_find_active(const ARegion *region) +uiBut *ui_view_item_find_active(const ARegion *region) { - return ui_but_find(region, ui_but_is_active_treerow, nullptr); + return ui_but_find(region, ui_but_is_active_view_item, nullptr); } /** \} */ diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index b11564f09c5..09902dd6c35 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -371,7 +371,7 @@ void ui_pie_menu_level_create(uiBlock *block, EnumPropertyItem *remaining = static_cast<EnumPropertyItem *>( MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array")); memcpy(remaining, items + totitem_parent, array_size); - /* A nullptr terminating sentinel element is required. */ + /* A null terminating sentinel element is required. */ memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem)); /* yuk, static... issue is we can't reliably free this without doing dangerous changes */ diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 82d4405e1b5..88fe866f717 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -1005,7 +1005,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, /* this could get its own 'BUT_GET_...' type */ /* never fails */ - /* move ownership (no need for re-alloc) */ + /* Move ownership (no need for re-allocation). */ if (rnaprop) { field->text = RNA_path_full_property_py_ex( CTX_data_main(C), &but->rnapoin, rnaprop, but->rnaindex, true); diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 4e587bd5338..0a684903f0f 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -50,7 +50,7 @@ static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttrib } void attribute_search_add_items(StringRefNull str, - const bool is_output, + const bool can_create_attribute, Span<const GeometryAttributeInfo *> infos, uiSearchItems *seach_items, const bool is_first) @@ -68,8 +68,12 @@ void attribute_search_add_items(StringRefNull str, } if (!contained) { dummy_info.name = str; - UI_search_item_add( - seach_items, str.c_str(), &dummy_info, is_output ? ICON_ADD : ICON_NONE, 0, 0); + UI_search_item_add(seach_items, + str.c_str(), + &dummy_info, + can_create_attribute ? ICON_ADD : ICON_NONE, + 0, + 0); } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 05ae5299e58..14da5a7cd62 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -741,8 +741,15 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + U.experimental.use_override_new_fully_editable); } else if (object_active != NULL && !ID_IS_LINKED(object_active) && &object_active->instance_collection->id == id) { @@ -755,7 +762,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, &object_active->id, &object_active->id, &id_override, - false); + U.experimental.use_override_new_fully_editable); } break; case ID_OB: @@ -765,8 +772,15 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + U.experimental.use_override_new_fully_editable); } break; case ID_ME: @@ -796,12 +810,19 @@ static void template_id_liboverride_hierarchy_create(bContext *C, &collection_active->id, NULL, &id_override, - false); + U.experimental.use_override_new_fully_editable); } else { object_active->id.tag |= LIB_TAG_DOIT; - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &object_active->id, + NULL, + &id_override, + U.experimental.use_override_new_fully_editable); } } break; @@ -1024,6 +1045,26 @@ static const char *template_id_browse_tip(const StructRNA *type) } /** + * Add a superimposed extra icon to \a but, for workspace pinning. + * Rather ugly special handling, but this is really a special case at this point, nothing worth + * generalizing. + */ +static void template_id_workspace_pin_extra_icon(const TemplateID *template_ui, uiBut *but) +{ + if ((template_ui->idcode != ID_SCE) || (template_ui->ptr.type != &RNA_Window)) { + return; + } + + const wmWindow *win = template_ui->ptr.data; + const WorkSpace *workspace = WM_window_get_active_workspace(win); + UI_but_extra_operator_icon_add(but, + "WORKSPACE_OT_scene_pin_toggle", + WM_OP_INVOKE_DEFAULT, + (workspace->flags & WORKSPACE_USE_PIN_SCENE) ? ICON_PINNED : + ICON_UNPINNED); +} + +/** * \return a type-based i18n context, needed e.g. by "New" button. * In most languages, this adjective takes different form based on gender of type name... */ @@ -1220,6 +1261,8 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_REDALERT); } + template_id_workspace_pin_extra_icon(template_ui, but); + if (ID_IS_LINKED(id)) { const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name)); if (id->tag & LIB_TAG_INDIRECT) { diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc deleted file mode 100644 index 699ac0c2b53..00000000000 --- a/source/blender/editors/interface/interface_view.cc +++ /dev/null @@ -1,229 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup edinterface - * - * This part of the UI-View API is mostly needed to support persistent state of items within the - * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we - * can compare the old view items with the new view items and keep state persistent for matching - * ones. - */ - -#include <memory> -#include <type_traits> -#include <variant> - -#include "DNA_screen_types.h" - -#include "BKE_screen.h" - -#include "BLI_listbase.h" - -#include "ED_screen.h" - -#include "interface_intern.h" - -#include "UI_interface.hh" - -#include "UI_grid_view.hh" -#include "UI_tree_view.hh" - -using namespace blender; -using namespace blender::ui; - -/** - * Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a - * #std::variant. - */ -struct ViewLink : public Link { - using TreeViewPtr = std::unique_ptr<AbstractTreeView>; - using GridViewPtr = std::unique_ptr<AbstractGridView>; - - std::string idname; - /* NOTE: Can't use std::get() on this until minimum macOS deployment target is 10.14. */ - std::variant<TreeViewPtr, GridViewPtr> view; -}; - -template<class T> constexpr void check_if_valid_view_type() -{ - static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>, - "Unsupported view type"); -} - -template<class T> T *get_view_from_link(ViewLink &link) -{ - auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view); - return t_uptr ? t_uptr->get() : nullptr; -} - -template<class T> -static T *ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr<T> view) -{ - check_if_valid_view_type<T>(); - - ViewLink *view_link = MEM_new<ViewLink>(__func__); - BLI_addtail(&block.views, view_link); - - view_link->view = std::move(view); - view_link->idname = idname; - - return get_view_from_link<T>(*view_link); -} - -AbstractGridView *UI_block_add_view(uiBlock &block, - StringRef idname, - std::unique_ptr<AbstractGridView> tree_view) -{ - return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(tree_view)); -} - -AbstractTreeView *UI_block_add_view(uiBlock &block, - StringRef idname, - std::unique_ptr<AbstractTreeView> tree_view) -{ - return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view)); -} - -void ui_block_free_views(uiBlock *block) -{ - LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { - MEM_delete(link); - } -} - -void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) -{ - ARegion *region = listener_params->region; - - LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { - if (AbstractGridView *grid_view = get_view_from_link<AbstractGridView>(*view_link)) { - if (UI_grid_view_listen_should_redraw(reinterpret_cast<uiGridViewHandle *>(grid_view), - listener_params->notifier)) { - ED_region_tag_redraw(region); - } - } - else if (AbstractTreeView *tree_view = get_view_from_link<AbstractTreeView>(*view_link)) { - if (UI_tree_view_listen_should_redraw(reinterpret_cast<uiTreeViewHandle *>(tree_view), - listener_params->notifier)) { - ED_region_tag_redraw(region); - } - } - } -} - -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) -{ - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); - if (!tree_row_but) { - return nullptr; - } - - return tree_row_but->tree_item; -} - -uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) -{ - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region); - if (!tree_row_but) { - return nullptr; - } - - return tree_row_but->tree_item; -} - -template<class T> static StringRef ui_block_view_find_idname(const uiBlock &block, const T &view) -{ - check_if_valid_view_type<T>(); - - /* First get the idname the of the view we're looking for. */ - LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { - if (get_view_from_link<T>(*view_link) == &view) { - return view_link->idname; - } - } - - return {}; -} - -template<class T> -static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const T &new_view) -{ - check_if_valid_view_type<T>(); - - uiBlock *old_block = new_block.oldblock; - if (!old_block) { - return nullptr; - } - - StringRef idname = ui_block_view_find_idname(new_block, new_view); - if (idname.is_empty()) { - return nullptr; - } - - LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { - if (old_view_link->idname == idname) { - return get_view_from_link<T>(*old_view_link); - } - } - - return nullptr; -} - -uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block( - const uiBlock *new_block, const uiTreeViewHandle *new_view_handle) -{ - BLI_assert(new_block && new_view_handle); - const AbstractTreeView &new_view = reinterpret_cast<const AbstractTreeView &>(*new_view_handle); - - AbstractTreeView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view); - return reinterpret_cast<uiTreeViewHandle *>(old_view); -} - -uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block( - const uiBlock *new_block, const uiGridViewHandle *new_view_handle) -{ - BLI_assert(new_block && new_view_handle); - const AbstractGridView &new_view = reinterpret_cast<const AbstractGridView &>(*new_view_handle); - - AbstractGridView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view); - return reinterpret_cast<uiGridViewHandle *>(old_view); -} - -uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, - const uiTreeViewItemHandle *new_item_handle) -{ - uiBlock *old_block = new_block->oldblock; - if (!old_block) { - return nullptr; - } - - const AbstractTreeViewItem &new_item = *reinterpret_cast<const AbstractTreeViewItem *>( - new_item_handle); - const AbstractTreeView *old_tree_view = ui_block_view_find_matching_in_old_block( - *new_block, new_item.get_tree_view()); - if (!old_tree_view) { - return nullptr; - } - - LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { - if (old_but->type != UI_BTYPE_TREEROW) { - continue; - } - uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but; - if (!old_treerow_but->tree_item) { - continue; - } - AbstractTreeViewItem &old_item = *reinterpret_cast<AbstractTreeViewItem *>( - old_treerow_but->tree_item); - /* Check if the row is from the expected tree-view. */ - if (&old_item.get_tree_view() != old_tree_view) { - continue; - } - - if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) { - return old_treerow_but; - } - } - - return nullptr; -} diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e2df2d77817..855e72788d2 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -104,8 +104,7 @@ typedef enum { UI_WTYPE_LISTITEM, UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, - UI_WTYPE_TREEROW, - UI_WTYPE_GRID_TILE, + UI_WTYPE_VIEW_ITEM, } uiWidgetTypeEnum; /** @@ -3672,12 +3671,11 @@ static void widget_progressbar(uiBut *but, widgetbase_draw(&wtb_bar, wcol); } -static void widget_treerow_exec(uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int UNUSED(roundboxalign), - int indentation, - const float zoom) +static void widget_view_item(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int UNUSED(roundboxalign), + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3690,31 +3688,6 @@ static void widget_treerow_exec(uiWidgetColors *wcol, if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) { widgetbase_draw(&wtb, wcol); } - - BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect)); - BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); -} - -static void widget_treerow(uiBut *but, - uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int roundboxalign, - const float zoom) -{ - uiButTreeRow *tree_row = (uiButTreeRow *)but; - BLI_assert(but->type == UI_BTYPE_TREEROW); - widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom); -} - -static void widget_gridtile(uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int roundboxalign, - const float zoom) -{ - /* TODO Reuse tree-row drawing. */ - widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom); } static void widget_nodesocket(uiBut *but, @@ -4608,14 +4581,9 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_progressbar; break; - case UI_WTYPE_TREEROW: - wt.wcol_theme = &btheme->tui.wcol_view_item; - wt.custom = widget_treerow; - break; - - case UI_WTYPE_GRID_TILE: + case UI_WTYPE_VIEW_ITEM: wt.wcol_theme = &btheme->tui.wcol_view_item; - wt.draw = widget_gridtile; + wt.draw = widget_view_item; break; case UI_WTYPE_NODESOCKET: @@ -4949,13 +4917,8 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; - case UI_BTYPE_TREEROW: - wt = widget_type(UI_WTYPE_TREEROW); - fstyle = &style->widgetlabel; - break; - - case UI_BTYPE_GRID_TILE: - wt = widget_type(UI_WTYPE_GRID_TILE); + case UI_BTYPE_VIEW_ITEM: + wt = widget_type(UI_WTYPE_VIEW_ITEM); fstyle = &style->widgetlabel; break; diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index ee4bfd351ae..1bf7e25b154 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -1695,6 +1695,41 @@ void UI_view2d_view_to_region_fl( *r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask)); } +bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, + const float xy_a[2], + const float xy_b[2], + int r_region_a[2], + int r_region_b[2]) +{ + rctf rect_unit; + rect_unit.xmin = rect_unit.ymin = 0.0f; + rect_unit.xmax = rect_unit.ymax = 1.0f; + + /* Express given coordinates as proportional values. */ + const float s_a[2] = { + (xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur), + (xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur), + }; + const float s_b[2] = { + (xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur), + (xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur), + }; + + /* Set initial value in case coordinates lie outside bounds. */ + r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED; + + if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) { + r_region_a[0] = (int)(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask))); + r_region_a[1] = (int)(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask))); + r_region_b[0] = (int)(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask))); + r_region_b[1] = (int)(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask))); + + return true; + } + + return false; +} + void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst) { const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)}; diff --git a/source/blender/editors/interface/views/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc new file mode 100644 index 00000000000..077c76a08f1 --- /dev/null +++ b/source/blender/editors/interface/views/abstract_view.cc @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "interface_intern.h" + +#include "UI_abstract_view.hh" + +namespace blender::ui { + +void AbstractView::register_item(AbstractViewItem &item) +{ + /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to + * have the view base class register the items, rather than setting the view on the item. */ + item.view_ = this; +} + +/* ---------------------------------------------------------------------- */ +/** \name View Reconstruction + * \{ */ + +bool AbstractView::is_reconstructed() const +{ + return is_reconstructed_; +} + +void AbstractView::update_from_old(uiBlock &new_block) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + is_reconstructed_ = true; + return; + } + + uiViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( + &new_block, reinterpret_cast<uiViewHandle *>(this)); + if (old_view_handle == nullptr) { + /* Initial construction, nothing to update. */ + is_reconstructed_ = true; + return; + } + + AbstractView &old_view = reinterpret_cast<AbstractView &>(*old_view_handle); + + /* Update own persistent data. */ + /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's + * pointer to identify itself over redraws. */ + rename_buffer_ = std::move(old_view.rename_buffer_); + old_view.rename_buffer_ = nullptr; + + update_children_from_old(old_view); + + /* Finished (re-)constructing the tree. */ + is_reconstructed_ = true; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Default implementations of virtual functions + * \{ */ + +bool AbstractView::listen(const wmNotifier & /*notifier*/) const +{ + /* Nothing by default. */ + return false; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Renaming + * \{ */ + +bool AbstractView::is_renaming() const +{ + return rename_buffer_ != nullptr; +} + +bool AbstractView::begin_renaming() +{ + if (is_renaming()) { + return false; + } + + rename_buffer_ = std::make_unique<decltype(rename_buffer_)::element_type>(); + return true; +} + +void AbstractView::end_renaming() +{ + BLI_assert(is_renaming()); + rename_buffer_ = nullptr; +} + +Span<char> AbstractView::get_rename_buffer() const +{ + return *rename_buffer_; +} +MutableSpan<char> AbstractView::get_rename_buffer() +{ + return *rename_buffer_; +} + +/** \} */ + +} // namespace blender::ui diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc new file mode 100644 index 00000000000..f73183d07e9 --- /dev/null +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +#include "UI_abstract_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ +/** \name View Reconstruction + * \{ */ + +void AbstractViewItem::update_from_old(const AbstractViewItem &old) +{ + is_active_ = old.is_active_; + is_renaming_ = old.is_renaming_; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Renaming + * \{ */ + +bool AbstractViewItem::supports_renaming() const +{ + /* No renaming by default. */ + return false; +} +bool AbstractViewItem::rename(StringRefNull /*new_name*/) +{ + /* No renaming by default. */ + return false; +} + +StringRef AbstractViewItem::get_rename_string() const +{ + /* No rename string by default. */ + return {}; +} + +bool AbstractViewItem::is_renaming() const +{ + return is_renaming_; +} + +void AbstractViewItem::begin_renaming() +{ + AbstractView &view = get_view(); + if (view.is_renaming() || !supports_renaming()) { + return; + } + + if (view.begin_renaming()) { + is_renaming_ = true; + } + + StringRef initial_str = get_rename_string(); + std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); +} + +void AbstractViewItem::rename_apply() +{ + const AbstractView &view = get_view(); + rename(view.get_rename_buffer().data()); + end_renaming(); +} + +void AbstractViewItem::end_renaming() +{ + if (!is_renaming()) { + return; + } + + is_renaming_ = false; + + AbstractView &view = get_view(); + view.end_renaming(); +} + +static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) +{ + /* A minimal sanity check, can't do much more here. */ + BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); + + LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + if (but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + + uiButViewItem *view_item_but = (uiButViewItem *)but; + AbstractViewItem *item = reinterpret_cast<AbstractViewItem *>(view_item_but->view_item); + const AbstractView &view = item->get_view(); + + if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) { + return item; + } + } + + return nullptr; +} + +static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) +{ + const uiBut *rename_but = static_cast<uiBut *>(arg); + AbstractViewItem *item = find_item_from_rename_button(*rename_but); + BLI_assert(item); + item->rename_apply(); +} + +void AbstractViewItem::add_rename_button(uiBlock &block) +{ + AbstractView &view = get_view(); + uiBut *rename_but = uiDefBut(&block, + UI_BTYPE_TEXT, + 1, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + view.get_rename_buffer().data(), + 1.0f, + view.get_rename_buffer().size(), + 0, + 0, + ""); + + /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the + * callback is executed. */ + UI_but_func_rename_set(rename_but, rename_button_fn, rename_but); + UI_but_flag_disable(rename_but, UI_BUT_UNDO); + + const bContext *evil_C = reinterpret_cast<bContext *>(block.evil_C); + ARegion *region = CTX_wm_region(evil_C); + /* Returns false if the button was removed. */ + if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { + end_renaming(); + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Context Menu + * \{ */ + +void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +{ + /* No context menu by default. */ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +std::unique_ptr<AbstractViewItemDragController> AbstractViewItem::create_drag_controller() const +{ + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; +} + +std::unique_ptr<AbstractViewItemDropController> AbstractViewItem::create_drop_controller() const +{ + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; +} + +AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view) +{ +} + +void AbstractViewItemDragController::on_drag_start() +{ + /* Do nothing by default. */ +} + +AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view) +{ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name General Getters & Setters + * \{ */ + +AbstractView &AbstractViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be registered through AbstractView::register_item()"); + } + return *view_; +} + +bool AbstractViewItem::is_active() const +{ + BLI_assert_msg(get_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; +} + +/** \} */ + +} // namespace blender::ui + +/* ---------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +namespace blender::ui { + +/** + * Helper class to provide a higher level public (C-)API. Has access to private/protected view item + * members and ensures some invariants that way. + */ +class ViewItemAPIWrapper { + public: + static bool matches(const AbstractViewItem &a, const AbstractViewItem &b) + { + if (typeid(a) != typeid(b)) { + return false; + } + /* TODO should match the view as well. */ + return a.matches(b); + } + + static bool can_rename(const AbstractViewItem &item) + { + const AbstractView &view = item.get_view(); + return !view.is_renaming() && item.supports_renaming(); + } + + static bool drag_start(bContext &C, const AbstractViewItem &item) + { + const std::unique_ptr<AbstractViewItemDragController> drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(&C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + drag_controller->on_drag_start(); + + return true; + } + + static bool can_drop(const AbstractViewItem &item, + const wmDrag &drag, + const char **r_disabled_hint) + { + const std::unique_ptr<AbstractViewItemDropController> drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(drag, r_disabled_hint); + } + + static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag) + { + const std::unique_ptr<AbstractViewItemDropController> drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return {}; + } + + return drop_controller->drop_tooltip(drag); + } + + static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags) + { + std::unique_ptr<AbstractViewItemDropController> drop_controller = + item.create_drop_controller(); + + const char *disabled_hint_dummy = nullptr; + LISTBASE_FOREACH (const wmDrag *, drag, &drags) { + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { + return drop_controller->on_drop(&C, *drag); + } + } + + return false; + } +}; + +} // namespace blender::ui + +using namespace blender::ui; + +bool UI_view_item_is_active(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle); + return item.is_active(); +} + +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle) +{ + const AbstractViewItem &a = reinterpret_cast<const AbstractViewItem &>(*a_handle); + const AbstractViewItem &b = reinterpret_cast<const AbstractViewItem &>(*b_handle); + return ViewItemAPIWrapper::matches(a, b); +} + +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle); + return ViewItemAPIWrapper::can_rename(item); +} + +void UI_view_item_begin_rename(uiViewItemHandle *item_handle) +{ + AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle); + item.begin_renaming(); +} + +void UI_view_item_context_menu_build(bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle); + item.build_context_menu(*C, *column); +} + +bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + return ViewItemAPIWrapper::drag_start(*C, item); +} + +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); +} + +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + + const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag); + return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); +} + +bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + return ViewItemAPIWrapper::drop_handle(*C, item, *drags); +} + +/** \} */ diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index 194052862cf..52ff1460cbd 100644 --- a/source/blender/editors/interface/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -29,9 +29,8 @@ AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridVie items_.append(std::move(item)); AbstractGridViewItem &added_item = *items_.last(); - added_item.view_ = this; - item_map_.add(added_item.identifier_, &added_item); + register_item(added_item); return added_item; } @@ -43,12 +42,6 @@ void AbstractGridView::foreach_item(ItemIterFn iter_fn) const } } -bool AbstractGridView::listen(const wmNotifier & /*notifier*/) const -{ - /* Nothing by default. */ - return false; -} - AbstractGridViewItem *AbstractGridView::find_matching_item( const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const { @@ -67,34 +60,18 @@ void AbstractGridView::change_state_delayed() foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); }); } -void AbstractGridView::update_from_old(uiBlock &new_block) +void AbstractGridView::update_children_from_old(const AbstractView &old_view) { - uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block( - &new_block, reinterpret_cast<uiGridViewHandle *>(this)); - if (!old_view_handle) { - /* Initial construction, nothing to update. */ - is_reconstructed_ = true; - return; - } + const AbstractGridView &old_grid_view = dynamic_cast<const AbstractGridView &>(old_view); - AbstractGridView &old_view = reinterpret_cast<AbstractGridView &>(*old_view_handle); - - foreach_item([this, &old_view](AbstractGridViewItem &new_item) { - const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_view); + foreach_item([this, &old_grid_view](AbstractGridViewItem &new_item) { + const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_grid_view); if (!matching_old_item) { return; } new_item.update_from_old(*matching_old_item); }); - - /* Finished (re-)constructing the tree. */ - is_reconstructed_ = true; -} - -bool AbstractGridView::is_reconstructed() const -{ - return is_reconstructed_; } const GridViewStyle &AbstractGridView::get_style() const @@ -117,18 +94,19 @@ AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(i { } -bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const +bool AbstractGridViewItem::matches(const AbstractViewItem &other) const { - return identifier_ == other.identifier_; + const AbstractGridViewItem &other_grid_item = dynamic_cast<const AbstractGridViewItem &>(other); + return identifier_ == other_grid_item.identifier_; } void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { - uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1; + uiButViewItem *view_item_but = (uiButViewItem *)but_arg1; AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>( - *grid_tile_but->view_item); + *view_item_but->view_item); grid_item.activate(); } @@ -136,8 +114,8 @@ void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) { const GridViewStyle &style = get_view().get_style(); - grid_tile_but_ = (uiButGridTile *)uiDefBut(&block, - UI_BTYPE_GRID_TILE, + view_item_but_ = (uiButViewItem *)uiDefBut(&block, + UI_BTYPE_VIEW_ITEM, 0, "", 0, @@ -151,15 +129,8 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) 0, ""); - grid_tile_but_->view_item = reinterpret_cast<uiGridViewItemHandle *>(this); - UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr); -} - -bool AbstractGridViewItem::is_active() const -{ - BLI_assert_msg(get_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; + view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this); + UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr); } void AbstractGridViewItem::on_activate() @@ -180,11 +151,6 @@ void AbstractGridViewItem::change_state_delayed() } } -void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old) -{ - is_active_ = old.is_active_; -} - void AbstractGridViewItem::activate() { BLI_assert_msg(get_view().is_reconstructed(), @@ -213,7 +179,7 @@ const AbstractGridView &AbstractGridViewItem::get_view() const throw std::runtime_error( "Invalid state, item must be added through AbstractGridView::add_item()"); } - return *view_; + return dynamic_cast<AbstractGridView &>(*view_); } /* ---------------------------------------------------------------------- */ @@ -495,31 +461,3 @@ std::optional<bool> PreviewGridItem::should_be_active() const } } // namespace blender::ui - -using namespace blender::ui; - -/* ---------------------------------------------------------------------- */ -/* C-API */ - -using namespace blender::ui; - -bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle) -{ - const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle); - return item.is_active(); -} - -bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view_handle, - const wmNotifier *notifier) -{ - const AbstractGridView &view = *reinterpret_cast<const AbstractGridView *>(view_handle); - return view.listen(*notifier); -} - -bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle, - const uiGridViewItemHandle *b_handle) -{ - const AbstractGridViewItem &a = reinterpret_cast<const AbstractGridViewItem &>(*a_handle); - const AbstractGridViewItem &b = reinterpret_cast<const AbstractGridViewItem &>(*b_handle); - return a.matches(b); -} diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc new file mode 100644 index 00000000000..c568a8cab74 --- /dev/null +++ b/source/blender/editors/interface/views/interface_view.cc @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + * + * Code to manage views as part of the regular screen hierarchy. E.g. managing ownership of views + * inside blocks (#uiBlock.views), looking up items in the region, passing WM notifiers to views, + * etc. + * + * Blocks and their contained views are reconstructed on every redraw. This file also contains + * functions related to this recreation of views inside blocks. For example to query state + * information before the view is done reconstructing (#AbstractView.is_reconstructed() returns + * false), it may be enough to query the previous version of the block/view/view-item. Since such + * queries rely on the details of the UI reconstruction process, they should remain internal to + * `interface/` code. + */ + +#include <memory> +#include <type_traits> +#include <variant> + +#include "DNA_screen_types.h" + +#include "BKE_screen.h" + +#include "BLI_listbase.h" + +#include "ED_screen.h" + +#include "interface_intern.h" + +#include "UI_interface.hh" + +#include "UI_abstract_view.hh" +#include "UI_grid_view.hh" +#include "UI_tree_view.hh" + +using namespace blender; +using namespace blender::ui; + +/** + * Wrapper to store views in a #ListBase, addressable via an identifier. + */ +struct ViewLink : public Link { + std::string idname; + std::unique_ptr<AbstractView> view; +}; + +template<class T> +static T *ui_block_add_view_impl(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractView> view) +{ + ViewLink *view_link = MEM_new<ViewLink>(__func__); + BLI_addtail(&block.views, view_link); + + view_link->view = std::move(view); + view_link->idname = idname; + + return dynamic_cast<T *>(view_link->view.get()); +} + +AbstractGridView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractGridView> grid_view) +{ + return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_view)); +} + +AbstractTreeView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr<AbstractTreeView> tree_view) +{ + return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view)); +} + +void ui_block_free_views(uiBlock *block) +{ + LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { + MEM_delete(link); + } +} + +void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) +{ + ARegion *region = listener_params->region; + + LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { + if (view_link->view->listen(*listener_params->notifier)) { + ED_region_tag_redraw(region); + } + } +} + +uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2]) +{ + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); + if (!item_but) { + return nullptr; + } + + return item_but->view_item; +} + +uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region) +{ + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region); + if (!item_but) { + return nullptr; + } + + return item_but->view_item; +} + +static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view) +{ + /* First get the idname the of the view we're looking for. */ + LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { + if (view_link->view.get() == &view) { + return view_link->idname; + } + } + + return {}; +} + +template<class T> +static T *ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block, + const T &new_view) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + return nullptr; + } + + StringRef idname = ui_block_view_find_idname(new_block, new_view); + if (idname.is_empty()) { + return nullptr; + } + + LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { + if (old_view_link->idname == idname) { + return dynamic_cast<T *>(old_view_link->view.get()); + } + } + + return nullptr; +} + +uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiViewHandle *new_view_handle) +{ + BLI_assert(new_block && new_view_handle); + const AbstractView &new_view = reinterpret_cast<const AbstractView &>(*new_view_handle); + + AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(*new_block, new_view); + return reinterpret_cast<uiViewHandle *>(old_view); +} + +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle) +{ + uiBlock *old_block = new_block->oldblock; + if (!old_block) { + return nullptr; + } + + const AbstractViewItem &new_item = *reinterpret_cast<const AbstractViewItem *>(new_item_handle); + const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl( + *new_block, new_item.get_view()); + if (!old_view) { + return nullptr; + } + + LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { + if (old_but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + uiButViewItem *old_item_but = (uiButViewItem *)old_but; + if (!old_item_but->view_item) { + continue; + } + AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item); + /* Check if the item is from the expected view. */ + if (&old_item.get_view() != old_view) { + continue; + } + + if (UI_view_item_matches(reinterpret_cast<const uiViewItemHandle *>(&new_item), + reinterpret_cast<const uiViewItemHandle *>(&old_item))) { + return old_item_but; + } + } + + return nullptr; +} diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 96158ee48f6..43fdf741ac5 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -37,9 +37,11 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( if (root_ == nullptr) { root_ = this; } - + AbstractTreeView &tree_view = static_cast<AbstractTreeView &>(*root_); AbstractTreeViewItem &added_item = *children_.last(); added_item.root_ = root_; + tree_view.register_item(added_item); + if (root_ != this) { /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely * nice to static_cast this, but well... */ @@ -68,45 +70,11 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con foreach_item_recursive(iter_fn, options); } -bool AbstractTreeView::listen(const wmNotifier & /*notifier*/) const +void AbstractTreeView::update_children_from_old(const AbstractView &old_view) { - /* Nothing by default. */ - return false; -} + const AbstractTreeView &old_tree_view = dynamic_cast<const AbstractTreeView &>(old_view); -bool AbstractTreeView::is_renaming() const -{ - return rename_buffer_ != nullptr; -} - -void AbstractTreeView::update_from_old(uiBlock &new_block) -{ - uiBlock *old_block = new_block.oldblock; - if (!old_block) { - /* Initial construction, nothing to update. */ - is_reconstructed_ = true; - return; - } - - uiTreeViewHandle *old_view_handle = ui_block_tree_view_find_matching_in_old_block( - &new_block, reinterpret_cast<uiTreeViewHandle *>(this)); - if (old_view_handle == nullptr) { - is_reconstructed_ = true; - return; - } - - AbstractTreeView &old_view = reinterpret_cast<AbstractTreeView &>(*old_view_handle); - - /* Update own persistent data. */ - /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's - * pointer to identify itself over redraws. */ - rename_buffer_ = std::move(old_view.rename_buffer_); - old_view.rename_buffer_ = nullptr; - - update_children_from_old_recursive(*this, old_view); - - /* Finished (re-)constructing the tree. */ - is_reconstructed_ = true; + update_children_from_old_recursive(*this, old_tree_view); } void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items, @@ -129,7 +97,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items) { for (const auto &iter_item : items.children_) { - if (lookup_item.matches(*iter_item)) { + if (lookup_item.matches_single(*iter_item)) { /* We have a matching item! */ return iter_item.get(); } @@ -138,11 +106,6 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( return nullptr; } -bool AbstractTreeView::is_reconstructed() const -{ - return is_reconstructed_; -} - void AbstractTreeView::change_state_delayed() { BLI_assert_msg( @@ -157,9 +120,8 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; - AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>( - *tree_row_but->tree_item); + uiButViewItem *item_but = (uiButViewItem *)but_arg1; + AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(*item_but->view_item); tree_item.activate(); /* Not only activate the item, also show its children. Maybe this should be optional, or @@ -170,11 +132,11 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void AbstractTreeViewItem::add_treerow_button(uiBlock &block) { /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ - tree_row_but_ = (uiButTreeRow *)uiDefBut( - &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + view_item_but_ = (uiButViewItem *)uiDefBut( + &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); - tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this); - UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); + view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this); + UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr); } void AbstractTreeViewItem::add_indent(uiLayout &row) const @@ -206,10 +168,10 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region, - win->eventstate->xy); - AbstractTreeViewItem *hovered_item = reinterpret_cast<AbstractTreeViewItem *>( - hovered_item_handle); + uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region, + win->eventstate->xy); + + AbstractTreeViewItem *hovered_item = from_item_handle<AbstractTreeViewItem>(hovered_item_handle); BLI_assert(hovered_item != nullptr); hovered_item->toggle_collapsed(); @@ -243,40 +205,6 @@ void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const BLI_assert(is_collapse_chevron_but(but)); } -AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button( - const uiBut &rename_but) -{ - /* A minimal sanity check, can't do much more here. */ - BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); - - LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { - if (but->type != UI_BTYPE_TREEROW) { - continue; - } - - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - AbstractTreeViewItem *item = reinterpret_cast<AbstractTreeViewItem *>(tree_row_but->tree_item); - const AbstractTreeView &tree_view = item->get_tree_view(); - - if (item->is_renaming() && (tree_view.rename_buffer_->data() == rename_but.poin)) { - return item; - } - } - - return nullptr; -} - -void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) -{ - const uiBut *rename_but = static_cast<uiBut *>(arg); - AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but); - BLI_assert(item); - - const AbstractTreeView &tree_view = item->get_tree_view(); - item->rename(tree_view.rename_buffer_->data()); - item->end_renaming(); -} - void AbstractTreeViewItem::add_rename_button(uiLayout &row) { uiBlock *block = uiLayoutGetBlock(&row); @@ -286,33 +214,7 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row) /* Enable emboss for the text button. */ UI_block_emboss_set(block, UI_EMBOSS); - AbstractTreeView &tree_view = get_tree_view(); - uiBut *rename_but = uiDefBut(block, - UI_BTYPE_TEXT, - 1, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - tree_view.rename_buffer_->data(), - 1.0f, - tree_view.rename_buffer_->max_size(), - 0, - 0, - ""); - - /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the - * callback is executed. */ - UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); - UI_but_flag_disable(rename_but, UI_BUT_UNDO); - - const bContext *evil_C = static_cast<bContext *>(block->evil_C); - ARegion *region = CTX_wm_region(evil_C); - /* Returns false if the button was removed. */ - if (UI_but_active_only(evil_C, region, block, rename_but) == false) { - end_renaming(); - } + AbstractViewItem::add_rename_button(*block); UI_block_emboss_set(block, previous_emboss); UI_block_layout_set_current(block, &row); @@ -345,79 +247,35 @@ bool AbstractTreeViewItem::supports_collapsing() const return true; } -std::unique_ptr<AbstractTreeViewItemDragController> AbstractTreeViewItem::create_drag_controller() - const +StringRef AbstractTreeViewItem::get_rename_string() const { - /* There's no drag controller (and hence no drag support) by default. */ - return nullptr; -} - -std::unique_ptr<AbstractTreeViewItemDropController> AbstractTreeViewItem::create_drop_controller() - const -{ - /* There's no drop controller (and hence no drop support) by default. */ - return nullptr; -} - -bool AbstractTreeViewItem::supports_renaming() const -{ - /* No renaming by default. */ - return false; + return label_; } bool AbstractTreeViewItem::rename(StringRefNull new_name) { - /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches() + /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() * recognizes the item. (It only compares labels by default.) */ label_ = new_name; return true; } -void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old) { - /* No context menu by default. */ -} + AbstractViewItem::update_from_old(old); -void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) -{ - is_open_ = old.is_open_; - is_active_ = old.is_active_; - is_renaming_ = old.is_renaming_; + const AbstractTreeViewItem &old_tree_item = dynamic_cast<const AbstractTreeViewItem &>(old); + is_open_ = old_tree_item.is_open_; } -bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const +bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const { return label_ == other.label_; } -void AbstractTreeViewItem::begin_renaming() -{ - AbstractTreeView &tree_view = get_tree_view(); - if (tree_view.is_renaming() || !supports_renaming()) { - return; - } - - is_renaming_ = true; - - tree_view.rename_buffer_ = std::make_unique<decltype(tree_view.rename_buffer_)::element_type>(); - std::copy(std::begin(label_), std::end(label_), std::begin(*tree_view.rename_buffer_)); -} - -void AbstractTreeViewItem::end_renaming() -{ - if (!is_renaming()) { - return; - } - - is_renaming_ = false; - - AbstractTreeView &tree_view = get_tree_view(); - tree_view.rename_buffer_ = nullptr; -} - AbstractTreeView &AbstractTreeViewItem::get_tree_view() const { - return static_cast<AbstractTreeView &>(*root_); + return dynamic_cast<AbstractTreeView &>(get_view()); } int AbstractTreeViewItem::count_parents() const @@ -453,26 +311,19 @@ void AbstractTreeViewItem::deactivate() is_active_ = false; } -bool AbstractTreeViewItem::is_active() const -{ - BLI_assert_msg(get_tree_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; -} - bool AbstractTreeViewItem::is_hovered() const { BLI_assert_msg(get_tree_view().is_reconstructed(), "State can't be queried until reconstruction is completed"); - BLI_assert_msg(tree_row_but_ != nullptr, + BLI_assert_msg(view_item_but_ != nullptr, "Hovered state can't be queried before the tree row is being built"); - const uiTreeViewItemHandle *this_handle = reinterpret_cast<const uiTreeViewItemHandle *>(this); + const uiViewItemHandle *this_item_handle = reinterpret_cast<const uiViewItemHandle *>(this); /* The new layout hasn't finished construction yet, so the final state of the button is unknown. * Get the matching button from the previous redraw instead. */ - uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block, - this_handle); - return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE); + uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block( + view_item_but_->but.block, this_item_handle); + return old_item_but && (old_item_but->but.flag & UI_ACTIVE); } bool AbstractTreeViewItem::is_collapsed() const @@ -500,11 +351,6 @@ bool AbstractTreeViewItem::is_collapsible() const return this->supports_collapsing(); } -bool AbstractTreeViewItem::is_renaming() const -{ - return is_renaming_; -} - void AbstractTreeViewItem::ensure_parents_uncollapsed() { for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { @@ -512,19 +358,21 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed() } } -bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const +bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const { - if (!matches(other)) { + const AbstractTreeViewItem &other_tree_item = dynamic_cast<const AbstractTreeViewItem &>(other); + + if (!matches_single(other_tree_item)) { return false; } - if (count_parents() != other.count_parents()) { + if (count_parents() != other_tree_item.count_parents()) { return false; } - for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_; + for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_; parent && other_parent; parent = parent->parent_, other_parent = other_parent->parent_) { - if (!parent->matches(*other_parent)) { + if (!parent->matches_single(*other_parent)) { return false; } } @@ -532,9 +380,9 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem return true; } -uiButTreeRow *AbstractTreeViewItem::tree_row_button() +uiButViewItem *AbstractTreeViewItem::view_item_button() { - return tree_row_but_; + return view_item_but_; } void AbstractTreeViewItem::change_state_delayed() @@ -547,25 +395,6 @@ void AbstractTreeViewItem::change_state_delayed() /* ---------------------------------------------------------------------- */ -AbstractTreeViewItemDragController::AbstractTreeViewItemDragController(AbstractTreeView &tree_view) - : tree_view_(tree_view) -{ -} - -void AbstractTreeViewItemDragController::on_drag_start() -{ - /* Do nothing by default. */ -} - -/* ---------------------------------------------------------------------- */ - -AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view) - : tree_view_(tree_view) -{ -} - -/* ---------------------------------------------------------------------- */ - class TreeViewLayoutBuilder { uiBlock &block_; @@ -611,7 +440,7 @@ void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) UI_but_drawflag_enable(static_cast<uiBut *>(but->next), UI_BUT_NO_TEXT_PADDING); } - if (but->type == UI_BTYPE_TREEROW) { + if (but->type == UI_BTYPE_VIEW_ITEM) { break; } } @@ -723,161 +552,4 @@ std::optional<bool> BasicTreeViewItem::should_be_active() const return std::nullopt; } -/* ---------------------------------------------------------------------- */ - -/** - * Helper for a public (C-)API, presenting higher level functionality. Has access to internal - * data/functionality (friend of #AbstractTreeViewItem), which is sometimes needed when - * functionality of the API needs to be constructed from multiple internal conditions and/or - * functions that on their own shouldn't be part of the API. - */ -class TreeViewItemAPIWrapper { - public: - static bool matches(const AbstractTreeViewItem &a, const AbstractTreeViewItem &b) - { - /* TODO should match the tree-view as well. */ - return a.matches_including_parents(b); - } - - static bool drag_start(bContext &C, const AbstractTreeViewItem &item) - { - const std::unique_ptr<AbstractTreeViewItemDragController> drag_controller = - item.create_drag_controller(); - if (!drag_controller) { - return false; - } - - WM_event_start_drag(&C, - ICON_NONE, - drag_controller->get_drag_type(), - drag_controller->create_drag_data(), - 0, - WM_DRAG_FREE_DATA); - drag_controller->on_drag_start(); - - return true; - } - - static bool can_drop(const AbstractTreeViewItem &item, - const wmDrag &drag, - const char **r_disabled_hint) - { - const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return false; - } - - return drop_controller->can_drop(drag, r_disabled_hint); - } - - static std::string drop_tooltip(const AbstractTreeViewItem &item, const wmDrag &drag) - { - const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return {}; - } - - return drop_controller->drop_tooltip(drag); - } - - static bool drop_handle(bContext &C, const AbstractTreeViewItem &item, const ListBase &drags) - { - std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = - item.create_drop_controller(); - - const char *disabled_hint_dummy = nullptr; - LISTBASE_FOREACH (const wmDrag *, drag, &drags) { - if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { - return drop_controller->on_drop(&C, *drag); - } - } - - return false; - } - - static bool can_rename(const AbstractTreeViewItem &item) - { - const AbstractTreeView &tree_view = item.get_tree_view(); - return !tree_view.is_renaming() && item.supports_renaming(); - } -}; - } // namespace blender::ui - -/* ---------------------------------------------------------------------- */ -/* C-API */ - -using namespace blender::ui; - -bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view_handle, - const wmNotifier *notifier) -{ - const AbstractTreeView &view = *reinterpret_cast<const AbstractTreeView *>(view_handle); - return view.listen(*notifier); -} - -bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); - return item.is_active(); -} - -bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, - const uiTreeViewItemHandle *b_handle) -{ - const AbstractTreeViewItem &a = reinterpret_cast<const AbstractTreeViewItem &>(*a_handle); - const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle); - return TreeViewItemAPIWrapper::matches(a, b); -} - -bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return TreeViewItemAPIWrapper::drag_start(*C, item); -} - -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, - const wmDrag *drag, - const char **r_disabled_hint) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return TreeViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); -} - -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - - const std::string tooltip = TreeViewItemAPIWrapper::drop_tooltip(item, *drag); - return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); -} - -bool UI_tree_view_item_drop_handle(bContext *C, - const uiTreeViewItemHandle *item_, - const ListBase *drags) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return TreeViewItemAPIWrapper::drop_handle(*C, item, *drags); -} - -bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); - return TreeViewItemAPIWrapper::can_rename(item); -} - -void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle) -{ - AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_handle); - item.begin_renaming(); -} - -void UI_tree_view_item_context_menu_build(bContext *C, - const uiTreeViewItemHandle *item_handle, - uiLayout *column) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); - item.build_context_menu(*C, *column); -} diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 0e8e0f83597..0068586730f 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -144,10 +144,10 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op) /* Take some defaults from the scene, if not specified explicitly. */ Scene *scene = CTX_data_scene(C); if (params.frame_start == INT_MIN) { - params.frame_start = SFRA; + params.frame_start = scene->r.sfra; } if (params.frame_end == INT_MIN) { - params.frame_end = EFRA; + params.frame_end = scene->r.efra; } const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job"); @@ -248,8 +248,8 @@ static void wm_alembic_export_draw(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) { - RNA_int_set(op->ptr, "start", SFRA); - RNA_int_set(op->ptr, "end", EFRA); + RNA_int_set(op->ptr, "start", scene->r.sfra); + RNA_int_set(op->ptr, "end", scene->r.efra); RNA_boolean_set(op->ptr, "init_scene_frame_range", false); } diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c index 6e5ae9f3cba..3f905dd7de0 100644 --- a/source/blender/editors/io/io_gpencil_export.c +++ b/source/blender/editors/io/io_gpencil_export.c @@ -153,9 +153,9 @@ static int wm_gpencil_export_svg_exec(bContext *C, wmOperator *op) .v3d = v3d, .ob = ob, .mode = GP_EXPORT_TO_SVG, - .frame_start = CFRA, - .frame_end = CFRA, - .frame_cur = CFRA, + .frame_start = scene->r.cfra, + .frame_end = scene->r.cfra, + .frame_cur = scene->r.cfra, .flag = flag, .scale = 1.0f, .select_mode = select_mode, @@ -306,9 +306,9 @@ static int wm_gpencil_export_pdf_exec(bContext *C, wmOperator *op) .v3d = v3d, .ob = ob, .mode = GP_EXPORT_TO_PDF, - .frame_start = SFRA, - .frame_end = EFRA, - .frame_cur = CFRA, + .frame_start = scene->r.sfra, + .frame_end = scene->r.efra, + .frame_cur = scene->r.cfra, .flag = flag, .scale = 1.0f, .select_mode = select_mode, diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c index 45f5441616f..9ac64407dcf 100644 --- a/source/blender/editors/io/io_gpencil_import.c +++ b/source/blender/editors/io/io_gpencil_import.c @@ -90,9 +90,9 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op) .v3d = v3d, .ob = NULL, .mode = GP_IMPORT_FROM_SVG, - .frame_start = CFRA, - .frame_end = CFRA, - .frame_cur = CFRA, + .frame_start = scene->r.cfra, + .frame_end = scene->r.cfra, + .frame_cur = scene->r.cfra, .flag = flag, .scale = scale, .select_mode = 0, diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 53fa4788d0e..79ec7ebf2a5 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -208,11 +208,11 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op) int end = RNA_int_get(op->ptr, "end_frame"); /* Set the defaults. */ if (start == INT_MIN) { - start = SFRA; + start = scene->r.sfra; changed = true; } if (end == INT_MAX) { - end = EFRA; + end = scene->r.efra; changed = true; } /* Fix user errors. */ @@ -266,7 +266,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export multiple frames instead of the current frame only"); RNA_def_int(ot->srna, "start_frame", - INT_MIN, /* wm_obj_export_check uses this to set SFRA. */ + INT_MIN, /* wm_obj_export_check uses this to set scene->r.sfra. */ INT_MIN, INT_MAX, "Start Frame", @@ -275,7 +275,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) INT_MAX); RNA_def_int(ot->srna, "end_frame", - INT_MAX, /* wm_obj_export_check uses this to set EFRA. */ + INT_MAX, /* wm_obj_export_check uses this to set scene->r.efra. */ INT_MIN, INT_MAX, "End Frame", diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 37c1815fca3..df30870007f 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -258,7 +258,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 &u, NULL)) { Scene *scene = CTX_data_scene(C); - const float ctime = CFRA; + const float ctime = scene->r.cfra; MaskSplinePoint *new_point; int point_index = point - spline->points; @@ -295,7 +295,7 @@ static bool add_vertex_extrude(const bContext *C, const float co[2]) { Scene *scene = CTX_data_scene(C); - const float ctime = CFRA; + const float ctime = scene->r.cfra; MaskSpline *spline; MaskSplinePoint *point; @@ -394,7 +394,7 @@ static bool add_vertex_extrude(const bContext *C, static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer, const float co[2]) { Scene *scene = CTX_data_scene(C); - const float ctime = CFRA; + const float ctime = scene->r.cfra; MaskSpline *spline; MaskSplinePoint *new_point = NULL, *ref_point = NULL; @@ -874,7 +874,7 @@ void MASK_OT_primitive_circle_add(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Primitive Add Suqare Operator +/** \name Primitive Add Square Operator * \{ */ static int primitive_square_add_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 4662fe9d1a8..4c01154ba49 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -802,7 +802,7 @@ void ED_mask_draw_frames( mask_layer_shape = mask_layer_shape->next) { int frame = mask_layer_shape->frame; - // draw_keyframe(i, CFRA, sfra, framelen, 1); + // draw_keyframe(i, scene->r.cfra, sfra, framelen, 1); int height = (frame == cfra) ? 22 : 10; int x = (frame - sfra) * framelen; immVertex2i(pos, x, region_bottom); diff --git a/source/blender/editors/mask/mask_editaction.c b/source/blender/editors/mask/mask_editaction.c index 8a23a53a5d2..9819532224e 100644 --- a/source/blender/editors/mask/mask_editaction.c +++ b/source/blender/editors/mask/mask_editaction.c @@ -304,7 +304,7 @@ static bool snap_mask_layer_nearestsec(MaskLayerShape *mask_layer_shape, Scene * static bool snap_mask_layer_cframe(MaskLayerShape *mask_layer_shape, Scene *scene) { if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { - mask_layer_shape->frame = (int)CFRA; + mask_layer_shape->frame = (int)scene->r.cfra; } return false; } diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 14976d860f6..d34b274c111 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -856,7 +856,7 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Don't key sliding feather UW's. */ if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == false) { if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(data->mask_layer, CFRA); + ED_mask_layer_shape_auto_key(data->mask_layer, scene->r.cfra); } } @@ -1262,7 +1262,7 @@ static int slide_spline_curvature_modal(bContext *C, wmOperator *op, const wmEve if (event->type == slide_data->event_invoke_type && event->val == KM_RELEASE) { /* Don't key sliding feather UW's. */ if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(slide_data->mask_layer, CFRA); + ED_mask_layer_shape_auto_key(slide_data->mask_layer, scene->r.cfra); } WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask); @@ -1525,7 +1525,7 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) if (changed_layer) { if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(mask_layer, CFRA); + ED_mask_layer_shape_auto_key(mask_layer, scene->r.cfra); } } } @@ -1587,7 +1587,7 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op) if (changed_layer) { if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(mask_layer, CFRA); + ED_mask_layer_shape_auto_key(mask_layer, scene->r.cfra); } } } diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index 02e1524e23e..bb865e925d7 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -682,8 +682,7 @@ void ED_mask_get_size(ScrArea *area, int *width, int *height) } case SPACE_SEQ: { // Scene *scene = CTX_data_scene(C); - // *width = (scene->r.size * scene->r.xsch) / 100; - // *height = (scene->r.size * scene->r.ysch) / 100; + // BKE_render_resolution(&scene->r, false, width, height); break; } case SPACE_IMAGE: { diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 4bd80208b9e..95cad3f54ca 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -31,8 +31,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "DEG_depsgraph.h" - #include "mask_intern.h" /* own include */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index 55145f27012..48944c081a8 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -33,7 +33,7 @@ static int mask_shape_key_insert_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - const int frame = CFRA; + const int frame = scene->r.cfra; Mask *mask = CTX_data_edit_mask(C); bool changed = false; @@ -76,7 +76,7 @@ void MASK_OT_shape_key_insert(wmOperatorType *ot) static int mask_shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - const int frame = CFRA; + const int frame = scene->r.cfra; Mask *mask = CTX_data_edit_mask(C); bool changed = false; @@ -122,7 +122,7 @@ void MASK_OT_shape_key_clear(wmOperatorType *ot) static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - const int frame = CFRA; + const int frame = scene->r.cfra; Mask *mask = CTX_data_edit_mask(C); bool changed = false; @@ -214,7 +214,7 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot) static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - const int frame = CFRA; + const int frame = scene->r.cfra; Mask *mask = CTX_data_edit_mask(C); bool changed = false; diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index 69fe69fe117..b69cd8b8606 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -551,3 +551,54 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_flush_flags(ob); } } + +void paintvert_hide(bContext *C, Object *ob, const bool unselected) +{ + Mesh *const me = BKE_mesh_from_object(ob); + + if (me == NULL || me->totvert == 0) { + return; + } + + for (int i = 0; i < me->totvert; i++) { + MVert *const mvert = &me->mvert[i]; + + if ((mvert->flag & ME_HIDE) == 0) { + if (((mvert->flag & SELECT) == 0) == unselected) { + mvert->flag |= ME_HIDE; + } + } + + if (mvert->flag & ME_HIDE) { + mvert->flag &= ~SELECT; + } + } + + BKE_mesh_flush_hidden_from_verts(me); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); +} + +void paintvert_reveal(bContext *C, Object *ob, const bool select) +{ + Mesh *const me = BKE_mesh_from_object(ob); + + if (me == NULL || me->totvert == 0) { + return; + } + + for (int i = 0; i < me->totvert; i++) { + MVert *const mvert = &me->mvert[i]; + + if (mvert->flag & ME_HIDE) { + SET_FLAG_FROM_TEST(mvert->flag, select, SELECT); + mvert->flag &= ~ME_HIDE; + } + } + + BKE_mesh_flush_hidden_from_verts(me); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); +} 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_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 1febc429edc..c5add97fb00 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -8692,7 +8692,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * * Free the data here, then use #point_normals_ensure to add it back on demand. */ if (ret == OPERATOR_PASS_THROUGH) { /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */ - if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (!ISMOUSE_MOTION(event->type)) { point_normals_free(op); } } diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 04030583f5c..ac5530c8ea9 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -593,13 +593,143 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) return vmap->vert[v]; } +#define INVALID_ISLAND ((unsigned int)-1) + +static void bm_uv_assign_island(UvElementMap *element_map, + UvElement *element, + int nisland, + uint *map, + UvElement *islandbuf, + int islandbufsize) +{ + element->island = nisland; + map[element - element_map->buf] = islandbufsize; + + /* Copy *element to islandbuf[islandbufsize]. */ + islandbuf[islandbufsize].l = element->l; + islandbuf[islandbufsize].separate = element->separate; + islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index; + islandbuf[islandbufsize].island = element->island; + islandbuf[islandbufsize].flag = element->flag; +} + +static int bm_uv_edge_select_build_islands(UvElementMap *element_map, + const Scene *scene, + UvElement *islandbuf, + uint *map, + bool uv_selected, + int cd_loop_uv_offset) +{ + int totuv = element_map->totalUVs; + + /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ + UvElement **head_table = MEM_mallocN(sizeof(*head_table) * totuv, "uv_island_head_table"); + for (int i = 0; i < totuv; i++) { + UvElement *head = element_map->buf + i; + if (head->separate) { + UvElement *element = head; + while (element) { + head_table[element - element_map->buf] = head; + element = element->next; + if (element && element->separate) { + break; + } + } + } + } + + /* Depth first search the graph, building islands as we go. */ + int nislands = 0; + int islandbufsize = 0; + int stack_upper_bound = totuv; + UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, + "uv_island_element_stack"); + int stacksize_uv = 0; + for (int i = 0; i < totuv; i++) { + UvElement *element = element_map->buf + i; + if (element->island != INVALID_ISLAND) { + /* Unique UV (element and all it's children) are already part of an island. */ + continue; + } + + /* Create a new island, i.e. nislands++. */ + + BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */ + + /* Seed the graph search. */ + stack_uv[stacksize_uv++] = element; + while (element) { + bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++); + element = element->next; + if (element && element->separate) { + break; + } + } + + /* Traverse the graph. */ + while (stacksize_uv) { + BLI_assert(stacksize_uv < stack_upper_bound); + element = stack_uv[--stacksize_uv]; + while (element) { + + /* Scan forwards around the BMFace that contains element->l. */ + if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) { + UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next); + if (next->island == INVALID_ISLAND) { + UvElement *tail = head_table[next - element_map->buf]; + stack_uv[stacksize_uv++] = tail; + while (tail) { + bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + } + + /* Scan backwards around the BMFace that contains element->l. */ + if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) { + UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev); + if (prev->island == INVALID_ISLAND) { + UvElement *tail = head_table[prev - element_map->buf]; + stack_uv[stacksize_uv++] = tail; + while (tail) { + bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + } + + /* The same for all the UvElements in this unique UV. */ + element = element->next; + if (element && element->separate) { + break; + } + } + } + nislands++; + } + BLI_assert(islandbufsize == totuv); + + MEM_SAFE_FREE(stack_uv); + MEM_SAFE_FREE(head_table); + + return nislands; +} + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, - const bool face_selected, const bool uv_selected, const bool use_winding, const bool do_islands) { + /* In uv sync selection, all UVs are visible. */ + const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION); + BMVert *ev; BMFace *efa; BMLoop *l; @@ -623,15 +753,21 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, /* generate UvElement array */ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - if (!uv_selected) { - totuv += efa->len; - } - else { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - totuv++; - } + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + continue; + } + + if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } + + if (!uv_selected) { + totuv += efa->len; + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + totuv++; } } } @@ -649,46 +785,48 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, "UvElement"); if (use_winding) { - winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding"); + winding = MEM_callocN(sizeof(*winding) * totfaces, "winding"); } BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) { - if (use_winding) { - winding[j] = false; + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + continue; } - if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - float(*tf_uv)[2] = NULL; + if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } - if (use_winding) { - tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); - } + float(*tf_uv)[2] = NULL; - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - continue; - } - - buf->l = l; - buf->separate = 0; - buf->island = INVALID_ISLAND; - buf->loop_of_poly_index = i; + if (use_winding) { + tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); + } - buf->next = element_map->vert[BM_elem_index_get(l->v)]; - element_map->vert[BM_elem_index_get(l->v)] = buf; + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } - if (use_winding) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - copy_v2_v2(tf_uv[i], luv->uv); - } + buf->l = l; + buf->separate = 0; + buf->island = INVALID_ISLAND; + buf->loop_of_poly_index = i; - buf++; - } + buf->next = element_map->vert[BM_elem_index_get(l->v)]; + element_map->vert[BM_elem_index_get(l->v)] = buf; if (use_winding) { - winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(tf_uv[i], luv->uv); } + + buf++; + } + + if (winding) { + winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; } } @@ -771,6 +909,15 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face"); copy_vn_i(island_number, totfaces, INVALID_ISLAND); + const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? + scene->toolsettings->selectmode & SCE_SELECT_EDGE : + scene->toolsettings->uv_selectmode & UV_SELECT_EDGE; + if (use_uv_edge_connectivity) { + nislands = bm_uv_edge_select_build_islands( + element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset); + islandbufsize = totuv; + } + /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. * Now we should sort uv's in islands. */ for (i = 0; i < totuv; i++) { @@ -798,13 +945,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, if (element->l->f == efa) { /* found the uv corresponding to our face and vertex. * Now fill it to the buffer */ - element->island = nislands; - map[element - element_map->buf] = islandbufsize; - islandbuf[islandbufsize].l = element->l; - islandbuf[islandbufsize].separate = element->separate; - islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index; - islandbuf[islandbufsize].island = nislands; - islandbufsize++; + bm_uv_assign_island( + element_map, element, nislands, map, islandbuf, islandbufsize++); for (element = initelement; element; element = element->next) { if (element->separate && element != initelement) { @@ -1646,12 +1788,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/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 97376a495c1..a2f993c92b9 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -76,7 +76,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 041a1383b28..e9abf8c1441 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -24,6 +24,7 @@ #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_fluidsim_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" @@ -36,6 +37,7 @@ #include "BLI_math.h" #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BLT_translation.h" @@ -71,6 +73,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_nla.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcloud.h" @@ -113,6 +116,10 @@ #include "object_intern.h" +using blender::float3; +using blender::float4x4; +using blender::Vector; + /* -------------------------------------------------------------------- */ /** \name Local Enum Declarations * \{ */ @@ -1328,21 +1335,21 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const char *ob_name = nullptr; switch (type) { case GP_EMPTY: { - ob_name = "GPencil"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "GPencil"); break; } case GP_MONKEY: { - ob_name = "Suzanne"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Suzanne"); break; } case GP_STROKE: { - ob_name = "Stroke"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Stroke"); break; } case GP_LRT_OBJECT: case GP_LRT_SCENE: case GP_LRT_COLLECTION: { - ob_name = "Line Art"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "LineArt"); break; } default: { @@ -1984,7 +1991,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) AnimData *adt = BKE_animdata_ensure_id(&ob->id); NlaTrack *nlt = BKE_nlatrack_add(adt, nullptr, is_liboverride); NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, static_cast<Speaker *>(ob->data)); - strip->start = CFRA; + strip->start = scene->r.cfra; strip->end += strip->start; /* hook them up */ @@ -2023,14 +2030,6 @@ void OBJECT_OT_speaker_add(wmOperatorType *ot) /** \name Add Curves Operator * \{ */ -static bool object_curves_add_poll(bContext *C) -{ - if (!U.experimental.use_new_curves_type) { - return false; - } - return ED_operator_objectmode(C); -} - static int object_curves_random_add_exec(bContext *C, wmOperator *op) { using namespace blender; @@ -2043,7 +2042,6 @@ static int object_curves_random_add_exec(bContext *C, wmOperator *op) } Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits); - object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ Curves *curves_id = static_cast<Curves *>(object->data); bke::CurvesGeometry::wrap(curves_id->geometry) = ed::curves::primitive_random_sphere(500, 8); @@ -2060,7 +2058,7 @@ void OBJECT_OT_curves_random_add(wmOperatorType *ot) /* api callbacks */ ot->exec = object_curves_random_add_exec; - ot->poll = object_curves_add_poll; + ot->poll = ED_operator_objectmode; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2070,38 +2068,48 @@ void OBJECT_OT_curves_random_add(wmOperatorType *ot) static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) { + Scene *scene = CTX_data_scene(C); + ushort local_view_bits; - float loc[3], rot[3]; if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { + C, op, 'Z', nullptr, nullptr, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } Object *surface_ob = CTX_data_active_object(C); + BLI_assert(surface_ob != nullptr); - Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits); - object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ + Object *curves_ob = ED_object_add_type( + C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits); + BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false); - if (surface_ob != nullptr && surface_ob->type == OB_MESH) { - Curves *curves_id = static_cast<Curves *>(object->data); - curves_id->surface = surface_ob; - id_us_plus(&surface_ob->id); + /* Set surface object. */ + Curves *curves_id = static_cast<Curves *>(curves_ob->data); + curves_id->surface = surface_ob; - Mesh *surface_mesh = static_cast<Mesh *>(surface_ob->data); - const char *uv_name = CustomData_get_active_layer_name(&surface_mesh->ldata, CD_MLOOPUV); - if (uv_name != nullptr) { - curves_id->surface_uv_map = BLI_strdup(uv_name); - } + /* Parent to surface object. */ + ED_object_parent_set( + op->reports, C, scene, curves_ob, surface_ob, PAR_OBJECT, false, true, nullptr); + + /* Decide which UV map to use for attachment. */ + Mesh *surface_mesh = static_cast<Mesh *>(surface_ob->data); + const char *uv_name = CustomData_get_active_layer_name(&surface_mesh->ldata, CD_MLOOPUV); + if (uv_name != nullptr) { + curves_id->surface_uv_map = BLI_strdup(uv_name); } + /* Add deformation modifier. */ + blender::ed::curves::ensure_surface_deformation_node_exists(*C, *curves_ob); + + /* Make sure the surface object has a rest position attribute which is necessary for + * deformations. */ + surface_ob->modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION; + return OPERATOR_FINISHED; } static bool object_curves_empty_hair_add_poll(bContext *C) { - if (!U.experimental.use_new_curves_type) { - return false; - } if (!ED_operator_objectmode(C)) { return false; } @@ -2763,28 +2771,6 @@ static const EnumPropertyItem convert_target_items[] = { {0, nullptr, 0, nullptr, nullptr}, }; -static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - EnumPropertyItem *items = nullptr; - int items_num = 0; - for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) { - if (item->value == OB_CURVES) { - if (U.experimental.use_new_curves_type) { - RNA_enum_item_add(&items, &items_num, item); - } - } - else { - RNA_enum_item_add(&items, &items_num, item); - } - } - RNA_enum_item_end(&items, &items_num); - *r_free = true; - return items; -} - static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == nullptr) { @@ -3192,9 +3178,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) } /* Anonymous attributes shouldn't be available on the applied geometry. */ - MeshComponent component; - component.replace(new_mesh, GeometryOwnershipType::Editable); - component.attributes_remove_anonymous(); + blender::bke::mesh_attributes_for_write(*new_mesh).remove_anonymous(); BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */ } @@ -3544,7 +3528,6 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); - RNA_def_enum_funcs(ot->prop, convert_target_items_fn); RNA_def_boolean(ot->srna, "keep_original", false, @@ -3600,7 +3583,8 @@ static Base *object_add_duplicate_internal(Main *bmain, ViewLayer *view_layer, Object *ob, const eDupli_ID_Flags dupflag, - const eLibIDDuplicateFlags duplicate_options) + const eLibIDDuplicateFlags duplicate_options, + Object **r_ob_new) { Base *base, *basen = nullptr; Object *obn; @@ -3611,6 +3595,9 @@ static Base *object_add_duplicate_internal(Main *bmain, else { obn = static_cast<Object *>( ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options))); + if (r_ob_new) { + *r_ob_new = obn; + } DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); base = BKE_view_layer_base_find(view_layer, ob); @@ -3623,7 +3610,7 @@ static Base *object_add_duplicate_internal(Main *bmain, } basen = BKE_view_layer_base_find(view_layer, obn); - if (base != nullptr) { + if (base != nullptr && basen != nullptr) { basen->local_view_bits = base->local_view_bits; } @@ -3654,7 +3641,8 @@ Base *ED_object_add_duplicate( base->object, dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS | - LIB_ID_DUPLICATE_IS_ROOT_ID); + LIB_ID_DUPLICATE_IS_ROOT_ID, + nullptr); if (basen == nullptr) { return nullptr; } @@ -3687,45 +3675,73 @@ static int duplicate_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; - bool changed = false; /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ BKE_main_id_newptr_and_tag_clear(bmain); + /* Do not do collection re-syncs for each object; will do it once afterwards. + * However this means we can't get to new duplicated Base's immediately, will + * have to process them after the sync. */ + BKE_layer_collection_resync_forbid(); + + /* Duplicate the selected objects, remember data needed to process + * after the sync (the base of the original object, and the copy of the + * original object). */ + blender::Vector<std::pair<Base *, Object *>> source_bases_new_objects; + Object *ob_new_active = nullptr; + CTX_DATA_BEGIN (C, Base *, base, selected_bases) { - Base *basen = object_add_duplicate_internal(bmain, - scene, - view_layer, - base->object, - dupflag, - LIB_ID_DUPLICATE_IS_SUBPROCESS | - LIB_ID_DUPLICATE_IS_ROOT_ID); + Object *ob_new = NULL; + object_add_duplicate_internal(bmain, + scene, + view_layer, + base->object, + dupflag, + LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID, + &ob_new); + if (ob_new == nullptr) { + continue; + } + source_bases_new_objects.append({base, ob_new}); /* note that this is safe to do with this context iterator, * the list is made in advance */ ED_object_base_select(base, BA_DESELECT); - ED_object_base_select(basen, BA_SELECT); - changed = true; - if (basen == nullptr) { - continue; - } - - /* new object becomes active */ + /* new object will become active */ if (BASACT(view_layer) == base) { - ED_object_base_activate(C, basen); - } - - if (basen->object->data) { - DEG_id_tag_update(static_cast<ID *>(basen->object->data), 0); + ob_new_active = ob_new; } } CTX_DATA_END; - if (!changed) { + if (source_bases_new_objects.is_empty()) { return OPERATOR_CANCELLED; } + /* Sync the collection now, after everything is duplicated. */ + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync(bmain); + + /* After sync we can get to the new Base data, process it here. */ + for (const auto &item : source_bases_new_objects) { + Object *ob_new = item.second; + Base *base_source = item.first; + Base *base_new = BKE_view_layer_base_find(view_layer, ob_new); + if (base_new == nullptr) { + continue; + } + ED_object_base_select(base_new, BA_SELECT); + if (ob_new == ob_new_active) { + ED_object_base_activate(C, base_new); + } + if (base_new->object->data) { + DEG_id_tag_update(static_cast<ID *>(base_new->object->data), 0); + } + /* #object_add_duplicate_internal will not have done this, since + * before the collection sync it would not have found the new base yet. */ + base_new->local_view_bits = base_source->local_view_bits; + } /* Note that this will also clear newid pointers and tags. */ copy_object_set_idnew(C); @@ -3808,7 +3824,8 @@ static int object_add_named_exec(bContext *C, wmOperator *op) * the case here. So we have to do the new-ID relinking ourselves * (#copy_object_set_idnew()). */ - LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); + LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID, + nullptr); if (basen == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); @@ -3976,7 +3993,7 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) /* api callbacks */ ot->invoke = object_add_drop_xy_generic_invoke; ot->exec = object_transform_to_mouse_exec; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 58daf753679..a664d93bb2e 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -324,7 +324,7 @@ static bool write_external_bake_pixels(const char *filepath, const int height, const int margin, const int margin_type, - ImageFormatData *im_format, + ImageFormatData const *im_format, const bool is_noncolor, Mesh const *mesh_eval, char const *uv_layer, diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index e65b5a61299..4896ddb5258 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1469,8 +1469,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); - const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); - const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1518,7 +1516,11 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); - BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); + if (use_smooth) { + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); + } BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 202c6d96a47..e7cfcf48fd3 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -757,9 +757,7 @@ static bool modifier_apply_obdata( BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true); /* Anonymous attributes shouldn't be available on the applied geometry. */ - MeshComponent component; - component.replace(me, GeometryOwnershipType::Editable); - component.attributes_remove_anonymous(); + blender::bke::mesh_attributes_for_write(*me).remove_anonymous(); if (md_eval->type == eModifierType_Multires) { multires_customdata_delete(me); @@ -820,7 +818,7 @@ static bool modifier_apply_obdata( /* Create a temporary geometry set and component. */ GeometrySet geometry_set; geometry_set.get_component_for_write<CurveComponent>().replace( - &curves, GeometryOwnershipType::Editable); + &curves, GeometryOwnershipType::ReadOnly); ModifierEvalContext mectx = {depsgraph, ob, (ModifierApplyFlag)0}; mti->modifyGeometrySet(md_eval, &mectx, &geometry_set); @@ -828,20 +826,18 @@ static bool modifier_apply_obdata( BKE_report(reports, RPT_ERROR, "Evaluated geometry from modifier does not contain curves"); return false; } - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); Curves &curves_eval = *geometry_set.get_curves_for_write(); /* Anonymous attributes shouldn't be available on the applied geometry. */ - component.attributes_remove_anonymous(); + blender::bke::CurvesGeometry::wrap(curves_eval.geometry) + .attributes_for_write() + .remove_anonymous(); - /* If the modifier's output is a different curves data-block, copy the relevant information to - * the original. */ - if (&curves_eval != &curves) { - blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move( - blender::bke::CurvesGeometry::wrap(curves_eval.geometry)); - Main *bmain = DEG_get_bmain(depsgraph); - BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id); - } + /* Copy the relevant information to the original. */ + blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move( + blender::bke::CurvesGeometry::wrap(curves_eval.geometry)); + Main *bmain = DEG_get_bmain(depsgraph); + BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id); } else { /* TODO: implement for point clouds and volumes. */ @@ -1440,7 +1436,6 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); - const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); const bool do_report = RNA_boolean_get(op->ptr, "report"); const bool do_single_user = RNA_boolean_get(op->ptr, "single_user"); const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata"); @@ -1449,6 +1444,8 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo return OPERATOR_CANCELLED; } + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); + if (do_single_user && ID_REAL_USERS(ob->data) > 1) { ED_object_single_obdata_user(bmain, scene, ob); BKE_main_id_newptr_and_tag_clear(bmain); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index f55ffdf0fcd..01042824aac 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -951,7 +951,7 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot) 1); struct { - bool mesh, gpencil; + bool mesh, gpencil, curves; } has_children_of_type = {0}; CTX_DATA_BEGIN (C, Object *, child, selected_editable_objects) { @@ -964,6 +964,9 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot) if (child->type == OB_GPENCIL) { has_children_of_type.gpencil = true; } + if (child->type == OB_CURVES) { + has_children_of_type.curves = true; + } } CTX_DATA_END; @@ -987,6 +990,11 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot) else if (parent->type == OB_LATTICE) { uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_LATTICE); } + else if (parent->type == OB_MESH) { + if (has_children_of_type.curves) { + uiItemO(layout, "Object (Attach Curves to Surface)", ICON_NONE, "CURVES_OT_surface_set"); + } + } /* vertex parenting */ if (OB_TYPE_SUPPORT_PARVERT(parent->type)) { @@ -2267,7 +2275,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) ID *id_root = NULL; bool is_override_instancing_object = false; - const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable"); + const bool do_fully_editable = U.experimental.use_override_new_fully_editable; GSet *user_overrides_objects_uids = do_fully_editable ? NULL : BLI_gset_new(BLI_ghashutil_inthash_p, @@ -2495,13 +2503,6 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); ot->prop = prop; - - prop = RNA_def_boolean(ot->srna, - "do_fully_editable", - false, - "Create Fully Editable", - "Make all created override data-blocks fully editable"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index ebcf8573ccd..0e3945bff15 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -20,6 +20,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" @@ -299,6 +301,10 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op) bool changed = false; if (RNA_boolean_get(op->ptr, "all")) { + if (RNA_boolean_get(op->ptr, "apply_mix")) { + float *arr = BKE_key_evaluate_object_ex(ob, NULL, NULL, 0, ob->data); + MEM_freeN(arr); + } changed = BKE_object_shapekey_free(bmain, ob); } else { @@ -315,6 +321,34 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } +static bool shape_key_remove_poll_property(const bContext *UNUSED(C), + wmOperator *op, + const PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + const bool do_all = RNA_enum_get(op->ptr, "all"); + + /* Only show seed for randomize action! */ + if (STREQ(prop_id, "apply_mix") && !do_all) { + return false; + } + return true; +} + +static char *shape_key_remove_get_description(bContext *UNUSED(C), + wmOperatorType *UNUSED(ot), + PointerRNA *ptr) +{ + const bool do_apply_mix = RNA_boolean_get(ptr, "apply_mix"); + + if (do_apply_mix) { + return BLI_strdup( + TIP_("Apply current visible shape to the object data, and delete all shape keys")); + } + + return NULL; +} + void OBJECT_OT_shape_key_remove(wmOperatorType *ot) { /* identifiers */ @@ -325,12 +359,19 @@ void OBJECT_OT_shape_key_remove(wmOperatorType *ot) /* api callbacks */ ot->poll = shape_key_mode_exists_poll; ot->exec = shape_key_remove_exec; + ot->poll_property = shape_key_remove_poll_property; + ot->get_description = shape_key_remove_get_description; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys"); + RNA_def_boolean(ot->srna, "all", false, "All", "Remove all shape keys"); + RNA_def_boolean(ot->srna, + "apply_mix", + false, + "Apply Mix", + "Apply current mix of shape keys to the geometry before removing them"); } /** \} */ diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc index 70c3eed3768..c9c96900af3 100644 --- a/source/blender/editors/object/object_transform.cc +++ b/source/blender/editors/object/object_transform.cc @@ -2222,7 +2222,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const bool is_finished = false; - if (ISMOUSE(xfd->init_event)) { + if (ISMOUSE_BUTTON(xfd->init_event)) { if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) { is_finished = true; } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 9ad36cacc7d..17b7fe7fe5e 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -693,7 +693,7 @@ static const EnumPropertyItem WT_vertex_group_select_item[] = { const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *C, PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), + PropertyRNA *prop, bool *r_free, const uint selection_mask) { @@ -731,6 +731,12 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext * RNA_enum_items_add_value(&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_ALL); } + /* Set `Deform Bone` as default selection if armature is present. */ + if (ob) { + RNA_def_property_enum_default( + prop, BKE_modifiers_is_deformed_by_armature(ob) ? WT_VGROUP_BONE_DEFORM : WT_VGROUP_ALL); + } + RNA_enum_item_end(&item, &totitem); *r_free = true; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 0e03feba340..03f9b4eb867 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -32,6 +32,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_object.h" @@ -1512,7 +1513,7 @@ static void PE_update_selection(Depsgraph *depsgraph, Scene *scene, Object *ob, } } - psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering); + psys_cache_edit_paths(depsgraph, scene, ob, edit, scene->r.cfra, G.is_rendering); /* disable update flag */ LOOP_POINTS { @@ -1647,11 +1648,11 @@ void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int usefla * and flagging with PEK_HIDE will prevent selection. This might get restored once this is * supported in drawing (but doesn't make much sense for hair anyways). */ if (edit->psys && edit->psys->part->type == PART_EMITTER) { - PE_hide_keys_time(scene, edit, CFRA); + PE_hide_keys_time(scene, edit, scene->r.cfra); } /* regenerate path caches */ - psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering); + psys_cache_edit_paths(depsgraph, scene, ob, edit, scene->r.cfra, G.is_rendering); /* disable update flag */ LOOP_POINTS { diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 6bea6e2c19e..96aea0ededf 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -26,6 +26,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_legacy_convert.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_object.h" diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 96195bdcc2e..80de8fae072 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -256,8 +256,8 @@ static void fluid_bake_sequence(FluidJob *job) frame = is_first_frame ? fds->cache_frame_start : (*pause_frame); /* Save orig frame and update scene frame. */ - orig_frame = CFRA; - CFRA = frame; + orig_frame = scene->r.cfra; + scene->r.cfra = frame; /* Loop through selected frames. */ for (; frame <= fds->cache_frame_end; frame++) { @@ -280,7 +280,7 @@ static void fluid_bake_sequence(FluidJob *job) *(job->progress) = progress; } - CFRA = frame; + scene->r.cfra = frame; /* Update animation system. */ ED_update_for_newframe(job->bmain, job->depsgraph); @@ -293,7 +293,7 @@ static void fluid_bake_sequence(FluidJob *job) } /* Restore frame position that we were on before bake. */ - CFRA = orig_frame; + scene->r.cfra = orig_frame; } static void fluid_bake_endjob(void *customdata) diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index d907a52543c..77ad23f1e3f 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -494,7 +494,8 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender) for (view_id = 0; view_id < oglrender->views_len; view_id++) { context.view_id = view_id; context.gpu_offscreen = oglrender->ofs; - oglrender->seq_data.ibufs_arr[view_id] = SEQ_render_give_ibuf(&context, CFRA, chanshown); + oglrender->seq_data.ibufs_arr[view_id] = SEQ_render_give_ibuf( + &context, scene->r.cfra, chanshown); } } @@ -757,8 +758,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) WM_jobs_kill_all_except(wm, CTX_wm_screen(C)); /* create offscreen buffer */ - sizex = (scene->r.size * scene->r.xsch) / 100; - sizey = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &sizex, &sizey); /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ @@ -1136,12 +1136,12 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) RenderResult *rr; /* go to next frame */ - if (CFRA < oglrender->nfra) { - CFRA++; + if (scene->r.cfra < oglrender->nfra) { + scene->r.cfra++; } - while (CFRA < oglrender->nfra) { + while (scene->r.cfra < oglrender->nfra) { BKE_scene_graph_update_for_newframe(depsgraph); - CFRA++; + scene->r.cfra++; } is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); @@ -1184,7 +1184,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) } if (oglrender->render_frames == nullptr || - BLI_BITMAP_TEST_BOOL(oglrender->render_frames, CFRA - PSFRA)) { + BLI_BITMAP_TEST_BOOL(oglrender->render_frames, scene->r.cfra - PSFRA)) { /* render into offscreen buffer */ screen_opengl_render_apply(C, oglrender); } @@ -1204,7 +1204,7 @@ finally: /* Step the frame and bail early if needed */ oglrender->nfra += scene->r.frame_step; /* stop at the end or on error */ - if (CFRA >= PEFRA || !ok) { + if (scene->r.cfra >= PEFRA || !ok) { screen_opengl_render_end(C, static_cast<OGLRender *>(op->customdata)); return false; } diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index addcedc4481..97bbcaa102f 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -805,7 +805,7 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene"); /* Preview need to be in the current frame to get a thumbnail similar of what * viewport displays. */ - CFRA = preview_data->cfra; + scene->r.cfra = preview_data->cfra; ViewLayer *view_layer = static_cast<ViewLayer *>(scene->view_layers.first); Depsgraph *depsgraph = DEG_graph_new( diff --git a/source/blender/editors/render/render_view.cc b/source/blender/editors/render/render_view.cc index a7ff2aad05a..9a16c910205 100644 --- a/source/blender/editors/render/render_view.cc +++ b/source/blender/editors/render/render_view.cc @@ -19,6 +19,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BLT_translation.h" @@ -130,8 +131,11 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) } if (U.render_display_type == USER_RENDER_DISPLAY_WINDOW) { - int sizex = 30 * UI_DPI_FAC + (scene->r.xsch * scene->r.size) / 100; - int sizey = 60 * UI_DPI_FAC + (scene->r.ysch * scene->r.size) / 100; + int sizex, sizey; + BKE_render_resolution(&scene->r, false, &sizex, &sizey); + + sizex += 30 * UI_DPI_FAC; + sizey += 60 * UI_DPI_FAC; /* arbitrary... miniature image window views don't make much sense */ if (sizex < 320) { diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index fe69759481b..0d6b6ee1d78 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -100,6 +100,7 @@ const char *screen_context_dir[] = { "active_gpencil_frame", "active_annotation_layer", "active_operator", + "active_action", "selected_visible_actions", "selected_editable_actions", "visible_fcurves", @@ -969,6 +970,7 @@ static eContextResult screen_ctx_active_operator(const bContext *C, bContextData } static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bContextDataResult *result, + bool active_only, bool editable) { bAnimContext ac; @@ -978,11 +980,17 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, SpaceAction *saction = (SpaceAction *)ac.sl; if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY)) { - if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { - CTX_data_id_list_add(result, &saction->action->id); + if (active_only) { + CTX_data_id_pointer_set(result, (ID *)saction->action); + } + else { + if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { + CTX_data_id_list_add(result, &saction->action->id); + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } - CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } } @@ -995,7 +1003,8 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, switch (ac.spacetype) { case SPACE_GRAPH: - filter |= ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL; + filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | + (active_only ? ANIMFILTER_ACTIVE : ANIMFILTER_SEL); break; case SPACE_ACTION: @@ -1006,7 +1015,7 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - GSet *seen_set = BLI_gset_ptr_new("seen actions"); + GSet *seen_set = active_only ? NULL : BLI_gset_ptr_new("seen actions"); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { /* In dopesheet check selection status of individual items, skipping @@ -1019,36 +1028,48 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bAction *action = ANIM_channel_action_get(ale); if (action) { - if (editable && ID_IS_LINKED(action)) { - continue; + if (active_only) { + CTX_data_id_pointer_set(result, (ID *)action); + break; } + else { + if (editable && ID_IS_LINKED(action)) { + continue; + } - /* Add the action to the output list if not already added. */ - if (BLI_gset_add(seen_set, action)) { - CTX_data_id_list_add(result, &action->id); + /* Add the action to the output list if not already added. */ + if (BLI_gset_add(seen_set, action)) { + CTX_data_id_list_add(result, &action->id); + } } } } - BLI_gset_free(seen_set, NULL); - ANIM_animdata_freelist(&anim_data); - CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + if (!active_only) { + BLI_gset_free(seen_set, NULL); + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + } + return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } - +static eContextResult screen_ctx_active_action(const bContext *C, bContextDataResult *result) +{ + return screen_ctx_sel_actions_impl(C, result, true, false); +} static eContextResult screen_ctx_selected_visible_actions(const bContext *C, bContextDataResult *result) { - return screen_ctx_sel_actions_impl(C, result, false); + return screen_ctx_sel_actions_impl(C, result, false, false); } static eContextResult screen_ctx_selected_editable_actions(const bContext *C, bContextDataResult *result) { - return screen_ctx_sel_actions_impl(C, result, true); + return screen_ctx_sel_actions_impl(C, result, false, true); } static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C, bContextDataResult *result, @@ -1059,8 +1080,9 @@ static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C, ListBase anim_data = {NULL, NULL}; int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS) | - (ac.spacetype == SPACE_GRAPH ? ANIMFILTER_CURVE_VISIBLE : - ANIMFILTER_LIST_VISIBLE) | + (ac.spacetype == SPACE_GRAPH ? + (ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY) : + ANIMFILTER_LIST_VISIBLE) | extra_filter; ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1104,7 +1126,7 @@ static eContextResult screen_ctx_active_editable_fcurve(const bContext *C, ListBase anim_data = {NULL, NULL}; int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | - ANIMFILTER_CURVE_VISIBLE); + ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1130,8 +1152,9 @@ static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C, /* Use keyframes from editable selected FCurves. */ int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FOREDIT | ANIMFILTER_SEL) | - (ac.spacetype == SPACE_GRAPH ? ANIMFILTER_CURVE_VISIBLE : - ANIMFILTER_LIST_VISIBLE); + (ac.spacetype == SPACE_GRAPH ? + (ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY) : + ANIMFILTER_LIST_VISIBLE); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1260,6 +1283,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("editable_gpencil_layers", screen_ctx_editable_gpencil_layers); register_context_function("editable_gpencil_strokes", screen_ctx_editable_gpencil_strokes); register_context_function("active_operator", screen_ctx_active_operator); + register_context_function("active_action", screen_ctx_active_action); register_context_function("selected_visible_actions", screen_ctx_selected_visible_actions); register_context_function("selected_editable_actions", screen_ctx_selected_editable_actions); register_context_function("editable_fcurves", screen_ctx_editable_fcurves); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index d35bd5ab2fe..3618b933443 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2951,9 +2951,9 @@ static int frame_offset_exec(bContext *C, wmOperator *op) int delta = RNA_int_get(op->ptr, "delta"); - CFRA += delta; - FRAMENUMBER_MIN_CLAMP(CFRA); - SUBFRA = 0.0f; + scene->r.cfra += delta; + FRAMENUMBER_MIN_CLAMP(scene->r.cfra); + scene->r.subframe = 0.0f; areas_do_frame_follow(C, false); @@ -2992,7 +2992,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); wmTimer *animtimer = CTX_wm_screen(C)->animtimer; - /* Don't change CFRA directly if animtimer is running as this can cause + /* Don't change scene->r.cfra directly if animtimer is running as this can cause * first/last frame not to be actually shown (bad since for example physics * simulations aren't reset properly). */ @@ -3010,10 +3010,10 @@ static int frame_jump_exec(bContext *C, wmOperator *op) } else { if (RNA_boolean_get(op->ptr, "end")) { - CFRA = PEFRA; + scene->r.cfra = PEFRA; } else { - CFRA = PSFRA; + scene->r.cfra = PSFRA; } areas_do_frame_follow(C, true); @@ -3062,7 +3062,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - float cfra = (float)(CFRA); + float cfra = (float)(scene->r.cfra); /* Initialize binary-tree-list for getting keyframes. */ struct AnimKeylist *keylist = ED_keylist_create(); @@ -3104,9 +3104,9 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } while ((ak != NULL) && (done == false)) { - if (CFRA != (int)ak->cfra) { + if (scene->r.cfra != (int)ak->cfra) { /* this changes the frame, so set the frame and we're done */ - CFRA = (int)ak->cfra; + scene->r.cfra = (int)ak->cfra; done = true; } else { @@ -3165,20 +3165,20 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot) static int marker_jump_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - int closest = CFRA; + int closest = scene->r.cfra; const bool next = RNA_boolean_get(op->ptr, "next"); bool found = false; /* find matching marker in the right direction */ LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { if (next) { - if ((marker->frame > CFRA) && (!found || closest > marker->frame)) { + if ((marker->frame > scene->r.cfra) && (!found || closest > marker->frame)) { closest = marker->frame; found = true; } } else { - if ((marker->frame < CFRA) && (!found || closest < marker->frame)) { + if ((marker->frame < scene->r.cfra) && (!found || closest < marker->frame)) { closest = marker->frame; found = true; } @@ -3192,7 +3192,7 @@ static int marker_jump_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - CFRA = closest; + scene->r.cfra = closest; areas_do_frame_follow(C, true); @@ -4714,11 +4714,11 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con if (sad->flag & ANIMPLAY_FLAG_JUMPED) { DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); #ifdef PROFILE_AUDIO_SYNCH - old_frame = CFRA; + old_frame = scene->r.cfra; #endif } - /* since we follow drawflags, we can't send notifier but tag regions ourselves */ + /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */ if (depsgraph != NULL) { ED_update_for_newframe(bmain, depsgraph); } diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 1156452310c..9d66debda6f 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -214,7 +214,14 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false); if (ot != NULL) { IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL; - uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL); + uiItemFullO_ptr(menu->layout, + ot, + CTX_IFACE_(ot->translation_context, ui_name), + ICON_NONE, + prop, + umi_op->opcontext, + 0, + NULL); is_empty = false; } else { diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index 01417650b3d..cb29f15420c 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -54,17 +54,87 @@ WorkSpace *ED_workspace_add(Main *bmain, const char *name) return BKE_workspace_add(bmain, name); } +static void workspace_exit(WorkSpace *workspace, wmWindow *win) +{ + /* Scene pinning: Store whatever scene was active when leaving the workspace. It's reactivated + * when the workspace gets reactivated as well. */ + if (workspace->flags & WORKSPACE_USE_PIN_SCENE) { + workspace->pin_scene = WM_window_get_active_scene(win); + } + else { + /* The active scene may have been changed. So also always update the unpinned scene to the + * latest when leaving a workspace that has no scene pinning. */ + win->unpinned_scene = WM_window_get_active_scene(win); + } +} + +/** + * State changes (old workspace to new workspace): + * 1) unpinned -> pinned + * * Store current scene as the unpinned one (done in #workspace_exit()). + * * Change the current scene to the pinned one. + * 2) pinned -> pinned + * * Change the current scene to the new pinned one. + * 3) pinned -> unpinned + * * Change current scene back to the unpinned one + * 4) unpinned -> unpinned + * * Make sure the unpinned scene is active. + * + * Note that the pin scene must also be updated when leaving a workspace with a pinned scene. + * That's done separately via workspace_exit() above. + */ +static void workspace_scene_pinning_update(WorkSpace *workspace_new, + const WorkSpace *workspace_old, + bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + Main *bmain = CTX_data_main(C); + Scene *active_scene = WM_window_get_active_scene(win); + + const bool is_new_pinned = (workspace_new->flags & WORKSPACE_USE_PIN_SCENE); + const bool is_old_pinned = (workspace_old->flags & WORKSPACE_USE_PIN_SCENE); + + /* State changes 1 and 2. */ + if (is_new_pinned) { + if (workspace_new->pin_scene && (workspace_new->pin_scene != active_scene)) { + WM_window_set_active_scene(bmain, C, win, workspace_new->pin_scene); + workspace_new->pin_scene = NULL; + } + } + /* State change 3 - Changing from workspace with pinned scene to unpinned scene. */ + else if (is_old_pinned) { + if (win->unpinned_scene) { + WM_window_set_active_scene(bmain, C, win, win->unpinned_scene); + } + else { + /* When leaving a workspace where the pinning was just enabled, the unpinned scene wasn't set + * yet. */ + win->unpinned_scene = active_scene; + } + } + else { + /* When leaving a workspace where the pinning was just disabled, we still want to restore the + * unpinned scene. */ + if (win->unpinned_scene) { + WM_window_set_active_scene(bmain, C, win, win->unpinned_scene); + } + } + + BLI_assert(WM_window_get_active_scene(win)); +} + /** * Changes the object mode (if needed) to the one set in \a workspace_new. * Object mode is still stored on object level. In future it should all be workspace level instead. */ static void workspace_change_update(WorkSpace *workspace_new, - const WorkSpace *workspace_old, + WorkSpace *workspace_old, bContext *C, wmWindowManager *wm) { + workspace_scene_pinning_update(workspace_new, workspace_old, C); /* needs to be done before changing mode! (to ensure right context) */ - UNUSED_VARS(workspace_old, workspace_new, C, wm); + UNUSED_VARS(wm); #if 0 Object *ob_act = CTX_data_active_object(C) eObjectMode mode_old = workspace_old->object_mode; eObjectMode mode_new = workspace_new->object_mode; @@ -113,6 +183,8 @@ bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager return false; } + workspace_exit(workspace_old, win); + screen_change_prepare(screen_old, screen_new, bmain, C, win); if (screen_new == NULL) { @@ -143,6 +215,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo WorkSpace *workspace_new = ED_workspace_add(bmain, workspace_old->id.name + 2); workspace_new->flags = workspace_old->flags; + workspace_new->pin_scene = workspace_old->pin_scene; workspace_new->object_mode = workspace_old->object_mode; workspace_new->order = workspace_old->order; BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids); @@ -512,6 +585,35 @@ static void WORKSPACE_OT_reorder_to_front(wmOperatorType *ot) ot->exec = workspace_reorder_to_front_exec; } +static int workspace_scene_pin_toggle(bContext *C, wmOperator *UNUSED(op)) +{ + WorkSpace *workspace = workspace_context_get(C); + + /* Trivial. The operator is only needed to display a superimposed extra icon, which + * requires an operator. */ + workspace->flags ^= WORKSPACE_USE_PIN_SCENE; + + WM_event_add_notifier(C, NC_WORKSPACE, NULL); + + return OPERATOR_FINISHED; +} + +static void WORKSPACE_OT_scene_pin_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Pin Scene to Workspace"; + ot->description = + "Remember the last used scene for the current workspace and switch to it whenever this " + "workspace is activated again"; + ot->idname = "WORKSPACE_OT_scene_pin_toggle"; + + /* api callbacks */ + ot->poll = workspace_context_poll; + ot->exec = workspace_scene_pin_toggle; + + ot->flag = OPTYPE_INTERNAL; +} + void ED_operatortypes_workspace(void) { WM_operatortype_append(WORKSPACE_OT_duplicate); @@ -520,6 +622,7 @@ void ED_operatortypes_workspace(void) WM_operatortype_append(WORKSPACE_OT_append_activate); WM_operatortype_append(WORKSPACE_OT_reorder_to_back); WM_operatortype_append(WORKSPACE_OT_reorder_to_front); + WM_operatortype_append(WORKSPACE_OT_scene_pin_toggle); } /** \} Workspace Operators */ diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 34247b4ef75..edb0f1cda4d 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -32,10 +32,15 @@ set(SRC curves_sculpt_brush.cc curves_sculpt_comb.cc curves_sculpt_delete.cc + curves_sculpt_density.cc curves_sculpt_grow_shrink.cc curves_sculpt_ops.cc - curves_sculpt_selection.cc + curves_sculpt_pinch.cc + curves_sculpt_puff.cc curves_sculpt_selection_paint.cc + curves_sculpt_selection.cc + curves_sculpt_slide.cc + curves_sculpt_smooth.cc curves_sculpt_snake_hook.cc paint_canvas.cc paint_cursor.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index bac03de77e7..6c693376ad3 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -23,6 +23,8 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" @@ -42,6 +44,8 @@ #include "WM_api.h" +#include "DEG_depsgraph_query.h" + /** * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -80,13 +84,15 @@ struct AddOperationExecutor { AddOperation *self_ = nullptr; CurvesSculptCommonContext ctx_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; - Span<MLoopTri> surface_looptris_; + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; @@ -97,15 +103,7 @@ struct AddOperationExecutor { float brush_radius_re_; float2 brush_pos_re_; - CurvesSculptTransforms transforms_; - - BVHTreeFromMesh surface_bvh_; - - struct AddedPoints { - Vector<float3> positions_cu; - Vector<float3> bary_coords; - Vector<int> looptri_indices; - }; + CurvesSurfaceTransforms transforms_; AddOperationExecutor(const bContext &C) : ctx_(C) { @@ -114,19 +112,26 @@ struct AddOperationExecutor { void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; - object_ = CTX_data_active_object(&C); + curves_ob_orig_ = CTX_data_active_object(&C); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); - if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); return; } - transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); + + Object &surface_ob_orig = *curves_id_orig_->surface; + Mesh &surface_orig = *static_cast<Mesh *>(surface_ob_orig.data); - surface_ob_ = curves_id_->surface; - surface_ = static_cast<Mesh *>(surface_ob_->data); + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, &surface_ob_orig); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -143,59 +148,70 @@ struct AddOperationExecutor { return; } + /* Find UV map. */ + VArraySpan<float2> surface_uv_map; + if (curves_id_orig_->surface_uv_map != nullptr) { + surface_uv_map = bke::mesh_attributes(surface_orig) + .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_) + .lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + } + + if (surface_uv_map.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + const double time = PIL_check_seconds_timer() * 1000000.0; /* Use a pointer cast to avoid overflow warnings. */ RandomNumberGenerator rng{*(uint32_t *)(&time)}; - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; + surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), + BKE_mesh_runtime_looptri_len(surface_eval_)}; /* Sample points on the surface using one of multiple strategies. */ - AddedPoints added_points; + Vector<float2> sampled_uvs; if (add_amount_ == 1) { - this->sample_in_center_with_symmetry(added_points); + this->sample_in_center_with_symmetry(sampled_uvs); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected_with_symmetry(rng, added_points); + this->sample_projected_with_symmetry(rng, sampled_uvs); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical_with_symmetry(rng, added_points); + this->sample_spherical_with_symmetry(rng, sampled_uvs); } else { BLI_assert_unreachable(); } - if (added_points.bary_coords.is_empty()) { + if (sampled_uvs.is_empty()) { /* No new points have been added. */ return; } - /* Find UV map. */ - VArray_Span<float2> surface_uv_map; - if (curves_id_->surface_uv_map != nullptr) { - MeshComponent surface_component; - surface_component.replace(surface_, GeometryOwnershipType::ReadOnly); - surface_uv_map = surface_component - .attribute_try_get_for_read(curves_id_->surface_uv_map, - ATTR_DOMAIN_CORNER) - .typed<float2>(); - } + const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig), + BKE_mesh_runtime_looptri_len(&surface_orig)}; /* Find normals. */ - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); + if (!CustomData_has_layer(&surface_orig.ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(&surface_orig); } const Span<float3> corner_normals_su = { - reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), - surface_->totloop}; + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig.ldata, CD_NORMAL)), + surface_orig.totloop}; + + const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; geometry::AddCurvesOnMeshInputs add_inputs; - add_inputs.root_positions_cu = added_points.positions_cu; - add_inputs.bary_coords = added_points.bary_coords; - add_inputs.looptri_indices = added_points.looptri_indices; + add_inputs.uvs = sampled_uvs; add_inputs.interpolate_length = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; add_inputs.interpolate_shape = brush_settings_->flag & @@ -204,13 +220,10 @@ struct AddOperationExecutor { BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; add_inputs.fallback_curve_length = brush_settings_->curve_length; add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); - add_inputs.surface = surface_; - add_inputs.surface_bvh = &surface_bvh_; - add_inputs.surface_looptris = surface_looptris_; - add_inputs.surface_uv_map = surface_uv_map; + add_inputs.transforms = &transforms_; + add_inputs.reverse_uv_sampler = &reverse_uv_sampler; + add_inputs.surface = &surface_orig; add_inputs.corner_normals_su = corner_normals_su; - add_inputs.curves_to_surface_mat = transforms_.curves_to_surface; - add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal; if (add_inputs.interpolate_length || add_inputs.interpolate_shape || add_inputs.interpolate_point_count) { @@ -218,17 +231,22 @@ struct AddOperationExecutor { add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_; } - geometry::add_curves_on_mesh(*curves_, add_inputs); + const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh( + *curves_orig_, add_inputs); + + if (add_outputs.uv_error) { + report_invalid_uv_map(stroke_extension.reports); + } - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } /** * Sample a single point exactly at the mouse position. */ - void sample_in_center_with_symmetry(AddedPoints &r_added_points) + void sample_in_center_with_symmetry(Vector<float2> &r_sampled_uvs) { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( @@ -237,15 +255,15 @@ struct AddOperationExecutor { const float3 ray_end_cu = transforms_.world_to_curves * ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float4x4 transform = transforms_.curves_to_surface * brush_transform; - this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu); + this->sample_in_center(r_sampled_uvs, transform * ray_start_cu, transform * ray_end_cu); } } - void sample_in_center(AddedPoints &r_added_points, + void sample_in_center(Vector<float2> &r_sampled_uvs, const float3 &ray_start_su, const float3 &ray_end_su) { @@ -254,58 +272,61 @@ struct AddOperationExecutor { BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, + BLI_bvhtree_ray_cast(surface_bvh_eval_.tree, ray_start_su, ray_direction_su, 0.0f, &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); + surface_bvh_eval_.raycast_callback, + &surface_bvh_eval_); if (ray_hit.index == -1) { return; } const int looptri_index = ray_hit.index; + const MLoopTri &looptri = surface_looptris_eval_[looptri_index]; const float3 brush_pos_su = ray_hit.co; const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle( - *surface_, surface_looptris_[looptri_index], brush_pos_su); + *surface_eval_, looptri, brush_pos_su); - const float3 brush_pos_cu = transforms_.surface_to_curves * brush_pos_su; - - r_added_points.positions_cu.append(brush_pos_cu); - r_added_points.bary_coords.append(bary_coords); - r_added_points.looptri_indices.append(looptri_index); + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords, looptri, surface_uv_map_eval_); + r_sampled_uvs.append(uv); } /** * Sample points by shooting rays within the brush radius in the 3D view. */ - void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->sample_projected(rng, r_added_points, brush_transform); + this->sample_projected(rng, r_sampled_uvs, brush_transform); } } void sample_projected(RandomNumberGenerator &rng, - AddedPoints &r_added_points, + Vector<float2> &r_sampled_uvs, const float4x4 &brush_transform) { - const int old_amount = r_added_points.bary_coords.size(); + const int old_amount = r_sampled_uvs.size(); const int max_iterations = 100; int current_iteration = 0; - while (r_added_points.bary_coords.size() < old_amount + add_amount_) { + while (r_sampled_uvs.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } - const int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size(); + Vector<float3> bary_coords; + Vector<int> looptri_indices; + Vector<float3> positions_su; + + const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size(); const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( rng, - *surface_, - surface_bvh_, + *surface_eval_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_, [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { @@ -320,11 +341,14 @@ struct AddOperationExecutor { use_front_face_, add_amount_, missing_amount, - r_added_points.bary_coords, - r_added_points.looptri_indices, - r_added_points.positions_cu); - for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { - pos = transforms_.surface_to_curves * pos; + bary_coords, + looptri_indices, + positions_su); + + for (const int i : IndexRange(new_points)) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_sampled_uvs.append(uv); } } } @@ -332,13 +356,13 @@ struct AddOperationExecutor { /** * Sample points in a 3D sphere around the surface position that the mouse hovers over. */ - void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs) { const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, transforms_, - surface_bvh_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_); if (!brush_3d.has_value()) { @@ -353,24 +377,27 @@ struct AddOperationExecutor { view_ray_start_wo, view_ray_end_wo, true); - const float3 view_direction_su = math::normalize( - transforms_.world_to_surface * view_ray_end_wo - - transforms_.world_to_surface * view_ray_start_wo); + + const float3 view_ray_start_cu = transforms_.world_to_curves * view_ray_start_wo; + const float3 view_ray_end_cu = transforms_.world_to_curves * view_ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float4x4 transform = transforms_.curves_to_surface * brush_transform; + const float3 brush_pos_su = transform * brush_3d->position_cu; + const float3 view_direction_su = math::normalize(transform * view_ray_end_cu - + transform * view_ray_start_cu); const float brush_radius_su = transform_brush_radius( transform, brush_3d->position_cu, brush_3d->radius_cu); - this->sample_spherical( - rng, r_added_points, brush_pos_su, brush_radius_su, view_direction_su); + + this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su); } } void sample_spherical(RandomNumberGenerator &rng, - AddedPoints &r_added_points, + Vector<float2> &r_sampled_uvs, const float3 &brush_pos_su, const float brush_radius_su, const float3 &view_direction_su) @@ -378,32 +405,32 @@ struct AddOperationExecutor { const float brush_radius_sq_su = pow2f(brush_radius_su); /* Find surface triangles within brush radius. */ - Vector<int> looptri_indices; + Vector<int> selected_looptri_indices; if (use_front_face_) { BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - const MLoopTri &looptri = surface_looptris_[index]; - const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; - const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; - const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; + const MLoopTri &looptri = surface_looptris_eval_[index]; + const float3 v0_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[0]].v].co; + const float3 v1_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[1]].v].co; + const float3 v2_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[2]].v].co; float3 normal_su; normal_tri_v3(normal_su, v0_su, v1_su, v2_su); if (math::dot(normal_su, view_direction_su) >= 0.0f) { return; } - looptri_indices.append(index); + selected_looptri_indices.append(index); }); } else { BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - looptri_indices.append(index); + selected_looptri_indices.append(index); }); } @@ -417,42 +444,45 @@ struct AddOperationExecutor { const int max_iterations = 5; int current_iteration = 0; - const int old_amount = r_added_points.bary_coords.size(); - while (r_added_points.bary_coords.size() < old_amount + add_amount_) { + const int old_amount = r_sampled_uvs.size(); + while (r_sampled_uvs.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } + Vector<float3> bary_coords; + Vector<int> looptri_indices; + Vector<float3> positions_su; const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( rng, - *surface_, - looptri_indices, + *surface_eval_, + selected_looptri_indices, brush_pos_su, brush_radius_su, approximate_density_su, - r_added_points.bary_coords, - r_added_points.looptri_indices, - r_added_points.positions_cu); - for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { - pos = transforms_.surface_to_curves * pos; + bary_coords, + looptri_indices, + positions_su); + for (const int i : IndexRange(new_points)) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_sampled_uvs.append(uv); } } /* Remove samples when there are too many. */ - while (r_added_points.bary_coords.size() > old_amount + add_amount_) { + while (r_sampled_uvs.size() > old_amount + add_amount_) { const int index_to_remove = rng.get_int32(add_amount_) + old_amount; - r_added_points.bary_coords.remove_and_reorder(index_to_remove); - r_added_points.looptri_indices.remove_and_reorder(index_to_remove); - r_added_points.positions_cu.remove_and_reorder(index_to_remove); + r_sampled_uvs.remove_and_reorder(index_to_remove); } } void ensure_curve_roots_kdtree() { if (self_->curve_roots_kdtree_ == nullptr) { - self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num()); - for (const int curve_i : curves_->curves_range()) { - const int root_point_i = curves_->offsets()[curve_i]; - const float3 &root_pos_cu = curves_->positions()[root_point_i]; + self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num()); + for (const int curve_i : curves_orig_->curves_range()) { + const int root_point_i = curves_orig_->offsets()[curve_i]; + const float3 &root_pos_cu = curves_orig_->positions()[root_point_i]; BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); } BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); @@ -466,17 +496,8 @@ void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension & executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, - ReportList *reports) +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() { - const Object &ob_active = *CTX_data_active_object(&C); - BLI_assert(ob_active.type == OB_CURVES); - const Curves &curves_id = *static_cast<Curves *>(ob_active.data); - if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { - BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); - return {}; - } - return std::make_unique<AddOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 1c785fa6452..a180a232189 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -8,6 +8,9 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_report.h" #include "ED_view3d.h" @@ -20,6 +23,10 @@ #include "BLI_length_parameterize.hh" #include "BLI_task.hh" +#include "DEG_depsgraph_query.h" + +#include "BLT_translation.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -48,7 +55,8 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float brush_radius_re, const ARegion ®ion, const RegionView3D &rv3d, - const Object &object) + const Object &object, + const Span<float3> positions) { /* This value might have to be adjusted based on user feedback. */ const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); @@ -88,8 +96,6 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu } }; - const Span<float3> positions = curves.positions(); - BrushPositionCandidate best_candidate = threading::parallel_reduce( curves.curves_range(), 128, @@ -175,20 +181,21 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, { const Curves &curves_id = *static_cast<Curves *>(curves_object.data); const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - const Object *surface_object = curves_id.surface; + Object *surface_object = curves_id.surface; + Object *surface_object_eval = DEG_get_evaluated_object(&depsgraph, surface_object); float3 center_ray_start_wo, center_ray_end_wo; ED_view3d_win_to_segment_clipped( &depsgraph, ®ion, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); /* Shorten ray when the surface object is hit. */ - if (surface_object != nullptr) { + if (surface_object_eval != nullptr) { const float4x4 surface_to_world_mat = surface_object->obmat; const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - Mesh &surface = *static_cast<Mesh *>(surface_object->data); + Mesh *surface_eval = BKE_object_get_evaluated_mesh(surface_object_eval); BVHTreeFromMesh surface_bvh; - BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2); + BKE_bvhtree_from_mesh_get(&surface_bvh, surface_eval, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo; @@ -222,6 +229,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo; const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo; + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_object); + const std::optional<float3> brush_position_optional_cu = find_curves_brush_position( curves, center_ray_start_cu, @@ -229,7 +239,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, brush_radius_re, region, rv3d, - curves_object); + curves_object, + deformation.positions); if (!brush_position_optional_cu.has_value()) { /* Nothing found. */ return std::nullopt; @@ -258,7 +269,7 @@ std::optional<CurvesBrush3D> sample_curves_surface_3d_brush( const Depsgraph &depsgraph, const ARegion ®ion, const View3D &v3d, - const CurvesSculptTransforms &transforms, + const CurvesSurfaceTransforms &transforms, const BVHTreeFromMesh &surface_bvh, const float2 &brush_pos_re, const float brush_radius_re) @@ -345,7 +356,7 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n { /* Find the accumulated length of each point in the original curve, * treating it as a poly curve for performance reasons and simplicity. */ - Array<float> orig_lengths(length_parameterize::lengths_num(positions.size(), false)); + Array<float> orig_lengths(length_parameterize::segments_num(positions.size(), false)); length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths); const float orig_total_length = orig_lengths.last(); @@ -363,11 +374,10 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n Array<int> indices(positions.size() - 1); Array<float> factors(positions.size() - 1); - length_parameterize::create_samples_from_sorted_lengths( - orig_lengths, new_lengths, false, indices, factors); + length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors); Array<float3> new_positions(positions.size() - 1); - length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions); + length_parameterize::interpolate<float3>(positions, indices, factors, new_positions); positions.drop_back(1).copy_from(new_positions); positions.last() = new_last_position; } @@ -381,18 +391,26 @@ CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C) this->rv3d = CTX_wm_region_view3d(&C); } -CurvesSculptTransforms::CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob) +void report_missing_surface(ReportList *reports) { - this->curves_to_world = curves_ob.obmat; - this->world_to_curves = this->curves_to_world.inverted(); - - if (surface_ob != nullptr) { - this->surface_to_world = surface_ob->obmat; - this->world_to_surface = this->surface_to_world.inverted(); - this->surface_to_curves = this->world_to_curves * this->surface_to_world; - this->curves_to_surface = this->world_to_surface * this->curves_to_world; - this->surface_to_curves_normal = this->surface_to_curves.inverted().transposed(); - } + BKE_report(reports, RPT_WARNING, TIP_("Missing surface mesh")); +} + +void report_missing_uv_map_on_original_surface(ReportList *reports) +{ + BKE_report( + reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on original surface")); +} + +void report_missing_uv_map_on_evaluated_surface(ReportList *reports) +{ + BKE_report( + reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on evaluated surface")); +} + +void report_invalid_uv_map(ReportList *reports) +{ + BKE_report(reports, RPT_WARNING, TIP_("Invalid UV map: UV islands must not overlap")); } } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 541bf9d8253..52f2ddc6550 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -13,12 +13,15 @@ #include "PIL_time.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "BKE_attribute_math.hh" #include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" +#include "BKE_crazyspace.hh" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" @@ -88,9 +91,9 @@ struct CombOperationExecutor { eBrushFalloffShape falloff_shape_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; VArray<float> point_factors_; Vector<int64_t> selected_curve_indices_; @@ -100,7 +103,7 @@ struct CombOperationExecutor { float2 brush_pos_re_; float2 brush_pos_diff_re_; - CurvesSculptTransforms transforms_; + CurvesSurfaceTransforms transforms_; CombOperationExecutor(const bContext &C) : ctx_(C) { @@ -112,7 +115,12 @@ struct CombOperationExecutor { BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); - object_ = CTX_data_active_object(&C); + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + if (curves_orig_->curves_num() == 0) { + return; + } curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -122,16 +130,10 @@ struct CombOperationExecutor { falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); - if (curves_->curves_num() == 0) { - return; - } - - transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - point_factors_ = get_point_selection(*curves_id_); - curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + point_factors_ = get_point_selection(*curves_id_orig_); + curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_); brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; @@ -160,9 +162,9 @@ struct CombOperationExecutor { this->restore_segment_lengths(changed_curves); - curves_->tag_positions_changed(); - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + curves_orig_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } @@ -172,7 +174,7 @@ struct CombOperationExecutor { void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { this->comb_projected(r_changed_curves, brush_transform); } @@ -183,10 +185,12 @@ struct CombOperationExecutor { { const float4x4 brush_transform_inv = brush_transform.inverted(); - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); @@ -195,16 +199,18 @@ struct CombOperationExecutor { Vector<int> &local_changed_curves = r_changed_curves.local(); for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 old_pos_cu = deformation.positions[point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; /* Find the position of the point in screen space. */ - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2( - old_pos_re, brush_pos_prev_re_, brush_pos_re_); + old_symm_pos_re, brush_pos_prev_re_, brush_pos_re_); if (distance_to_brush_sq_re > brush_radius_sq_re) { /* Ignore the point because it's too far away. */ continue; @@ -219,16 +225,20 @@ struct CombOperationExecutor { /* Offset the old point position in screen space and transform it back into 3D space. */ - const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; - float3 new_position_wo; + const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight; + float3 new_symm_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - transforms_.curves_to_world * old_pos_cu, - new_position_re, - new_position_wo); - const float3 new_position_cu = brush_transform * - (transforms_.world_to_curves * new_position_wo); - positions_cu[point_i] = new_position_cu; + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + const float3 new_pos_cu = brush_transform * + (transforms_.world_to_curves * new_symm_pos_wo); + + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu_orig[point_i] += translation_orig; curve_changed = true; } @@ -245,7 +255,7 @@ struct CombOperationExecutor { void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); float3 brush_start_wo, brush_end_wo; ED_view3d_win_to_3d(ctx_.v3d, @@ -264,7 +274,7 @@ struct CombOperationExecutor { const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { this->comb_spherical(r_changed_curves, brush_transform * brush_start_cu, @@ -278,17 +288,20 @@ struct CombOperationExecutor { const float3 &brush_end_cu, const float brush_radius_cu) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 pos_old_cu = positions_cu[point_i]; + const float3 pos_old_cu = deformation.positions[point_i]; /* Compute distance to the brush. */ const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( @@ -306,8 +319,12 @@ struct CombOperationExecutor { /* Combine the falloff and brush strength. */ const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; + const float3 translation_eval_cu = weight * brush_diff_cu; + const float3 translation_orig_cu = deformation.translation_from_deformed_to_original( + point_i, translation_eval_cu); + /* Update the point position. */ - positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu; + positions_cu[point_i] += translation_orig_cu; curve_changed = true; } if (curve_changed) { @@ -326,7 +343,7 @@ struct CombOperationExecutor { *ctx_.region, *ctx_.v3d, *ctx_.rv3d, - *object_, + *curves_ob_orig_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { @@ -340,11 +357,11 @@ struct CombOperationExecutor { */ void initialize_segment_lengths() { - const Span<float3> positions_cu = curves_->positions(); - self_->segment_lengths_cu_.reinitialize(curves_->points_num()); - threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) { + const Span<float3> positions_cu = curves_orig_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num()); + threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) { for (const int curve_i : range) { - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_back(1)) { const float3 &p1_cu = positions_cu[point_i]; const float3 &p2_cu = positions_cu[point_i + 1]; @@ -361,12 +378,12 @@ struct CombOperationExecutor { void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves) { const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) { threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { for (const int curve_i : changed_curves.as_span().slice(range)) { - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int segment_i : points.drop_back(1)) { const float3 &p1_cu = positions_cu[segment_i]; float3 &p2_cu = positions_cu[segment_i + 1]; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index eab7dabcd22..a44499ce133 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -51,6 +51,12 @@ using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: CurvesBrush3D brush_3d_; + /** + * Need to store those in case the brush is evaluated more than once before the curves are + * evaluated again. This can happen when the mouse is moved quickly and the brush spacing is + * small. + */ + Vector<float3> deformed_positions_; friend struct DeleteOperationExecutor; @@ -76,7 +82,7 @@ struct DeleteOperationExecutor { float2 brush_pos_re_; - CurvesSculptTransforms transforms_; + CurvesSurfaceTransforms transforms_; DeleteOperationExecutor(const bContext &C) : ctx_(C) { @@ -100,7 +106,7 @@ struct DeleteOperationExecutor { brush_pos_re_ = stroke_extension.mouse_position; - transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); @@ -109,6 +115,9 @@ struct DeleteOperationExecutor { if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { this->initialize_spherical_brush_reference_point(); } + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + self_->deformed_positions_ = deformation.positions; } Array<bool> curves_to_delete(curves_->curves_num(), false); @@ -123,12 +132,22 @@ struct DeleteOperationExecutor { } Vector<int64_t> indices; - const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate( curves_->curves_range(), 4096, indices, [&](const int curve_i) { return curves_to_delete[curve_i]; }); - curves_->remove_curves(mask); + /* Remove deleted curves from the stored deformed positions. */ + const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert( + curves_->curves_range()); + Vector<float3> new_deformed_positions; + for (const IndexRange curves_range : ranges_to_keep) { + new_deformed_positions.extend( + self_->deformed_positions_.as_span().slice(curves_->points_for_curves(curves_range))); + } + self_->deformed_positions_ = std::move(new_deformed_positions); + + curves_->remove_curves(mask_to_delete); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); @@ -151,8 +170,6 @@ struct DeleteOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - Span<float3> positions_cu = curves_->positions(); - const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); @@ -160,7 +177,7 @@ struct DeleteOperationExecutor { for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); if (points.size() == 1) { - const float3 pos_cu = brush_transform_inv * positions_cu[points.first()]; + const float3 pos_cu = brush_transform_inv * self_->deformed_positions_[points.first()]; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); @@ -171,8 +188,8 @@ struct DeleteOperationExecutor { } for (const int segment_i : points.drop_back(1)) { - const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 pos1_cu = brush_transform_inv * self_->deformed_positions_[segment_i]; + const float3 pos2_cu = brush_transform_inv * self_->deformed_positions_[segment_i + 1]; float2 pos1_re, pos2_re; ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values); @@ -212,8 +229,6 @@ struct DeleteOperationExecutor { void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete) { - Span<float3> positions_cu = curves_->positions(); - const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -222,7 +237,7 @@ struct DeleteOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); if (points.size() == 1) { - const float3 &pos_cu = positions_cu[points.first()]; + const float3 &pos_cu = self_->deformed_positions_[points.first()]; const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu); if (distance_sq_cu < brush_radius_sq_cu) { curves_to_delete[curve_i] = true; @@ -231,8 +246,8 @@ struct DeleteOperationExecutor { } for (const int segment_i : points.drop_back(1)) { - const float3 &pos1_cu = positions_cu[segment_i]; - const float3 &pos2_cu = positions_cu[segment_i + 1]; + const float3 &pos1_cu = self_->deformed_positions_[segment_i]; + const float3 &pos2_cu = self_->deformed_positions_[segment_i + 1]; const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu); if (distance_sq_cu > brush_radius_sq_cu) { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc new file mode 100644 index 00000000000..6ad3d0e4aad --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -0,0 +1,912 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <numeric> + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "BLI_index_mask_ops.hh" +#include "BLI_kdtree.h" +#include "BLI_rand.hh" +#include "BLI_task.hh" + +#include "PIL_time.h" + +#include "GEO_add_curves_on_mesh.hh" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" + +#include "WM_api.h" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +class DensityAddOperation : public CurvesSculptStrokeOperation { + private: + /** Used when some data should be interpolated from existing curves. */ + KDTree_3d *original_curve_roots_kdtree_ = nullptr; + /** Contains curve roots of all curves that existed before the brush started. */ + KDTree_3d *deformed_curve_roots_kdtree_ = nullptr; + /** Root positions of curves that have been added in the current brush stroke. */ + Vector<float3> new_deformed_root_positions_; + int original_curve_num_ = 0; + + friend struct DensityAddOperationExecutor; + + public: + ~DensityAddOperation() override + { + if (original_curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(original_curve_roots_kdtree_); + } + if (deformed_curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(deformed_curve_roots_kdtree_); + } + } + + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +struct DensityAddOperationExecutor { + DensityAddOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + const BrushCurvesSculptSettings *brush_settings_ = nullptr; + + float brush_strength_; + float brush_radius_re_; + float2 brush_pos_re_; + + CurvesSurfaceTransforms transforms_; + + DensityAddOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(DensityAddOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) + { + self_ = &self; + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + + if (stroke_extension.is_first) { + self_->original_curve_num_ = curves_orig_->curves_num(); + } + + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); + return; + } + + surface_ob_orig_ = curves_id_orig_->surface; + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); + surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), + BKE_mesh_runtime_looptri_len(surface_eval_)}; + /* Find UV map. */ + VArraySpan<float2> surface_uv_map; + if (curves_id_orig_->surface_uv_map != nullptr) { + surface_uv_map = bke::mesh_attributes(*surface_orig_) + .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_) + .lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + } + if (surface_uv_map.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_settings_ = brush_->curves_sculpt_settings; + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + Vector<float3> new_positions_cu; + Vector<float2> new_uvs; + const double time = PIL_check_seconds_timer() * 1000000.0; + RandomNumberGenerator rng{*(uint32_t *)(&time)}; + + /* Find potential new curve root points. */ + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu); + } + else { + BLI_assert_unreachable(); + } + for (float3 &pos : new_positions_cu) { + pos = transforms_.surface_to_curves * pos; + } + + if (stroke_extension.is_first) { + this->prepare_curve_roots_kdtrees(); + } + + const int already_added_curves = self_->new_deformed_root_positions_.size(); + KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves + + new_positions_cu.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); }); + + /* Used to tag all curves that are too close to existing curves or too close to other new + * curves. */ + Array<bool> new_curve_skipped(new_positions_cu.size(), false); + threading::parallel_invoke( + /* Build kdtree from root points created by the current stroke. */ + [&]() { + for (const int i : IndexRange(already_added_curves)) { + BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]); + } + for (const int new_i : new_positions_cu.index_range()) { + const float3 &root_pos_cu = new_positions_cu[new_i]; + BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu); + } + BLI_kdtree_3d_balance(new_roots_kdtree); + }, + /* Check which new root points are close to roots that existed before the current stroke + * started. */ + [&]() { + threading::parallel_for( + new_positions_cu.index_range(), 128, [&](const IndexRange range) { + for (const int new_i : range) { + const float3 &new_root_pos_cu = new_positions_cu[new_i]; + KDTreeNearest_3d nearest; + nearest.dist = FLT_MAX; + BLI_kdtree_3d_find_nearest( + self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest); + if (nearest.dist < brush_settings_->minimum_distance) { + new_curve_skipped[new_i] = true; + } + } + }); + }); + + /* Find new points that are too close too other new points. */ + for (const int new_i : new_positions_cu.index_range()) { + if (new_curve_skipped[new_i]) { + continue; + } + const float3 &root_pos_cu = new_positions_cu[new_i]; + BLI_kdtree_3d_range_search_cb_cpp( + new_roots_kdtree, + root_pos_cu, + brush_settings_->minimum_distance, + [&](const int other_new_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_new_i == -1) { + new_curve_skipped[new_i] = true; + return false; + } + if (new_i == other_new_i) { + return true; + } + new_curve_skipped[other_new_i] = true; + return true; + }); + } + + /* Remove points that are too close to others. */ + for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) { + if (new_curve_skipped[i]) { + new_positions_cu.remove_and_reorder(i); + new_uvs.remove_and_reorder(i); + } + } + self_->new_deformed_root_positions_.extend(new_positions_cu); + + /* Find normals. */ + if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_orig_); + } + const Span<float3> corner_normals_su = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)), + surface_orig_->totloop}; + + const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(surface_orig_), + BKE_mesh_runtime_looptri_len(surface_orig_)}; + const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; + + geometry::AddCurvesOnMeshInputs add_inputs; + add_inputs.uvs = new_uvs; + add_inputs.interpolate_length = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; + add_inputs.interpolate_shape = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; + add_inputs.interpolate_point_count = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + add_inputs.fallback_curve_length = brush_settings_->curve_length; + add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); + add_inputs.transforms = &transforms_; + add_inputs.surface = surface_orig_; + add_inputs.corner_normals_su = corner_normals_su; + add_inputs.reverse_uv_sampler = &reverse_uv_sampler; + add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_; + + const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh( + *curves_orig_, add_inputs); + + if (add_outputs.uv_error) { + report_invalid_uv_map(stroke_extension.reports); + } + + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); + ED_region_tag_redraw(ctx_.region); + } + + void prepare_curve_roots_kdtrees() + { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + const Span<int> curve_offsets = curves_orig_->offsets(); + const Span<float3> original_positions = curves_orig_->positions(); + const Span<float3> deformed_positions = deformation.positions; + BLI_assert(original_positions.size() == deformed_positions.size()); + + auto roots_kdtree_from_positions = [&](const Span<float3> positions) { + KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num()); + for (const int curve_i : curves_orig_->curves_range()) { + const int root_point_i = curve_offsets[curve_i]; + BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]); + } + BLI_kdtree_3d_balance(kdtree); + return kdtree; + }; + + threading::parallel_invoke( + [&]() { + self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions); + }, + [&]() { + self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions); + }); + } + + void sample_projected_with_symmetry(RandomNumberGenerator &rng, + Vector<float2> &r_uvs, + Vector<float3> &r_positions_su) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float4x4 brush_transform_inv = brush_transform.inverted(); + const float4x4 transform = transforms_.curves_to_surface * brush_transform * + transforms_.world_to_curves; + Vector<float3> positions_su; + Vector<float3> bary_coords; + Vector<int> looptri_indices; + const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( + rng, + *surface_eval_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re_, + [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { + float3 start_wo, end_wo; + ED_view3d_win_to_segment_clipped( + ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true); + r_start_su = transform * start_wo; + r_end_su = transform * end_wo; + }, + true, + brush_settings_->density_add_attempts, + brush_settings_->density_add_attempts, + bary_coords, + looptri_indices, + positions_su); + + /* Remove some sampled points randomly based on the brush falloff and strength. */ + for (int i = new_points - 1; i >= 0; i--) { + const float3 pos_su = positions_su[i]; + const float3 pos_cu = brush_transform_inv * transforms_.surface_to_curves * pos_su; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_re = math::distance(brush_pos_re_, pos_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re_); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() > weight) { + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + positions_su.remove_and_reorder(i); + } + } + + for (const int i : bary_coords.index_range()) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_uvs.append(uv); + } + r_positions_su.extend(positions_su); + } + } + + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, + Vector<float2> &r_uvs, + Vector<float3> &r_positions_su) + { + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re_); + if (!brush_3d.has_value()) { + return; + } + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_cu = brush_transform * brush_3d->position_cu; + const float3 brush_pos_su = transforms_.curves_to_surface * brush_pos_cu; + const float brush_radius_su = transform_brush_radius( + transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu); + const float brush_radius_sq_su = pow2f(brush_radius_su); + + Vector<int> selected_looptri_indices; + BLI_bvhtree_range_query_cpp( + *surface_bvh_eval_.tree, + brush_pos_su, + brush_radius_su, + [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { + selected_looptri_indices.append(index); + }); + + const float brush_plane_area_su = M_PI * brush_radius_sq_su; + const float approximate_density_su = brush_settings_->density_add_attempts / + brush_plane_area_su; + + Vector<float3> positions_su; + Vector<float3> bary_coords; + Vector<int> looptri_indices; + const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( + rng, + *surface_eval_, + selected_looptri_indices, + brush_pos_su, + brush_radius_su, + approximate_density_su, + bary_coords, + looptri_indices, + positions_su); + + /* Remove some sampled points randomly based on the brush falloff and strength. */ + for (int i = new_points - 1; i >= 0; i--) { + const float3 pos_su = positions_su[i]; + const float3 pos_cu = transforms_.surface_to_curves * pos_su; + const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_3d->radius_cu); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() > weight) { + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + positions_su.remove_and_reorder(i); + } + } + + for (const int i : bary_coords.index_range()) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_uvs.append(uv); + } + r_positions_su.extend(positions_su); + } + } +}; + +void DensityAddOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + DensityAddOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +class DensitySubtractOperation : public CurvesSculptStrokeOperation { + private: + friend struct DensitySubtractOperationExecutor; + + /** + * Deformed root positions of curves that still exist. This has to be stored in case the brush is + * executed more than once before the curves are evaluated again. This can happen when the mouse + * is moved quickly and the brush spacing is small. + */ + Vector<float3> deformed_root_positions_; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct DensitySubtractOperationExecutor { + DensitySubtractOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + BVHTreeFromMesh surface_bvh_eval_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + float2 brush_pos_re_; + + float minimum_distance_; + + CurvesSurfaceTransforms transforms_; + + KDTree_3d *root_points_kdtree_; + + DensitySubtractOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(DensitySubtractOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) + { + self_ = &self; + + object_ = CTX_data_active_object(&C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + + surface_ob_orig_ = curves_id_->surface; + if (surface_ob_orig_ == nullptr) { + return; + } + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + minimum_distance_ = brush_->curves_sculpt_settings->minimum_distance; + + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + if (stroke_extension.is_first) { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + for (const int curve_i : curves_->curves_range()) { + const int first_point_i = curves_->offsets()[curve_i]; + self_->deformed_root_positions_.append(deformation.positions[first_point_i]); + } + } + + root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); }); + for (const int curve_i : curve_selection_) { + const float3 &pos_cu = self_->deformed_root_positions_[curve_i]; + BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu); + } + BLI_kdtree_3d_balance(root_points_kdtree_); + + /* Find all curves that should be deleted. */ + Array<bool> curves_to_delete(curves_->curves_num(), false); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->reduce_density_projected_with_symmetry(curves_to_delete); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->reduce_density_spherical_with_symmetry(curves_to_delete); + } + else { + BLI_assert_unreachable(); + } + + Vector<int64_t> indices; + const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate( + curves_->curves_range(), 4096, indices, [&](const int curve_i) { + return curves_to_delete[curve_i]; + }); + + /* Remove deleted curves fromt he stored deformed root positions. */ + const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert( + curves_->curves_range()); + BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size()); + Vector<float3> new_deformed_positions; + for (const IndexRange range : ranges_to_keep) { + new_deformed_positions.extend(self_->deformed_root_positions_.as_span().slice(range)); + } + self_->deformed_root_positions_ = std::move(new_deformed_positions); + + curves_->remove_curves(mask_to_delete); + BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size()); + + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void reduce_density_projected_with_symmetry(MutableSpan<bool> curves_to_delete) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->reduce_density_projected(brush_transform, curves_to_delete); + } + } + + void reduce_density_projected(const float4x4 &brush_transform, + MutableSpan<bool> curves_to_delete) + { + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + /* Randomly select the curves that are allowed to be removed, based on the brush radius and + * strength. */ + Array<bool> allow_remove_curve(curves_->curves_num(), false); + threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) { + RandomNumberGenerator rng((int)(PIL_check_seconds_timer() * 1000000.0)); + + for (const int curve_i : range) { + if (curves_to_delete[curve_i]) { + allow_remove_curve[curve_i] = true; + continue; + } + const float3 pos_cu = brush_transform * self_->deformed_root_positions_[curve_i]; + + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() < weight) { + allow_remove_curve[curve_i] = true; + } + } + }); + + /* Detect curves that are too close to other existing curves. */ + for (const int curve_i : curve_selection_) { + if (curves_to_delete[curve_i]) { + continue; + } + if (!allow_remove_curve[curve_i]) { + continue; + } + const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i]; + const float3 pos_cu = brush_transform * orig_pos_cu; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_sq_re = math::distance_squared(brush_pos_re_, pos_re); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + BLI_kdtree_3d_range_search_cb_cpp( + root_points_kdtree_, + orig_pos_cu, + minimum_distance_, + [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_curve_i == curve_i) { + return true; + } + if (allow_remove_curve[other_curve_i]) { + curves_to_delete[other_curve_i] = true; + } + return true; + }); + } + } + + void reduce_density_spherical_with_symmetry(MutableSpan<bool> curves_to_delete) + { + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re); + if (!brush_3d.has_value()) { + return; + } + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_cu = brush_transform * brush_3d->position_cu; + this->reduce_density_spherical(brush_pos_cu, brush_3d->radius_cu, curves_to_delete); + } + } + + void reduce_density_spherical(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<bool> curves_to_delete) + { + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + /* Randomly select the curves that are allowed to be removed, based on the brush radius and + * strength. */ + Array<bool> allow_remove_curve(curves_->curves_num(), false); + threading::parallel_for(curves_->curves_range(), 512, [&](const IndexRange range) { + RandomNumberGenerator rng((int)(PIL_check_seconds_timer() * 1000000.0)); + + for (const int curve_i : range) { + if (curves_to_delete[curve_i]) { + allow_remove_curve[curve_i] = true; + continue; + } + const float3 pos_cu = self_->deformed_root_positions_[curve_i]; + + const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + if (rng.get_float() < weight) { + allow_remove_curve[curve_i] = true; + } + } + }); + + /* Detect curves that are too close to other existing curves. */ + for (const int curve_i : curve_selection_) { + if (curves_to_delete[curve_i]) { + continue; + } + if (!allow_remove_curve[curve_i]) { + continue; + } + const float3 &pos_cu = self_->deformed_root_positions_[curve_i]; + const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + BLI_kdtree_3d_range_search_cb_cpp( + root_points_kdtree_, + pos_cu, + minimum_distance_, + [&](const int other_curve_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_curve_i == curve_i) { + return true; + } + if (allow_remove_curve[other_curve_i]) { + curves_to_delete[other_curve_i] = true; + } + return true; + }); + } + } +}; + +void DensitySubtractOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + DensitySubtractOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +/** + * Detects whether the brush should be in Add or Subtract mode. + */ +static bool use_add_density_mode(const BrushStrokeMode brush_mode, + const bContext &C, + const StrokeExtension &stroke_start) +{ + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); + const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C); + const ARegion ®ion = *CTX_wm_region(&C); + const View3D &v3d = *CTX_wm_view3d(&C); + + const eBrushCurvesSculptDensityMode density_mode = static_cast<eBrushCurvesSculptDensityMode>( + brush.curves_sculpt_settings->density_mode); + const bool use_invert = brush_mode == BRUSH_STROKE_INVERT; + + if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_ADD) { + return !use_invert; + } + if (density_mode == BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE) { + return use_invert; + } + + const Object &curves_ob_orig = *CTX_data_active_object(&C); + const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data); + Object *surface_ob_orig = curves_id_orig.surface; + if (surface_ob_orig == nullptr) { + return true; + } + Object *surface_ob_eval = DEG_get_evaluated_object(&depsgraph, surface_ob_orig); + if (surface_ob_eval == nullptr) { + return true; + } + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id_orig.geometry); + if (curves.curves_num() <= 1) { + return true; + } + const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval); + if (surface_mesh_eval == nullptr) { + return true; + } + + const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface); + BVHTreeFromMesh surface_bvh_eval; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_mesh_eval, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); }); + + const float2 brush_pos_re = stroke_start.mouse_position; + /* Reduce radius so that only an inner circle is used to determine the existing density. */ + const float brush_radius_re = BKE_brush_size_get(&scene, &brush) * 0.5f; + + /* Find the surface point under the brush. */ + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush( + depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re); + if (!brush_3d.has_value()) { + return true; + } + + const float3 brush_pos_cu = brush_3d->position_cu; + const float brush_radius_cu = brush_3d->radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob_orig); + const Span<int> offsets = curves.offsets(); + + /* Compute distance from brush to curve roots. */ + Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num()); + threading::EnumerableThreadSpecific<int> valid_curve_count_by_thread; + threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) { + int &valid_curve_count = valid_curve_count_by_thread.local(); + for (const int curve_i : range) { + const int root_point_i = offsets[curve_i]; + const float3 &root_pos_cu = deformation.positions[root_point_i]; + const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu); + if (dist_sq_cu < brush_radius_sq_cu) { + distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu), + curve_i}; + valid_curve_count++; + } + else { + distances_sq_to_brush[curve_i] = {FLT_MAX, -1}; + } + } + }); + const int valid_curve_count = std::accumulate( + valid_curve_count_by_thread.begin(), valid_curve_count_by_thread.end(), 0); + + /* Find a couple of curves that are closest to the brush center. */ + const int check_curve_count = std::min<int>(8, valid_curve_count); + std::partial_sort(distances_sq_to_brush.begin(), + distances_sq_to_brush.begin() + check_curve_count, + distances_sq_to_brush.end()); + + /* Compute the minimum pair-wise distance between the curve roots that are close to the brush + * center. */ + float min_dist_sq_cu = FLT_MAX; + for (const int i : IndexRange(check_curve_count)) { + const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]]; + for (int j = i + 1; j < check_curve_count; j++) { + const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]]; + const float dist_sq_cu = math::distance_squared(pos_i, pos_j); + math::min_inplace(min_dist_sq_cu, dist_sq_cu); + } + } + + const float min_dist_cu = std::sqrt(min_dist_sq_cu); + if (min_dist_cu > brush.curves_sculpt_settings->minimum_distance) { + return true; + } + + return false; +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation( + const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start) +{ + if (use_add_density_mode(brush_mode, C, stroke_start)) { + return std::make_unique<DensityAddOperation>(); + } + return std::make_unique<DensitySubtractOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index cf893f09fc6..1ee43d98e6f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -4,8 +4,7 @@ #include "BLI_enumerable_thread_specific.hh" #include "BLI_float4x4.hh" -#include "BLI_kdtree.h" -#include "BLI_rand.hh" +#include "BLI_length_parameterize.hh" #include "BLI_vector.hh" #include "PIL_time.h" @@ -14,19 +13,13 @@ #include "BKE_attribute_math.hh" #include "BKE_brush.h" -#include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_paint.h" -#include "BKE_spline.hh" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -70,6 +63,24 @@ class ShrinkCurvesEffect : public CurvesEffect { private: const Brush &brush_; + /** Storage of per-curve parameterization data to avoid reallocation. */ + struct ParameterizationBuffers { + Array<float3> old_positions; + Array<float> old_lengths; + Array<float> sample_lengths; + Array<int> indices; + Array<float> factors; + + void reinitialize(const int points_num) + { + this->old_positions.reinitialize(points_num); + this->old_lengths.reinitialize(length_parameterize::segments_num(points_num, false)); + this->sample_lengths.reinitialize(points_num); + this->indices.reinitialize(points_num); + this->factors.reinitialize(points_num); + } + }; + public: ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { @@ -81,46 +92,42 @@ class ShrinkCurvesEffect : public CurvesEffect { { MutableSpan<float3> positions_cu = curves.positions_for_write(); threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + ParameterizationBuffers data; for (const int influence_i : range) { const int curve_i = curve_indices[influence_i]; const float move_distance_cu = move_distances_cu[influence_i]; - const IndexRange curve_points = curves.points_for_curve(curve_i); - this->shrink_curve(positions_cu, curve_points, move_distance_cu); + const IndexRange points = curves.points_for_curve(curve_i); + this->shrink_curve(positions_cu.slice(points), move_distance_cu, data); } }); } + private: void shrink_curve(MutableSpan<float3> positions, - const IndexRange curve_points, - const float shrink_length) const + const float shrink_length, + ParameterizationBuffers &data) const { - PolySpline spline; - spline.resize(curve_points.size()); - MutableSpan<float3> spline_positions = spline.positions(); - spline_positions.copy_from(positions.slice(curve_points)); - spline.mark_cache_invalid(); + namespace lp = length_parameterize; + data.reinitialize(positions.size()); + + /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */ + data.old_positions.as_mutable_span().copy_from(positions); + + lp::accumulate_lengths<float3>(data.old_positions, false, data.old_lengths); + const float min_length = brush_.curves_sculpt_settings->minimum_length; - const float old_length = spline.length(); + const float old_length = data.old_lengths.last(); const float new_length = std::max(min_length, old_length - shrink_length); const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); - Vector<float> old_point_lengths; - old_point_lengths.append(0.0f); - for (const int i : spline_positions.index_range().drop_back(1)) { - const float3 &p1 = spline_positions[i]; - const float3 &p2 = spline_positions[i + 1]; - const float length = math::distance(p1, p2); - old_point_lengths.append(old_point_lengths.last() + length); + data.sample_lengths.first() = 0.0f; + for (const int i : data.old_lengths.index_range()) { + data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor; } - for (const int i : spline_positions.index_range()) { - const float eval_length = old_point_lengths[i] * length_factor; - const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } + lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors); + + lp::interpolate<float3>(data.old_positions, data.indices, data.factors, positions); } }; @@ -247,7 +254,7 @@ struct CurvesEffectOperationExecutor { eBrushFalloffShape falloff_shape_; - CurvesSculptTransforms transforms_; + CurvesSurfaceTransforms transforms_; float2 brush_pos_start_re_; float2 brush_pos_end_re_; @@ -289,7 +296,7 @@ struct CurvesEffectOperationExecutor { falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape); - transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); brush_pos_start_re_ = self.last_mouse_position_; brush_pos_end_re_ = stroke_extension.mouse_position; @@ -335,7 +342,8 @@ struct CurvesEffectOperationExecutor { void gather_influences_projected( threading::EnumerableThreadSpecific<Influences> &influences_for_thread) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -361,8 +369,8 @@ struct CurvesEffectOperationExecutor { float max_move_distance_cu = 0.0f; for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { for (const int segment_i : points.drop_back(1)) { - const float3 p1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 p1_cu = brush_transform_inv * deformation.positions[segment_i]; + const float3 p2_cu = brush_transform_inv * deformation.positions[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values); @@ -423,7 +431,8 @@ struct CurvesEffectOperationExecutor { void gather_influences_spherical( threading::EnumerableThreadSpecific<Influences> &influences_for_thread) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); float3 brush_pos_start_wo, brush_pos_end_wo; ED_view3d_win_to_3d(ctx_.v3d, @@ -461,8 +470,8 @@ struct CurvesEffectOperationExecutor { const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; for (const int segment_i : points.drop_back(1)) { - const float3 &p1_cu = positions_cu[segment_i]; - const float3 &p2_cu = positions_cu[segment_i + 1]; + const float3 &p1_cu = deformation.positions[segment_i]; + const float3 &p2_cu = deformation.positions[segment_i + 1]; float3 closest_on_segment_cu; float3 closest_on_brush_cu; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index ad3871bee45..7d40ed80a65 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -12,8 +12,11 @@ #include "BLI_virtual_array.hh" #include "BKE_attribute.h" +#include "BKE_crazyspace.hh" #include "BKE_curves.hh" +#include "ED_curves_sculpt.h" + struct ARegion; struct RegionView3D; struct Depsgraph; @@ -22,15 +25,18 @@ struct Object; struct Brush; struct Scene; struct BVHTreeFromMesh; +struct ReportList; namespace blender::ed::sculpt_paint { using bke::CurvesGeometry; +using bke::CurvesSurfaceTransforms; struct StrokeExtension { bool is_first; float2 mouse_position; float pressure; + ReportList *reports = nullptr; }; float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension); @@ -52,8 +58,7 @@ class CurvesSculptStrokeOperation { virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, - ReportList *reports); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); @@ -66,7 +71,7 @@ std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStro std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_density_operation( - const BrushStrokeMode brush_mode, const bContext &C); + const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start); std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation(); struct CurvesBrush3D { @@ -97,12 +102,6 @@ VArray<float> get_curves_selection(const Curves &curves_id); */ VArray<float> get_point_selection(const Curves &curves_id); -/** - * Find curves that have any point selected (a selection factor greater than zero), - * or curves that have their own selection factor greater than zero. - */ -IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices); - void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position); class CurvesSculptCommonContext { @@ -111,29 +110,16 @@ class CurvesSculptCommonContext { const Scene *scene = nullptr; ARegion *region = nullptr; const View3D *v3d = nullptr; - const RegionView3D *rv3d = nullptr; + RegionView3D *rv3d = nullptr; CurvesSculptCommonContext(const bContext &C); }; -struct CurvesSculptTransforms { - float4x4 curves_to_world; - float4x4 curves_to_surface; - float4x4 world_to_curves; - float4x4 world_to_surface; - float4x4 surface_to_world; - float4x4 surface_to_curves; - float4x4 surface_to_curves_normal; - - CurvesSculptTransforms() = default; - CurvesSculptTransforms(const Object &curves_ob, const Object *surface_ob); -}; - std::optional<CurvesBrush3D> sample_curves_surface_3d_brush( const Depsgraph &depsgraph, const ARegion ®ion, const View3D &v3d, - const CurvesSculptTransforms &transforms, + const CurvesSurfaceTransforms &transforms, const BVHTreeFromMesh &surface_bvh, const float2 &brush_pos_re, const float brush_radius_re); @@ -142,4 +128,9 @@ float transform_brush_radius(const float4x4 &transform, const float3 &brush_position, const float old_radius); +void report_missing_surface(ReportList *reports); +void report_missing_uv_map_on_original_surface(ReportList *reports); +void report_missing_uv_map_on_evaluated_surface(ReportList *reports); +void report_invalid_uv_map(ReportList *reports); + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index e6da2039433..02ebd622469 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -1,34 +1,53 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_kdtree.h" +#include "BLI_rand.hh" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" #include "BKE_brush.h" +#include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "WM_api.h" #include "WM_message.h" #include "WM_toolsystem.h" +#include "ED_curves.h" #include "ED_curves_sculpt.h" #include "ED_image.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_space_api.h" #include "ED_view3d.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" #include "DNA_screen_types.h" #include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" #include "curves_sculpt_intern.h" #include "curves_sculpt_intern.hh" #include "paint_intern.h" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + /* -------------------------------------------------------------------- */ /** \name Poll Functions * \{ */ @@ -90,8 +109,8 @@ float brush_strength_get(const Scene &scene, return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension); } -static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, - wmOperator &op) +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation( + bContext &C, wmOperator &op, const StrokeExtension &stroke_start) { const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode")); @@ -106,11 +125,21 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte case CURVES_SCULPT_TOOL_SNAKE_HOOK: return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: - return new_add_operation(C, op.reports); + return new_add_operation(); case CURVES_SCULPT_TOOL_GROW_SHRINK: return new_grow_shrink_operation(mode, C); case CURVES_SCULPT_TOOL_SELECTION_PAINT: return new_selection_paint_operation(mode, C); + case CURVES_SCULPT_TOOL_PINCH: + return new_pinch_operation(mode, C); + case CURVES_SCULPT_TOOL_SMOOTH: + return new_smooth_operation(); + case CURVES_SCULPT_TOOL_PUFF: + return new_puff_operation(); + case CURVES_SCULPT_TOOL_DENSITY: + return new_density_operation(mode, C, stroke_start); + case CURVES_SCULPT_TOOL_SLIDE: + return new_slide_operation(); } BLI_assert_unreachable(); return {}; @@ -121,7 +150,10 @@ struct SculptCurvesBrushStrokeData { PaintStroke *stroke; }; -static bool stroke_get_location(bContext *C, float out[3], const float mouse[2]) +static bool stroke_get_location(bContext *C, + float out[3], + const float mouse[2], + bool UNUSED(force_original)) { out[0] = mouse[0]; out[1] = mouse[1]; @@ -147,10 +179,11 @@ static void stroke_update_step(bContext *C, StrokeExtension stroke_extension; RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); stroke_extension.pressure = RNA_float_get(stroke_element, "pressure"); + stroke_extension.reports = op->reports; if (!op_data->operation) { stroke_extension.is_first = true; - op_data->operation = start_brush_operation(*C, *op); + op_data->operation = start_brush_operation(*C, *op, stroke_extension); } else { stroke_extension.is_first = false; @@ -236,18 +269,6 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) /** \name * CURVES_OT_sculptmode_toggle * \{ */ -static bool curves_sculptmode_toggle_poll(bContext *C) -{ - const Object *ob = CTX_data_active_object(C); - if (ob == nullptr) { - return false; - } - if (ob->type != OB_CURVES) { - return false; - } - return true; -} - static void curves_sculptmode_enter(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -309,13 +330,935 @@ static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) ot->description = "Enter/Exit sculpt mode for curves"; ot->exec = curves_sculptmode_toggle_exec; - ot->poll = curves_sculptmode_toggle_poll; + ot->poll = curves::curves_poll; ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } /** \} */ +namespace select_random { + +static int select_random_exec(bContext *C, wmOperator *op) +{ + VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C); + + const int seed = RNA_int_get(op->ptr, "seed"); + RandomNumberGenerator rng{static_cast<uint32_t>(seed)}; + + const bool partial = RNA_boolean_get(op->ptr, "partial"); + const bool constant_per_curve = RNA_boolean_get(op->ptr, "constant_per_curve"); + const float probability = RNA_float_get(op->ptr, "probability"); + const float min_value = RNA_float_get(op->ptr, "min"); + const auto next_partial_random_value = [&]() { + return rng.get_float() * (1.0f - min_value) + min_value; + }; + const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; }; + + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + const bool was_anything_selected = curves::has_anything_selected(*curves_id); + switch (curves_id->selection_domain) { + case ATTR_DOMAIN_POINT: { + MutableSpan<float> selection = curves.selection_point_float_for_write(); + if (!was_anything_selected) { + selection.fill(1.0f); + } + if (partial) { + if (constant_per_curve) { + for (const int curve_i : curves.curves_range()) { + const float random_value = next_partial_random_value(); + const IndexRange points = curves.points_for_curve(curve_i); + for (const int point_i : points) { + selection[point_i] *= random_value; + } + } + } + else { + for (const int point_i : selection.index_range()) { + const float random_value = next_partial_random_value(); + selection[point_i] *= random_value; + } + } + } + else { + if (constant_per_curve) { + for (const int curve_i : curves.curves_range()) { + const bool random_value = next_bool_random_value(); + const IndexRange points = curves.points_for_curve(curve_i); + if (!random_value) { + selection.slice(points).fill(0.0f); + } + } + } + else { + for (const int point_i : selection.index_range()) { + const bool random_value = next_bool_random_value(); + if (!random_value) { + selection[point_i] = 0.0f; + } + } + } + } + break; + } + case ATTR_DOMAIN_CURVE: { + MutableSpan<float> selection = curves.selection_curve_float_for_write(); + if (!was_anything_selected) { + selection.fill(1.0f); + } + if (partial) { + for (const int curve_i : curves.curves_range()) { + const float random_value = next_partial_random_value(); + selection[curve_i] *= random_value; + } + } + else { + for (const int curve_i : curves.curves_range()) { + const bool random_value = next_bool_random_value(); + if (!random_value) { + selection[curve_i] = 0.0f; + } + } + } + break; + } + } + MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? + curves.selection_point_float_for_write() : + curves.selection_curve_float_for_write(); + const bool was_any_selected = std::any_of( + selection.begin(), selection.end(), [](const float v) { return v > 0.0f; }); + if (was_any_selected) { + for (float &v : selection) { + v *= rng.get_float(); + } + } + else { + for (float &v : selection) { + v = rng.get_float(); + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + return OPERATOR_FINISHED; +} + +static void select_random_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + + uiItemR(layout, op->ptr, "seed", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "constant_per_curve", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "partial", 0, nullptr, ICON_NONE); + + if (RNA_boolean_get(op->ptr, "partial")) { + uiItemR(layout, op->ptr, "min", UI_ITEM_R_SLIDER, "Min", ICON_NONE); + } + else { + uiItemR(layout, op->ptr, "probability", UI_ITEM_R_SLIDER, "Probability", ICON_NONE); + } +} + +} // namespace select_random + +static void SCULPT_CURVES_OT_select_random(wmOperatorType *ot) +{ + ot->name = "Select Random"; + ot->idname = __func__; + ot->description = "Randomizes existing selection or create new random selection"; + + ot->exec = select_random::select_random_exec; + ot->poll = curves::editable_curves_poll; + ot->ui = select_random::select_random_ui; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int(ot->srna, + "seed", + 0, + INT32_MIN, + INT32_MAX, + "Seed", + "Source of randomness", + INT32_MIN, + INT32_MAX); + RNA_def_boolean( + ot->srna, "partial", false, "Partial", "Allow points or curves to be selected partially"); + RNA_def_float(ot->srna, + "probability", + 0.5f, + 0.0f, + 1.0f, + "Probability", + "Chance of every point or curve being included in the selection", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "min", + 0.0f, + 0.0f, + 1.0f, + "Min", + "Minimum value for the random selection", + 0.0f, + 1.0f); + RNA_def_boolean(ot->srna, + "constant_per_curve", + true, + "Constant per Curve", + "The generated random number is the same for every control point of a curve"); +} + +namespace select_end { +static bool select_end_poll(bContext *C) +{ + if (!curves::editable_curves_poll(C)) { + return false; + } + const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data); + if (curves_id->selection_domain != ATTR_DOMAIN_POINT) { + CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode"); + return false; + } + return true; +} + +static int select_end_exec(bContext *C, wmOperator *op) +{ + VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C); + const bool end_points = RNA_boolean_get(op->ptr, "end_points"); + const int amount = RNA_int_get(op->ptr, "amount"); + + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + const bool was_anything_selected = curves::has_anything_selected(*curves_id); + MutableSpan<float> selection = curves.selection_point_float_for_write(); + if (!was_anything_selected) { + selection.fill(1.0f); + } + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves.points_for_curve(curve_i); + if (end_points) { + selection.slice(points.drop_back(amount)).fill(0.0f); + } + else { + selection.slice(points.drop_front(amount)).fill(0.0f); + } + } + }); + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + return OPERATOR_FINISHED; +} +} // namespace select_end + +static void SCULPT_CURVES_OT_select_end(wmOperatorType *ot) +{ + ot->name = "Select End"; + ot->idname = __func__; + ot->description = "Select end points of curves"; + + ot->exec = select_end::select_end_exec; + ot->poll = select_end::select_end_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "end_points", + true, + "End Points", + "Select points at the end of the curve as opposed to the beginning"); + RNA_def_int( + ot->srna, "amount", 1, 0, INT32_MAX, "Amount", "Number of points to select", 0, INT32_MAX); +} + +namespace select_grow { + +struct GrowOperatorDataPerCurve : NonCopyable, NonMovable { + Curves *curves_id; + Vector<int> selected_point_indices; + Vector<int> unselected_point_indices; + Array<float> distances_to_selected; + Array<float> distances_to_unselected; + + Array<float> original_selection; + float pixel_to_distance_factor; +}; + +struct GrowOperatorData { + int initial_mouse_x; + Vector<std::unique_ptr<GrowOperatorDataPerCurve>> per_curve; +}; + +static void update_points_selection(const GrowOperatorDataPerCurve &data, + const float distance, + MutableSpan<float> points_selection) +{ + if (distance > 0.0f) { + threading::parallel_for( + data.unselected_point_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int point_i = data.unselected_point_indices[i]; + const float distance_to_selected = data.distances_to_selected[i]; + const float selection = distance_to_selected <= distance ? 1.0f : 0.0f; + points_selection[point_i] = selection; + } + }); + threading::parallel_for( + data.selected_point_indices.index_range(), 512, [&](const IndexRange range) { + for (const int point_i : data.selected_point_indices.as_span().slice(range)) { + points_selection[point_i] = 1.0f; + } + }); + } + else { + threading::parallel_for( + data.selected_point_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int point_i = data.selected_point_indices[i]; + const float distance_to_unselected = data.distances_to_unselected[i]; + const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f; + points_selection[point_i] = selection; + } + }); + threading::parallel_for( + data.unselected_point_indices.index_range(), 512, [&](const IndexRange range) { + for (const int point_i : data.unselected_point_indices.as_span().slice(range)) { + points_selection[point_i] = 0.0f; + } + }); + } +} + +static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x) +{ + GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata); + + for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { + Curves &curves_id = *curve_op_data->curves_id; + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x; + + /* Grow or shrink selection based on precomputed distances. */ + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: { + MutableSpan<float> points_selection = curves.selection_point_float_for_write(); + update_points_selection(*curve_op_data, distance, points_selection); + break; + } + case ATTR_DOMAIN_CURVE: { + Array<float> new_points_selection(curves.points_num()); + update_points_selection(*curve_op_data, distance, new_points_selection); + /* Propagate grown point selection to the curve selection. */ + MutableSpan<float> curves_selection = curves.selection_curve_float_for_write(); + for (const int curve_i : curves.curves_range()) { + const IndexRange points = curves.points_for_curve(curve_i); + const Span<float> points_selection = new_points_selection.as_span().slice(points); + const float max_selection = *std::max_element(points_selection.begin(), + points_selection.end()); + curves_selection[curve_i] = max_selection; + } + break; + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id); + } + + return OPERATOR_FINISHED; +} + +static void select_grow_invoke_per_curve(Curves &curves_id, + Object &curves_ob, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + GrowOperatorDataPerCurve &curve_op_data) +{ + curve_op_data.curves_id = &curves_id; + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const Span<float3> positions = curves.positions(); + + /* Find indices of selected and unselected points. */ + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: { + const VArray<float> points_selection = curves.selection_point_float(); + curve_op_data.original_selection.reinitialize(points_selection.size()); + points_selection.materialize(curve_op_data.original_selection); + for (const int point_i : points_selection.index_range()) { + const float point_selection = points_selection[point_i]; + if (point_selection > 0.0f) { + curve_op_data.selected_point_indices.append(point_i); + } + else { + curve_op_data.unselected_point_indices.append(point_i); + } + } + + break; + } + case ATTR_DOMAIN_CURVE: { + const VArray<float> curves_selection = curves.selection_curve_float(); + curve_op_data.original_selection.reinitialize(curves_selection.size()); + curves_selection.materialize(curve_op_data.original_selection); + for (const int curve_i : curves_selection.index_range()) { + const float curve_selection = curves_selection[curve_i]; + const IndexRange points = curves.points_for_curve(curve_i); + if (curve_selection > 0.0f) { + for (const int point_i : points) { + curve_op_data.selected_point_indices.append(point_i); + } + } + else { + for (const int point_i : points) { + curve_op_data.unselected_point_indices.append(point_i); + } + } + } + break; + } + } + + threading::parallel_invoke( + [&]() { + /* Build KD-tree for the selected points. */ + KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_point_indices.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); + for (const int point_i : curve_op_data.selected_point_indices) { + const float3 &position = positions[point_i]; + BLI_kdtree_3d_insert(kdtree, point_i, position); + } + BLI_kdtree_3d_balance(kdtree); + + /* For each unselected point, compute the distance to the closest selected point. */ + curve_op_data.distances_to_selected.reinitialize( + curve_op_data.unselected_point_indices.size()); + threading::parallel_for(curve_op_data.unselected_point_indices.index_range(), + 256, + [&](const IndexRange range) { + for (const int i : range) { + const int point_i = curve_op_data.unselected_point_indices[i]; + const float3 &position = positions[point_i]; + KDTreeNearest_3d nearest; + BLI_kdtree_3d_find_nearest(kdtree, position, &nearest); + curve_op_data.distances_to_selected[i] = nearest.dist; + } + }); + }, + [&]() { + /* Build KD-tree for the unselected points. */ + KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.unselected_point_indices.size()); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); + for (const int point_i : curve_op_data.unselected_point_indices) { + const float3 &position = positions[point_i]; + BLI_kdtree_3d_insert(kdtree, point_i, position); + } + BLI_kdtree_3d_balance(kdtree); + + /* For each selected point, compute the distance to the closest unselected point. */ + curve_op_data.distances_to_unselected.reinitialize( + curve_op_data.selected_point_indices.size()); + threading::parallel_for( + curve_op_data.selected_point_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int point_i = curve_op_data.selected_point_indices[i]; + const float3 &position = positions[point_i]; + KDTreeNearest_3d nearest; + BLI_kdtree_3d_find_nearest(kdtree, position, &nearest); + curve_op_data.distances_to_unselected[i] = nearest.dist; + } + }); + }); + + float4x4 curves_to_world_mat = curves_ob.obmat; + float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(&rv3d, &curves_ob, projection.values); + + /* Compute how mouse movements in screen space are converted into grow/shrink distances in + * object space. */ + curve_op_data.pixel_to_distance_factor = threading::parallel_reduce( + curve_op_data.selected_point_indices.index_range(), + 256, + FLT_MAX, + [&](const IndexRange range, float pixel_to_distance_factor) { + for (const int i : range) { + const int point_i = curve_op_data.selected_point_indices[i]; + const float3 &pos_cu = positions[point_i]; + + float2 pos_re; + ED_view3d_project_float_v2_m4(®ion, pos_cu, pos_re, projection.values); + if (pos_re.x < 0 || pos_re.y < 0 || pos_re.x > region.winx || pos_re.y > region.winy) { + continue; + } + /* Compute how far this point moves in curve space when it moves one unit in screen + * space. */ + const float2 pos_offset_re = pos_re + float2(1, 0); + float3 pos_offset_wo; + ED_view3d_win_to_3d( + &v3d, ®ion, curves_to_world_mat * pos_cu, pos_offset_re, pos_offset_wo); + const float3 pos_offset_cu = world_to_curves_mat * pos_offset_wo; + const float dist_cu = math::distance(pos_cu, pos_offset_cu); + const float dist_re = math::distance(pos_re, pos_offset_re); + const float factor = dist_cu / dist_re; + math::min_inplace(pixel_to_distance_factor, factor); + } + return pixel_to_distance_factor; + }, + [](const float a, const float b) { return std::min(a, b); }); +} + +static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *active_ob = CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__); + op->customdata = op_data; + + op_data->initial_mouse_x = event->xy[0]; + + Curves &curves_id = *static_cast<Curves *>(active_ob->data); + auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>(); + select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data); + op_data->per_curve.append(std::move(curve_op_data)); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata); + const int mouse_x = event->xy[0]; + const int mouse_diff_x = mouse_x - op_data.initial_mouse_x; + switch (event->type) { + case MOUSEMOVE: { + select_grow_update(C, op, mouse_diff_x); + break; + } + case LEFTMOUSE: { + MEM_delete(&op_data); + return OPERATOR_FINISHED; + } + case EVT_ESCKEY: + case RIGHTMOUSE: { + /* Undo operator by resetting the selection to the original value. */ + for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) { + Curves &curves_id = *curve_op_data->curves_id; + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + switch (curves_id.selection_domain) { + case ATTR_DOMAIN_POINT: { + MutableSpan<float> points_selection = curves.selection_point_float_for_write(); + points_selection.copy_from(curve_op_data->original_selection); + break; + } + case ATTR_DOMAIN_CURVE: { + MutableSpan<float> curves_seletion = curves.selection_curve_float_for_write(); + curves_seletion.copy_from(curve_op_data->original_selection); + break; + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id); + } + MEM_delete(&op_data); + return OPERATOR_CANCELLED; + } + } + return OPERATOR_RUNNING_MODAL; +} + +} // namespace select_grow + +static void SCULPT_CURVES_OT_select_grow(wmOperatorType *ot) +{ + ot->name = "Select Grow"; + ot->idname = __func__; + ot->description = "Select curves which are close to curves that are selected already"; + + ot->invoke = select_grow::select_grow_invoke; + ot->modal = select_grow::select_grow_modal; + ot->poll = curves::editable_curves_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + prop = RNA_def_float(ot->srna, + "distance", + 0.1f, + -FLT_MAX, + FLT_MAX, + "Distance", + "By how much to grow the selection", + -10.0f, + 10.f); + RNA_def_property_subtype(prop, PROP_DISTANCE); +} + +namespace min_distance_edit { + +static bool min_distance_edit_poll(bContext *C) +{ + if (!curves::curves_with_surface_poll(C)) { + return false; + } + Scene *scene = CTX_data_scene(C); + const Brush *brush = BKE_paint_brush_for_read(&scene->toolsettings->curves_sculpt->paint); + if (brush == nullptr) { + return false; + } + if (brush->curves_sculpt_tool != CURVES_SCULPT_TOOL_DENSITY) { + return false; + } + return true; +} + +struct MinDistanceEditData { + /** Brush whose minimum distance is modified. */ + Brush *brush; + float4x4 curves_to_world_mat; + + /** Where the preview is drawn. */ + float3 pos_cu; + float3 normal_cu; + + int2 initial_mouse; + float initial_minimum_distance; + + /** The operator uses a new cursor, but the existing cursors should be restored afterwards. */ + ListBase orig_paintcursors; + void *cursor; + + /** Store the viewport region in case the operator was called from the header. */ + ARegion *region; + RegionView3D *rv3d; +}; + +static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data) +{ + Scene *scene = CTX_data_scene(C); + ARegion *region = op_data.region; + + const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance; + const float brush_radius = BKE_brush_size_get(scene, op_data.brush); + + float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1}); + if (math::is_zero(tangent_x_cu)) { + tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0}); + } + tangent_x_cu = math::normalize(tangent_x_cu); + const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu)); + + /* Sample a few points to get a good estimate of how large the grid has to be. */ + Vector<float3> points_wo; + points_wo.append(op_data.pos_cu + min_distance * tangent_x_cu); + points_wo.append(op_data.pos_cu + min_distance * tangent_y_cu); + points_wo.append(op_data.pos_cu - min_distance * tangent_x_cu); + points_wo.append(op_data.pos_cu - min_distance * tangent_y_cu); + + Vector<float2> points_re; + for (const float3 &pos_wo : points_wo) { + float2 pos_re; + ED_view3d_project_v2(region, pos_wo, pos_re); + points_re.append(pos_re); + } + + float2 origin_re; + ED_view3d_project_v2(region, op_data.pos_cu, origin_re); + + int needed_points = 0; + for (const float2 &pos_re : points_re) { + const float distance = math::length(pos_re - origin_re); + const int needed_points_iter = (brush_radius * 2.0f) / distance; + + if (needed_points_iter > needed_points) { + needed_points = needed_points_iter; + } + } + + /* Limit to a hard-coded number since it only adds noise at some point. */ + return std::min(300, needed_points); +} + +static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), void *customdata) +{ + Scene *scene = CTX_data_scene(C); + MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(customdata); + + const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance; + + float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1}); + if (math::is_zero(tangent_x_cu)) { + tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0}); + } + tangent_x_cu = math::normalize(tangent_x_cu); + const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu)); + + const int points_per_side = calculate_points_per_side(C, op_data); + const int points_per_axis_num = 2 * points_per_side + 1; + + Vector<float3> points_wo; + for (const int x_i : IndexRange(points_per_axis_num)) { + for (const int y_i : IndexRange(points_per_axis_num)) { + const float x_iter = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f); + const float y_iter = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f); + + const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f + + x_iter * tangent_x_cu + y_iter * tangent_y_cu; + const float3 point_pos_wo = op_data.curves_to_world_mat * point_pos_cu; + points_wo.append(point_pos_wo); + } + } + + float4 circle_col = float4(op_data.brush->add_col); + float circle_alpha = op_data.brush->cursor_overlay_alpha; + float brush_radius_re = BKE_brush_size_get(scene, op_data.brush); + + /* Draw the grid. */ + GPU_matrix_push(); + GPU_matrix_push_projection(); + GPU_blend(GPU_BLEND_ALPHA); + + ARegion *region = op_data.region; + RegionView3D *rv3d = op_data.rv3d; + wmWindow *win = CTX_wm_window(C); + + /* It does the same as: `view3d_operator_needs_opengl(C);`. */ + wmViewport(®ion->winrct); + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + GPUVertFormat *format3d = immVertexFormat(); + + const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR); + + GPU_point_size(3.0f); + immBegin(GPU_PRIM_POINTS, points_wo.size()); + + float3 brush_origin_wo = op_data.curves_to_world_mat * op_data.pos_cu; + float2 brush_origin_re; + ED_view3d_project_v2(region, brush_origin_wo, brush_origin_re); + + /* Smooth alpha transition until the brush edge. */ + const int alpha_border_re = 20; + const float dist_to_inner_border_re = brush_radius_re - alpha_border_re; + + for (const float3 &pos_wo : points_wo) { + float2 pos_re; + ED_view3d_project_v2(region, pos_wo, pos_re); + + const float dist_to_point_re = math::distance(pos_re, brush_origin_re); + const float alpha = 1.0f - ((dist_to_point_re - dist_to_inner_border_re) / alpha_border_re); + + immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha); + immVertex3fv(pos3d, pos_wo); + } + immEnd(); + immUnbindProgram(); + + /* Reset the drawing settings. */ + GPU_point_size(1.0f); + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + int4 scissor; + GPU_scissor_get(scissor); + wmWindowViewport(win); + GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]); + + /* Draw the brush circle. */ + GPU_matrix_translate_2f((float)op_data.initial_mouse.x, (float)op_data.initial_mouse.y); + + GPUVertFormat *format = immVertexFormat(); + uint pos2d = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3fvAlpha(circle_col, circle_alpha); + imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius_re, 80); + + immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); +} + +static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); + + Object &curves_ob_orig = *CTX_data_active_object(C); + Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data); + Object &surface_ob_orig = *curves_id_orig.surface; + Object *surface_ob_eval = DEG_get_evaluated_object(depsgraph, &surface_ob_orig); + if (surface_ob_eval == nullptr) { + return OPERATOR_CANCELLED; + } + Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval); + if (surface_me_eval == nullptr) { + return OPERATOR_CANCELLED; + } + + BVHTreeFromMesh surface_bvh_eval; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_me_eval, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); }); + + const int2 mouse_pos_int_re{event->mval}; + const float2 mouse_pos_re{mouse_pos_int_re}; + + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped( + depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true); + + const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig}; + + const float3 ray_start_su = transforms.world_to_surface * ray_start_wo; + const float3 ray_end_su = transforms.world_to_surface * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + BVHTreeRayHit ray_hit; + ray_hit.dist = FLT_MAX; + ray_hit.index = -1; + BLI_bvhtree_ray_cast(surface_bvh_eval.tree, + ray_start_su, + ray_direction_su, + 0.0f, + &ray_hit, + surface_bvh_eval.raycast_callback, + &surface_bvh_eval); + if (ray_hit.index == -1) { + WM_report(RPT_ERROR, "Cursor must be over the surface mesh"); + return OPERATOR_CANCELLED; + } + + const float3 hit_pos_su = ray_hit.co; + const float3 hit_normal_su = ray_hit.no; + + const float3 hit_pos_cu = transforms.surface_to_curves * hit_pos_su; + const float3 hit_normal_cu = math::normalize(transforms.surface_to_curves_normal * + hit_normal_su); + + MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__); + op_data->curves_to_world_mat = transforms.curves_to_world; + op_data->normal_cu = hit_normal_cu; + op_data->pos_cu = hit_pos_cu; + op_data->initial_mouse = event->xy; + op_data->brush = BKE_paint_brush(&scene->toolsettings->curves_sculpt->paint); + op_data->initial_minimum_distance = op_data->brush->curves_sculpt_settings->minimum_distance; + + if (op_data->initial_minimum_distance <= 0.0f) { + op_data->initial_minimum_distance = 0.01f; + } + + op->customdata = op_data; + + /* Temporarily disable other paint cursors. */ + wmWindowManager *wm = CTX_wm_manager(C); + op_data->orig_paintcursors = wm->paintcursors; + BLI_listbase_clear(&wm->paintcursors); + + /* Add minimum distance paint cursor. */ + op_data->cursor = WM_paint_cursor_activate( + SPACE_TYPE_ANY, RGN_TYPE_ANY, op->type->poll, min_distance_edit_draw, op_data); + + op_data->region = CTX_wm_region(C); + op_data->rv3d = CTX_wm_region_view3d(C); + + WM_event_add_modal_handler(C, op); + ED_region_tag_redraw(region); + return OPERATOR_RUNNING_MODAL; +} + +static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(op->customdata); + + auto finish = [&]() { + wmWindowManager *wm = CTX_wm_manager(C); + + /* Remove own cursor. */ + WM_paint_cursor_end(static_cast<wmPaintCursor *>(op_data.cursor)); + /* Restore original paint cursors. */ + wm->paintcursors = op_data.orig_paintcursors; + + ED_region_tag_redraw(region); + MEM_freeN(&op_data); + }; + + switch (event->type) { + case MOUSEMOVE: { + const int2 mouse_pos_int_re{event->xy}; + const float2 mouse_pos_re{mouse_pos_int_re}; + + const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse.x; + const float factor = powf(2, mouse_diff_x / UI_UNIT_X / 10.0f); + op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance * + factor; + + ED_region_tag_redraw(region); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + break; + } + case LEFTMOUSE: { + if (event->val == KM_PRESS) { + finish(); + return OPERATOR_FINISHED; + } + break; + } + case RIGHTMOUSE: + case EVT_ESCKEY: { + op_data.brush->curves_sculpt_settings->minimum_distance = op_data.initial_minimum_distance; + finish(); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr); + return OPERATOR_CANCELLED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +} // namespace min_distance_edit + +static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot) +{ + ot->name = "Edit Minimum Distance"; + ot->idname = __func__; + ot->description = "Change the minimum distance used by the density brush"; + + ot->poll = min_distance_edit::min_distance_edit_poll; + ot->invoke = min_distance_edit::min_distance_edit_invoke; + ot->modal = min_distance_edit::min_distance_edit_modal; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR; +} + } // namespace blender::ed::sculpt_paint /* -------------------------------------------------------------------- */ @@ -327,6 +1270,10 @@ void ED_operatortypes_sculpt_curves() using namespace blender::ed::sculpt_paint; WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke); WM_operatortype_append(CURVES_OT_sculptmode_toggle); + WM_operatortype_append(SCULPT_CURVES_OT_select_random); + WM_operatortype_append(SCULPT_CURVES_OT_select_end); + WM_operatortype_append(SCULPT_CURVES_OT_select_grow); + WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc new file mode 100644 index 00000000000..3e43b1a6361 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_paint.h" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "WM_api.h" + +/** + * The code below uses a prefix naming convention to indicate the coordinate space: + * `cu`: Local space of the curves object that is being edited. + * `su`: Local space of the surface object. + * `wo`: World space. + * `re`: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +class PinchOperation : public CurvesSculptStrokeOperation { + private: + bool invert_pinch_; + Array<float> segment_lengths_cu_; + + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + friend struct PinchOperationExecutor; + + public: + PinchOperation(const bool invert_pinch) : invert_pinch_(invert_pinch) + { + } + + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +struct PinchOperationExecutor { + PinchOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + CurvesSurfaceTransforms transforms_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + + float invert_factor_; + + float2 brush_pos_re_; + + PinchOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(PinchOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + self_ = &self; + + object_ = CTX_data_active_object(&C); + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = BKE_brush_alpha_get(ctx_.scene, brush_); + + invert_factor_ = self_->invert_pinch_ ? -1.0f : 1.0f; + + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + brush_pos_re_ = stroke_extension.mouse_position; + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + if (stroke_extension.is_first) { + this->initialize_segment_lengths(); + + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + self_->brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + } + } + + Array<bool> changed_curves(curves_->curves_num(), false); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->pinch_projected_with_symmetry(changed_curves); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->pinch_spherical_with_symmetry(changed_curves); + } + else { + BLI_assert_unreachable(); + } + + this->restore_segment_lengths(changed_curves); + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void pinch_projected_with_symmetry(MutableSpan<bool> r_changed_curves) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->pinch_projected(brush_transform, r_changed_curves); + } + } + + void pinch_projected(const float4x4 &brush_transform, MutableSpan<bool> r_changed_curves) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 old_pos_cu = deformation.positions[point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); + + const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_re_); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float t = safe_divide(dist_to_brush_re, brush_radius_base_re_); + const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f); + const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff * + point_factors_[point_i]; + + const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight); + + float3 new_symm_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + + const float3 new_pos_cu = brush_transform * transforms_.world_to_curves * + new_symm_pos_wo; + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu[point_i] += translation_orig; + r_changed_curves[curve_i] = true; + } + } + }); + } + + void pinch_spherical_with_symmetry(MutableSpan<bool> r_changed_curves) + { + float3 brush_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_pos_wo); + const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->pinch_spherical(brush_transform * brush_pos_cu, brush_radius_cu, r_changed_curves); + } + } + + void pinch_spherical(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<bool> r_changed_curves) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 old_pos_cu = deformation.positions[point_i]; + + const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float t = safe_divide(dist_to_brush_cu, brush_radius_cu); + const float radius_falloff = t * BKE_brush_curve_strength(brush_, t, 1.0f); + const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff * + point_factors_[point_i]; + + const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight); + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu[point_i] += translation_orig; + + r_changed_curves[curve_i] = true; + } + } + }); + } + + void initialize_segment_lengths() + { + const Span<float3> positions_cu = curves_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_->points_num()); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[point_i]; + const float3 &p2_cu = positions_cu[point_i + 1]; + const float length_cu = math::distance(p1_cu, p2_cu); + self_->segment_lengths_cu_[point_i] = length_cu; + } + } + }); + } + + void restore_segment_lengths(const Span<bool> changed_curves) + { + const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + + threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + if (!changed_curves[curve_i]) { + continue; + } + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : IndexRange(points.size() - 1)) { + const float3 &p1_cu = positions_cu[points[segment_i]]; + float3 &p2_cu = positions_cu[points[segment_i] + 1]; + const float3 direction = math::normalize(p2_cu - p1_cu); + const float expected_length_cu = expected_lengths_cu[points[segment_i]]; + p2_cu = p1_cu + direction * expected_length_cu; + } + } + }); + } +}; + +void PinchOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) +{ + PinchOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_pinch_operation(const BrushStrokeMode brush_mode, + const bContext &C) +{ + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); + + const bool invert_pinch = (brush_mode == BRUSH_STROKE_INVERT) != + ((brush.flag & BRUSH_DIR_IN) != 0); + return std::make_unique<PinchOperation>(invert_pinch); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc new file mode 100644 index 00000000000..139e0d67e89 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -0,0 +1,382 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_crazyspace.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "WM_api.h" + +#include "BLI_length_parameterize.hh" + +#include "GEO_add_curves_on_mesh.hh" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +class PuffOperation : public CurvesSculptStrokeOperation { + private: + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + /** Length of each segment indexed by the index of the first point in the segment. */ + Array<float> segment_lengths_cu_; + + friend struct PuffOperationExecutor; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct PuffOperationExecutor { + PuffOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + float2 brush_pos_re_; + + eBrushFalloffShape falloff_shape_; + + CurvesSurfaceTransforms transforms_; + + Object *surface_ob_ = nullptr; + Mesh *surface_ = nullptr; + Span<MLoopTri> surface_looptris_; + Span<float3> corner_normals_su_; + BVHTreeFromMesh surface_bvh_; + + PuffOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(PuffOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + UNUSED_VARS(C, stroke_extension); + self_ = &self; + + object_ = CTX_data_active_object(&C); + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + return; + } + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); + + surface_ob_ = curves_id_->surface; + surface_ = static_cast<Mesh *>(surface_ob_->data); + + transforms_ = CurvesSurfaceTransforms(*object_, surface_ob_); + + if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_); + } + corner_normals_su_ = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), + surface_->totloop}; + + BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); + + surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), + BKE_mesh_runtime_looptri_len(surface_)}; + + if (stroke_extension.is_first) { + this->initialize_segment_lengths(); + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + } + } + + Array<float> curve_weights(curve_selection_.size(), 0.0f); + + if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { + this->find_curve_weights_projected_with_symmetry(curve_weights); + } + else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { + this->find_curves_weights_spherical_with_symmetry(curve_weights); + } + else { + BLI_assert_unreachable(); + } + + this->puff(curve_weights); + this->restore_segment_lengths(); + + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void find_curve_weights_projected_with_symmetry(MutableSpan<float> r_curve_weights) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_curve_weights_projected(brush_transform, r_curve_weights); + } + } + + void find_curve_weights_projected(const float4x4 &brush_transform, + MutableSpan<float> r_curve_weights) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_selection_i : range) { + const int curve_i = curve_selection_[curve_selection_i]; + const IndexRange points = curves_->points_for_curve(curve_i); + const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]]; + float2 prev_pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values); + for (const int point_i : points.drop_front(1)) { + const float3 pos_cu = brush_transform_inv * deformation.positions[point_i]; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + BLI_SCOPED_DEFER([&]() { prev_pos_re = pos_re; }); + + const float dist_to_brush_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, prev_pos_re, pos_re); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + const float weight = radius_falloff; + math::max_inplace(r_curve_weights[curve_selection_i], weight); + } + } + }); + } + + void find_curves_weights_spherical_with_symmetry(MutableSpan<float> r_curve_weights) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_pos_wo); + const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_curves_weights_spherical( + brush_transform * brush_pos_cu, brush_radius_cu, r_curve_weights); + } + } + + void find_curves_weights_spherical(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<float> r_curve_weights) + { + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_selection_i : range) { + const int curve_i = curve_selection_[curve_selection_i]; + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_front(1)) { + const float3 &prev_pos_cu = deformation.positions[point_i - 1]; + const float3 &pos_cu = deformation.positions[point_i]; + const float dist_to_brush_sq_cu = dist_squared_to_line_segment_v3( + brush_pos_cu, prev_pos_cu, pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = radius_falloff; + math::max_inplace(r_curve_weights[curve_selection_i], weight); + } + } + }); + } + + void puff(const Span<float> curve_weights) + { + BLI_assert(curve_weights.size() == curve_selection_.size()); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + Vector<float> accumulated_lengths_cu; + for (const int curve_selection_i : range) { + const int curve_i = curve_selection_[curve_selection_i]; + const IndexRange points = curves_->points_for_curve(curve_i); + const int first_point_i = points[0]; + const float3 first_pos_cu = positions_cu[first_point_i]; + const float3 first_pos_su = transforms_.curves_to_surface * first_pos_cu; + + /* Find the nearest position on the surface. The curve will be aligned to the normal of + * that point. */ + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest(surface_bvh_.tree, + first_pos_su, + &nearest, + surface_bvh_.nearest_callback, + &surface_bvh_); + + const MLoopTri &looptri = surface_looptris_[nearest.index]; + const float3 closest_pos_su = nearest.co; + const float3 &v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; + const float3 &v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; + const float3 &v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, v0_su, v1_su, v2_su, closest_pos_su); + const float3 normal_su = geometry::compute_surface_point_normal( + looptri, bary_coords, corner_normals_su_); + const float3 normal_cu = math::normalize(transforms_.surface_to_curves_normal * normal_su); + + accumulated_lengths_cu.reinitialize(points.size() - 1); + length_parameterize::accumulate_lengths<float3>( + positions_cu.slice(points), false, accumulated_lengths_cu); + + /* Align curve to the surface normal while making sure that the curve does not fold up much + * in the process (e.g. when the curve was pointing in the opposite direction before). */ + for (const int i : IndexRange(points.size()).drop_front(1)) { + const int point_i = points[i]; + const float3 old_pos_cu = positions_cu[point_i]; + + /* Compute final position of the point. */ + const float length_param_cu = accumulated_lengths_cu[i - 1]; + const float3 goal_pos_cu = first_pos_cu + length_param_cu * normal_cu; + + const float weight = 0.01f * brush_strength_ * point_factors_[point_i] * + curve_weights[curve_selection_i]; + float3 new_pos_cu = math::interpolate(old_pos_cu, goal_pos_cu, weight); + + /* Make sure the point does not move closer to the root point than it was initially. This + * makes the curve kind of "rotate up". */ + const float old_dist_to_root_cu = math::distance(old_pos_cu, first_pos_cu); + const float new_dist_to_root_cu = math::distance(new_pos_cu, first_pos_cu); + if (new_dist_to_root_cu < old_dist_to_root_cu) { + const float3 offset = math::normalize(new_pos_cu - first_pos_cu); + new_pos_cu += (old_dist_to_root_cu - new_dist_to_root_cu) * offset; + } + + positions_cu[point_i] = new_pos_cu; + } + } + }); + } + + void initialize_segment_lengths() + { + const Span<float3> positions_cu = curves_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_->points_num()); + threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[point_i]; + const float3 &p2_cu = positions_cu[point_i + 1]; + const float length_cu = math::distance(p1_cu, p2_cu); + self_->segment_lengths_cu_[point_i] = length_cu; + } + } + }); + } + + void restore_segment_lengths() + { + const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + float3 &p2_cu = positions_cu[segment_i + 1]; + const float3 direction = math::normalize(p2_cu - p1_cu); + const float expected_length_cu = expected_lengths_cu[segment_i]; + p2_cu = p1_cu + direction * expected_length_cu; + } + } + }); + } +}; + +void PuffOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) +{ + PuffOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_puff_operation() +{ + return std::make_unique<PuffOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc index f620fed5761..5bfc8ccc667 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc @@ -6,6 +6,8 @@ #include "curves_sculpt_intern.hh" +#include "ED_curves_sculpt.h" + namespace blender::ed::sculpt_paint { static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain) @@ -62,7 +64,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, case ATTR_DOMAIN_POINT: { const VArray<float> selection = curves.selection_point_float(); if (selection.is_single()) { - return selection.get_internal_single() == 0.0f ? IndexMask(0) : + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : IndexMask(curves.curves_num()); } return index_mask_ops::find_indices_based_on_predicate( @@ -78,7 +80,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, case ATTR_DOMAIN_CURVE: { const VArray<float> selection = curves.selection_curve_float(); if (selection.is_single()) { - return selection.get_internal_single() == 0.0f ? IndexMask(0) : + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : IndexMask(curves.curves_num()); } return index_mask_ops::find_indices_based_on_predicate( @@ -102,4 +104,49 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_i r_indices); } +static IndexMask retrieve_selected_points(const CurvesGeometry &curves, + const eAttrDomain domain, + Vector<int64_t> &r_indices) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: { + const VArray<float> selection = curves.selection_point_float(); + if (selection.is_single()) { + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : + IndexMask(curves.points_num()); + } + return index_mask_ops::find_indices_based_on_predicate( + curves.points_range(), 2048, r_indices, [&](const int i) { + return selection[i] > 0.0f; + }); + } + case ATTR_DOMAIN_CURVE: { + const VArray<float> selection = curves.selection_curve_float(); + if (selection.is_single()) { + return selection.get_internal_single() <= 0.0f ? IndexMask(0) : + IndexMask(curves.points_num()); + } + const VArray<float> point_selection = curves.adapt_domain( + selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + return index_mask_ops::find_indices_based_on_predicate( + curves.points_range(), 2048, r_indices, [&](const int i) { + return point_selection[i] > 0.0f; + }); + } + default: + BLI_assert_unreachable(); + return {}; + } +} + +IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return CurvesGeometry::wrap(curves_id.geometry).points_range(); + } + return retrieve_selected_points(CurvesGeometry::wrap(curves_id.geometry), + eAttrDomain(curves_id.selection_domain), + r_indices); +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc index b40aebcaaf1..cc5a5e7ae8a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc @@ -66,7 +66,7 @@ struct SelectionPaintOperationExecutor { float2 brush_pos_re_; - CurvesSculptTransforms transforms_; + CurvesSurfaceTransforms transforms_; SelectionPaintOperationExecutor(const bContext &C) : ctx_(C) { @@ -104,7 +104,7 @@ struct SelectionPaintOperationExecutor { } } - transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); @@ -140,6 +140,7 @@ struct SelectionPaintOperationExecutor { * selection is handled as a generic attribute for now. */ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ctx_.rv3d->rflag &= ~RV3D_PAINTING; ED_region_tag_redraw(ctx_.region); } @@ -160,14 +161,15 @@ struct SelectionPaintOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { for (const int point_i : point_range) { - const float3 pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 pos_cu = brush_transform_inv * deformation.positions[point_i]; /* Find the position of the point in screen space. */ float2 pos_re; @@ -214,14 +216,15 @@ struct SelectionPaintOperationExecutor { void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) { - Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { for (const int i : point_range) { - const float3 pos_old_cu = positions_cu[i]; + const float3 pos_old_cu = deformation.positions[i]; /* Compute distance to the brush. */ const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu); @@ -255,9 +258,11 @@ struct SelectionPaintOperationExecutor { void paint_curve_selection_projected(const float4x4 &brush_transform, MutableSpan<float> selection) { - const Span<float3> positions_cu = curves_->positions(); const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -273,8 +278,8 @@ struct SelectionPaintOperationExecutor { [&](const IndexRange segment_range, const float init) { float max_weight = init; for (const int segment_i : segment_range) { - const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 pos1_cu = brush_transform_inv * deformation.positions[segment_i]; + const float3 pos2_cu = brush_transform_inv * deformation.positions[segment_i + 1]; float2 pos1_re; float2 pos2_re; @@ -322,7 +327,8 @@ struct SelectionPaintOperationExecutor { void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -336,8 +342,8 @@ struct SelectionPaintOperationExecutor { [&](const IndexRange segment_range, const float init) { float max_weight = init; for (const int segment_i : segment_range) { - const float3 &pos1_cu = positions_cu[segment_i]; - const float3 &pos2_cu = positions_cu[segment_i + 1]; + const float3 &pos1_cu = deformation.positions[segment_i]; + const float3 &pos2_cu = deformation.positions[segment_i + 1]; const float distance_sq_cu = dist_squared_to_line_segment_v3( brush_cu, pos1_cu, pos2_cu); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc new file mode 100644 index 00000000000..443fbcb883c --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -0,0 +1,480 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> + +#include "curves_sculpt_intern.hh" + +#include "BLI_float3x3.hh" +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DEG_depsgraph.h" + +#include "BKE_attribute_math.hh" +#include "BKE_brush.h" +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_report.h" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "UI_interface.h" + +#include "WM_api.h" + +#include "DEG_depsgraph_query.h" + +#include "GEO_add_curves_on_mesh.hh" +#include "GEO_reverse_uv_sampler.hh" + +#include "BLT_translation.h" + +namespace blender::ed::sculpt_paint { + +using geometry::ReverseUVSampler; + +struct SlideCurveInfo { + /** Index of the curve to slide. */ + int curve_i; + /** A weight based on the initial distance to the brush. */ + float radius_falloff; + /** + * Normal of the surface where the curve was attached. This is used to rotate the curve if it is + * moved to a place with a different normal. + */ + float3 initial_normal_cu; +}; + +struct SlideInfo { + /** The transform used for the curves below (e.g. for symmetry). */ + float4x4 brush_transform; + Vector<SlideCurveInfo> curves_to_slide; +}; + +class SlideOperation : public CurvesSculptStrokeOperation { + private: + float2 initial_brush_pos_re_; + /** Information about which curves to slide. This is initialized when the brush starts. */ + Vector<SlideInfo> slide_info_; + /** Positions of all curve points at the start of sliding. */ + Array<float3> initial_positions_cu_; + /** Deformed positions of all curve points at the start of sliding. */ + Array<float3> initial_deformed_positions_cu_; + + friend struct SlideOperationExecutor; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct SlideOperationExecutor { + SlideOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + Span<MLoopTri> surface_looptris_orig_; + VArraySpan<float2> surface_uv_map_orig_; + Span<float3> corner_normals_orig_su_; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; + + VArray<float> curve_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + float2 brush_pos_re_; + + CurvesSurfaceTransforms transforms_; + + std::atomic<bool> found_invalid_uv_mapping_{false}; + + SlideOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(SlideOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + UNUSED_VARS(C, stroke_extension); + self_ = &self; + + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); + return; + } + if (curves_orig_->curves_num() == 0) { + return; + } + if (curves_id_orig_->surface_uv_map == nullptr) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + const StringRefNull uv_map_name = curves_id_orig_->surface_uv_map; + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + + curve_factors_ = get_curves_selection(*curves_id_orig_); + curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_); + + brush_pos_re_ = stroke_extension.mouse_position; + + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); + + surface_ob_orig_ = curves_id_orig_->surface; + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + surface_looptris_orig_ = {BKE_mesh_runtime_looptri_ensure(surface_orig_), + BKE_mesh_runtime_looptri_len(surface_orig_)}; + surface_uv_map_orig_ = + bke::mesh_attributes(*surface_orig_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); + if (surface_uv_map_orig_.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_orig_); + } + corner_normals_orig_su_ = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)), + surface_orig_->totloop}; + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + if (surface_eval_ == nullptr) { + return; + } + surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), + BKE_mesh_runtime_looptri_len(surface_eval_)}; + surface_uv_map_eval_ = + bke::mesh_attributes(*surface_eval_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); + + if (stroke_extension.is_first) { + self_->initial_brush_pos_re_ = brush_pos_re_; + /* Remember original and deformed positions of all points. Otherwise this information is lost + * when sliding starts, but it's still used. */ + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + self_->initial_positions_cu_ = curves_orig_->positions(); + self_->initial_deformed_positions_cu_ = deformation.positions; + + /* First find all curves to slide. When the mouse moves, only those curves will be moved. */ + this->find_curves_to_slide_with_symmetry(); + return; + } + this->slide_with_symmetry(); + + if (found_invalid_uv_mapping_) { + BKE_report( + stroke_extension.reports, RPT_WARNING, TIP_("UV map or surface attachment is invalid")); + } + + curves_orig_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); + ED_region_tag_redraw(ctx_.region); + } + + void find_curves_to_slide_with_symmetry() + { + const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re); + if (!brush_3d.has_value()) { + return; + } + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_}; + for (const float4x4 &brush_transform : brush_transforms) { + self_->slide_info_.append_as(); + SlideInfo &slide_info = self_->slide_info_.last(); + slide_info.brush_transform = brush_transform; + this->find_curves_to_slide(brush_transform * brush_3d->position_cu, + brush_3d->radius_cu, + reverse_uv_sampler_orig, + slide_info.curves_to_slide); + } + } + + void find_curves_to_slide(const float3 &brush_pos_cu, + const float brush_radius_cu, + const ReverseUVSampler &reverse_uv_sampler_orig, + Vector<SlideCurveInfo> &r_curves_to_slide) + { + const Span<float2> surface_uv_coords = curves_orig_->surface_uv_coords(); + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const Span<int> offsets = curves_orig_->offsets(); + for (const int curve_i : curve_selection_) { + const int first_point_i = offsets[curve_i]; + const float3 old_pos_cu = self_->initial_deformed_positions_cu_[first_point_i]; + const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + /* Root point is too far away from curve center. */ + continue; + } + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + + const float2 uv = surface_uv_coords[curve_i]; + ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + /* The curve does not have a valid surface attachment. */ + found_invalid_uv_mapping_.store(true); + continue; + } + /* Compute the normal at the initial surface position. */ + const float3 normal_cu = math::normalize( + transforms_.surface_to_curves_normal * + geometry::compute_surface_point_normal( + *result.looptri, result.bary_weights, corner_normals_orig_su_)); + + r_curves_to_slide.append({curve_i, radius_falloff, normal_cu}); + } + } + + void slide_with_symmetry() + { + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_}; + for (const SlideInfo &slide_info : self_->slide_info_) { + this->slide(slide_info.curves_to_slide, reverse_uv_sampler_orig, slide_info.brush_transform); + } + } + + void slide(const Span<SlideCurveInfo> slide_curves, + const ReverseUVSampler &reverse_uv_sampler_orig, + const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + const Span<MVert> verts_orig_su{surface_orig_->mvert, surface_orig_->totvert}; + const Span<MLoop> loops_orig{surface_orig_->mloop, surface_orig_->totloop}; + + MutableSpan<float3> positions_orig_cu = curves_orig_->positions_for_write(); + MutableSpan<float2> surface_uv_coords = curves_orig_->surface_uv_coords_for_write(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); + + const float2 brush_pos_diff_re = brush_pos_re_ - self_->initial_brush_pos_re_; + + /* The brush transformation has to be applied in curves space. */ + const float4x4 world_to_surface_with_symmetry_mat = transforms_.curves_to_surface * + brush_transform * + transforms_.world_to_curves; + + threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) { + for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) { + const int curve_i = slide_curve_info.curve_i; + const IndexRange points = curves_orig_->points_for_curve(curve_i); + const int first_point_i = points[0]; + + const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i]; + const float3 old_first_symm_pos_eval_cu = brush_transform_inv * old_first_pos_eval_cu; + const float3 old_first_pos_eval_su = transforms_.curves_to_surface * old_first_pos_eval_cu; + + float2 old_first_symm_pos_eval_re; + ED_view3d_project_float_v2_m4(ctx_.region, + old_first_symm_pos_eval_cu, + old_first_symm_pos_eval_re, + projection.values); + + const float radius_falloff = slide_curve_info.radius_falloff; + const float curve_weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; + const float2 new_first_symm_pos_eval_re = old_first_symm_pos_eval_re + + curve_weight * brush_pos_diff_re; + + /* Compute the ray that will be used to find the new position on the surface. */ + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped(ctx_.depsgraph, + ctx_.region, + ctx_.v3d, + new_first_symm_pos_eval_re, + ray_start_wo, + ray_end_wo, + true); + const float3 ray_start_su = world_to_surface_with_symmetry_mat * ray_start_wo; + const float3 ray_end_su = world_to_surface_with_symmetry_mat * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + /* Find the ray hit that is closest to the initial curve root position. */ + int looptri_index_eval; + float3 hit_pos_eval_su; + if (!this->find_closest_ray_hit(ray_start_su, + ray_direction_su, + old_first_pos_eval_su, + looptri_index_eval, + hit_pos_eval_su)) { + continue; + } + + /* Compute the uv of the new surface position on the evaluated mesh. */ + const MLoopTri &looptri_eval = surface_looptris_eval_[looptri_index_eval]; + const float3 bary_weights_eval = bke::mesh_surface_sample::compute_bary_coord_in_triangle( + *surface_eval_, looptri_eval, hit_pos_eval_su); + const float2 uv = attribute_math::mix3(bary_weights_eval, + surface_uv_map_eval_[looptri_eval.tri[0]], + surface_uv_map_eval_[looptri_eval.tri[1]], + surface_uv_map_eval_[looptri_eval.tri[2]]); + + /* Try to find the same uv on the original surface. */ + const ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + found_invalid_uv_mapping_.store(true); + continue; + } + const MLoopTri &looptri_orig = *result.looptri; + const float3 &bary_weights_orig = result.bary_weights; + + /* Gather old and new surface normal. */ + const float3 &initial_normal_cu = slide_curve_info.initial_normal_cu; + const float3 new_normal_cu = math::normalize( + transforms_.surface_to_curves_normal * + geometry::compute_surface_point_normal( + *result.looptri, result.bary_weights, corner_normals_orig_su_)); + + /* Gather old and new surface position. */ + const float3 old_first_pos_orig_cu = self_->initial_positions_cu_[first_point_i]; + const float3 new_first_pos_orig_cu = + transforms_.surface_to_curves * + attribute_math::mix3<float3>(bary_weights_orig, + verts_orig_su[loops_orig[looptri_orig.tri[0]].v].co, + verts_orig_su[loops_orig[looptri_orig.tri[1]].v].co, + verts_orig_su[loops_orig[looptri_orig.tri[2]].v].co); + + /* Actually transform curve points. */ + const float4x4 slide_transform = this->get_slide_transform( + old_first_pos_orig_cu, new_first_pos_orig_cu, initial_normal_cu, new_normal_cu); + for (const int point_i : points) { + positions_orig_cu[point_i] = slide_transform * self_->initial_positions_cu_[point_i]; + } + surface_uv_coords[curve_i] = uv; + } + }); + } + + bool find_closest_ray_hit(const float3 &ray_start_su, + const float3 &ray_direction_su, + const float3 &point_su, + int &r_looptri_index, + float3 &r_hit_pos) + { + float best_dist_sq_su = FLT_MAX; + int best_looptri_index_eval; + float3 best_hit_pos_su; + BLI_bvhtree_ray_cast_all_cpp( + *surface_bvh_eval_.tree, + ray_start_su, + ray_direction_su, + 0.0f, + FLT_MAX, + [&](const int looptri_index, const BVHTreeRay &ray, BVHTreeRayHit &hit) { + surface_bvh_eval_.raycast_callback(&surface_bvh_eval_, looptri_index, &ray, &hit); + if (hit.index < 0) { + return; + } + const float3 &hit_pos_su = hit.co; + const float dist_sq_su = math::distance_squared(hit_pos_su, point_su); + if (dist_sq_su < best_dist_sq_su) { + best_dist_sq_su = dist_sq_su; + best_hit_pos_su = hit_pos_su; + best_looptri_index_eval = hit.index; + } + }); + + if (best_dist_sq_su == FLT_MAX) { + return false; + } + r_looptri_index = best_looptri_index_eval; + r_hit_pos = best_hit_pos_su; + return true; + } + + float4x4 get_slide_transform(const float3 &old_root_pos, + const float3 &new_root_pos, + const float3 &old_normal, + const float3 &new_normal) + { + float3x3 rotation_3x3; + rotation_between_vecs_to_mat3(rotation_3x3.values, old_normal, new_normal); + float4x4 rotation_4x4; + copy_m4_m3(rotation_4x4.values, rotation_3x3.values); + + float4x4 transform = float4x4::identity(); + sub_v3_v3(transform.values[3], old_root_pos); + mul_m4_m4_pre(transform.values, rotation_4x4.values); + add_v3_v3(transform.values[3], new_root_pos); + return transform; + } +}; + +void SlideOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) +{ + SlideOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_slide_operation() +{ + return std::make_unique<SlideOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc new file mode 100644 index 00000000000..37a7f1c83ed --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_crazyspace.hh" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" + +#include "DNA_brush_types.h" + +#include "WM_api.h" + +#include "BLI_enumerable_thread_specific.hh" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +class SmoothOperation : public CurvesSculptStrokeOperation { + private: + /** Only used when a 3D brush is used. */ + CurvesBrush3D brush_3d_; + + friend struct SmoothOperationExecutor; + + public: + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +/** + * Utility class that actually executes the update when the stroke is updated. That's useful + * because it avoids passing a very large number of parameters between functions. + */ +struct SmoothOperationExecutor { + SmoothOperation *self_ = nullptr; + CurvesSculptCommonContext ctx_; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + float2 brush_pos_re_; + + CurvesSurfaceTransforms transforms_; + + SmoothOperationExecutor(const bContext &C) : ctx_(C) + { + } + + void execute(SmoothOperation &self, const bContext &C, const StrokeExtension &stroke_extension) + { + UNUSED_VARS(C, stroke_extension); + self_ = &self; + + object_ = CTX_data_active_object(&C); + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } + + curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(ctx_.scene, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); + brush_pos_re_ = stroke_extension.mouse_position; + + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + if (stroke_extension.is_first) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + self.brush_3d_ = *sample_curves_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + *ctx_.rv3d, + *object_, + brush_pos_re_, + brush_radius_base_re_); + } + } + + Array<float> point_smooth_factors(curves_->points_num(), 0.0f); + + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->find_projected_smooth_factors_with_symmetry(point_smooth_factors); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->find_spherical_smooth_factors_with_symmetry(point_smooth_factors); + } + else { + BLI_assert_unreachable(); + } + + this->smooth(point_smooth_factors); + curves_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(ctx_.region); + } + + void find_projected_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_projected_smooth_factors(brush_transform, r_point_smooth_factors); + } + } + + void find_projected_smooth_factors(const float4x4 &brush_transform, + MutableSpan<float> r_point_smooth_factors) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points) { + const float3 &pos_cu = brush_transform_inv * deformation.positions[point_i]; + float2 pos_re; + ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); + const float dist_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_); + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too + * large. */ + const float weight_factor = 0.1f; + const float weight = weight_factor * brush_strength_ * radius_falloff * + point_factors_[point_i]; + math::max_inplace(r_point_smooth_factors[point_i], weight); + } + } + }); + } + + void find_spherical_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + + float3 brush_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_pos_wo); + const float3 brush_pos_cu = transforms_.world_to_curves * brush_pos_wo; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->find_spherical_smooth_factors( + brush_transform * brush_pos_cu, brush_radius_cu, r_point_smooth_factors); + } + } + + void find_spherical_smooth_factors(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<float> r_point_smooth_factors) + { + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + for (const int point_i : points) { + const float3 &pos_cu = deformation.positions[point_i]; + const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + /* Used to make the brush easier to use. Otherwise a strength of 1 would be way too + * large. */ + const float weight_factor = 0.1f; + const float weight = weight_factor * brush_strength_ * radius_falloff * + point_factors_[point_i]; + math::max_inplace(r_point_smooth_factors[point_i], weight); + } + } + }); + } + + void smooth(const Span<float> point_smooth_factors) + { + MutableSpan<float3> positions = curves_->positions_for_write(); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + Vector<float3> old_positions; + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + old_positions.clear(); + old_positions.extend(positions.slice(points)); + for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { + const int point_i = points[i]; + const float smooth_factor = point_smooth_factors[point_i]; + if (smooth_factor == 0.0f) { + continue; + } + /* Move towards the middle of the neighboring points. */ + const float3 old_pos = old_positions[i]; + const float3 &prev_pos = old_positions[i - 1]; + const float3 &next_pos = old_positions[i + 1]; + const float3 goal_pos = math::midpoint(prev_pos, next_pos); + const float3 new_pos = math::interpolate(old_pos, goal_pos, smooth_factor); + positions[point_i] = new_pos; + } + } + }); + } +}; + +void SmoothOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + SmoothOperationExecutor executor{C}; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_smooth_operation() +{ + return std::make_unique<SmoothOperation>(); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index b63e5a7756b..54b81fa221d 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -89,7 +89,7 @@ struct SnakeHookOperatorExecutor { Vector<int64_t> selected_curve_indices_; IndexMask curve_selection_; - CurvesSculptTransforms transforms_; + CurvesSurfaceTransforms transforms_; float2 brush_pos_prev_re_; float2 brush_pos_re_; @@ -123,7 +123,7 @@ struct SnakeHookOperatorExecutor { return; } - transforms_ = CurvesSculptTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); curve_factors_ = get_curves_selection(*curves_id_); curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); @@ -176,6 +176,8 @@ struct SnakeHookOperatorExecutor { void projected_snake_hook(const float4x4 &brush_transform) { const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); MutableSpan<float3> positions_cu = curves_->positions_for_write(); @@ -189,12 +191,14 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; + const float3 old_pos_cu = deformation.positions[last_point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); - const float distance_to_brush_sq_re = math::distance_squared(old_pos_re, + const float distance_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_prev_re_); if (distance_to_brush_sq_re > brush_radius_sq_re) { continue; @@ -204,17 +208,21 @@ struct SnakeHookOperatorExecutor { brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re); const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; - const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; - float3 new_position_wo; + const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight; + float3 new_symm_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - transforms_.curves_to_world * old_pos_cu, - new_position_re, - new_position_wo); - const float3 new_position_cu = brush_transform * - (transforms_.world_to_curves * new_position_wo); - - move_last_point_and_resample(positions_cu.slice(points), new_position_cu); + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + const float3 new_pos_cu = brush_transform * + (transforms_.world_to_curves * new_symm_pos_wo); + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + last_point_i, translation_eval); + + move_last_point_and_resample(positions_cu.slice(points), + positions_cu[last_point_i] + translation_orig); } }); } @@ -252,6 +260,9 @@ struct SnakeHookOperatorExecutor { const float3 &brush_end_cu, const float brush_radius_cu) { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -260,7 +271,7 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = positions_cu[last_point_i]; + const float3 old_pos_cu = deformation.positions[last_point_i]; const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( old_pos_cu, brush_start_cu, brush_end_cu); @@ -274,9 +285,12 @@ struct SnakeHookOperatorExecutor { brush_, distance_to_brush_cu, brush_radius_cu); const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; - const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu; + const float3 translation_eval = weight * brush_diff_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + last_point_i, translation_eval); - move_last_point_and_resample(positions_cu.slice(points), new_pos_cu); + move_last_point_and_resample(positions_cu.slice(points), + positions_cu[last_point_i] + translation_orig); } }); } diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc index 56cd4751881..24290fed323 100644 --- a/source/blender/editors/sculpt_paint/paint_image.cc +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -125,7 +125,7 @@ void ED_imapaint_dirty_region( imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { @@ -276,10 +276,11 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) (ID_IS_LINKED(sima->image) || ID_IS_OVERRIDE_LIBRARY(sima->image))) { return false; } - ARegion *region = CTX_wm_region(C); - - if ((sima->mode == SI_MODE_PAINT) && region->regiontype == RGN_TYPE_WINDOW) { - return true; + if (sima->mode == SI_MODE_PAINT) { + const ARegion *region = CTX_wm_region(C); + if (region->regiontype == RGN_TYPE_WINDOW) { + return true; + } } } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index fae2e6863fa..5df65e596b9 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1196,7 +1196,7 @@ static void paint_2d_do_making_brush(ImagePaintState *s, ImBuf tmpbuf; IMB_initImBuf(&tmpbuf, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, 0); - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); for (int ty = tiley; ty <= tileh; ty++) { for (int tx = tilex; tx <= tilew; tx++) { diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 50480b8aef0..9449cc6eb8d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1813,7 +1813,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) } if (generate_tile) { - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + struct PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); volatile void *undorect; if (tinf->masked) { undorect = ED_image_paint_tile_push(undo_tiles, diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index ea17114efa5..99c25953d50 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -46,7 +46,10 @@ typedef struct CoNo { /* paint_stroke.c */ -typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); +typedef bool (*StrokeGetLocation)(struct bContext *C, + float location[3], + const float mouse[2], + bool force_original); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, struct wmOperator *op, @@ -370,10 +373,12 @@ void PAINT_OT_face_select_linked(struct wmOperatorType *ot); void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot); void PAINT_OT_face_select_all(struct wmOperatorType *ot); void PAINT_OT_face_select_hide(struct wmOperatorType *ot); -void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); + +void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); +void PAINT_OT_vert_select_hide(struct wmOperatorType *ot); bool vert_paint_poll(struct bContext *C); bool mask_paint_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index ce6b397af15..994ae4011b4 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1454,6 +1454,7 @@ void ED_operatortypes_paint(void) /* vertex selection */ WM_operatortype_append(PAINT_OT_vert_select_all); WM_operatortype_append(PAINT_OT_vert_select_ungrouped); + WM_operatortype_append(PAINT_OT_vert_select_hide); /* vertex */ WM_operatortype_append(PAINT_OT_vertex_paint_toggle); @@ -1472,7 +1473,8 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_linked_pick); WM_operatortype_append(PAINT_OT_face_select_all); WM_operatortype_append(PAINT_OT_face_select_hide); - WM_operatortype_append(PAINT_OT_face_select_reveal); + + WM_operatortype_append(PAINT_OT_face_vert_reveal); /* partial visibility */ WM_operatortype_append(PAINT_OT_hide_show); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 63e6dc7e965..97fa2b936e0 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -79,6 +79,8 @@ typedef struct PaintStroke { float last_mouse_position[2]; float last_world_space_position[3]; + float last_scene_spacing_delta[3]; + bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; @@ -120,6 +122,8 @@ typedef struct PaintStroke { StrokeUpdateStep update_step; StrokeRedraw redraw; StrokeDone done; + + bool original; /* Ray-cast original mesh at start of stroke. */ } PaintStroke; /*** Cursors ***/ @@ -243,6 +247,11 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) return false; } +static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode)) +{ + return brush->flag & BRUSH_ANCHORED; +} + static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) { if (brush->flag & BRUSH_ANCHORED) { @@ -392,7 +401,7 @@ static bool paint_brush_update(bContext *C, halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; if (stroke->get_location) { - if (stroke->get_location(C, r_location, halfway)) { + if (stroke->get_location(C, r_location, halfway, stroke->original)) { hit = true; location_sampled = true; location_success = true; @@ -466,7 +475,7 @@ static bool paint_brush_update(bContext *C, if (!location_sampled) { if (stroke->get_location) { - if (stroke->get_location(C, r_location, mouse)) { + if (stroke->get_location(C, r_location, mouse, stroke->original)) { location_success = true; *r_location_is_set = true; } @@ -550,8 +559,16 @@ static void paint_brush_stroke_add_step( stroke->last_pressure = pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { - SCULPT_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + float world_space_position[3]; + + if (SCULPT_stroke_get_location( + C, world_space_position, stroke->last_mouse_position, stroke->original)) { + copy_v3_v3(stroke->last_world_space_position, world_space_position); + mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + } + else { + add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta); + } } if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { @@ -698,7 +715,7 @@ static float paint_space_stroke_spacing(bContext *C, spacing *= stroke->zoom_2d; if (paint_stroke_use_scene_spacing(brush, mode)) { - return max_ff(0.001f, size_clamp * spacing / 50.0f); + return size_clamp * spacing / 50.0f; } return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f); } @@ -807,7 +824,7 @@ static int paint_space_stroke(bContext *C, if (use_scene_spacing) { float world_space_position[3]; - bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse); + bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position); if (hit && stroke->stroke_over_mesh) { sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position); @@ -838,6 +855,8 @@ static int paint_space_stroke(bContext *C, stroke->last_world_space_position, final_world_space_position); ED_view3d_project_v2(region, final_world_space_position, mouse); + + mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -896,6 +915,8 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->ups = ups; stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); + stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C)); + get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); @@ -995,7 +1016,7 @@ static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptTool tool) { - return ELEM(tool, CURVES_SCULPT_TOOL_ADD); + return ELEM(tool, CURVES_SCULPT_TOOL_ADD, CURVES_SCULPT_TOOL_DENSITY); } bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) @@ -1191,8 +1212,10 @@ static void paint_line_strokes_spacing(bContext *C, copy_v2_v2(stroke->last_mouse_position, old_pos); if (use_scene_spacing) { - bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos); - bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos); + bool hit_old = SCULPT_stroke_get_location( + C, world_space_position_old, old_pos, stroke->original); + bool hit_new = SCULPT_stroke_get_location( + C, world_space_position_new, new_pos, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old); mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new); if (hit_old && hit_new && stroke->stroke_over_mesh) { @@ -1336,7 +1359,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( - C, stroke->last_world_space_position, data + 2 * j); + C, stroke->last_world_space_position, data + 2 * j, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } @@ -1468,7 +1491,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); if (paint_stroke_use_scene_spacing(br, mode)) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( - C, stroke->last_world_space_position, sample_average.mouse); + C, stroke->last_world_space_position, sample_average.mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); @@ -1527,8 +1550,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS copy_v2_fl2(mouse, event->mval[0], event->mval[1]); paint_stroke_line_constrain(stroke, mouse); - if (stroke->stroke_started && - (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) { + if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) { if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); @@ -1538,7 +1560,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS } else if (first_modal || /* regular dabs */ - (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || + (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) || /* airbrush */ ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) { diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 3f26f590b70..1f272882100 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -749,25 +749,68 @@ void PAINT_OT_face_select_hide(wmOperatorType *ot) ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); } -static int face_select_reveal_exec(bContext *C, wmOperator *op) +static int vert_select_hide_exec(bContext *C, wmOperator *op) +{ + const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + Object *ob = CTX_data_active_object(C); + paintvert_hide(C, ob, unselected); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; +} + +void PAINT_OT_vert_select_hide(wmOperatorType *ot) +{ + ot->name = "Vertex Select Hide"; + ot->description = "Hide selected vertices"; + ot->idname = "PAINT_OT_vert_select_hide"; + + ot->exec = vert_select_hide_exec; + ot->poll = vert_paint_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected vertices"); +} + +static int face_vert_reveal_exec(bContext *C, wmOperator *op) { const bool select = RNA_boolean_get(op->ptr, "select"); Object *ob = CTX_data_active_object(C); - paintface_reveal(C, ob, select); + + if (BKE_paint_select_vert_test(ob)) { + paintvert_reveal(C, ob, select); + } + else { + paintface_reveal(C, ob, select); + } + ED_region_tag_redraw(CTX_wm_region(C)); return OPERATOR_FINISHED; } -void PAINT_OT_face_select_reveal(wmOperatorType *ot) +static bool face_vert_reveal_poll(bContext *C) { - ot->name = "Face Select Reveal"; - ot->description = "Reveal hidden faces"; - ot->idname = "PAINT_OT_face_select_reveal"; + Object *ob = CTX_data_active_object(C); - ot->exec = face_select_reveal_exec; - ot->poll = facemask_paint_poll; + /* Allow using this operator when no selection is enabled but hiding is applied. */ + return BKE_paint_select_elem_test(ob) || BKE_paint_always_hide_test(ob); +} + +void PAINT_OT_face_vert_reveal(wmOperatorType *ot) +{ + ot->name = "Reveal Faces/Vertices"; + ot->description = "Reveal hidden faces and vertices"; + ot->idname = "PAINT_OT_face_vert_reveal"; + + ot->exec = face_vert_reveal_exec; + ot->poll = face_vert_reveal_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, + "select", + true, + "Select", + "Specifies whether the newly revealed geometry should be selected"); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index c93b0fc3aa8..6dc8375bb0d 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -1176,12 +1176,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph, BLI_assert(ob->sculpt == nullptr); ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), "sculpt session"); ob->sculpt->mode_type = object_mode; - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); } static void vwpaint_init_stroke(Depsgraph *depsgraph, Object *ob) { - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); SculptSession *ss = ob->sculpt; /* Ensure ss->cache is allocated. It will mostly be initialized in diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc index 9a3f2b9b708..8b726c7b942 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc @@ -92,11 +92,9 @@ static bool vertex_paint_from_weight(Object *ob) return false; } - MeshComponent component; - component.replace(me, GeometryOwnershipType::Editable); + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); - bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write( - active_color_layer->name); + bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name); if (!color_attribute) { BLI_assert_unreachable(); return false; @@ -104,7 +102,7 @@ static bool vertex_paint_from_weight(Object *ob) /* Retrieve the vertex group with the domain and type of the existing color * attribute, in order to let the attribute API handle both conversions. */ - const GVArray vertex_group = component.attribute_get_for_read( + const GVArray vertex_group = attributes.lookup( deform_group->name, ATTR_DOMAIN_POINT, bke::cpp_type_to_custom_data_type(color_attribute.varray.type())); @@ -113,14 +111,11 @@ static bool vertex_paint_from_weight(Object *ob) return false; } - GVArray_GSpan interpolated{component.attribute_try_adapt_domain( - vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)}; + GVArraySpan interpolated{ + attributes.adapt_domain(vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)}; color_attribute.varray.set_all(interpolated.data()); - - if (color_attribute.tag_modified_fn) { - color_attribute.tag_modified_fn(); - } + color_attribute.finish(); tag_object_after_update(ob); return true; @@ -167,29 +162,28 @@ static IndexMask get_selected_indices(const Mesh &mesh, Span<MVert> verts(mesh.mvert, mesh.totvert); Span<MPoly> faces(mesh.mpoly, mesh.totpoly); - MeshComponent component; - component.replace(&const_cast<Mesh &>(mesh), GeometryOwnershipType::ReadOnly); + bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) { - const VArray<bool> selection = component.attribute_try_adapt_domain( + const VArray<bool> selection = attributes.adapt_domain( VArray<bool>::ForFunc(faces.size(), [&](const int i) { return faces[i].flag & ME_FACE_SEL; }), ATTR_DOMAIN_FACE, domain); return index_mask_ops::find_indices_from_virtual_array( - IndexMask(component.attribute_domain_num(domain)), selection, 4096, indices); + IndexMask(attributes.domain_size(domain)), selection, 4096, indices); } if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) { - const VArray<bool> selection = component.attribute_try_adapt_domain( + const VArray<bool> selection = attributes.adapt_domain( VArray<bool>::ForFunc(verts.size(), [&](const int i) { return verts[i].flag & SELECT; }), ATTR_DOMAIN_POINT, domain); return index_mask_ops::find_indices_from_virtual_array( - IndexMask(component.attribute_domain_num(domain)), selection, 4096, indices); + IndexMask(attributes.domain_size(domain)), selection, 4096, indices); } - return IndexMask(component.attribute_domain_num(domain)); + return IndexMask(attributes.domain_size(domain)); } static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask selection) @@ -202,17 +196,15 @@ static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask sele return; } - MeshComponent component; - component.replace(&mesh, GeometryOwnershipType::Editable); + bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); - if (component.attribute_get_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) { + if (attributes.lookup_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) { return; } - GVArray color_attribute_point = component.attribute_try_get_for_read(active_color_layer->name, - ATTR_DOMAIN_POINT); + GVArray color_attribute_point = attributes.lookup(active_color_layer->name, ATTR_DOMAIN_POINT); - GVArray color_attribute_corner = component.attribute_try_adapt_domain( + GVArray color_attribute_corner = attributes.adapt_domain( color_attribute_point, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER); color_attribute_corner.materialize(selection, active_color_layer->data); @@ -278,11 +270,9 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn) return false; } - MeshComponent component; - component.replace(&mesh, GeometryOwnershipType::Editable); + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); - bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write( - active_color_layer->name); + bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name); if (!color_attribute) { BLI_assert_unreachable(); return false; @@ -310,6 +300,8 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn) }); }); + color_attribute.finish(); + DEG_id_tag_update(&mesh.id, 0); return true; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index cbf7701efe9..7cb370efb94 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3219,7 +3219,7 @@ static void do_brush_action_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); BKE_pbvh_node_mark_update_mask(data->nodes[n]); } - else if (SCULPT_TOOL_NEEDS_COLOR(data->brush->sculpt_tool)) { + else if (SCULPT_tool_is_paint(data->brush->sculpt_tool)) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); BKE_pbvh_node_mark_update_color(data->nodes[n]); } @@ -3246,7 +3246,7 @@ static void do_brush_action(Sculpt *sd, /* Check for unsupported features. */ PBVHType type = BKE_pbvh_type(ss->pbvh); - if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { + if (SCULPT_tool_is_paint(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { if (type != PBVH_FACES) { return; } @@ -3362,7 +3362,7 @@ static void do_brush_action(Sculpt *sd, if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { if (!ss->cache->cloth_sim) { ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( - ss, 1.0f, 0.0f, 0.0f, false, true); + ob, 1.0f, 0.0f, 0.0f, false, true); SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim); } SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim); @@ -4693,8 +4693,8 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, (brush->sculpt_tool == SCULPT_TOOL_POSE) || (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) || (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) || - SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) || - (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || + SCULPT_tool_is_paint(brush->sculpt_tool) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || + (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) || (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_PAINT)); @@ -4710,7 +4710,8 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b if (ss->shapekey_active || ss->deform_modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false); + BKE_sculpt_update_object_for_edit( + depsgraph, ob, need_pmap, false, SCULPT_tool_is_paint(brush->sculpt_tool)); } } @@ -4951,7 +4952,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, return true; } -bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) +bool SCULPT_stroke_get_location(bContext *C, + float out[3], + const float mval[2], + bool force_original) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; @@ -4967,7 +4971,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) ss = ob->sculpt; cache = ss->cache; - original = (cache) ? cache->original : false; + original = force_original || ((cache) ? cache->original : false); const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); @@ -5049,7 +5053,8 @@ static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss) static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ToolSettings *tool_settings = CTX_data_tool_settings(C); + Sculpt *sd = tool_settings->sculpt; SculptSession *ss = CTX_data_active_object(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); int mode = RNA_enum_get(op->ptr, "mode"); @@ -5069,7 +5074,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) sculpt_brush_init_tex(sd, ss); need_pmap = sculpt_needs_connectivity_info(sd, brush, ss, mode); - needs_colors = SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool); + needs_colors = SCULPT_tool_is_paint(brush->sculpt_tool) && + !SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob); if (needs_colors) { BKE_sculpt_color_layer_create_if_needed(ob); @@ -5078,7 +5084,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, need_mask, needs_colors); + BKE_sculpt_update_object_for_edit( + depsgraph, ob, need_pmap, need_mask, SCULPT_tool_is_paint(brush->sculpt_tool)); ED_paint_tool_update_sticky_shading_color(C, ob); } @@ -5280,7 +5287,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) { float co_dummy[3]; - return SCULPT_stroke_get_location(C, co_dummy, mval); + return SCULPT_stroke_get_location(C, co_dummy, mval, false); } bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) @@ -5316,7 +5323,8 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the * canvas it is painting on. (ref. use_sculpt_texture_paint). */ - if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { + if (brush && SCULPT_tool_is_paint(brush->sculpt_tool) && + !SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { View3D *v3d = CTX_wm_view3d(C); if (v3d->shading.type == OB_SOLID) { v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; @@ -5408,7 +5416,7 @@ static void sculpt_stroke_update_step(bContext *C, if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); } - else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { + else if (SCULPT_tool_is_paint(brush->sculpt_tool)) { if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { SCULPT_flush_update_step(C, SCULPT_UPDATE_IMAGE); } @@ -5494,14 +5502,27 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent struct PaintStroke *stroke; int ignore_background_click; int retval; + Object *ob = CTX_data_active_object(C); + + /* Test that ob is visible; otherwise we won't be able to get evaluated data + * from the depsgraph. We do this here instead of SCULPT_mode_poll + * to avoid falling through to the translate operator in the + * global view3d keymap. + * + * Note: BKE_object_is_visible_in_viewport is not working here (it returns false + * if the object is in local view); instead, test for OB_HIDE_VIEWPORT directly. + */ + + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { + return OPERATOR_CANCELLED; + } sculpt_brush_stroke_init(C, op); - Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && + if (SCULPT_tool_is_paint(brush->sculpt_tool) && !SCULPT_handles_colors_report(ob->sculpt, op->reports)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 9d231f2ccd2..bf3e8a24fdf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -611,13 +611,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } -static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph) +static ListBase *cloth_brush_collider_cache_create(Object *object, Depsgraph *depsgraph) { ListBase *cache = NULL; DEG_OBJECT_ITER_BEGIN (depsgraph, ob, DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | DEG_ITER_OBJECT_FLAG_DUPLI) { + if (STREQ(object->id.name, ob->id.name)) { + continue; + } + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( ob, eModifierType_Collision); if (!cmd) { @@ -1029,13 +1033,14 @@ static void cloth_sim_initialize_default_node_state(SculptSession *ss, MEM_SAFE_FREE(nodes); } -SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, +SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob, const float cloth_mass, const float cloth_damping, const float cloth_softbody_strength, const bool use_collisions, const bool needs_deform_coords) { + SculptSession *ss = ob->sculpt; const int totverts = SCULPT_vertex_count_get(ss); SculptClothSimulation *cloth_sim; @@ -1073,7 +1078,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, cloth_sim->softbody_strength = cloth_softbody_strength; if (use_collisions) { - cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph); + cloth_sim->collider_list = cloth_brush_collider_cache_create(ob, ss->depsgraph); } cloth_sim_initialize_default_node_state(ss, cloth_sim); @@ -1184,7 +1189,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* The simulation structure only needs to be created on the first symmetry pass. */ if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) { ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( - ss, + ob, brush->cloth_mass, brush->cloth_damping, brush->cloth_constraint_softbody_strength, @@ -1571,7 +1576,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions"); ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create( - ss, + ob, cloth_mass, cloth_damping, 0.0f, diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index a5d9f5306e2..a9186010a9f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -93,7 +93,7 @@ static void color_filter_task_cb(void *__restrict userdata, const int mode = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 2c4f2f7370a..179e07cc7fa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -845,7 +845,10 @@ void SCULPT_tag_update_overlays(bContext *C); * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise. */ -bool SCULPT_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]); +bool SCULPT_stroke_get_location(struct bContext *C, + float out[3], + const float mouse[2], + bool force_original); /** * Gets the normal, location and active vertex location of the geometry under the cursor. This also * updates the active vertex and cursor related data of the SculptSession using the mouse position @@ -1341,7 +1344,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim); /* Public functions. */ -struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss, +struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Object *ob, float cloth_mass, float cloth_damping, float cloth_softbody_strength, @@ -1805,7 +1808,10 @@ void SCULPT_OT_brush_stroke(struct wmOperatorType *ot); /* end sculpt_ops.c */ -#define SCULPT_TOOL_NEEDS_COLOR(tool) ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) +BLI_INLINE bool SCULPT_tool_is_paint(int tool) +{ + return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR); +} #ifdef __cplusplus } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 2ac0dbdb9d4..7e813590e21 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -427,7 +427,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, madd_v3_v3fl(current_disp, no, -dot_v3v3(current_disp, no)); normalize_v3_v3(current_disp_norm, current_disp); - mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); + mul_v3_v3fl(current_disp, current_disp_norm, bstrength); float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float totw = 0.0f; @@ -457,12 +457,12 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); /* Weight by how close we are to our target distance from vd.co. */ - float w = (1.0f + fabsf(len_v3(vertex_disp) / ss->cache->bstrength - 1.0f)); + float w = (1.0f + fabsf(len_v3(vertex_disp) / bstrength - 1.0f)); /* TODO: use cotangents (or at least face areas) here. */ float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco); if (len > 0.0f) { - len = ss->cache->bstrength / len; + len = bstrength / len; } else { /* Coincident point. */ len = 1.0f; @@ -532,7 +532,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - if (!SCULPT_has_colors(ss)) { + if (!SCULPT_has_colors(ss) || ss->cache->bstrength == 0.0f) { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 975a8f21aaf..f51a603ee5d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -383,7 +383,7 @@ static void push_undo(const NodeData &node_data, continue; } int tilex, tiley, tilew, tileh; - ListBase *undo_tiles = ED_image_paint_tile_list_get(); + PaintTileMap *undo_tiles = ED_image_paint_tile_map_get(); undo_region_tiles(&image_buffer, tile_undo.region.xmin, tile_undo.region.ymin, diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 365000ab163..8856e3bf3db 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -426,7 +426,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"), }; - if (SCULPT_stroke_get_location(C, stroke_location, mval)) { + if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) { copy_v3_v3(ss->pivot_pos, stroke_location); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 8e1f4f4d495..dfa85e8e56d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -211,7 +211,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, } } - MEM_freeN(tmp_uvdata); + MEM_SAFE_FREE(tmp_uvdata); } static void laplacian_relaxation_iteration_uv(BMEditMesh *em, @@ -240,7 +240,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); } - /* Original Lacplacian algorithm included removal of normal component of translation. + /* Original Laplacian algorithm included removal of normal component of translation. * here it is not needed since we translate along the UV plane always. */ for (i = 0; i < sculptdata->totalUniqueUvs; i++) { copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co); @@ -283,7 +283,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, } } - MEM_freeN(tmp_uvdata); + MEM_SAFE_FREE(tmp_uvdata); } static void uv_sculpt_stroke_apply(bContext *C, @@ -417,20 +417,14 @@ static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) if (data->elementMap) { BM_uv_element_map_free(data->elementMap); } - if (data->uv) { - MEM_freeN(data->uv); - } - if (data->uvedges) { - MEM_freeN(data->uvedges); - } + MEM_SAFE_FREE(data->uv); + MEM_SAFE_FREE(data->uvedges); if (data->initial_stroke) { - if (data->initial_stroke->initialSelection) { - MEM_freeN(data->initial_stroke->initialSelection); - } - MEM_freeN(data->initial_stroke); + MEM_SAFE_FREE(data->initial_stroke->initialSelection); + MEM_SAFE_FREE(data->initial_stroke); } - MEM_freeN(data); + MEM_SAFE_FREE(data); op->customdata = NULL; } @@ -489,7 +483,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); int island_index = 0; - /* Holds, for each UvElement in elementMap, a pointer to its unique UV. */ + /* Holds, for each UvElement in elementMap, an index of its unique UV. */ int *uniqueUv; data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ? UV_SCULPT_TOOL_RELAX : @@ -500,20 +494,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm if (do_island_optimization) { /* We will need island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true); - } + data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true); } else { - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false); - } + data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false); } if (!data->elementMap) { @@ -550,12 +534,8 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm /* we have at most totalUVs edges */ edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges"); if (!data->uv || !uniqueUv || !edgeHash || !edges) { - if (edges) { - MEM_freeN(edges); - } - if (uniqueUv) { - MEM_freeN(uniqueUv); - } + MEM_SAFE_FREE(edges); + MEM_SAFE_FREE(uniqueUv); if (edgeHash) { BLI_ghash_free(edgeHash, NULL, NULL); } @@ -635,14 +615,14 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } } - MEM_freeN(uniqueUv); + MEM_SAFE_FREE(uniqueUv); /* Allocate connectivity data, we allocate edges once */ data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash), "uv_brush_edge_connectivity_data"); if (!data->uvedges) { BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); + MEM_SAFE_FREE(edges); uv_sculpt_stroke_exit(C, op); return NULL; } @@ -656,7 +636,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm /* cleanup temporary stuff */ BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); + MEM_SAFE_FREE(edges); /* transfer boundary edge property to UV's */ if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 2a8a2be8b65..08b795db0c3 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -340,7 +340,8 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) AUD_DeviceSpecs specs; AUD_Container container; AUD_Codec codec; - const char *result; + int result; + char error_message[1024] = {'\0'}; sound_bake_animation_exec(C, op); @@ -372,7 +373,9 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) codec, bitrate, NULL, - NULL); + NULL, + error_message, + sizeof(error_message)); } else { result = AUD_mixdown(scene_eval->sound_scene, @@ -385,13 +388,15 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) codec, bitrate, NULL, - NULL); + NULL, + error_message, + sizeof(error_message)); } BKE_sound_reset_scene_specs(scene_eval); - if (result) { - BKE_report(op->reports, RPT_ERROR, result); + if (!result) { + BKE_report(op->reports, RPT_ERROR, error_message); return OPERATOR_CANCELLED; } #else /* WITH_AUDASPACE */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 87a271543d1..23c92cbdaa0 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -158,8 +158,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const /* get data to filter, from Action or Dopesheet */ /* XXX: what is sel doing here?! * Commented it, was breaking things (eg. the "auto preview range" tool). */ - filter = (ANIMFILTER_DATA_VISIBLE | - ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_SEL */ /*| ANIMFILTER_CURVESONLY*/ | + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_SEL */ | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -178,10 +177,12 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const /* Find gp-frame which is less than or equal to current-frame. */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - const float framenum = (float)gpf->framenum; - *min = min_ff(*min, framenum); - *max = max_ff(*max, framenum); - found = true; + if (!onlySel || (gpf->flag & GP_FRAME_SELECT)) { + const float framenum = (float)gpf->framenum; + *min = min_ff(*min, framenum); + *max = max_ff(*max, framenum); + found = true; + } } } else if (ale->datatype == ALE_MASKLAY) { @@ -498,7 +499,7 @@ static short copy_action_keys(bAnimContext *ac) ANIM_fcurves_copybuf_free(); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -511,13 +512,13 @@ static short copy_action_keys(bAnimContext *ac) return ok; } -static short paste_action_keys(bAnimContext *ac, - const eKeyPasteOffset offset_mode, - const eKeyMergeMode merge_mode, - bool flip) +static eKeyPasteError paste_action_keys(bAnimContext *ac, + const eKeyPasteOffset offset_mode, + const eKeyMergeMode merge_mode, + bool flip) { ListBase anim_data = {NULL, NULL}; - int filter, ok = 0; + int filter; /* filter data * - First time we try to filter more strictly, allowing only selected channels @@ -525,15 +526,15 @@ static short paste_action_keys(bAnimContext *ac, * - Second time, we loosen things up if nothing was found the first time, allowing * users to just paste keyframes back into the original curve again T31670. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) { ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); } /* paste keyframes */ - ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); + const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); /* clean up */ ANIM_animdata_freelist(&anim_data); @@ -555,7 +556,8 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op) /* copy keyframes */ if (ac.datatype == ANIMCONT_GPENCIL) { if (ED_gpencil_anim_copybuf_copy(&ac) == false) { - /* Nothing got copied - An error about this should be been logged already */ + /* check if anything ended up in the buffer */ + BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); return OPERATOR_CANCELLED; } } @@ -565,7 +567,11 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else { - if (copy_action_keys(&ac)) { + /* Both copy function needs to be evaluated to account for mixed selection */ + const short kf_empty = copy_action_keys(&ac); + const bool gpf_ok = ED_gpencil_anim_copybuf_copy(&ac); + + if (kf_empty && !gpf_ok) { BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); return OPERATOR_CANCELLED; } @@ -597,6 +603,8 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge"); const bool flipped = RNA_boolean_get(op->ptr, "flipped"); + bool gpframes_inbuf = false; + /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) { return OPERATOR_CANCELLED; @@ -608,7 +616,7 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) /* paste keyframes */ if (ac.datatype == ANIMCONT_GPENCIL) { if (ED_gpencil_anim_copybuf_paste(&ac, offset_mode) == false) { - /* An error occurred - Reports should have been fired already */ + BKE_report(op->reports, RPT_ERROR, "No data in buffer to paste"); return OPERATOR_CANCELLED; } } @@ -620,14 +628,31 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else { + /* Both paste function needs to be evaluated to account for mixed selection */ + const eKeyPasteError kf_empty = paste_action_keys(&ac, offset_mode, merge_mode, flipped); /* non-zero return means an error occurred while trying to paste */ - if (paste_action_keys(&ac, offset_mode, merge_mode, flipped)) { - return OPERATOR_CANCELLED; + gpframes_inbuf = ED_gpencil_anim_copybuf_paste(&ac, offset_mode); + + /* Only report an error if nothing was pasted, i.e. when both FCurve and GPencil failed. */ + if (!gpframes_inbuf) { + switch (kf_empty) { + case KEYFRAME_PASTE_OK: + /* FCurve paste was ok, so it's all good. */ + break; + + case KEYFRAME_PASTE_NOWHERE_TO_PASTE: + BKE_report(op->reports, RPT_ERROR, "No selected F-Curves to paste into"); + return OPERATOR_CANCELLED; + + case KEYFRAME_PASTE_NOTHING_TO_PASTE: + BKE_report(op->reports, RPT_ERROR, "No data in buffer to paste"); + return OPERATOR_CANCELLED; + } } } /* Grease Pencil needs extra update to refresh the added keyframes. */ - if (ac.datatype == ANIMCONT_GPENCIL) { + if (ac.datatype == ANIMCONT_GPENCIL || gpframes_inbuf) { WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); } /* set notifier that keyframes have changed */ @@ -697,94 +722,86 @@ static const EnumPropertyItem prop_actkeys_insertkey_types[] = { {0, NULL, 0, NULL, NULL}, }; -/* this function is responsible for inserting new keyframes */ -static void insert_action_keys(bAnimContext *ac, short mode) +static void insert_gpencil_key(bAnimContext *ac, + bAnimListElem *ale, + const eGP_GetFrame_Mode add_frame_mode, + bGPdata **gpd_old) { - ListBase anim_data = {NULL, NULL}; - ListBase nla_cache = {NULL, NULL}; - bAnimListElem *ale; - int filter; + Scene *scene = ac->scene; + bGPdata *gpd = (bGPdata *)ale->id; + bGPDlayer *gpl = (bGPDlayer *)ale->data; + BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, add_frame_mode); + /* Check if the gpd changes to tag only once. */ + if (gpd != *gpd_old) { + BKE_gpencil_tag(gpd); + *gpd_old = gpd; + } +} + +static void insert_fcurve_key(bAnimContext *ac, + bAnimListElem *ale, + const AnimationEvalContext anim_eval_context, + eInsertKeyFlags flag, + ListBase *nla_cache) +{ + FCurve *fcu = (FCurve *)ale->key_data; ReportList *reports = ac->reports; Scene *scene = ac->scene; ToolSettings *ts = scene->toolsettings; - eInsertKeyFlags flag; - /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); - if (mode == 2) { - filter |= ANIMFILTER_SEL; - } - else if (mode == 3) { - filter |= ANIMFILTER_ACTGROUPED; + /* Read value from property the F-Curve represents, or from the curve only? + * - ale->id != NULL: + * Typically, this means that we have enough info to try resolving the path. + * + * - ale->owner != NULL: + * If this is set, then the path may not be resolvable from the ID alone, + * so it's easier for now to just read the F-Curve directly. + * (TODO: add the full-blown PointerRNA relative parsing case here...) + */ + if (ale->id && !ale->owner) { + insert_keyframe(ac->bmain, + reports, + ale->id, + NULL, + ((fcu->grp) ? (fcu->grp->name) : (NULL)), + fcu->rna_path, + fcu->array_index, + &anim_eval_context, + ts->keyframe_type, + nla_cache, + flag); } + else { + AnimData *adt = ANIM_nla_mapping_get(ac, ale); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* Init keyframing flag. */ - flag = ANIM_get_keyframing_flags(scene, true); - - /* insert keyframes */ - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(ac->depsgraph, - (float)CFRA); - for (ale = anim_data.first; ale; ale = ale->next) { - FCurve *fcu = (FCurve *)ale->key_data; - - /* Read value from property the F-Curve represents, or from the curve only? - * - ale->id != NULL: - * Typically, this means that we have enough info to try resolving the path. - * - * - ale->owner != NULL: - * If this is set, then the path may not be resolvable from the ID alone, - * so it's easier for now to just read the F-Curve directly. - * (TODO: add the full-blown PointerRNA relative parsing case here...) - */ - if (ale->id && !ale->owner) { - insert_keyframe(ac->bmain, - reports, - ale->id, - NULL, - ((fcu->grp) ? (fcu->grp->name) : (NULL)), - fcu->rna_path, - fcu->array_index, - &anim_eval_context, - ts->keyframe_type, - &nla_cache, - flag); - } - else { - AnimData *adt = ANIM_nla_mapping_get(ac, ale); - - /* adjust current frame for NLA-scaling */ - float cfra = anim_eval_context.eval_time; - if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); - } - - const float curval = evaluate_fcurve(fcu, cfra); - insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0); + /* adjust current frame for NLA-scaling */ + float cfra = anim_eval_context.eval_time; + if (adt) { + cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); } - ale->update |= ANIM_UPDATE_DEFAULT; + const float curval = evaluate_fcurve(fcu, cfra); + insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0); } - BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); - - ANIM_animdata_update(ac, &anim_data); - ANIM_animdata_freelist(&anim_data); + ale->update |= ANIM_UPDATE_DEFAULT; } -/* this function is for inserting new grease pencil frames */ -static void insert_gpencil_keys(bAnimContext *ac, short mode) +/* this function is responsible for inserting new keyframes */ +static void insert_action_keys(bAnimContext *ac, short mode) { ListBase anim_data = {NULL, NULL}; + ListBase nla_cache = {NULL, NULL}; bAnimListElem *ale; int filter; Scene *scene = ac->scene; ToolSettings *ts = scene->toolsettings; + eInsertKeyFlags flag; + eGP_GetFrame_Mode add_frame_mode; + bGPdata *gpd_old = NULL; /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | @@ -792,30 +809,43 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode) if (mode == 2) { filter |= ANIMFILTER_SEL; } + else if (mode == 3) { + filter |= ANIMFILTER_ACTGROUPED; + } ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - /* add a copy or a blank frame? */ + /* Init keyframing flag. */ + flag = ANIM_get_keyframing_flags(scene, true); + + /* GPLayers specific flags */ if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { - add_frame_mode = GP_GETFRAME_ADD_COPY; /* XXX: actframe may not be what we want? */ + add_frame_mode = GP_GETFRAME_ADD_COPY; } else { add_frame_mode = GP_GETFRAME_ADD_NEW; } - /* Insert gp frames. */ - bGPdata *gpd_old = NULL; + /* insert keyframes */ + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( + ac->depsgraph, (float)scene->r.cfra); for (ale = anim_data.first; ale; ale = ale->next) { - bGPdata *gpd = (bGPdata *)ale->id; - bGPDlayer *gpl = (bGPDlayer *)ale->data; - BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode); - /* Check if the gpd changes to tag only once. */ - if (gpd != gpd_old) { - BKE_gpencil_tag(gpd); - gpd_old = gpd; + switch (ale->type) { + case ANIMTYPE_GPLAYER: + insert_gpencil_key(ac, ale, add_frame_mode, &gpd_old); + break; + + case ANIMTYPE_FCURVE: + insert_fcurve_key(ac, ale, anim_eval_context, flag, &nla_cache); + break; + + default: + BLI_assert_msg(false, "Keys cannot be inserted into this animation type."); } } + BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -841,12 +871,7 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op) mode = RNA_enum_get(op->ptr, "type"); /* insert keyframes */ - if (ac.datatype == ANIMCONT_GPENCIL) { - insert_gpencil_keys(&ac, mode); - } - else { - insert_action_keys(&ac, mode); - } + insert_action_keys(&ac, mode); /* set notifier that keyframes have changed */ if (ac.datatype == ANIMCONT_GPENCIL) { @@ -886,14 +911,8 @@ static bool duplicate_action_keys(bAnimContext *ac) bool changed = false; /* filter data */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and delete selected keys */ @@ -968,14 +987,8 @@ static bool delete_action_keys(bAnimContext *ac) bool changed_final = false; /* filter data */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and delete selected keys */ @@ -993,7 +1006,7 @@ static bool delete_action_keys(bAnimContext *ac) AnimData *adt = ale->adt; /* delete selected keyframes only */ - changed = delete_fcurve_keys(fcu); + changed = BKE_fcurve_delete_keys_selected(fcu); /* Only delete curve too if it won't be doing anything anymore */ if (BKE_fcurve_is_empty(fcu)) { @@ -1063,7 +1076,7 @@ static void clean_action_keys(bAnimContext *ac, float thresh, bool clean_chan) /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + ANIMFILTER_SEL | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and clean curves */ @@ -1139,8 +1152,8 @@ static void sample_action_keys(bAnimContext *ac) int filter; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and add keys between selected keyframes on every frame. */ @@ -1238,7 +1251,7 @@ static void setexpo_action_keys(bAnimContext *ac, short mode) /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + ANIMFILTER_SEL | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through setting mode per F-Curve */ @@ -1352,7 +1365,8 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op) /* set handle type */ ANIM_animdata_keyframe_callback(&ac, (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS), + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS | + ANIMFILTER_FCURVESONLY), ANIM_editkeyframes_ipo(mode)); /* set notifier that keyframe properties have changed */ @@ -1401,7 +1415,8 @@ static int actkeys_easing_exec(bContext *C, wmOperator *op) /* set handle type */ ANIM_animdata_keyframe_callback(&ac, (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS), + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS | + ANIMFILTER_FCURVESONLY), ANIM_editkeyframes_easing(mode)); /* set notifier that keyframe properties have changed */ @@ -1444,8 +1459,8 @@ static void sethandles_action_keys(bAnimContext *ac, short mode) KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through setting flags for handles @@ -1458,7 +1473,7 @@ static void sethandles_action_keys(bAnimContext *ac, short mode) /* any selected keyframes for editing? */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* change type of selected handles */ - ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1527,8 +1542,8 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode) KeyframeEditFunc set_cb = ANIM_editkeyframes_keytype(mode); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through setting BezTriple interpolation @@ -1536,32 +1551,19 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, NULL); - - ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES; - } - - ANIM_animdata_update(ac, &anim_data); - ANIM_animdata_freelist(&anim_data); -} - -/* this function is responsible for setting the keyframe type for Grease Pencil frames */ -static void setkeytype_gpencil_keys(bAnimContext *ac, short mode) -{ - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; + switch (ale->type) { + case ANIMTYPE_GPLAYER: + ED_gpencil_layer_frames_keytype_set(ale->data, mode); + ale->update |= ANIM_UPDATE_DEPS; + break; - /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + case ANIMTYPE_FCURVE: + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, NULL); + ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES; + break; - /* loop through each layer */ - for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type == ANIMTYPE_GPLAYER) { - ED_gpencil_layer_frames_keytype_set(ale->data, mode); - ale->update |= ANIM_UPDATE_DEPS; + default: + BLI_assert_msg(false, "Keytype cannot be set into this animation type."); } } @@ -1590,12 +1592,7 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op) mode = RNA_enum_get(op->ptr, "type"); /* set handle type */ - if (ac.datatype == ANIMCONT_GPENCIL) { - setkeytype_gpencil_keys(&ac, mode); - } - else { - setkeytype_action_keys(&ac, mode); - } + setkeytype_action_keys(&ac, mode); /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1653,19 +1650,44 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) /* init edit data */ /* loop over action data, averaging values */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(&ac, ale); - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL); - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); - } - else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL); + switch (ale->datatype) { + case ALE_GPFRAME: { + bGPDlayer *gpl = ale->data; + bGPDframe *gpf; + + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* only if selected */ + if (!(gpf->flag & GP_FRAME_SELECT)) { + continue; + } + /* store average time in float 1 (only do rounding at last step) */ + ked.f1 += gpf->framenum; + + /* increment number of items */ + ked.i1++; + } + break; + } + + case ALE_FCURVE: { + AnimData *adt = ANIM_nla_mapping_get(&ac, ale); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); + } + else { + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL); + } + break; + } + + default: + BLI_assert_msg(false, "Cannot jump to keyframe into this animation type."); } } @@ -1674,8 +1696,8 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) /* set the new current frame value, based on the average time */ if (ked.i1) { Scene *scene = ac.scene; - CFRA = round_fl_to_int(ked.f1 / ked.i1); - SUBFRA = 0.0f; + scene->r.cfra = round_fl_to_int(ked.f1 / ked.i1); + scene->r.subframe = 0.0f; } /* set notifier that things have changed */ @@ -1742,8 +1764,8 @@ static void snap_action_keys(bAnimContext *ac, short mode) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); } else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_NODUPLIS); } ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -1768,11 +1790,11 @@ static void snap_action_keys(bAnimContext *ac, short mode) } else if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -1876,14 +1898,8 @@ static void mirror_action_keys(bAnimContext *ac, short mode) } /* filter data */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | - ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* mirror keyframes */ @@ -1898,11 +1914,11 @@ static void mirror_action_keys(bAnimContext *ac, short mode) } else if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index aff888818e0..d1a8592ae9d 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -235,13 +235,7 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) KeyframeEditFunc test_cb, sel_cb; /* determine type-based settings */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | - ANIMFILTER_NODUPLIS); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); /* filter data */ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -416,7 +410,7 @@ static void box_select_elem( break; } - if (ale->type == ANIMTYPE_SUMMARY && ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { + if (ale->type == ANIMTYPE_SUMMARY) { ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac->data, ac->datatype); @@ -428,8 +422,10 @@ static void box_select_elem( ANIM_animdata_freelist(&anim_data); } - ANIM_animchannel_keyframes_loop( - &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL); + if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { + ANIM_animchannel_keyframes_loop( + &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL); + } } } } @@ -658,7 +654,7 @@ static void region_select_elem(RegionSelectData *sel_data, bAnimListElem *ale, b break; } - if (ale->type == ANIMTYPE_SUMMARY && ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { + if (ale->type == ANIMTYPE_SUMMARY) { ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac->data, ac->datatype); @@ -670,8 +666,10 @@ static void region_select_elem(RegionSelectData *sel_data, bAnimListElem *ale, b ANIM_animdata_freelist(&anim_data); } - ANIM_animchannel_keyframes_loop( - &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL); + if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { + ANIM_animchannel_keyframes_loop( + &sel_data->ked, ac->ads, ale, sel_data->ok_cb, sel_data->select_cb, NULL); + } } } } @@ -946,28 +944,36 @@ static void markers_selectkeys_between(bAnimContext *ac) ked.f2 = max; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* select keys in-between */ for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(ac, ale); + switch (ale->type) { + case ANIMTYPE_GPLAYER: + ED_gpencil_layer_frames_select_box(ale->data, min, max, SELECT_ADD); + ale->update |= ANIM_UPDATE_DEPS; + break; - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); - } - else if (ale->type == ANIMTYPE_GPLAYER) { - ED_gpencil_layer_frames_select_box(ale->data, min, max, SELECT_ADD); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (ale->type == ANIMTYPE_MASKLAYER) { - ED_masklayer_frames_select_box(ale->data, min, max, SELECT_ADD); - } - else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + case ANIMTYPE_MASKLAYER: + ED_masklayer_frames_select_box(ale->data, min, max, SELECT_ADD); + break; + + case ANIMTYPE_FCURVE: { + AnimData *adt = ANIM_nla_mapping_get(ac, ale); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); + } + else { + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } + break; + } + + default: + BLI_assert_msg(false, "Keys cannot be selected into this animation type."); } } @@ -1000,11 +1006,16 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) } } else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL); + if (ale->datatype == ALE_GPFRAME) { + ED_gpencil_layer_make_cfra_list(ale->data, &ked.list, 1); + } + else { + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL); + } } } ANIM_animdata_freelist(&anim_data); @@ -1015,7 +1026,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) ce = MEM_callocN(sizeof(CfraElem), "cfraElem"); BLI_addtail(&ked.list, ce); - ce->cfra = (float)CFRA; + ce->cfra = (float)scene->r.cfra; break; case ACTKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */ @@ -1033,12 +1044,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -1144,7 +1150,7 @@ static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) } /* loop through all of the keys and select additional keyframes based on these */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1200,7 +1206,7 @@ static void select_moreless_action_keys(bAnimContext *ac, short mode) build_cb = ANIM_editkeyframes_buildselmap(mode); /* loop through all of the keys and select additional keyframes based on these */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -1346,41 +1352,44 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se if (leftright == ACTKEYS_LRSEL_LEFT) { ked.f1 = MINAFRAMEF; - ked.f2 = (float)(CFRA + 0.1f); + ked.f2 = (float)(scene->r.cfra + 0.1f); } else { - ked.f1 = (float)(CFRA - 0.1f); + ked.f1 = (float)(scene->r.cfra - 0.1f); ked.f2 = MAXFRAMEF; } /* filter data */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | - ANIMFILTER_NODUPLIS); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* select keys */ for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(ac, ale); + switch (ale->type) { + case ANIMTYPE_GPLAYER: + ED_gpencil_layer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode); + ale->update |= ANIM_UPDATE_DEPS; + break; - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); - } - else if (ale->type == ANIMTYPE_GPLAYER) { - ED_gpencil_layer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (ale->type == ANIMTYPE_MASKLAYER) { - ED_masklayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode); - } - else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + case ANIMTYPE_MASKLAYER: + ED_masklayer_frames_select_box(ale->data, ked.f1, ked.f2, select_mode); + break; + + case ANIMTYPE_FCURVE: { + AnimData *adt = ANIM_nla_mapping_get(ac, ale); + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); + } + else { + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } + break; + } + + default: + BLI_assert_msg(false, "Keys cannot be selected into this animation type."); } } @@ -1393,8 +1402,8 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se TimeMarker *marker; for (marker = markers->first; marker; marker = marker->next) { - if (((leftright == ACTKEYS_LRSEL_LEFT) && (marker->frame < CFRA)) || - ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= CFRA))) { + if (((leftright == ACTKEYS_LRSEL_LEFT) && (marker->frame < scene->r.cfra)) || + ((leftright == ACTKEYS_LRSEL_RIGHT) && (marker->frame >= scene->r.cfra))) { marker->flag |= SELECT; } else { @@ -1464,7 +1473,7 @@ static int actkeys_select_leftright_invoke(bContext *C, wmOperator *op, const wm /* determine which side of the current frame mouse is on */ x = UI_view2d_region_to_view_x(v2d, event->mval[0]); - if (x < CFRA) { + if (x < scene->r.cfra) { RNA_enum_set(op->ptr, "mode", ACTKEYS_LRSEL_LEFT); } else { @@ -1539,29 +1548,29 @@ static void actkeys_mselect_single(bAnimContext *ac, ED_mask_select_frame(ale->data, selx, select_mode); } else { - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) && - (ale->datatype == ALE_ALL)) { + if (ale->type == ANIMTYPE_SUMMARY && ale->datatype == ALE_ALL) { ListBase anim_data = {NULL, NULL}; int filter; - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type == ANIMTYPE_GPLAYER) { - ED_gpencil_select_frame(ale->data, selx, select_mode); - ale->update |= ANIM_UPDATE_DEPS; + /* Loop over all keys that are represented by this summary key. */ + LISTBASE_FOREACH (bAnimListElem *, ale2, &anim_data) { + if (ale2->type == ANIMTYPE_GPLAYER) { + ED_gpencil_select_frame(ale2->data, selx, select_mode); + ale2->update |= ANIM_UPDATE_DEPS; } - else if (ale->type == ANIMTYPE_MASKLAYER) { - ED_mask_select_frame(ale->data, selx, select_mode); + else if (ale2->type == ANIMTYPE_MASKLAYER) { + ED_mask_select_frame(ale2->data, selx, select_mode); } } ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } - else { + + if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL); } } @@ -1588,35 +1597,29 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | - ANIMFILTER_NODUPLIS); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(ac, ale); - - /* set frame for validation callback to refer to */ - if (adt) { - ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP); - } - else { - ked.f1 = selx; - } - /* select elements with frame number matching cfra */ if (ale->type == ANIMTYPE_GPLAYER) { - ED_gpencil_select_frame(ale->key_data, selx, select_mode); + ED_gpencil_select_frame(ale->data, selx, select_mode); ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { - ED_mask_select_frame(ale->key_data, selx, select_mode); + ED_mask_select_frame(ale->data, selx, select_mode); } else { + AnimData *adt = ANIM_nla_mapping_get(ac, ale); + + /* set frame for validation callback to refer to */ + if (adt) { + ked.f1 = BKE_nla_tweakedit_remap(adt, selx, NLATIME_CONVERT_UNMAP); + } + else { + ked.f1 = selx; + } + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); } } @@ -1645,29 +1648,28 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s ED_mask_select_frames(ale->data, select_mode); } else { - if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK) && (ale->type == ANIMTYPE_SUMMARY) && - (ale->datatype == ALE_ALL)) { + if (ale->type == ANIMTYPE_SUMMARY && ale->datatype == ALE_ALL) { ListBase anim_data = {NULL, NULL}; int filter; - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type == ANIMTYPE_GPLAYER) { - ED_gpencil_select_frames(ale->data, select_mode); - ale->update |= ANIM_UPDATE_DEPS; + LISTBASE_FOREACH (bAnimListElem *, ale2, &anim_data) { + if (ale2->type == ANIMTYPE_GPLAYER) { + ED_gpencil_select_frames(ale2->data, select_mode); + ale2->update |= ANIM_UPDATE_DEPS; } - else if (ale->type == ANIMTYPE_MASKLAYER) { - ED_mask_select_frames(ale->data, select_mode); + else if (ale2->type == ANIMTYPE_MASKLAYER) { + ED_mask_select_frames(ale2->data, select_mode); } } ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } - else { + + if (!ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { ANIM_animchannel_keyframes_loop(NULL, ac->ads, ale, NULL, select_cb, NULL); } } @@ -1734,6 +1736,12 @@ static int mouse_action_keys(bAnimContext *ac, fcu->flag |= FCURVE_SELECTED; ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type); } + else if (ale->type == ANIMTYPE_GPLAYER) { + bGPdata *gpd = (bGPdata *)ale->id; + bGPDlayer *gpl = ale->data; + + ED_gpencil_set_active_channel(gpd, gpl); + } } } else if (ac->datatype == ANIMCONT_GPENCIL) { @@ -1745,13 +1753,7 @@ static int mouse_action_keys(bAnimContext *ac, bGPdata *gpd = (bGPdata *)ale->id; bGPDlayer *gpl = ale->data; - gpl->flag |= GP_LAYER_SELECT; - /* Update other layer status. */ - if (BKE_gpencil_layer_active_get(gpd) != gpl) { - BKE_gpencil_layer_active_set(gpd, gpl); - BKE_gpencil_layer_autolock_set(gpd, false); - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } + ED_gpencil_set_active_channel(gpd, gpl); } } else if (ac->datatype == ANIMCONT_MASK) { diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 09163842587..3e507f73d1a 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -98,9 +98,9 @@ static SpaceLink *action_create(const ScrArea *area, const Scene *scene) BLI_addtail(&saction->regionbase, region); region->regiontype = RGN_TYPE_WINDOW; - region->v2d.tot.xmin = (float)(SFRA - 10); + region->v2d.tot.xmin = (float)(scene->r.sfra - 10); region->v2d.tot.ymin = (float)(-area->winy) / 3.0f; - region->v2d.tot.xmax = (float)(EFRA + 10); + region->v2d.tot.xmax = (float)(scene->r.efra + 10); region->v2d.tot.ymax = 0.0f; region->v2d.cur = region->v2d.tot; @@ -506,8 +506,8 @@ static void action_listener(const wmSpaceTypeListenerParams *params) /* context changes */ switch (wmn->category) { case NC_GPENCIL: - /* only handle these events in GPencil mode for performance considerations */ - if (saction->mode == SACTCONT_GPENCIL) { + /* only handle these events for containers in which GPencil frames are displayed */ + if (ELEM(saction->mode, SACTCONT_GPENCIL, SACTCONT_DOPESHEET, SACTCONT_TIMELINE)) { if (wmn->action == NA_EDITED) { ED_area_tag_redraw(area); } @@ -561,8 +561,8 @@ static void action_listener(const wmSpaceTypeListenerParams *params) LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { Scene *scene = wmn->reference; - region->v2d.tot.xmin = (float)(SFRA - 4); - region->v2d.tot.xmax = (float)(EFRA + 4); + region->v2d.tot.xmin = (float)(scene->r.sfra - 4); + region->v2d.tot.xmax = (float)(scene->r.efra + 4); break; } } diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index d53fe2efb03..3d964a95bc0 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -163,6 +163,7 @@ void ED_spacemacros_init(void) ED_operatormacros_sequencer(); ED_operatormacros_paint(); ED_operatormacros_gpencil(); + ED_operatormacros_nla(); /* Register dropboxes (can use macros). */ ED_dropboxes_ui(); diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index e2f1df74446..7d4f38b1841 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -35,7 +35,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 5780b0c9df7..c3479409f0d 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -258,11 +258,9 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) { return true; } -#ifdef WITH_NEW_CURVES_TYPE if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) { return true; } -#endif #ifdef WITH_POINT_CLOUD if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) { return true; @@ -830,9 +828,7 @@ const char *buttons_context_dir[] = { "line_style", "collection", "gpencil", -#ifdef WITH_NEW_CURVES_TYPE "curves", -#endif #ifdef WITH_POINT_CLOUD "pointcloud", #endif @@ -926,12 +922,10 @@ int /*eContextResult*/ buttons_context(const bContext *C, set_pointer_type(path, result, &RNA_LightProbe); return CTX_RESULT_OK; } -#ifdef WITH_NEW_CURVES_TYPE if (CTX_data_equals(member, "curves")) { set_pointer_type(path, result, &RNA_Curves); return CTX_RESULT_OK; } -#endif #ifdef WITH_POINT_CLOUD if (CTX_data_equals(member, "pointcloud")) { set_pointer_type(path, result, &RNA_PointCloud); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 052af39319c..e60946b8f66 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -778,6 +778,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) sbuts->preview = 1; } break; + case NC_WORKSPACE: + buttons_area_redraw(area, BCONTEXT_TOOL); + break; case NC_SPACE: if (wmn->data == ND_SPACE_PROPERTIES) { ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 7800ce797aa..78174160eb8 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -131,7 +131,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip { float x; int *points, totseg, i, a; - float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1); + float sfra = scene->r.sfra, efra = scene->r.efra, framelen = region->winx / (efra - sfra + 1); MovieTracking *tracking = &clip->tracking; MovieTrackingObject *act_object = BKE_tracking_object_get_active(tracking); MovieTrackingTrack *act_track = BKE_tracking_track_get_active(&clip->tracking); @@ -245,14 +245,16 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip /* solver keyframes */ immUniformColor4ub(175, 255, 0, 255); - draw_keyframe(act_object->keyframe1 + clip->start_frame - 1, CFRA, sfra, framelen, 2, pos); - draw_keyframe(act_object->keyframe2 + clip->start_frame - 1, CFRA, sfra, framelen, 2, pos); + draw_keyframe( + act_object->keyframe1 + clip->start_frame - 1, scene->r.cfra, sfra, framelen, 2, pos); + draw_keyframe( + act_object->keyframe2 + clip->start_frame - 1, scene->r.cfra, sfra, framelen, 2, pos); immUnbindProgram(); /* movie clip animation */ if ((sc->mode == SC_MODE_MASKEDIT) && sc->mask_info.mask) { - ED_mask_draw_frames(sc->mask_info.mask, region, CFRA, sfra, efra); + ED_mask_draw_frames(sc->mask_info.mask, region, scene->r.cfra, sfra, efra); } } @@ -1175,17 +1177,9 @@ static void draw_plane_marker_image(Scene *scene, ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); if (ibuf) { - uchar *display_buffer; void *cache_handle; - - if (image->flag & IMA_VIEW_AS_RENDER) { - display_buffer = IMB_display_buffer_acquire( - ibuf, &scene->view_settings, &scene->display_settings, &cache_handle); - } - else { - display_buffer = IMB_display_buffer_acquire( - ibuf, NULL, &scene->display_settings, &cache_handle); - } + uchar *display_buffer = IMB_display_buffer_acquire( + ibuf, &scene->view_settings, &scene->display_settings, &cache_handle); if (display_buffer) { float frame_corners[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}}; diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index cf7c3b51ae3..9a690f36aab 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -1046,7 +1046,7 @@ static int prefetch_get_start_frame(const bContext *C) { Scene *scene = CTX_data_scene(C); - return SFRA; + return scene->r.sfra; } static int prefetch_get_final_frame(const bContext *C) @@ -1057,10 +1057,10 @@ static int prefetch_get_final_frame(const bContext *C) int end_frame; /* check whether all the frames from prefetch range are cached */ - end_frame = EFRA; + end_frame = scene->r.efra; if (clip->len) { - end_frame = min_ii(end_frame, SFRA + clip->len - 1); + end_frame = min_ii(end_frame, scene->r.sfra + clip->len - 1); } return end_frame; diff --git a/source/blender/editors/space_clip/clip_graph_ops.c b/source/blender/editors/space_clip/clip_graph_ops.c index d07cf09ffa6..67565a88bab 100644 --- a/source/blender/editors/space_clip/clip_graph_ops.c +++ b/source/blender/editors/space_clip/clip_graph_ops.c @@ -629,8 +629,8 @@ static int view_all_exec(bContext *C, wmOperator *UNUSED(op)) NULL); /* set extents of view to start/end frames */ - v2d->cur.xmin = (float)SFRA; - v2d->cur.xmax = (float)EFRA; + v2d->cur.xmin = (float)scene->r.sfra; + v2d->cur.xmax = (float)scene->r.efra; if (userdata.min < userdata.max) { v2d->cur.ymin = userdata.min; @@ -675,8 +675,8 @@ void ED_clip_graph_center_current_frame(Scene *scene, ARegion *region) float extra = BLI_rctf_size_x(&v2d->cur) / 2.0f; /* set extents of view to start/end frames */ - v2d->cur.xmin = (float)CFRA - extra; - v2d->cur.xmax = (float)CFRA + extra; + v2d->cur.xmin = (float)scene->r.cfra - extra; + v2d->cur.xmax = (float)scene->r.cfra + extra; } static int center_current_frame_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index 7f9cf61b748..2efd6b6b473 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -251,6 +251,9 @@ void CLIP_OT_slide_plane_marker(struct wmOperatorType *ot); void CLIP_OT_keyframe_insert(struct wmOperatorType *ot); void CLIP_OT_keyframe_delete(struct wmOperatorType *ot); +void CLIP_OT_new_image_from_plane_marker(struct wmOperatorType *ot); +void CLIP_OT_update_image_from_plane_marker(struct wmOperatorType *ot); + /* tracking_select.c */ void CLIP_OT_select(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index f5bf850791a..f276c2acd1a 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1061,9 +1061,9 @@ static void change_frame_apply(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); /* set the new frame number */ - CFRA = RNA_int_get(op->ptr, "frame"); - FRAMENUMBER_MIN_CLAMP(CFRA); - SUBFRA = 0.0f; + scene->r.cfra = RNA_int_get(op->ptr, "frame"); + FRAMENUMBER_MIN_CLAMP(scene->r.cfra); + scene->r.subframe = 0.0f; /* do updates */ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); @@ -1084,7 +1084,7 @@ static int frame_from_event(bContext *C, const wmEvent *event) int framenr = 0; if (region->regiontype == RGN_TYPE_WINDOW) { - float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1); + float sfra = scene->r.sfra, efra = scene->r.efra, framelen = region->winx / (efra - sfra + 1); framenr = sfra + event->mval[0] / framelen; } @@ -1399,7 +1399,7 @@ static void do_sequence_proxy(void *pjv, ProxyJob *pj = pjv; MovieClip *clip = pj->clip; Scene *scene = pj->scene; - int sfra = SFRA, efra = EFRA; + int sfra = scene->r.sfra, efra = scene->r.efra; ProxyThread *handles; int tot_thread = BLI_task_scheduler_num_threads(); int width, height; diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 01f2b24c8a4..221b87a8b5a 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -617,8 +617,8 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f); - immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)SFRA, v2d->cur.ymax); - immRectf(pos, (float)EFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)scene->r.sfra, v2d->cur.ymax); + immRectf(pos, (float)scene->r.efra, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); GPU_blend(GPU_BLEND_NONE); @@ -628,10 +628,10 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene) GPU_line_width(1.0f); immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, (float)SFRA, v2d->cur.ymin); - immVertex2f(pos, (float)SFRA, v2d->cur.ymax); - immVertex2f(pos, (float)EFRA, v2d->cur.ymin); - immVertex2f(pos, (float)EFRA, v2d->cur.ymax); + immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymin); + immVertex2f(pos, (float)scene->r.sfra, v2d->cur.ymax); + immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymin); + immVertex2f(pos, (float)scene->r.efra, v2d->cur.ymax); immEnd(); immUnbindProgram(); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index a73883e7624..ce6409a7784 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -515,6 +515,9 @@ static void clip_operatortypes(void) WM_operatortype_append(CLIP_OT_keyframe_insert); WM_operatortype_append(CLIP_OT_keyframe_delete); + WM_operatortype_append(CLIP_OT_new_image_from_plane_marker); + WM_operatortype_append(CLIP_OT_update_image_from_plane_marker); + /* ** clip_graph_ops.c ** */ /* graph editing */ diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index ca224b04da5..cba4157d044 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -16,6 +16,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_movieclip.h" #include "BKE_report.h" #include "BKE_tracking.h" @@ -33,6 +34,9 @@ #include "BLT_translation.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "clip_intern.h" #include "tracking_ops_intern.h" @@ -1267,7 +1271,8 @@ static int frame_jump_exec(bContext *C, wmOperator *op) } delta = pos == 1 ? 1 : -1; - while (sc->user.framenr + delta >= SFRA && sc->user.framenr + delta <= EFRA) { + while (sc->user.framenr + delta >= scene->r.sfra && + sc->user.framenr + delta <= scene->r.efra) { int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr + delta); MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr); @@ -1286,7 +1291,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op) delta = pos == 3 ? 1 : -1; framenr += delta; - while (framenr + delta >= SFRA && framenr + delta <= EFRA) { + while (framenr + delta >= scene->r.sfra && framenr + delta <= scene->r.efra) { MovieReconstructedCamera *cam = BKE_tracking_camera_get_reconstructed( tracking, object, framenr); @@ -1300,8 +1305,8 @@ static int frame_jump_exec(bContext *C, wmOperator *op) } } - if (CFRA != sc->user.framenr) { - CFRA = sc->user.framenr; + if (scene->r.cfra != sc->user.framenr) { + scene->r.cfra = sc->user.framenr; DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -2212,3 +2217,144 @@ void CLIP_OT_keyframe_delete(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image from plane track marker + * \{ */ + +static ImBuf *sample_plane_marker_image_for_operator(bContext *C) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + const int clip_frame_number = ED_space_clip_get_clip_frame_number(space_clip); + + MovieClip *clip = ED_space_clip_get_clip(space_clip); + + MovieTracking *tracking = &clip->tracking; + MovieTrackingPlaneTrack *plane_track = tracking->act_plane_track; + const MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, + clip_frame_number); + + ImBuf *frame_ibuf = ED_space_clip_get_buffer(space_clip); + if (frame_ibuf == NULL) { + return NULL; + } + + ImBuf *plane_ibuf = BKE_tracking_get_plane_imbuf(frame_ibuf, plane_marker); + + IMB_freeImBuf(frame_ibuf); + + return plane_ibuf; +} + +static bool new_image_from_plane_marker_poll(bContext *C) +{ + if (!ED_space_clip_tracking_poll(C)) { + return false; + } + + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + const MovieTracking *tracking = &clip->tracking; + + if (tracking->act_plane_track == NULL) { + return false; + } + + return true; +} + +static int new_image_from_plane_marker_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + MovieTracking *tracking = &clip->tracking; + MovieTrackingPlaneTrack *plane_track = tracking->act_plane_track; + + ImBuf *plane_ibuf = sample_plane_marker_image_for_operator(C); + if (plane_ibuf == NULL) { + return OPERATOR_CANCELLED; + } + + plane_track->image = BKE_image_add_from_imbuf(CTX_data_main(C), plane_ibuf, plane_track->name); + + IMB_freeImBuf(plane_ibuf); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_new_image_from_plane_marker(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Image from Plane Marker"; + ot->description = "Create new image from the content of the plane marker"; + ot->idname = "CLIP_OT_new_image_from_plane_marker"; + + /* api callbacks */ + ot->poll = new_image_from_plane_marker_poll; + ot->exec = new_image_from_plane_marker_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static bool update_image_from_plane_marker_poll(bContext *C) +{ + if (!ED_space_clip_tracking_poll(C)) { + return false; + } + + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + const MovieTracking *tracking = &clip->tracking; + + if (tracking->act_plane_track == NULL || tracking->act_plane_track->image == NULL) { + return false; + } + + const Image *image = tracking->act_plane_track->image; + return image->type == IMA_TYPE_IMAGE && ELEM(image->source, IMA_SRC_FILE, IMA_SRC_GENERATED); +} + +static int update_image_from_plane_marker_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + MovieTracking *tracking = &clip->tracking; + MovieTrackingPlaneTrack *plane_track = tracking->act_plane_track; + + ImBuf *plane_ibuf = sample_plane_marker_image_for_operator(C); + if (plane_ibuf == NULL) { + return OPERATOR_CANCELLED; + } + + BKE_image_replace_imbuf(plane_track->image, plane_ibuf); + + IMB_freeImBuf(plane_ibuf); + + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, plane_track->image); + + BKE_image_partial_update_mark_full_update(plane_track->image); + + return OPERATOR_FINISHED; +} + +void CLIP_OT_update_image_from_plane_marker(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update Image from Plane Marker"; + ot->description = + "Update current image used by plane marker from the content of the plane marker"; + ot->idname = "CLIP_OT_update_image_from_plane_marker"; + + /* api callbacks */ + ot->poll = update_image_from_plane_marker_poll; + ot->exec = update_image_from_plane_marker_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index d5223d57490..f6fd2980c19 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -131,10 +131,10 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa if (sequence) { if (backwards) { - tmj->efra = SFRA; + tmj->efra = scene->r.sfra; } else { - tmj->efra = EFRA; + tmj->efra = scene->r.efra; } tmj->efra = BKE_movieclip_remap_scene_to_clip_frame(clip, tmj->efra); } diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 17fbef23eac..ef22b1b9f0b 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -413,16 +413,8 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve } char str[BLI_UTF8_MAX + 1]; - size_t len; - - if (event->utf8_buf[0]) { - len = BLI_str_utf8_size_safe(event->utf8_buf); - memcpy(str, event->utf8_buf, len); - } - else { - /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); - } + const size_t len = BLI_str_utf8_size_safe(event->utf8_buf); + memcpy(str, event->utf8_buf, len); str[len] = '\0'; RNA_string_set(op->ptr, "text", str); } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 87595ecdb88..1829f19bfd6 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -86,12 +86,12 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { bool rename(StringRefNull new_name) override; /** Add drag support for catalog items. */ - std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override; + std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override; /** Add dropping support for catalog items. */ - std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; + std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override; }; -class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController { +class AssetCatalogDragController : public ui::AbstractViewItemDragController { AssetCatalogTreeItem &catalog_item_; public: @@ -103,7 +103,7 @@ class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController void on_drag_start() override; }; -class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { +class AssetCatalogDropController : public ui::AbstractViewItemDropController { AssetCatalogTreeItem &catalog_item_; public: @@ -142,7 +142,7 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { void build_row(uiLayout &row) override; - struct DropController : public ui::AbstractTreeViewItemDropController { + struct DropController : public ui::AbstractViewItemDropController { DropController(AssetCatalogTreeView &tree_view); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; @@ -150,13 +150,13 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { bool on_drop(struct bContext *C, const wmDrag &drag) override; }; - std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; + std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override; }; class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - struct DropController : public ui::AbstractTreeViewItemDropController { + struct DropController : public ui::AbstractViewItemDropController { DropController(AssetCatalogTreeView &tree_view); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; @@ -164,7 +164,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { bool on_drop(struct bContext *C, const wmDrag &drag) override; }; - std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; + std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override; }; /* ---------------------------------------------------------------------- */ @@ -272,11 +272,11 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) return; } - uiButTreeRow *tree_row_but = tree_row_button(); + uiButViewItem *view_item_but = view_item_button(); PointerRNA *props; props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)view_item_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); } @@ -305,7 +305,7 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) 0, &props); RNA_string_set(&props, "catalog_id", catalog_id_str_buffer); - uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename"); + uiItemO(&column, "Rename", ICON_NONE, "UI_OT_view_item_rename"); /* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done * in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on @@ -333,14 +333,14 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) return true; } -std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem:: +std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewItem:: create_drop_controller() const { return std::make_unique<AssetCatalogDropController>( static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_); } -std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem:: +std::unique_ptr<ui::AbstractViewItemDragController> AssetCatalogTreeViewItem:: create_drag_controller() const { return std::make_unique<AssetCatalogDragController>( @@ -351,7 +351,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item) - : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item) + : ui::AbstractViewItemDropController(tree_view), catalog_item_(catalog_item) { } @@ -422,10 +422,10 @@ bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) { if (drag.type == WM_DRAG_ASSET_CATALOG) { return drop_asset_catalog_into_catalog( - drag, tree_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id()); + drag, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id()); } return drop_assets_into_catalog(C, - tree_view<AssetCatalogTreeView>(), + get_view<AssetCatalogTreeView>(), drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); @@ -512,14 +512,14 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, ::AssetLibrary &AssetCatalogDropController::get_asset_library() const { - return *tree_view<AssetCatalogTreeView>().asset_library_; + return *get_view<AssetCatalogTreeView>().asset_library_; } /* ---------------------------------------------------------------------- */ AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item) - : ui::AbstractTreeViewItemDragController(tree_view), catalog_item_(catalog_item) + : ui::AbstractViewItemDragController(tree_view), catalog_item_(catalog_item) { } @@ -538,7 +538,7 @@ void *AssetCatalogDragController::create_drag_data() const void AssetCatalogDragController::on_drag_start() { - AssetCatalogTreeView &tree_view_ = tree_view<AssetCatalogTreeView>(); + AssetCatalogTreeView &tree_view_ = get_view<AssetCatalogTreeView>(); tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id()); } @@ -551,15 +551,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) PointerRNA *props; UI_but_extra_operator_icon_add( - (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); + (uiBut *)view_item_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); /* No parent path to use the root level. */ RNA_string_set(props, "parent_path", nullptr); } -std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllItem:: +std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewAllItem:: create_drop_controller() const { return std::make_unique<AssetCatalogTreeViewAllItem::DropController>( @@ -567,7 +567,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllI } AssetCatalogTreeViewAllItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractTreeViewItemDropController(tree_view) + : ui::AbstractViewItemDropController(tree_view) { } @@ -579,7 +579,7 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag, } const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *tree_view<AssetCatalogTreeView>().asset_library_); + drag, *get_view<AssetCatalogTreeView>().asset_library_); if (drag_catalog->path.parent() == "") { *r_disabled_hint = "Catalog is already placed at the highest level"; return false; @@ -592,7 +592,7 @@ std::string AssetCatalogTreeViewAllItem::DropController::drop_tooltip(const wmDr { BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *tree_view<AssetCatalogTreeView>().asset_library_); + drag, *get_view<AssetCatalogTreeView>().asset_library_); return std::string(TIP_("Move Catalog")) + " '" + drag_catalog->path.name() + "' " + TIP_("to the top level of the tree"); @@ -604,14 +604,14 @@ bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext *UNUSE BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); return AssetCatalogDropController::drop_asset_catalog_into_catalog( drag, - tree_view<AssetCatalogTreeView>(), + get_view<AssetCatalogTreeView>(), /* No value to drop into the root level. */ std::nullopt); } /* ---------------------------------------------------------------------- */ -std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem:: +std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewUnassignedItem:: create_drop_controller() const { return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>( @@ -619,7 +619,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnas } AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractTreeViewItemDropController(tree_view) + : ui::AbstractViewItemDropController(tree_view) { } @@ -647,7 +647,7 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext { /* Assign to nil catalog ID. */ return AssetCatalogDropController::drop_assets_into_catalog( - C, tree_view<AssetCatalogTreeView>(), drag, CatalogID{}); + C, get_view<AssetCatalogTreeView>(), drag, CatalogID{}); } } // namespace blender::ed::asset_browser diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 62bdd583bc1..59d9a15fbab 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1793,7 +1793,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) } ED_file_change_dir(C); } - /* opening file - sends events now, so things get handled on windowqueue level */ + /* Opening file, sends events now, so things get handled on window-queue level. */ else if (sfile->op) { wmOperator *op = sfile->op; char filepath[FILE_MAX]; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index bc2705df314..3b8c6cbd1d0 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -277,7 +277,7 @@ static void graphedit_activekey_update_cb(bContext *UNUSED(C), /* make sure F-Curve and its handles are still valid after this editing */ sort_time_fcurve(fcu); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* update callback for active keyframe properties - handle-editing wrapper */ diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index a946ce22139..608a1f4d73e 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -1348,7 +1348,7 @@ void graph_draw_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, shor int filter; /* build list of curves to draw */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY); filter |= ((sel) ? (ANIMFILTER_SEL) : (ANIMFILTER_UNSEL)); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -1393,7 +1393,8 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region) size_t items; /* build list of channels to draw */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Update max-extent of channels here (taking into account scrollers): diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index cfc4fcf8dad..64a3c603e73 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -109,8 +109,8 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode) eInsertKeyFlags flag = 0; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); if (mode & GRAPHKEYS_INSERTKEY_SEL) { filter |= ANIMFILTER_SEL; } @@ -156,10 +156,10 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode) x = sipo->cursorTime; } else if (adt) { - x = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + x = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } else { - x = (float)CFRA; + x = (float)scene->r.cfra; } /* Normalize units of cursor's value. */ @@ -178,7 +178,7 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode) } else { const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( - ac->depsgraph, (float)CFRA); + ac->depsgraph, (float)scene->r.cfra); for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->key_data; @@ -211,12 +211,12 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode) AnimData *adt = ANIM_nla_mapping_get(ac, ale); /* Adjust current frame for NLA-mapping. */ - float cfra = (float)CFRA; + float cfra = (float)scene->r.cfra; if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) { cfra = sipo->cursorTime; } else if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } const float curval = evaluate_fcurve_only_curve(fcu, cfra); @@ -457,7 +457,8 @@ static short copy_graph_keys(bAnimContext *ac) * - First time we try to filter more strictly, allowing only selected channels * to allow copying animation between channels. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) { ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -472,13 +473,13 @@ static short copy_graph_keys(bAnimContext *ac) return ok; } -static short paste_graph_keys(bAnimContext *ac, - const eKeyPasteOffset offset_mode, - const eKeyMergeMode merge_mode, - bool flip) +static eKeyPasteError paste_graph_keys(bAnimContext *ac, + const eKeyPasteOffset offset_mode, + const eKeyMergeMode merge_mode, + bool flip) { ListBase anim_data = {NULL, NULL}; - int filter, ok = 0; + int filter; /* Filter data * - First time we try to filter more strictly, allowing only selected channels @@ -486,15 +487,15 @@ static short paste_graph_keys(bAnimContext *ac, * - Second time, we loosen things up if nothing was found the first time, allowing * users to just paste keyframes back into the original curve again T31670. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) { ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); } /* Paste keyframes. */ - ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); + const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); /* Clean up. */ ANIM_animdata_freelist(&anim_data); @@ -554,9 +555,18 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) /* Ac.reports by default will be the global reports list, which won't show warnings. */ ac.reports = op->reports; - /* Paste keyframes - non-zero return means an error occurred while trying to paste. */ - if (paste_graph_keys(&ac, offset_mode, merge_mode, flipped)) { - return OPERATOR_CANCELLED; + const eKeyPasteError kf_empty = paste_graph_keys(&ac, offset_mode, merge_mode, flipped); + switch (kf_empty) { + case KEYFRAME_PASTE_OK: + break; + + case KEYFRAME_PASTE_NOWHERE_TO_PASTE: + BKE_report(op->reports, RPT_ERROR, "No selected F-Curves to paste into"); + return OPERATOR_CANCELLED; + + case KEYFRAME_PASTE_NOTHING_TO_PASTE: + BKE_report(op->reports, RPT_ERROR, "No data in buffer to paste"); + return OPERATOR_CANCELLED; } /* Set notifier that keyframes have changed. */ @@ -631,8 +641,8 @@ static bool duplicate_graph_keys(bAnimContext *ac) bool changed = false; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and delete selected keys. */ @@ -702,8 +712,8 @@ static bool delete_graph_keys(bAnimContext *ac) bool changed_final = false; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and delete selected keys. */ @@ -713,7 +723,7 @@ static bool delete_graph_keys(bAnimContext *ac) bool changed; /* Delete selected keyframes only. */ - changed = delete_fcurve_keys(fcu); + changed = BKE_fcurve_delete_keys_selected(fcu); if (changed) { ale->update |= ANIM_UPDATE_DEFAULT; @@ -785,8 +795,8 @@ static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan) int filter; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and clean curves. */ @@ -862,8 +872,8 @@ static void bake_graph_curves(bAnimContext *ac, int start, int end) int filter; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and add keys between selected keyframes on every frame. */ @@ -949,8 +959,8 @@ static void unbake_graph_curves(bAnimContext *ac, int start, int end) bAnimListElem *ale; /* Filter data. */ - const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and add keys between selected keyframes on every frame. */ @@ -1096,12 +1106,12 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op) } /* Determine extents of the baking. */ - sbi.cfra = start = CFRA; - end = CFRA + sbi.length - 1; + sbi.cfra = start = scene->r.cfra; + end = scene->r.cfra + sbi.length - 1; /* Filter anim channels. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* Loop through all selected F-Curves, replacing its data with the sound samples. */ @@ -1267,8 +1277,8 @@ static void sample_graph_keys(bAnimContext *ac) int filter; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and add keys between selected keyframes on every frame. */ @@ -1364,8 +1374,8 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode) int filter; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through setting mode per F-Curve. */ @@ -1469,8 +1479,8 @@ static void setipo_graph_keys(bAnimContext *ac, short mode) KeyframeEditFunc set_cb = ANIM_editkeyframes_ipo(mode); /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through setting BezTriple interpolation @@ -1478,7 +1488,7 @@ static void setipo_graph_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; } @@ -1547,8 +1557,8 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode) KeyframeEditFunc set_cb = ANIM_editkeyframes_easing(mode); /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through setting BezTriple easing. @@ -1556,7 +1566,7 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; } @@ -1625,8 +1635,8 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode) KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED); /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through setting flags for handles. @@ -1639,7 +1649,7 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode) /* Any selected keyframes for editing? */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* Change type of selected handles. */ - ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1945,7 +1955,7 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) /* Step 1: extract only the rotation f-curves. */ const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVE_VISIBLE | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + ANIMFILTER_FCURVESONLY | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -2056,7 +2066,8 @@ static KeyframeEditData sum_selected_keyframes(bAnimContext *ac) memset(&ked, 0, sizeof(KeyframeEditData)); /* Loop over action data, averaging values. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -2119,8 +2130,8 @@ static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) } else { /* Animation Mode - Affects current frame (int) */ - CFRA = round_fl_to_int(sum_time / num_keyframes); - SUBFRA = 0.0f; + scene->r.cfra = round_fl_to_int(sum_time / num_keyframes); + scene->r.subframe = 0.0f; } sipo->cursorVal = sum_value / (float)num_keyframes; @@ -2240,8 +2251,8 @@ static void snap_graph_keys(bAnimContext *ac, short mode) float cursor_value = 0.0f; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Init custom data for iterating over keyframes. */ @@ -2284,11 +2295,11 @@ static void snap_graph_keys(bAnimContext *ac, short mode) /* Perform snapping. */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -2361,8 +2372,8 @@ static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[] = { static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten) { /* Filter data. */ - const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -2523,8 +2534,8 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) edit_cb = ANIM_editkeyframes_mirror(mode); /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Mirror keyframes. */ @@ -2544,11 +2555,11 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) /* Perform actual mirroring. */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -2620,8 +2631,8 @@ static int graphkeys_smooth_exec(bContext *C, wmOperator *UNUSED(op)) } /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* Smooth keyframes. */ @@ -2720,7 +2731,8 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) type = RNA_enum_get(op->ptr, "type"); /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS | + ANIMFILTER_FCURVESONLY); if (RNA_boolean_get(op->ptr, "only_active")) { /* FIXME: enforce in this case only a single channel to get handled? */ filter |= ANIMFILTER_ACTIVE; @@ -2875,14 +2887,14 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op) /* Filter data. */ if (RNA_boolean_get(op->ptr, "only_active")) { /* This should be the default (for buttons) - Just paste to the active FCurve. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_ACTIVE | + ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); } else { /* This is only if the operator gets called from a hotkey or search - * Paste to all visible curves. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); } ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -3065,7 +3077,8 @@ static int graph_driver_delete_invalid_exec(bContext *C, wmOperator *op) /* NOTE: We might need a scene update to evaluate the driver flags. */ /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* Find invalid drivers. */ diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index 128925d4591..8deea21318c 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -72,21 +72,21 @@ static void graphview_cursor_apply(bContext *C, wmOperator *op) * NOTE: sync this part of the code with ANIM_OT_change_frame */ /* 1) frame is rounded to the nearest int, since frames are ints */ - CFRA = round_fl_to_int(frame); + scene->r.cfra = round_fl_to_int(frame); if (scene->r.flag & SCER_LOCK_FRAME_SELECTION) { /* Clip to preview range * NOTE: Preview range won't go into negative values, * so only clamping once should be fine. */ - CLAMP(CFRA, PSFRA, PEFRA); + CLAMP(scene->r.cfra, PSFRA, PEFRA); } else { /* Prevent negative frames */ - FRAMENUMBER_MIN_CLAMP(CFRA); + FRAMENUMBER_MIN_CLAMP(scene->r.cfra); } - SUBFRA = 0.0f; + scene->r.subframe = 0.0f; DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); } @@ -234,14 +234,16 @@ static int graphview_curves_hide_exec(bContext *C, wmOperator *op) /* get list of all channels that selection may need to be flushed to * - hierarchy must not affect what we have access to here... */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype); /* filter data * - of the remaining visible curves, we want to hide the ones that are * selected/unselected (depending on "unselected" prop) */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | + ANIMFILTER_NODUPLIS); if (unselected) { filter |= ANIMFILTER_UNSEL; } @@ -275,7 +277,8 @@ static int graphview_curves_hide_exec(bContext *C, wmOperator *op) /* unhide selected */ if (unselected) { /* turn off requirement for visible */ - filter = ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_LIST_CHANNELS; + filter = ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY; /* flushing has been done */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -344,13 +347,15 @@ static int graphview_curves_reveal_exec(bContext *C, wmOperator *op) /* get list of all channels that selection may need to be flushed to * - hierarchy must not affect what we have access to here... */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype); /* filter data * - just go through all visible channels, ensuring that everything is set to be curve-visible */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index e71c5114b0a..a36bd5c1461 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -172,7 +172,8 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L * - if the option to only show keyframes that belong to selected F-Curves is enabled, * include the 'only selected' flag... */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); if (sipo->flag & SIPO_SELCUVERTSONLY) { /* FIXME: this should really be check for by the filtering code... */ filter |= ANIMFILTER_SEL; @@ -342,7 +343,8 @@ void deselect_graph_keys(bAnimContext *ac, bool test, short sel, bool do_channel KeyframeEditFunc test_cb, sel_cb; /* determine type-based settings */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); /* filter data */ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -498,7 +500,8 @@ static rctf initialize_box_select_coords(const bAnimContext *ac, const rctf *rec static int initialize_animdata_selection_filter(const SpaceGraph *sipo) { - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); if (sipo->flag & SIPO_SELCUVERTSONLY) { filter |= ANIMFILTER_FOREDIT | ANIMFILTER_SELEDIT; } @@ -1150,7 +1153,8 @@ static void markers_selectkeys_between(bAnimContext *ac) ked.f2 = max; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* select keys in-between */ @@ -1189,7 +1193,8 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode) /* build list of columns */ switch (mode) { case GRAPHKEYS_COLUMNSEL_KEYS: /* list of selected keys */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -1204,7 +1209,7 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode) ce = MEM_callocN(sizeof(CfraElem), "cfraElem"); BLI_addtail(&ked.list, ce); - ce->cfra = (float)CFRA; + ce->cfra = (float)scene->r.cfra; break; case GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN: /* list of selected markers */ @@ -1222,7 +1227,8 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode) /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -1314,7 +1320,8 @@ static int graphkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) } /* loop through all of the keys and select additional keyframes based on these */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -1372,7 +1379,8 @@ static void select_moreless_graph_keys(bAnimContext *ac, short mode) memset(&ked, 0, sizeof(KeyframeEditData)); /* loop through all of the keys and select additional keyframes based on these */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -1513,15 +1521,15 @@ static void graphkeys_select_leftright(bAnimContext *ac, short leftright, short if (leftright == GRAPHKEYS_LRSEL_LEFT) { ked.f1 = MINAFRAMEF; - ked.f2 = (float)(CFRA + 0.1f); + ked.f2 = (float)(scene->r.cfra + 0.1f); } else { - ked.f1 = (float)(CFRA - 0.1f); + ked.f1 = (float)(scene->r.cfra - 0.1f); ked.f2 = MAXFRAMEF; } /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* select keys */ @@ -1597,7 +1605,7 @@ static int graphkeys_select_leftright_invoke(bContext *C, wmOperator *op, const /* determine which side of the current frame mouse is on */ x = UI_view2d_region_to_view_x(v2d, event->mval[0]); - if (x < CFRA) { + if (x < scene->r.cfra) { RNA_enum_set(op->ptr, "mode", GRAPHKEYS_LRSEL_LEFT); } else { @@ -1797,7 +1805,8 @@ static int mouse_graph_keys(bAnimContext *ac, * otherwise the active flag won't be set T26452. */ if (!run_modal && (nvi->fcu->flag & FCURVE_SELECTED) && something_was_selected) { /* NOTE: Sync the filter flags with findnearest_fcurve_vert. */ - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, nvi->ctype); } @@ -1873,7 +1882,8 @@ static int graphkeys_mselect_column(bAnimContext *ac, /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 313f6ca1561..f3d92911155 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -48,8 +48,8 @@ /* Used to obtain a list of animation channels for the operators to work on. */ #define OPERATOR_DATA_FILTER \ - (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | \ - ANIMFILTER_NODUPLIS) + (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | \ + ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS) /* This data type is only used for modal operation. */ typedef struct tGraphSliderOp { diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c index d8baa4c643d..82067661d57 100644 --- a/source/blender/editors/space_graph/graph_utils.c +++ b/source/blender/editors/space_graph/graph_utils.c @@ -81,7 +81,8 @@ void ED_drivers_editor_init(bContext *C, ScrArea *area) bAnimListElem *get_active_fcurve_channel(bAnimContext *ac) { ListBase anim_data = {NULL, NULL}; - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE); + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE | + ANIMFILTER_FCURVESONLY); size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* We take the first F-Curve only, since some other ones may have had 'active' flag set @@ -131,7 +132,7 @@ bool graphop_visible_keyframes_poll(bContext *C) /* loop over the visible (selection doesn't matter) F-Curves, and see if they're suitable * stopping on the first successful match */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY); items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); if (items == 0) { return found; @@ -183,7 +184,8 @@ bool graphop_editable_keyframes_poll(bContext *C) /* loop over the editable F-Curves, and see if they're suitable * stopping on the first successful match */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE | + ANIMFILTER_FCURVESONLY); items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); if (items == 0) { CTX_wm_operator_poll_msg_set(C, "There is no animation data to operate on"); @@ -286,7 +288,8 @@ bool graphop_selected_fcurve_poll(bContext *C) /* Get the editable + selected F-Curves, and as long as we got some, we can return. * NOTE: curve-visible flag isn't included, * otherwise selecting a curve via list to edit is too cumbersome. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); if (items == 0) { return false; diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index 18465018d35..f80c7c17c3a 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -56,7 +56,8 @@ void get_graph_keyframe_extents(bAnimContext *ac, int filter; /* Get data to filter, from Dopesheet. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); if (sipo->flag & SIPO_SELCUVERTSONLY) { filter |= ANIMFILTER_SEL; } @@ -398,8 +399,8 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end) } /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Loop through filtered data and add keys between selected keyframes on every frame. */ diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index 39fb41245bf..c6a1a6a77b4 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -28,7 +28,7 @@ set(SRC image_edit.c image_ops.c image_sequence.c - image_undo.c + image_undo.cc space_image.c image_intern.h diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index d0c21f85472..0a774ee679c 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1218,7 +1218,7 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { /* don't use iuser->framenr directly because it may not be updated if auto-refresh is off */ Scene *scene = CTX_data_scene(C); - const int framenr = BKE_image_user_frame_get(iuser, CFRA, NULL); + const int framenr = BKE_image_user_frame_get(iuser, scene->r.cfra, NULL); char str[MAX_IMAGE_INFO_LEN]; int duration = 0; diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 048c7345b97..f6f9428213f 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -515,7 +515,8 @@ void draw_image_cache(const bContext *C, ARegion *region) SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); Image *image = ED_space_image(sima); - float x, cfra = CFRA, sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1); + float x, cfra = scene->r.cfra, sfra = scene->r.sfra, efra = scene->r.efra, + framelen = region->winx / (efra - sfra + 1); Mask *mask = NULL; if (!ED_space_image_show_cache(sima)) { diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 950acd77f6a..0de50474ab8 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -20,6 +20,7 @@ #include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "IMB_imbuf_types.h" @@ -212,13 +213,7 @@ void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height) } else if (sima->image && sima->image->type == IMA_TYPE_R_RESULT && scene) { /* not very important, just nice */ - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } + BKE_render_resolution(&scene->r, true, r_width, r_height); } /* I know a bit weak... but preview uses not actual image size */ // XXX else if (image_preview_active(sima, r_width, r_height)); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 537132ac428..4036f859231 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -45,6 +45,7 @@ #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "DEG_depsgraph.h" @@ -197,10 +198,17 @@ static ImageUser *image_user_from_context(const bContext *C) return (sima) ? &sima->iuser : NULL; } -static ImageUser image_user_from_active_tile(Image *ima) +static ImageUser image_user_from_context_and_active_tile(const bContext *C, Image *ima) { + /* Try to get image user from context if available, otherwise use default. */ + ImageUser *iuser_context = image_user_from_context(C); ImageUser iuser; - BKE_imageuser_default(&iuser); + if (iuser_context) { + iuser = *iuser_context; + } + else { + BKE_imageuser_default(&iuser); + } /* Use the file associated with the active tile. Otherwise use the first tile. */ if (ima && ima->source == IMA_SRC_TILED) { @@ -233,7 +241,7 @@ static bool image_from_context_has_data_poll(bContext *C) static bool image_from_context_has_data_poll_active_tile(bContext *C) { Image *ima = image_from_context(C); - ImageUser iuser = image_user_from_active_tile(ima); + ImageUser iuser = image_user_from_context_and_active_tile(C, ima); return BKE_image_has_ibuf(ima, &iuser); } @@ -1602,7 +1610,7 @@ static int image_file_browse_invoke(bContext *C, wmOperator *op, const wmEvent * } } else if (ima->source == IMA_SRC_TILED) { - ImageUser iuser = image_user_from_active_tile(ima); + ImageUser iuser = image_user_from_context_and_active_tile(C, ima); BKE_image_user_file_path(&iuser, ima, filepath); } @@ -1823,7 +1831,7 @@ static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOp } static bool save_image_op( - Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) + Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, const ImageSaveOptions *opts) { WM_cursor_wait(true); @@ -2391,7 +2399,7 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports) if (image_has_valid_path(ima)) { ImageSaveOptions opts; Scene *scene = CTX_data_scene(C); - if (!BKE_image_save_options_init(&opts, bmain, scene, ima, NULL, false, false)) { + if (BKE_image_save_options_init(&opts, bmain, scene, ima, NULL, false, false)) { bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts); ok = ok && saved_successfully; } @@ -2698,7 +2706,7 @@ void IMAGE_OT_new(wmOperatorType *ot) static int image_flip_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); - ImageUser iuser = image_user_from_active_tile(ima); + ImageUser iuser = image_user_from_context_and_active_tile(C, ima); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); SpaceImage *sima = CTX_wm_space_image(C); const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); @@ -2716,7 +2724,7 @@ static int image_flip_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser); + ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser); if (is_paint) { ED_imapaint_clear_partial_redraw(); @@ -2819,7 +2827,7 @@ void IMAGE_OT_flip(wmOperatorType *ot) static int image_invert_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); - ImageUser iuser = image_user_from_active_tile(ima); + ImageUser iuser = image_user_from_context_and_active_tile(C, ima); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); SpaceImage *sima = CTX_wm_space_image(C); const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); @@ -2837,7 +2845,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser); + ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser); if (is_paint) { ED_imapaint_clear_partial_redraw(); @@ -2943,7 +2951,7 @@ void IMAGE_OT_invert(wmOperatorType *ot) static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Image *ima = image_from_context(C); - ImageUser iuser = image_user_from_active_tile(ima); + ImageUser iuser = image_user_from_context_and_active_tile(C, ima); PropertyRNA *prop = RNA_struct_find_property(op->ptr, "size"); if (!RNA_property_is_set(op->ptr, prop)) { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); @@ -2957,7 +2965,7 @@ static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED static int image_scale_exec(bContext *C, wmOperator *op) { Image *ima = image_from_context(C); - ImageUser iuser = image_user_from_active_tile(ima); + ImageUser iuser = image_user_from_context_and_active_tile(C, ima); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); SpaceImage *sima = CTX_wm_space_image(C); const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT)); @@ -2982,7 +2990,7 @@ static int image_scale_exec(bContext *C, wmOperator *op) RNA_property_int_set_array(op->ptr, prop, size); } - ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser); + ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser); ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; IMB_scaleImBuf(ibuf, size[0], size[1]); @@ -3464,10 +3472,10 @@ void IMAGE_OT_cycle_render_slot(wmOperatorType *ot) static int image_clear_render_slot_exec(bContext *C, wmOperator *UNUSED(op)) { - SpaceImage *sima = CTX_wm_space_image(C); Image *ima = image_from_context(C); + ImageUser *iuser = image_user_from_context(C); - if (!BKE_image_clear_renderslot(ima, &sima->iuser, ima->render_slot)) { + if (!BKE_image_clear_renderslot(ima, iuser, ima->render_slot)) { return OPERATOR_CANCELLED; } @@ -3532,10 +3540,10 @@ void IMAGE_OT_add_render_slot(wmOperatorType *ot) static int image_remove_render_slot_exec(bContext *C, wmOperator *UNUSED(op)) { - SpaceImage *sima = CTX_wm_space_image(C); Image *ima = image_from_context(C); + ImageUser *iuser = image_user_from_context(C); - if (!BKE_image_remove_renderslot(ima, &sima->iuser, ima->render_slot)) { + if (!BKE_image_remove_renderslot(ima, iuser, ima->render_slot)) { return OPERATOR_CANCELLED; } @@ -3580,9 +3588,9 @@ static void change_frame_apply(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); /* set the new frame number */ - CFRA = RNA_int_get(op->ptr, "frame"); - FRAMENUMBER_MIN_CLAMP(CFRA); - SUBFRA = 0.0f; + scene->r.cfra = RNA_int_get(op->ptr, "frame"); + FRAMENUMBER_MIN_CLAMP(scene->r.cfra); + scene->r.subframe = 0.0f; /* do updates */ DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); @@ -3603,7 +3611,7 @@ static int frame_from_event(bContext *C, const wmEvent *event) int framenr = 0; if (region->regiontype == RGN_TYPE_WINDOW) { - float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1); + float sfra = scene->r.sfra, efra = scene->r.efra, framelen = region->winx / (efra - sfra + 1); framenr = sfra + event->mval[0] / framelen; } @@ -3725,38 +3733,52 @@ static int render_border_exec(bContext *C, wmOperator *op) ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); Render *re = RE_GetSceneRender(scene); - RenderData *rd; - rctf border; + SpaceImage *sima = CTX_wm_space_image(C); if (re == NULL) { /* Shouldn't happen, but better be safe close to the release. */ return OPERATOR_CANCELLED; } - rd = RE_engine_get_render_data(re); - if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) { - BKE_report(op->reports, RPT_INFO, "Can not set border from a cropped render"); - return OPERATOR_CANCELLED; - } + /* Get information about the previous render, or current scene if no render yet. */ + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); + const RenderData *rd = ED_space_image_has_buffer(sima) ? RE_engine_get_render_data(re) : + &scene->r; - /* get rectangle from operator */ + /* Get rectangle from the operator. */ + rctf border; WM_operator_properties_border_to_rctf(op, &border); UI_view2d_region_to_view_rctf(®ion->v2d, &border, &border); - /* actually set border */ + /* Adjust for cropping. */ + if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) { + border.xmin = rd->border.xmin + border.xmin * (rd->border.xmax - rd->border.xmin); + border.xmax = rd->border.xmin + border.xmax * (rd->border.xmax - rd->border.xmin); + border.ymin = rd->border.ymin + border.ymin * (rd->border.ymax - rd->border.ymin); + border.ymax = rd->border.ymin + border.ymax * (rd->border.ymax - rd->border.ymin); + } + CLAMP(border.xmin, 0.0f, 1.0f); CLAMP(border.ymin, 0.0f, 1.0f); CLAMP(border.xmax, 0.0f, 1.0f); CLAMP(border.ymax, 0.0f, 1.0f); - scene->r.border = border; - /* drawing a border surrounding the entire camera view switches off border rendering - * or the border covers no pixels */ + /* Drawing a border surrounding the entire camera view switches off border rendering + * or the border covers no pixels. */ if ((border.xmin <= 0.0f && border.xmax >= 1.0f && border.ymin <= 0.0f && border.ymax >= 1.0f) || (border.xmin == border.xmax || border.ymin == border.ymax)) { scene->r.mode &= ~R_BORDER; } else { + /* Snap border to pixel boundaries, so drawing a border within a pixel selects that pixel. */ + border.xmin = floorf(border.xmin * width) / width; + border.xmax = ceilf(border.xmax * width) / width; + border.ymin = floorf(border.ymin * height) / height; + border.ymax = ceilf(border.ymax * height) / height; + + /* Set border. */ + scene->r.border = border; scene->r.mode |= R_BORDER; } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.cc index a7a8bde1115..986265afee0 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.cc @@ -25,6 +25,7 @@ #include "BLI_math.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_map.hh" #include "DNA_image_types.h" #include "DNA_object_types.h" @@ -82,14 +83,31 @@ void ED_image_paint_tile_lock_end(void) * * \{ */ -static ImBuf *imbuf_alloc_temp_tile(void) +static ImBuf *imbuf_alloc_temp_tile() { return IMB_allocImBuf( ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, IB_rectfloat | IB_rect); } -typedef struct PaintTile { - struct PaintTile *next, *prev; +struct PaintTileKey { + int x_tile, y_tile; + Image *image; + ImBuf *ibuf; + /* Copied from iuser.tile in PaintTile. */ + int iuser_tile; + + uint64_t hash() const + { + return blender::get_default_hash_4(x_tile, y_tile, image, ibuf); + } + bool operator==(const PaintTileKey &other) const + { + return x_tile == other.x_tile && y_tile == other.y_tile && image == other.image && + ibuf == other.ibuf && iuser_tile == other.iuser_tile; + } +}; + +struct PaintTile { Image *image; ImBuf *ibuf; /* For 2D image painting the ImageUser uses most of the values. @@ -99,14 +117,14 @@ typedef struct PaintTile { ImageUser iuser; union { float *fp; - uint *uint; + uint32_t *uint; void *pt; } rect; - ushort *mask; + uint16_t *mask; bool valid; bool use_float; int x_tile, y_tile; -} PaintTile; +}; static void ptile_free(PaintTile *ptile) { @@ -119,23 +137,25 @@ static void ptile_free(PaintTile *ptile) MEM_freeN(ptile); } -static void ptile_free_list(ListBase *paint_tiles) -{ - for (PaintTile *ptile = paint_tiles->first, *ptile_next; ptile; ptile = ptile_next) { - ptile_next = ptile->next; - ptile_free(ptile); +struct PaintTileMap { + blender::Map<PaintTileKey, PaintTile *> map; + + ~PaintTileMap() + { + for (PaintTile *ptile : map.values()) { + ptile_free(ptile); + } } - BLI_listbase_clear(paint_tiles); -} +}; -static void ptile_invalidate_list(ListBase *paint_tiles) +static void ptile_invalidate_map(PaintTileMap *paint_tile_map) { - LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) { + for (PaintTile *ptile : paint_tile_map->map.values()) { ptile->valid = false; } } -void *ED_image_paint_tile_find(ListBase *paint_tiles, +void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map, Image *image, ImBuf *ibuf, ImageUser *iuser, @@ -144,28 +164,32 @@ void *ED_image_paint_tile_find(ListBase *paint_tiles, ushort **r_mask, bool validate) { - LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) { - if (ptile->x_tile == x_tile && ptile->y_tile == y_tile) { - if (ptile->image == image && ptile->ibuf == ibuf && ptile->iuser.tile == iuser->tile) { - if (r_mask) { - /* allocate mask if requested. */ - if (!ptile->mask) { - ptile->mask = MEM_callocN(sizeof(ushort) * square_i(ED_IMAGE_UNDO_TILE_SIZE), - "UndoImageTile.mask"); - } - *r_mask = ptile->mask; - } - if (validate) { - ptile->valid = true; - } - return ptile->rect.pt; - } + PaintTileKey key; + key.ibuf = ibuf; + key.image = image; + key.iuser_tile = iuser->tile; + key.x_tile = x_tile; + key.y_tile = y_tile; + PaintTile **pptile = paint_tile_map->map.lookup_ptr(key); + if (pptile == nullptr) { + return nullptr; + } + PaintTile *ptile = *pptile; + if (r_mask) { + /* allocate mask if requested. */ + if (!ptile->mask) { + ptile->mask = static_cast<uint16_t *>(MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), + "UndoImageTile.mask")); } + *r_mask = ptile->mask; + } + if (validate) { + ptile->valid = true; } - return NULL; + return ptile->rect.pt; } -void *ED_image_paint_tile_push(ListBase *paint_tiles, +void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map, Image *image, ImBuf *ibuf, ImBuf **tmpibuf, @@ -177,37 +201,43 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles, bool use_thread_lock, bool find_prev) { - const bool has_float = (ibuf->rect_float != NULL); + if (use_thread_lock) { + BLI_spin_lock(&paint_tiles_lock); + } + const bool has_float = (ibuf->rect_float != nullptr); /* check if tile is already pushed */ /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ if (find_prev) { void *data = ED_image_paint_tile_find( - paint_tiles, image, ibuf, iuser, x_tile, y_tile, r_mask, true); + paint_tile_map, image, ibuf, iuser, x_tile, y_tile, r_mask, true); if (data) { + if (use_thread_lock) { + BLI_spin_unlock(&paint_tiles_lock); + } return data; } } - if (*tmpibuf == NULL) { + if (*tmpibuf == nullptr) { *tmpibuf = imbuf_alloc_temp_tile(); } - PaintTile *ptile = MEM_callocN(sizeof(PaintTile), "PaintTile"); + PaintTile *ptile = static_cast<PaintTile*>(MEM_callocN(sizeof(PaintTile), "PaintTile")); ptile->image = image; ptile->ibuf = ibuf; ptile->iuser = *iuser; - ptile->iuser.scene = NULL; + ptile->iuser.scene = nullptr; ptile->x_tile = x_tile; ptile->y_tile = y_tile; /* add mask explicitly here */ if (r_mask) { - *r_mask = ptile->mask = MEM_callocN(sizeof(ushort) * square_i(ED_IMAGE_UNDO_TILE_SIZE), - "PaintTile.mask"); + *r_mask = ptile->mask = static_cast<uint16_t*>(MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), + "PaintTile.mask")); } ptile->rect.pt = MEM_callocN((ibuf->rect_float ? sizeof(float[4]) : sizeof(char[4])) * @@ -234,13 +264,24 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles, SWAP(float *, ptile->rect.fp, (*tmpibuf)->rect_float); } else { - SWAP(uint *, ptile->rect.uint, (*tmpibuf)->rect); + SWAP(uint32_t *, ptile->rect.uint, (*tmpibuf)->rect); } - if (use_thread_lock) { - BLI_spin_lock(&paint_tiles_lock); + PaintTileKey key = {}; + key.ibuf = ibuf; + key.image = image; + key.iuser_tile = iuser->tile; + key.x_tile = x_tile; + key.y_tile = y_tile; + PaintTile *existing_tile = nullptr; + paint_tile_map->map.add_or_modify( + key, + [&](PaintTile **pptile) { *pptile = ptile; }, + [&](PaintTile **pptile) { existing_tile = *pptile; }); + if (existing_tile) { + ptile_free(ptile); + ptile = existing_tile; } - BLI_addtail(paint_tiles, ptile); if (use_thread_lock) { BLI_spin_unlock(&paint_tiles_lock); @@ -248,20 +289,20 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles, return ptile->rect.pt; } -static void ptile_restore_runtime_list(ListBase *paint_tiles) +static void ptile_restore_runtime_map(PaintTileMap *paint_tile_map) { ImBuf *tmpibuf = imbuf_alloc_temp_tile(); - LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) { + for (PaintTile *ptile : paint_tile_map->map.values()) { Image *image = ptile->image; - ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, NULL); - const bool has_float = (ibuf->rect_float != NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, nullptr); + const bool has_float = (ibuf->rect_float != nullptr); if (has_float) { SWAP(float *, ptile->rect.fp, tmpibuf->rect_float); } else { - SWAP(uint *, ptile->rect.uint, tmpibuf->rect); + SWAP(uint32_t *, ptile->rect.uint, tmpibuf->rect); } IMB_rectcpy(ibuf, @@ -277,7 +318,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) SWAP(float *, ptile->rect.fp, tmpibuf->rect_float); } else { - SWAP(uint *, ptile->rect.uint, tmpibuf->rect); + SWAP(uint32_t *, ptile->rect.uint, tmpibuf->rect); } /* Force OpenGL reload (maybe partial update will operate better?) */ @@ -291,7 +332,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - BKE_image_release_ibuf(image, ibuf, NULL); + BKE_image_release_ibuf(image, ibuf, nullptr); } IMB_freeImBuf(tmpibuf); @@ -303,35 +344,35 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) /** \name Image Undo Tile * \{ */ -static uint index_from_xy(uint tile_x, uint tile_y, const uint tiles_dims[2]) +static uint32_t index_from_xy(uint32_t tile_x, uint32_t tile_y, const uint32_t tiles_dims[2]) { BLI_assert(tile_x < tiles_dims[0] && tile_y < tiles_dims[1]); return (tile_y * tiles_dims[0]) + tile_x; } -typedef struct UndoImageTile { +struct UndoImageTile { union { float *fp; - uint *uint; + uint32_t *uint_ptr; void *pt; } rect; int users; -} UndoImageTile; +}; static UndoImageTile *utile_alloc(bool has_float) { - UndoImageTile *utile = MEM_callocN(sizeof(*utile), "ImageUndoTile"); + UndoImageTile *utile = static_cast<UndoImageTile *>(MEM_callocN(sizeof(*utile), "ImageUndoTile")); if (has_float) { - utile->rect.fp = MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__); + utile->rect.fp = static_cast<float*>(MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__)); } else { - utile->rect.uint = MEM_mallocN(sizeof(uint) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__); + utile->rect.uint_ptr = static_cast<uint32_t*>(MEM_mallocN(sizeof(uint32_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__)); } return utile; } static void utile_init_from_imbuf( - UndoImageTile *utile, const uint x, const uint y, const ImBuf *ibuf, ImBuf *tmpibuf) + UndoImageTile *utile, const uint32_t x, const uint32_t y, const ImBuf *ibuf, ImBuf *tmpibuf) { const bool has_float = ibuf->rect_float; @@ -339,7 +380,7 @@ static void utile_init_from_imbuf( SWAP(float *, utile->rect.fp, tmpibuf->rect_float); } else { - SWAP(uint *, utile->rect.uint, tmpibuf->rect); + SWAP(uint32_t *, utile->rect.uint_ptr, tmpibuf->rect); } IMB_rectcpy(tmpibuf, ibuf, 0, 0, x, y, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); @@ -348,7 +389,7 @@ static void utile_init_from_imbuf( SWAP(float *, utile->rect.fp, tmpibuf->rect_float); } else { - SWAP(uint *, utile->rect.uint, tmpibuf->rect); + SWAP(uint32_t *, utile->rect.uint_ptr, tmpibuf->rect); } } @@ -357,13 +398,13 @@ static void utile_restore( { const bool has_float = ibuf->rect_float; float *prev_rect_float = tmpibuf->rect_float; - uint *prev_rect = tmpibuf->rect; + uint32_t *prev_rect = tmpibuf->rect; if (has_float) { tmpibuf->rect_float = utile->rect.fp; } else { - tmpibuf->rect = utile->rect.uint; + tmpibuf->rect = utile->rect.uint_ptr; } IMB_rectcpy(ibuf, tmpibuf, x, y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); @@ -378,7 +419,7 @@ static void utile_decref(UndoImageTile *utile) BLI_assert(utile->users >= 0); if (utile->users == 0) { MEM_freeN(utile->rect.pt); - MEM_freeN(utile); + MEM_delete(utile); } } @@ -401,10 +442,10 @@ typedef struct UndoImageBuf { UndoImageTile **tiles; /** Can calculate these from dims, just for convenience. */ - uint tiles_len; - uint tiles_dims[2]; + uint32_t tiles_len; + uint32_t tiles_dims[2]; - uint image_dims[2]; + uint32_t image_dims[2]; /** Store variables from the image. */ struct { @@ -417,7 +458,7 @@ typedef struct UndoImageBuf { static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf) { - UndoImageBuf *ubuf = MEM_callocN(sizeof(*ubuf), __func__); + UndoImageBuf *ubuf = static_cast<UndoImageBuf*>(MEM_callocN(sizeof(*ubuf), __func__)); ubuf->image_dims[0] = ibuf->x; ubuf->image_dims[1] = ibuf->y; @@ -426,12 +467,12 @@ static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf) ubuf->tiles_dims[1] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[1]); ubuf->tiles_len = ubuf->tiles_dims[0] * ubuf->tiles_dims[1]; - ubuf->tiles = MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__); + ubuf->tiles = static_cast<UndoImageTile**>(MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__)); BLI_strncpy(ubuf->ibuf_name, ibuf->name, sizeof(ubuf->ibuf_name)); ubuf->image_state.gen_type = image->gen_type; ubuf->image_state.source = image->source; - ubuf->image_state.use_float = ibuf->rect_float != NULL; + ubuf->image_state.use_float = ibuf->rect_float != nullptr; return ubuf; } @@ -447,7 +488,7 @@ static void ubuf_from_image_all_tiles(UndoImageBuf *ubuf, const ImBuf *ibuf) for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) { uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS; - BLI_assert(ubuf->tiles[i] == NULL); + BLI_assert(ubuf->tiles[i] == nullptr); UndoImageTile *utile = utile_alloc(has_float); utile->users = 1; utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf); @@ -467,7 +508,7 @@ static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf) { /* We could have both float and rect buffers, * in this case free the float buffer if it's unused. */ - if ((ibuf->rect_float != NULL) && (ubuf->image_state.use_float == false)) { + if ((ibuf->rect_float != nullptr) && (ubuf->image_state.use_float == false)) { imb_freerectfloatImBuf(ibuf); } @@ -533,8 +574,8 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init) /* Tiles only added to second set of tiles. */ Image *image = uh->image_ref.ptr; - ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, NULL); - if (UNLIKELY(ibuf == NULL)) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, nullptr); + if (UNLIKELY(ibuf == nullptr)) { CLOG_ERROR(&LOG, "Unable to get buffer for image '%s'", image->id.name + 2); continue; } @@ -570,7 +611,7 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init) DEG_id_tag_update(&image->id, 0); } - BKE_image_release_ibuf(image, ibuf, NULL); + BKE_image_release_ibuf(image, ibuf, nullptr); } IMB_freeImBuf(tmpibuf); @@ -604,16 +645,16 @@ static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh, return ubuf; } } - return NULL; + return nullptr; } static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf) { - BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == NULL); + BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == nullptr); UndoImageBuf *ubuf = ubuf_from_image_no_tiles(image, ibuf); BLI_addtail(&uh->buffers, ubuf); - ubuf->post = NULL; + ubuf->post = nullptr; return ubuf; } @@ -621,7 +662,7 @@ static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf * static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf) { UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->name); - if (ubuf == NULL) { + if (ubuf == nullptr) { ubuf = uhandle_add_ubuf(uh, image, ibuf); } return ubuf; @@ -636,7 +677,7 @@ static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles, return uh; } } - return NULL; + return nullptr; } static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image, int tile_number) @@ -646,16 +687,16 @@ static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *imag return uh; } } - return NULL; + return nullptr; } static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageUser *iuser) { - BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == NULL); - UndoImageHandle *uh = MEM_callocN(sizeof(*uh), __func__); + BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == nullptr); + UndoImageHandle *uh = static_cast<UndoImageHandle*>(MEM_callocN(sizeof(*uh), __func__)); uh->image_ref.ptr = image; uh->iuser = *iuser; - uh->iuser.scene = NULL; + uh->iuser.scene = nullptr; BLI_addtail(undo_handles, uh); return uh; } @@ -663,7 +704,7 @@ static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageU static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, ImageUser *iuser) { UndoImageHandle *uh = uhandle_lookup(undo_handles, image, iuser->tile); - if (uh == NULL) { + if (uh == nullptr) { uh = uhandle_add(undo_handles, image, iuser); } return uh; @@ -675,7 +716,7 @@ static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, Ima /** \name Implements ED Undo System * \{ */ -typedef struct ImageUndoStep { +struct ImageUndoStep { UndoStep step; /** #UndoImageHandle */ @@ -685,12 +726,11 @@ typedef struct ImageUndoStep { * #PaintTile * Run-time only data (active during a paint stroke). */ - ListBase paint_tiles; + PaintTileMap* paint_tile_map; bool is_encode_init; ePaintMode paint_mode; - -} ImageUndoStep; +}; /** * Find the previous undo buffer from this one. @@ -703,7 +743,7 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev, { /* Use name lookup because the pointer is cleared for previous steps. */ UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image, tile_number); - if (uh_prev != NULL) { + if (uh_prev != nullptr) { UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_name); if (ubuf_reference) { ubuf_reference = ubuf_reference->post; @@ -713,7 +753,7 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev, } } } - return NULL; + return nullptr; } static bool image_undosys_poll(bContext *C) @@ -737,11 +777,11 @@ static bool image_undosys_poll(bContext *C) static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p); /* dummy, memory is cleared anyway. */ us->is_encode_init = true; BLI_listbase_clear(&us->handles); - BLI_listbase_clear(&us->paint_tiles); + us->paint_tile_map = MEM_new<PaintTileMap>(__func__); } static bool image_undosys_step_encode(struct bContext *C, @@ -753,7 +793,7 @@ static bool image_undosys_step_encode(struct bContext *C, * * This function ensures there are previous and current states of the image in the undo buffer. */ - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p); BLI_assert(us->step.data_size == 0); @@ -761,39 +801,38 @@ static bool image_undosys_step_encode(struct bContext *C, ImBuf *tmpibuf = imbuf_alloc_temp_tile(); - ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active; + ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(ED_undo_stack_get()->step_active); while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) { - us_reference = (ImageUndoStep *)us_reference->step.prev; + us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev); } /* Initialize undo tiles from ptiles (if they exist). */ - for (PaintTile *ptile = us->paint_tiles.first, *ptile_next; ptile; ptile = ptile_next) { + for (PaintTile *ptile : us->paint_tile_map->map.values()) { if (ptile->valid) { UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image, &ptile->iuser); UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, ptile->image, ptile->ibuf); - UndoImageTile *utile = MEM_callocN(sizeof(*utile), "UndoImageTile"); + UndoImageTile *utile = static_cast<UndoImageTile *>(MEM_callocN(sizeof(*utile), "UndoImageTile")); utile->users = 1; utile->rect.pt = ptile->rect.pt; - ptile->rect.pt = NULL; + ptile->rect.pt = nullptr; const uint tile_index = index_from_xy(ptile->x_tile, ptile->y_tile, ubuf_pre->tiles_dims); - BLI_assert(ubuf_pre->tiles[tile_index] == NULL); + BLI_assert(ubuf_pre->tiles[tile_index] == nullptr); ubuf_pre->tiles[tile_index] = utile; } - ptile_next = ptile->next; ptile_free(ptile); } - BLI_listbase_clear(&us->paint_tiles); + us->paint_tile_map->map.clear(); LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) { LISTBASE_FOREACH (UndoImageBuf *, ubuf_pre, &uh->buffers) { - ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, nullptr); const bool has_float = ibuf->rect_float; - BLI_assert(ubuf_pre->post == NULL); + BLI_assert(ubuf_pre->post == nullptr); ubuf_pre->post = ubuf_from_image_no_tiles(uh->image_ref.ptr, ibuf); UndoImageBuf *ubuf_post = ubuf_pre->post; @@ -806,7 +845,7 @@ static bool image_undosys_step_encode(struct bContext *C, UndoImageBuf *ubuf_reference = (us_reference ? ubuf_lookup_from_reference( us_reference, uh->image_ref.ptr, uh->iuser.tile, ubuf_post) : - NULL); + nullptr); int i = 0; for (uint y_tile = 0; y_tile < ubuf_pre->tiles_dims[1]; y_tile += 1) { @@ -814,34 +853,34 @@ static bool image_undosys_step_encode(struct bContext *C, for (uint x_tile = 0; x_tile < ubuf_pre->tiles_dims[0]; x_tile += 1) { uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS; - if ((ubuf_reference != NULL) && ((ubuf_pre->tiles[i] == NULL) || + if ((ubuf_reference != nullptr) && ((ubuf_pre->tiles[i] == nullptr) || /* In this case the paint stroke as has added a tile * which we have a duplicate reference available. */ (ubuf_pre->tiles[i]->users == 1))) { - if (ubuf_pre->tiles[i] != NULL) { + if (ubuf_pre->tiles[i] != nullptr) { /* If we have a reference, re-use this single use tile for the post state. */ BLI_assert(ubuf_pre->tiles[i]->users == 1); ubuf_post->tiles[i] = ubuf_pre->tiles[i]; - ubuf_pre->tiles[i] = NULL; + ubuf_pre->tiles[i] = nullptr; utile_init_from_imbuf(ubuf_post->tiles[i], x, y, ibuf, tmpibuf); } else { - BLI_assert(ubuf_post->tiles[i] == NULL); + BLI_assert(ubuf_post->tiles[i] == nullptr); ubuf_post->tiles[i] = ubuf_reference->tiles[i]; ubuf_post->tiles[i]->users += 1; } - BLI_assert(ubuf_pre->tiles[i] == NULL); + BLI_assert(ubuf_pre->tiles[i] == nullptr); ubuf_pre->tiles[i] = ubuf_reference->tiles[i]; ubuf_pre->tiles[i]->users += 1; - BLI_assert(ubuf_pre->tiles[i] != NULL); - BLI_assert(ubuf_post->tiles[i] != NULL); + BLI_assert(ubuf_pre->tiles[i] != nullptr); + BLI_assert(ubuf_post->tiles[i] != nullptr); } else { UndoImageTile *utile = utile_alloc(has_float); utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf); - if (ubuf_pre->tiles[i] != NULL) { + if (ubuf_pre->tiles[i] != nullptr) { ubuf_post->tiles[i] = utile; utile->users = 1; } @@ -851,15 +890,15 @@ static bool image_undosys_step_encode(struct bContext *C, utile->users = 2; } } - BLI_assert(ubuf_pre->tiles[i] != NULL); - BLI_assert(ubuf_post->tiles[i] != NULL); + BLI_assert(ubuf_pre->tiles[i] != nullptr); + BLI_assert(ubuf_post->tiles[i] != nullptr); i += 1; } } BLI_assert(i == ubuf_pre->tiles_len); BLI_assert(i == ubuf_post->tiles_len); } - BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, NULL); + BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, nullptr); } } @@ -871,7 +910,7 @@ static bool image_undosys_step_encode(struct bContext *C, } } else { - BLI_assert(C != NULL); + BLI_assert(C != nullptr); /* Happens when switching modes. */ ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); @@ -942,7 +981,7 @@ static void image_undosys_step_decode( /* NOTE: behavior for undo/redo closely matches sculpt undo. */ BLI_assert(dir != STEP_INVALID); - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p); if (dir == STEP_UNDO) { image_undosys_step_decode_undo(us, is_final); } @@ -951,7 +990,7 @@ static void image_undosys_step_decode( } if (us->paint_mode == PAINT_MODE_TEXTURE_3D) { - ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, NULL); + ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, nullptr); } /* Refresh texture slots. */ @@ -963,15 +1002,16 @@ static void image_undosys_step_free(UndoStep *us_p) ImageUndoStep *us = (ImageUndoStep *)us_p; uhandle_free_list(&us->handles); - /* Typically this list will have been cleared. */ - ptile_free_list(&us->paint_tiles); + /* Typically this map will have been cleared. */ + MEM_delete(us->paint_tile_map); + us->paint_tile_map = nullptr; } static void image_undosys_foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) { - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p); LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) { foreach_ID_ref_fn(user_data, ((UndoRefID *)&uh->image_ref)); } @@ -1011,12 +1051,12 @@ void ED_image_undosys_type(UndoType *ut) * - So operators can access the pixel-data before the stroke was applied, at run-time. * \{ */ -ListBase *ED_image_paint_tile_list_get(void) +PaintTileMap *ED_image_paint_tile_map_get(void) { UndoStack *ustack = ED_undo_stack_get(); UndoStep *us_prev = ustack->step_init; UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p); /* We should always have an undo push started when accessing tiles, * not doing this means we won't have paint_mode correctly set. */ BLI_assert(us_p == us_prev); @@ -1024,24 +1064,24 @@ ListBase *ED_image_paint_tile_list_get(void) /* Fallback value until we can be sure this never happens. */ us->paint_mode = PAINT_MODE_TEXTURE_2D; } - return &us->paint_tiles; + return us->paint_tile_map; } void ED_image_undo_restore(UndoStep *us) { - ListBase *paint_tiles = &((ImageUndoStep *)us)->paint_tiles; - ptile_restore_runtime_list(paint_tiles); - ptile_invalidate_list(paint_tiles); + PaintTileMap *paint_tile_map = reinterpret_cast<ImageUndoStep *>(us)->paint_tile_map; + ptile_restore_runtime_map(paint_tile_map); + ptile_invalidate_map(paint_tile_map); } static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode) { UndoStack *ustack = ED_undo_stack_get(); - bContext *C = NULL; /* special case, we never read from this. */ + bContext *C = nullptr; /* special case, we never read from this. */ UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p); BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT)); - us->paint_mode = paint_mode; + us->paint_mode = (ePaintMode)paint_mode; return us; } @@ -1060,19 +1100,19 @@ void ED_image_undo_push_begin_with_image(const char *name, BLI_assert(BKE_image_get_tile(image, iuser->tile)); UndoImageHandle *uh = uhandle_ensure(&us->handles, image, iuser); UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, image, ibuf); - BLI_assert(ubuf_pre->post == NULL); + BLI_assert(ubuf_pre->post == nullptr); - ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active; + ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(ED_undo_stack_get()->step_active); while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) { - us_reference = (ImageUndoStep *)us_reference->step.prev; + us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev); } UndoImageBuf *ubuf_reference = (us_reference ? ubuf_lookup_from_reference( us_reference, image, iuser->tile, ubuf_pre) : - NULL); + nullptr); if (ubuf_reference) { memcpy(ubuf_pre->tiles, ubuf_reference->tiles, sizeof(*ubuf_pre->tiles) * ubuf_pre->tiles_len); - for (uint i = 0; i < ubuf_pre->tiles_len; i++) { + for (uint32_t i = 0; i < ubuf_pre->tiles_len; i++) { UndoImageTile *utile = ubuf_pre->tiles[i]; utile->users += 1; } @@ -1085,7 +1125,7 @@ void ED_image_undo_push_begin_with_image(const char *name, void ED_image_undo_push_end(void) { UndoStack *ustack = ED_undo_stack_get(); - BKE_undosys_step_push(ustack, NULL, NULL); + BKE_undosys_step_push(ustack, nullptr, nullptr); BKE_undosys_stack_limit_steps_and_memory_defaults(ustack); WM_file_tag_modified(); } diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index f89bfd2a36a..9652819404e 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -79,7 +79,7 @@ bool nla_panel_context(const bContext *C, */ /* XXX: double-check active! */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ACTIVE | - ANIMFILTER_LIST_CHANNELS); + ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -393,8 +393,8 @@ static void nla_panel_properties(const bContext *C, Panel *panel) /* strip extents */ column = uiLayoutColumn(layout, true); - uiItemR(column, &strip_ptr, "frame_start", 0, IFACE_("Frame Start"), ICON_NONE); - uiItemR(column, &strip_ptr, "frame_end", 0, IFACE_("End"), ICON_NONE); + uiItemR(column, &strip_ptr, "frame_start_ui", 0, IFACE_("Frame Start"), ICON_NONE); + uiItemR(column, &strip_ptr, "frame_end_ui", 0, IFACE_("End"), ICON_NONE); /* Evaluation-Related Strip Properties ------------------ */ diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 40082b08806..a0c6a29c422 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -68,7 +68,8 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, /* get the channel that was clicked on */ /* filter channels */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get channel from index */ @@ -394,7 +395,8 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) int filter; /* filter channels */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* get channel from index */ @@ -561,7 +563,7 @@ bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel) /* get a list of the (selected) NLA Tracks being shown in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_NODUPLIS); + ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* add tracks... */ @@ -608,7 +610,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac) /* get a list of the selected AnimData blocks in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); + ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* check if selected AnimData blocks are empty, and add tracks if so... */ @@ -710,7 +712,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op)) /* get a list of the AnimData blocks being shown in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_NODUPLIS); + ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* delete tracks */ diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index bb9e201d94a..3b108a3ba8a 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -617,7 +617,7 @@ static void nla_draw_strip(SpaceNla *snla, immUnbindProgram(); } -/* add the relevant text to the cache of text-strings to draw in pixelspace */ +/** Add the relevant text to the cache of text-strings to draw in pixel-space. */ static void nla_draw_strip_text(AnimData *adt, NlaTrack *nlt, NlaStrip *strip, @@ -792,7 +792,8 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) /* build list of channels to draw */ ListBase anim_data = {NULL, NULL}; - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Update max-extent of channels here (taking into account scrollers): @@ -902,7 +903,8 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region) size_t items; /* build list of channels to draw */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* Update max-extent of channels here (taking into account scrollers): diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 81520445000..801d032a861 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -60,7 +60,8 @@ void ED_nla_postop_refresh(bAnimContext *ac) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FOREDIT); + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); /* get blocks to work on */ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -107,7 +108,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) } /* get a list of the AnimData blocks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* if no blocks, popup error? */ @@ -211,7 +212,7 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo) int filter; /* get a list of the AnimData blocks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* if no blocks, popup error? */ @@ -318,7 +319,8 @@ static void get_nlastrip_extents(bAnimContext *ac, float *min, float *max, const bool found_bounds = false; /* get data to filter */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* set large values to try to override */ @@ -436,7 +438,8 @@ static bool nla_channels_get_selected_extents(bAnimContext *ac, float *r_min, fl short found = 0; /* get all items - we need to do it this way */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through all channels, finding the first one that's selected */ @@ -625,7 +628,7 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) } scene = ac.scene; - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; /* get action to use */ act = BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action")); @@ -654,7 +657,8 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) /* get a list of the editable tracks being shown in the NLA * - this is limited to active ones for now, but could be expanded to */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); if (items == 0) { @@ -771,7 +775,8 @@ static int nlaedit_add_transition_exec(bContext *C, wmOperator *op) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each track, find pairs of strips to add transitions to */ @@ -901,11 +906,11 @@ static int nlaedit_add_sound_exec(bContext *C, wmOperator *UNUSED(op)) } scene = ac.scene; - cfra = CFRA; + cfra = scene->r.cfra; /* get a list of the editable tracks being shown in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | - ANIMFILTER_FOREDIT); + ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each track, add sound clips if it belongs to a speaker */ @@ -994,7 +999,8 @@ static int nlaedit_add_meta_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each track, find pairs of strips to add transitions to */ @@ -1070,7 +1076,8 @@ static int nlaedit_remove_meta_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each track, find pairs of strips to add transitions to */ @@ -1140,7 +1147,8 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) } /* get a list of editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* duplicate strips in tracks starting from the last one so that we're @@ -1208,13 +1216,10 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { nlaedit_duplicate_exec(C, op); - RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION); - WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr, event); - return OPERATOR_FINISHED; } @@ -1240,9 +1245,6 @@ void NLA_OT_duplicate(wmOperatorType *ot) false, "Linked", "When duplicating strips, assign new copies of the actions they use"); - - /* to give to transform */ - RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /** \} */ @@ -1267,7 +1269,8 @@ static int nlaedit_delete_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, delete all selected strips */ @@ -1430,7 +1433,8 @@ static int nlaedit_split_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, split all selected strips into two strips */ @@ -1518,7 +1522,8 @@ static int nlaedit_toggle_mute_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* go over all selected strips */ @@ -1587,7 +1592,8 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* consider each track in turn */ @@ -1769,7 +1775,8 @@ static int nlaedit_move_up_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* since we're potentially moving strips from lower tracks to higher tracks, we should @@ -1860,7 +1867,8 @@ static int nlaedit_move_down_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* loop through the tracks in normal order, since we're pushing strips down, @@ -1952,7 +1960,8 @@ static int nlaedit_sync_actlen_exec(bContext *C, wmOperator *op) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); if (active_only) { filter |= ANIMFILTER_ACTIVE; } @@ -2047,7 +2056,8 @@ static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* Ensure that each action used only has a single user @@ -2155,7 +2165,8 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, apply scale of all selected strips */ @@ -2187,8 +2198,13 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) /* setup iterator, and iterate over all the keyframes in the action, * applying this scaling */ ked.data = strip; - ANIM_animchanneldata_keyframes_loop( - &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve); + ANIM_animchanneldata_keyframes_loop(&ked, + ac.ads, + strip->act, + ALE_ACT, + NULL, + bezt_apply_nlamapping, + BKE_fcurve_handles_recalc); /* clear scale of strip now that it has been applied, * and recalculate the extents of the action now that it has been scaled @@ -2265,7 +2281,8 @@ static int nlaedit_clear_scale_exec(bContext *C, wmOperator *UNUSED(op)) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, reset scale of all selected strips */ @@ -2350,7 +2367,8 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* get some necessary vars */ @@ -2388,7 +2406,7 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op) /* calculate new start position based on snapping mode */ switch (mode) { case NLAEDIT_SNAP_CFRA: /* to current frame */ - strip->start = (float)CFRA; + strip->start = (float)scene->r.cfra; break; case NLAEDIT_SNAP_NEAREST_FRAME: /* to nearest frame */ strip->start = floorf(start + 0.5f); @@ -2544,7 +2562,8 @@ static int nla_fmodifier_add_exec(bContext *C, wmOperator *op) } /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, add the specified modifier to all selected strips */ @@ -2655,7 +2674,8 @@ static int nla_fmodifier_copy_exec(bContext *C, wmOperator *op) ANIM_fmodifiers_copybuf_free(); /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, add the specified modifier to all selected strips */ @@ -2734,7 +2754,7 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op) /* get a list of the editable tracks being shown in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_NODUPLIS); + ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, add the specified modifier to all selected strips */ diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 902e7a176a3..3ae73282230 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -16,6 +16,8 @@ #include "ED_anim_api.h" #include "ED_screen.h" +#include "RNA_access.h" + #include "WM_api.h" #include "WM_types.h" @@ -138,6 +140,28 @@ void nla_operatortypes(void) WM_operatortype_append(NLA_OT_fmodifier_paste); } +void ED_operatormacros_nla() +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("NLA_OT_duplicate_move", + "Duplicate", + "Duplicate selected strips and their Actions and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate"); + RNA_boolean_set(otmacro->ptr, "linked", false); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + + ot = WM_operatortype_append_macro("NLA_OT_duplicate_linked_move", + "Duplicate Linked", + "Duplicate selected strips and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate"); + RNA_boolean_set(otmacro->ptr, "linked", true); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); +} + /* ************************** registration - keymaps **********************************/ void nla_keymap(wmKeyConfig *keyconf) diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c index 1efb91bc99f..a816f8fa4f6 100644 --- a/source/blender/editors/space_nla/nla_select.c +++ b/source/blender/editors/space_nla/nla_select.c @@ -85,7 +85,7 @@ static void deselect_nla_strips(bAnimContext *ac, short test, short sel) /* determine type-based settings */ /* FIXME: double check whether ANIMFILTER_LIST_VISIBLE is needed! */ - filter = (ANIMFILTER_DATA_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY); /* filter data */ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); @@ -223,7 +223,8 @@ static void box_select_nla_strips(bAnimContext *ac, rcti rect, short mode, short UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* convert selection modes to selection modes */ @@ -278,7 +279,8 @@ static void nlaedit_strip_at_region_position( 0, NLACHANNEL_STEP(snla), 0, NLACHANNEL_FIRST_TOP(ac), view_x, view_y, NULL, &channel_index); ListBase anim_data = {NULL, NULL}; - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click @@ -455,17 +457,17 @@ static void nlaedit_select_leftright(bContext *C, /* get range, and get the right flag-setting mode */ if (leftright == NLAEDIT_LRSEL_LEFT) { xmin = MINAFRAMEF; - xmax = (float)(CFRA + 0.1f); + xmax = (float)(scene->r.cfra + 0.1f); } else { - xmin = (float)(CFRA - 0.1f); + xmin = (float)(scene->r.cfra - 0.1f); xmax = MAXFRAMEF; } select_mode = selmodes_to_flagmodes(select_mode); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* select strips on the side where most data occurs */ @@ -540,7 +542,7 @@ static int nlaedit_select_leftright_invoke(bContext *C, wmOperator *op, const wm /* determine which side of the current frame mouse is on */ x = UI_view2d_region_to_view_x(v2d, event->mval[0]); - if (x < CFRA) { + if (x < scene->r.cfra) { RNA_enum_set(op->ptr, "mode", NLAEDIT_LRSEL_LEFT); } else { diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 42d3d841f4b..13035a9d5fd 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -87,9 +87,9 @@ static SpaceLink *nla_create(const ScrArea *area, const Scene *scene) BLI_addtail(&snla->regionbase, region); region->regiontype = RGN_TYPE_WINDOW; - region->v2d.tot.xmin = (float)(SFRA - 10); + region->v2d.tot.xmin = (float)(scene->r.sfra - 10); region->v2d.tot.ymin = (float)(-area->winy) / 3.0f; - region->v2d.tot.xmax = (float)(EFRA + 10); + region->v2d.tot.xmax = (float)(scene->r.efra + 10); region->v2d.tot.ymax = 0.0f; region->v2d.cur = region->v2d.tot; @@ -235,7 +235,7 @@ static void nla_main_region_draw(const bContext *C, ARegion *region) /* strips and backdrops */ draw_nla_main_data(&ac, snla, region); - /* text draw cached, in pixelspace now */ + /* Text draw cached, in pixel-space now. */ UI_view2d_text_cache_draw(region); } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index d196f3911a4..6fbea0b0d83 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -329,7 +329,7 @@ static void node_buts_image_user(uiLayout *layout, Scene *scene = CTX_data_scene(C); char numstr[32]; - const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr); + const int framenr = BKE_image_user_frame_get(iuser, scene->r.cfra, nullptr); BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr); uiItemL(layout, numstr, ICON_NONE); } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 715b7c80d1d..17906dcae35 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -382,7 +382,7 @@ static bool node_add_group_poll(bContext *C) if (snode->edittree->type == NTREE_CUSTOM) { CTX_wm_operator_poll_msg_set(C, "This node editor displays a custom (Python defined) node tree. " - "Dropping node groups isn't supported for this."); + "Dropping node groups isn't supported for this"); return false; } return true; @@ -690,8 +690,8 @@ static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *even snode->runtime->cursor[0] /= UI_DPI_FAC; snode->runtime->cursor[1] /= UI_DPI_FAC; - if (RNA_struct_property_is_set(op->ptr, "filepath") || - RNA_struct_property_is_set(op->ptr, "name")) { + if (WM_operator_properties_id_lookup_is_set(op->ptr) || + RNA_struct_property_is_set(op->ptr, "filepath")) { return node_add_file_exec(C, op); } return WM_operator_filesel(C, op, event); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 7f781f5a1ae..e12b845ef53 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -940,6 +940,19 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo ss << TIP_("\u2022 Volume") << line_end; break; } + case GEO_COMPONENT_TYPE_EDIT: { + if (value_log.edit_data_info.has_value()) { + const geo_log::GeometryValueLog::EditDataInfo &edit_info = *value_log.edit_data_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Edit Curves: %s, %s"), + edit_info.has_deformed_positions ? TIP_("positions") : TIP_("no positions"), + edit_info.has_deform_matrices ? TIP_("matrices") : TIP_("no matrices")); + ss << line << line_end; + } + break; + } } } @@ -978,6 +991,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo ss << TIP_("Volume"); break; } + case GEO_COMPONENT_TYPE_EDIT: { + break; + } } ss << ((type == supported_types.last()) ? "" : ", "); } @@ -1700,7 +1716,7 @@ static std::string node_get_execution_time_label(const SpaceNode &snode, const b { int node_count = 0; std::chrono::microseconds exec_time = node_get_execution_time( - *snode.nodetree, node, snode, node_count); + *snode.edittree, node, snode, node_count); if (node_count == 0) { return std::string(""); diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index f08e23c8371..e328a86b0fd 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -113,14 +113,6 @@ static void attribute_search_update_fn( Vector<const GeometryAttributeInfo *> infos = get_attribute_info_from_context(*C, *data); - /* Remove the deprecated normal attribute from the search. */ - for (const int i : infos.index_range()) { - if (infos[i]->domain == ATTR_DOMAIN_FACE && infos[i]->name == "normal") { - infos.remove(i); - break; - } - } - ui::attribute_search_add_items(str, true, infos, items, is_first); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index e20958c1b1e..7435fa50a93 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -1124,8 +1124,7 @@ static Collection *collection_parent_from_ID(ID *id) return nullptr; } -static bool collection_drop_init( - bContext *C, wmDrag *drag, const int xy[2], const bool is_link, CollectionDrop *data) +static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], CollectionDrop *data) { /* Get collection to drop into. */ TreeElementInsertType insert_type; @@ -1157,9 +1156,6 @@ static bool collection_drop_init( /* Get collection to drag out of. */ ID *parent = drag_id->from_parent; Collection *from_collection = collection_parent_from_ID(parent); - if (is_link) { - from_collection = nullptr; - } /* Currently this should not be allowed, cannot edit items in an override of a Collection. */ if (from_collection != nullptr && ID_IS_OVERRIDE_LIBRARY(from_collection)) { @@ -1197,29 +1193,22 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; - if (((event->modifier & KM_SHIFT) == 0) && - collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { + if (((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, event->xy, &data)) { TreeElement *te = data.te; TreeStoreElem *tselem = TREESTORE(te); - if (!data.from || event->modifier & KM_CTRL) { - tselem->flag |= TSE_DRAG_INTO; - changed = true; - } - else { - switch (data.insert_type) { - case TE_INSERT_BEFORE: - tselem->flag |= TSE_DRAG_BEFORE; - changed = true; - break; - case TE_INSERT_AFTER: - tselem->flag |= TSE_DRAG_AFTER; - changed = true; - break; - case TE_INSERT_INTO: { - tselem->flag |= TSE_DRAG_INTO; - changed = true; - break; - } + switch (data.insert_type) { + case TE_INSERT_BEFORE: + tselem->flag |= TSE_DRAG_BEFORE; + changed = true; + break; + case TE_INSERT_AFTER: + tselem->flag |= TSE_DRAG_AFTER; + changed = true; + break; + case TE_INSERT_INTO: { + tselem->flag |= TSE_DRAG_INTO; + changed = true; + break; } } if (changed) { @@ -1242,30 +1231,49 @@ static char *collection_drop_tooltip(bContext *C, const wmEvent *event = win ? win->eventstate : nullptr; CollectionDrop data; - if (event && ((event->modifier & KM_SHIFT) == 0) && - collection_drop_init(C, drag, xy, event->modifier & KM_CTRL, &data)) { - TreeElement *te = data.te; - if (!data.from || event->modifier & KM_CTRL) { - return BLI_strdup(TIP_("Link inside Collection")); + if (event && ((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, xy, &data)) { + const bool is_link = !data.from || (event->modifier & KM_CTRL); + + /* Test if we are moving within same parent collection. */ + bool same_level = false; + LISTBASE_FOREACH (CollectionParent *, parent, &data.to->parents) { + if (data.from == parent->collection) { + same_level = true; + } } + + /* Tooltips when not moving directly into another collection i.e. mouse on border of + * collections. Later we will decide which tooltip to return. */ + const bool tooltip_link = (is_link && !same_level); + const char *tooltip_before = tooltip_link ? TIP_("Link before collection") : + TIP_("Move before collection"); + const char *tooltip_between = tooltip_link ? TIP_("Link between collections") : + TIP_("Move between collections"); + const char *tooltip_after = tooltip_link ? TIP_("Link after collection") : + TIP_("Move after collection"); + + TreeElement *te = data.te; switch (data.insert_type) { case TE_INSERT_BEFORE: if (te->prev && outliner_is_collection_tree_element(te->prev)) { - return BLI_strdup(TIP_("Move between collections")); + return BLI_strdup(tooltip_between); } else { - return BLI_strdup(TIP_("Move before collection")); + return BLI_strdup(tooltip_before); } break; case TE_INSERT_AFTER: if (te->next && outliner_is_collection_tree_element(te->next)) { - return BLI_strdup(TIP_("Move between collections")); + return BLI_strdup(tooltip_between); } else { - return BLI_strdup(TIP_("Move after collection")); + return BLI_strdup(tooltip_after); } break; case TE_INSERT_INTO: { + if (is_link) { + return BLI_strdup(TIP_("Link inside collection")); + } /* Check the type of the drag IDs to avoid the incorrect "Shift to parent" * for collections. Checking the type of the first ID works fine here since @@ -1296,7 +1304,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); CollectionDrop data; - if (!collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { + if (!collection_drop_init(C, drag, event->xy, &data)) { return OPERATOR_CANCELLED; } @@ -1447,7 +1455,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, TSE_GPENCIL_EFFECT_BASE); const int wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID; - wmDrag *drag = WM_event_start_drag(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); + wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); if (use_datastack_drag) { TreeElement *te_bone = nullptr; @@ -1537,6 +1545,8 @@ static int outliner_item_drag_drop_invoke(bContext *C, WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent); } + WM_event_start_prepared_drag(C, drag); + ED_outliner_select_sync_from_outliner(C, space_outliner); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 753de83a10d..6bab0b938e8 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -37,6 +37,7 @@ #include "BKE_lib_override.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_object.h" @@ -677,6 +678,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) TreeElement *te = outliner_find_tree_element(&space_outliner->tree, tselem); if (tselem->type == TSE_SOME_ID) { + BKE_main_namemap_remove_name(bmain, tselem->id, oldname); BLI_libblock_ensure_unique_name(bmain, tselem->id->name); WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name); @@ -742,6 +744,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) } case TSE_NLA_ACTION: { bAction *act = (bAction *)tselem->id; + BKE_main_namemap_remove_name(bmain, &act->id, oldname); BLI_libblock_ensure_unique_name(bmain, act->id.name); WM_msg_publish_rna_prop(mbus, &act->id, &act->id, ID, name); break; @@ -852,6 +855,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) case TSE_LAYER_COLLECTION: { /* The ID is a #Collection, not a #LayerCollection */ Collection *collection = (Collection *)tselem->id; + BKE_main_namemap_remove_name(bmain, &collection->id, oldname); BLI_libblock_ensure_unique_name(bmain, collection->id.name); WM_msg_publish_rna_prop(mbus, &collection->id, &collection->id, ID, name); WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index c4a9398a5f7..32860bc2cff 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -447,7 +447,7 @@ void OUTLINER_OT_item_rename(wmOperatorType *ot) /** \name ID Delete Operator * \{ */ -static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem) +static void id_delete_tag(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem) { Main *bmain = CTX_data_main(C); ID *id = tselem->id; @@ -484,35 +484,39 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto return; } if (te->idcode == ID_WS) { - BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT); - if (id->tag & LIB_TAG_DOIT) { + BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_PRE_EXISTING); + if (id->tag & LIB_TAG_PRE_EXISTING) { BKE_reportf( reports, RPT_WARNING, "Cannot delete currently visible workspace id '%s'", id->name); + BKE_main_id_tag_idcode(bmain, ID_WS, LIB_TAG_PRE_EXISTING, false); return; } + BKE_main_id_tag_idcode(bmain, ID_WS, LIB_TAG_PRE_EXISTING, false); } - BKE_id_delete(bmain, id); + id->tag |= LIB_TAG_DOIT; WM_event_add_notifier(C, NC_WINDOW, nullptr); } -void id_delete_fn(bContext *C, - ReportList *reports, - Scene *UNUSED(scene), - TreeElement *te, - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) +void id_delete_tag_fn(bContext *C, + ReportList *reports, + Scene *UNUSED(scene), + TreeElement *te, + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) { - id_delete(C, reports, te, tselem); + id_delete_tag(C, reports, te, tselem); } -static int outliner_id_delete_invoke_do(bContext *C, - ReportList *reports, - TreeElement *te, - const float mval[2]) +static int outliner_id_delete_tag(bContext *C, + ReportList *reports, + TreeElement *te, + const float mval[2]) { + int id_tagged_num = 0; + if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); @@ -522,26 +526,27 @@ static int outliner_id_delete_invoke_do(bContext *C, RPT_ERROR_INVALID_INPUT, "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath_abs); - return OPERATOR_CANCELLED; } - id_delete(C, reports, te, tselem); - return OPERATOR_FINISHED; + else { + id_delete_tag(C, reports, te, tselem); + id_tagged_num++; + } } } else { LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) { - int ret; - if ((ret = outliner_id_delete_invoke_do(C, reports, te_sub, mval))) { - return ret; + if ((id_tagged_num += outliner_id_delete_tag(C, reports, te_sub, mval)) != 0) { + break; } } } - return 0; + return id_tagged_num; } static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Main *bmain = CTX_data_main(C); ARegion *region = CTX_wm_region(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); float fmval[2]; @@ -550,15 +555,21 @@ static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + int id_tagged_num = 0; + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - int ret; - - if ((ret = outliner_id_delete_invoke_do(C, op->reports, te, fmval))) { - return ret; + if ((id_tagged_num += outliner_id_delete_tag(C, op->reports, te, fmval)) != 0) { + break; } } + if (id_tagged_num == 0) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + return OPERATOR_CANCELLED; + } - return OPERATOR_CANCELLED; + BKE_id_multi_tagged_delete(bmain); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + return OPERATOR_FINISHED; } void OUTLINER_OT_id_delete(wmOperatorType *ot) diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index a0dcb49aa43..18173b37123 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -437,13 +437,13 @@ void lib_reload_fn(struct bContext *C, struct TreeStoreElem *tselem, void *user_data); -void id_delete_fn(struct bContext *C, - struct ReportList *reports, - struct Scene *scene, - struct TreeElement *te, - struct TreeStoreElem *tsep, - struct TreeStoreElem *tselem, - void *user_data); +void id_delete_tag_fn(struct bContext *C, + struct ReportList *reports, + struct Scene *scene, + struct TreeElement *te, + struct TreeStoreElem *tsep, + struct TreeStoreElem *tselem, + void *user_data); void id_remap_fn(struct bContext *C, struct ReportList *reports, struct Scene *scene, diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index ec19e8d5e5b..80ee3bbb6a1 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -31,8 +31,11 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_anim_data.h" #include "BKE_animsys.h" @@ -90,7 +93,9 @@ static CLG_LogRef LOG = {"ed.outliner.tools"}; using namespace blender::ed::outliner; +using blender::Map; using blender::Set; +using blender::Vector; /* -------------------------------------------------------------------- */ /** \name ID/Library/Data Set/Un-link Utilities @@ -777,6 +782,25 @@ static void id_local_fn(bContext *C, } } +struct OutlinerLiboverrideDataIDRoot { + /** The linked ID that was selected for override. */ + ID *id_root_reference; + + /** The root of the override hierarchy to which the override of `id_root` belongs, once + * known/created. */ + ID *id_hierarchy_root_override; + + /** The ID that was detected as being a good candidate as instanciation hint for newly overridden + * objects, may be null. + * + * \note Typically currently only used when the root ID to override is a collection instanced by + * an emtpy object. */ + ID *id_instance_hint; + + /** If this override comes from an instancing object (which would be `id_instance_hint` then). */ + bool is_override_instancing_object; +}; + struct OutlinerLibOverrideData { bool do_hierarchy; @@ -789,21 +813,44 @@ struct OutlinerLibOverrideData { * solving broken overrides while not losing *all* of your overrides. */ bool do_resync_hierarchy_enforce; - /** The override hierarchy root, when known/created. */ - ID *id_hierarchy_root_override; - - /** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on + /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on * their newly-created liboverrides in post-process step of override hierarchy creation. */ Set<uint> selected_id_uid; + + /** A mapping from the found hierarchy roots to a linked list of IDs to override for each of + * these roots. + * + * \note the key may be either linked (in which case it will be replaced by the newly created + * override), or an actual already existing override. */ + Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots; + + /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */ + Set<uint> id_hierarchy_roots_uid; + + void id_root_add(ID *id_hierarchy_root_reference, + ID *id_root_reference, + ID *id_instance_hint, + const bool is_override_instancing_object) + { + OutlinerLiboverrideDataIDRoot id_root_data; + id_root_data.id_root_reference = id_root_reference; + id_root_data.id_hierarchy_root_override = nullptr; + id_root_data.id_instance_hint = id_instance_hint; + id_root_data.is_override_instancing_object = is_override_instancing_object; + + Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default( + id_hierarchy_root_reference); + value.append(id_root_data); + } }; /* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override * hierarchy. */ -static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C), - ReportList *UNUSED(reports), +static void id_override_library_create_hierarchy_pre_process_fn(bContext *C, + ReportList *reports, Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), + TreeElement *te, + TreeStoreElem *tsep, TreeStoreElem *tselem, void *user_data) { @@ -829,28 +876,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } -} - -static void id_override_library_create_fn(bContext *C, - ReportList *reports, - Scene *scene, - TreeElement *te, - TreeStoreElem *tsep, - TreeStoreElem *tselem, - void *user_data) -{ - BLI_assert(TSE_IS_REAL_ID(tselem)); - - /* We can only safely apply this operation on one item at a time, so only do it on the active - * one. */ - if ((tselem->flag & TSE_ACTIVE) == 0) { - return; - } - - ID *id_root_reference = tselem->id; - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); - const bool do_hierarchy = data->do_hierarchy; - bool success = false; ID *id_instance_hint = nullptr; bool is_override_instancing_object = false; @@ -866,172 +891,242 @@ static void id_override_library_create_fn(bContext *C, } } - if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) || - (ID_IS_LINKED(id_root_reference) && do_hierarchy)) { - Main *bmain = CTX_data_main(C); + if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) && + !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) { + return; + } - id_root_reference->tag |= LIB_TAG_DOIT; + Main *bmain = CTX_data_main(C); - /* For now, remap all local usages of linked ID to local override one here. */ - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter)) { - id_iter->tag &= ~LIB_TAG_DOIT; - } - else { - id_iter->tag |= LIB_TAG_DOIT; + if (do_hierarchy) { + /* Tag all linked parents in tree hierarchy to be also overridden. */ + ID *id_hierarchy_root_reference = id_root_reference; + while ((te = te->parent) != nullptr) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; } - } - FOREACH_MAIN_ID_END; - if (do_hierarchy) { - /* Tag all linked parents in tree hierarchy to be also overridden. */ - ID *id_hierarchy_root_reference = id_root_reference; - while ((te = te->parent) != nullptr) { - if (!TSE_IS_REAL_ID(te->store_elem)) { + /* Tentative hierarchy root. */ + ID *id_current_hierarchy_root = te->store_elem->id; + + /* If the parent ID is from a different library than the reference root one, we are done + * with upwards tree processing in any case. */ + if (id_current_hierarchy_root->lib != id_root_reference->lib) { + if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { + /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to + * get an actual real override. */ continue; } - /* Tentative hierarchy root. */ - ID *id_current_hierarchy_root = te->store_elem->id; - - /* If the parent ID is from a different library than the reference root one, we are done - * with upwards tree processing in any case. */ - if (id_current_hierarchy_root->lib != id_root_reference->lib) { - if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { - /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to - * get an actual real override. */ - continue; - } - - /* If the parent ID is already an override, and is valid (i.e. local override), we can - * access its hierarchy root directly. */ - if (!ID_IS_LINKED(id_current_hierarchy_root) && - ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && - id_current_hierarchy_root->override_library->reference->lib == - id_root_reference->lib) { - id_hierarchy_root_reference = - id_current_hierarchy_root->override_library->hierarchy_root; - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); - break; - } - - if (ID_IS_LINKED(id_current_hierarchy_root)) { - /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this - * would most likely generate invisible/confusing/hard to use and manage overrides. */ - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - BKE_reportf(reports, - RPT_WARNING, - "Invalid anchor ('%s') found, needed to create library override from " - "data-block '%s'", - id_current_hierarchy_root->name, - id_root_reference->name); - return; - } - - /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so - * current `id_hierarchy_root_reference` is our best candidate. */ - + /* If the parent ID is already an override, and is valid (i.e. local override), we can + * access its hierarchy root directly. */ + if (!ID_IS_LINKED(id_current_hierarchy_root) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && + id_current_hierarchy_root->override_library->reference->lib == + id_root_reference->lib) { + id_hierarchy_root_reference = + id_current_hierarchy_root->override_library->hierarchy_root; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); break; } - /* If some element in the tree needs to be overridden, but its ID is not overridable, - * abort. */ - if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { + if (ID_IS_LINKED(id_current_hierarchy_root)) { + /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this + * would most likely generate invisible/confusing/hard to use and manage overrides. */ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, RPT_WARNING, - "Could not create library override from data-block '%s', one of its parents " - "is not overridable ('%s')", - id_root_reference->name, - id_current_hierarchy_root->name); + "Invalid anchor ('%s') found, needed to create library override from " + "data-block '%s'", + id_current_hierarchy_root->name, + id_root_reference->name); return; } - id_current_hierarchy_root->tag |= LIB_TAG_DOIT; - id_hierarchy_root_reference = id_current_hierarchy_root; + + /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so + * current `id_hierarchy_root_reference` is our best candidate. */ + + break; } - /* That case can happen when linked data is a complex mix involving several libraries and/or - * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data - * from another library. Do not try to support such cases for now. */ - if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || - (!ID_IS_LINKED(id_hierarchy_root_reference) && - ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && - id_hierarchy_root_reference->override_library->reference->lib == - id_root_reference->lib))) { + /* If some element in the tree needs to be overridden, but its ID is not overridable, + * abort. */ + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, RPT_WARNING, - "Invalid hierarchy root ('%s') found, needed to create library override from " - "data-block '%s'", - id_hierarchy_root_reference->name, - id_root_reference->name); + "Could not create library override from data-block '%s', one of its parents " + "is not overridable ('%s')", + id_root_reference->name, + id_current_hierarchy_root->name); return; } + id_current_hierarchy_root->tag |= LIB_TAG_DOIT; + id_hierarchy_root_reference = id_current_hierarchy_root; + } + + /* That case can happen when linked data is a complex mix involving several libraries and/or + * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data + * from another library. Do not try to support such cases for now. */ + if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || + (!ID_IS_LINKED(id_hierarchy_root_reference) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && + id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib))) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Invalid hierarchy root ('%s') found, needed to create library override from " + "data-block '%s'", + id_hierarchy_root_reference->name, + id_root_reference->name); + return; + } + + data->id_root_add(id_hierarchy_root_reference, + id_root_reference, + id_instance_hint, + is_override_instancing_object); + } + else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { + data->id_root_add( + id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object); + } +} +static void id_override_library_create_hierarchy( + Main &bmain, + Scene *scene, + ViewLayer *view_layer, + OutlinerLibOverrideData &data, + ID *id_hierarchy_root_reference, + Vector<OutlinerLiboverrideDataIDRoot> &data_idroots, + bool &r_aggregated_success) +{ + BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) || + ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + + const bool do_hierarchy = data.do_hierarchy; + + /* NOTE: This process is not the most efficient, but allows to re-use existing code. + * If this becomes a bottle-neck at some point, we need to implement a new + * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of + * a same hierarchy in a single call. */ + for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) { + /* For now, remap all local usages of linked ID to local override one here. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) { + if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) { + id_iter->tag &= ~LIB_TAG_DOIT; + } + else { + id_iter->tag |= LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + + bool success = false; + if (do_hierarchy) { ID *id_root_override = nullptr; - success = BKE_lib_override_library_create(bmain, - CTX_data_scene(C), - CTX_data_view_layer(C), + success = BKE_lib_override_library_create(&bmain, + scene, + view_layer, nullptr, - id_root_reference, + data_idroot.id_root_reference, id_hierarchy_root_reference, - id_instance_hint, + data_idroot.id_instance_hint, &id_root_override, - data->do_fully_editable); - - BLI_assert(id_root_override != nullptr); - BLI_assert(!ID_IS_LINKED(id_root_override)); - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override)); - if (ID_IS_LINKED(id_hierarchy_root_reference)) { - BLI_assert( - id_root_override->override_library->hierarchy_root->override_library->reference == - id_hierarchy_root_reference); - data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; - } - else { - BLI_assert(id_root_override->override_library->hierarchy_root == - id_hierarchy_root_reference); - data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + data.do_fully_editable); + + if (success) { + BLI_assert(id_root_override != nullptr); + BLI_assert(!ID_IS_LINKED(id_root_override)); + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override)); + + ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override)); + if (ID_IS_LINKED(id_hierarchy_root_reference)) { + BLI_assert(id_hierarchy_root_override->override_library->reference == + id_hierarchy_root_reference); + /* If the hierarchy root reference was a linked data, after the first iteration there is + * now a matching override, which shall be used for all further partial overrides with + * this same hierarchy. */ + id_hierarchy_root_reference = id_hierarchy_root_override; + } + else { + BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference); + } + data_idroot.id_hierarchy_root_override = id_hierarchy_root_override; + data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid); } } - else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { - success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr; + else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) { + ID *id_root_override = BKE_lib_override_library_create_from_id( + &bmain, data_idroot.id_root_reference, true); + success = id_root_override != nullptr; + if (success) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override)); + id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } /* Cleanup. */ - BKE_main_id_newptr_and_tag_clear(bmain); - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_main_id_newptr_and_tag_clear(&bmain); + BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false); + } + else { + BLI_assert_unreachable(); } /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ - if (success && is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint); + if (success && data_idroot.is_override_instancing_object) { + BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB); + ED_object_base_free_and_unlink( + &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint)); } - } - if (!success) { - BKE_reportf(reports, - RPT_WARNING, - "Could not create library override from data-block '%s'", - id_root_reference->name); + + r_aggregated_success = r_aggregated_success && success; } } /* Clear system override flag from newly created overrides which linked reference were previously * selected in the Outliner tree. */ -static void id_override_library_create_hierarchy_post_process(bContext *C, - OutlinerLibOverrideData *data) +static void id_override_library_create_hierarchy_process(bContext *C, + ReportList *reports, + OutlinerLibOverrideData &data) { Main *bmain = CTX_data_main(C); - ID *id_hierarchy_root_override = data->id_hierarchy_root_override; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool do_hierarchy = data.do_hierarchy; + + bool success = true; + for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) { + id_override_library_create_hierarchy( + *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success); + } + + if (!success) { + BKE_reportf(reports, + RPT_WARNING, + "Could not create library override from one or more of the selected data-blocks"); + } + + if (!do_hierarchy) { + return; + } ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || - id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { + if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) { + continue; + } + if (!data.id_hierarchy_roots_uid.contains( + id_iter->override_library->hierarchy_root->session_uuid)) { continue; } - if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) { + if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) || + data.selected_id_uid.contains(id_iter->session_uuid)) { id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; } } @@ -1995,7 +2090,6 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, @@ -2041,12 +2135,6 @@ static const EnumPropertyItem prop_id_op_types[] = { "Make Library Override Hierarchy", "Make a local override of this linked data-block, and its hierarchy of dependencies - only " "applies to active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, - "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE", - 0, - "Make Library Override Hierarchy Fully Editable", - "Make a local override of this linked data-block, and its hierarchy of dependencies, making " - "them all fully user-editable - only applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, "OVERRIDE_LIBRARY_MAKE_EDITABLE", 0, @@ -2126,7 +2214,6 @@ static bool outliner_id_operation_item_poll(bContext *C, } return false; case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { return true; } @@ -2185,6 +2272,7 @@ static const EnumPropertyItem *outliner_id_operation_itemf(bContext *C, static int outliner_id_operation_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -2261,42 +2349,36 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - ED_undo_push(C, "Overridden Data"); - break; - } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; + override_data.do_hierarchy = false; + override_data.do_fully_editable = true; + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_override_library_create_hierarchy_pre_process_fn, &override_data); - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - id_override_library_create_hierarchy_post_process(C, &override_data); - ED_undo_push(C, "Overridden Data Hierarchy"); + id_override_library_create_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Overridden Data"); break; } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: { + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - override_data.do_fully_editable = true; + override_data.do_fully_editable = U.experimental.use_override_new_fully_editable; + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_override_library_create_hierarchy_pre_process_fn, &override_data); - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - id_override_library_create_hierarchy_post_process(C, &override_data); - ED_undo_push(C, "Overridden Data Hierarchy Fully Editable"); + id_override_library_create_hierarchy_process(C, op->reports, override_data); + + ED_undo_push(C, "Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { @@ -2381,8 +2463,10 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_DELETE: { if (idlevel > 0) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_delete_fn, nullptr); + C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr); + BKE_id_multi_tagged_delete(bmain); ED_undo_push(C, "Delete"); } break; @@ -2507,6 +2591,7 @@ static const EnumPropertyItem outliner_lib_op_type_items[] = { static int outliner_lib_operation_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; @@ -2522,7 +2607,10 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { case OL_LIB_DELETE: { - outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr); + BKE_id_multi_tagged_delete(bmain); ED_undo_push(C, "Delete Library"); break; } diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 190e35c81d6..f8e35655c26 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -161,7 +161,6 @@ class TreeDisplayOverrideLibraryHierarchies final : public AbstractTreeDisplay { ListBase build_hierarchy_for_lib_or_main(Main *bmain, TreeElement &parent_te, Library *lib = nullptr); - void build_hierarchy_for_ID(Main *bmain, ID &override_root_id, TreeElementID &te_id) const; }; /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index 67798e978ab..f8705c3f0ad 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -4,25 +4,23 @@ * \ingroup spoutliner */ -#include "DNA_ID.h" -#include "DNA_collection_types.h" #include "DNA_key_types.h" #include "DNA_space_types.h" -#include "BLI_listbase.h" +#include "BLI_function_ref.hh" +#include "BLI_ghash.h" #include "BLI_map.hh" + #include "BLI_set.hh" #include "BLT_translation.h" -#include "BKE_collection.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "../outliner_intern.hh" #include "common.hh" #include "tree_display.hh" -#include "tree_element_id.hh" namespace blender::ed::outliner { @@ -42,8 +40,8 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & TreeElement *current_file_te = outliner_add_element( &space_outliner_, &tree, source_data.bmain, nullptr, TSE_ID_BASE, -1); current_file_te->name = IFACE_("Current File"); + AbstractTreeElement::uncollapse_by_default(current_file_te); { - AbstractTreeElement::uncollapse_by_default(current_file_te); build_hierarchy_for_lib_or_main(source_data.bmain, *current_file_te); /* Add dummy child if there's nothing to display. */ @@ -76,11 +74,49 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & return tree; } +/* -------------------------------------------------------------------- */ +/** \name Library override hierarchy building + * \{ */ + +class OverrideIDHierarchyBuilder { + SpaceOutliner &space_outliner_; + MainIDRelations &id_relations_; + + struct HierarchyBuildData { + const ID &override_root_id_; + /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes + * with every level of recursion. */ + Set<const ID *> parent_ids{}; + /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with + * every level of recursion. */ + Set<const ID *> sibling_ids{}; + }; + + public: + OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations) + : space_outliner_(space_outliner), id_relations_(id_relations) + { + } + + void build_hierarchy_for_ID(ID &root_id, TreeElement &te_to_expand); + + private: + void build_hierarchy_for_ID_recursive(const ID &parent_id, + HierarchyBuildData &build_data, + TreeElement &te_to_expand); +}; + ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( Main *bmain, TreeElement &parent_te, Library *lib) { ListBase tree = {nullptr}; + /* Ensure #Main.relations contains the latest mapping of relations. Must be freed before + * returning. */ + BKE_main_relations_create(bmain, 0); + + OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations); + /* Keep track over which ID base elements were already added, and expand them once added. */ Map<ID_Type, TreeElement *> id_base_te_map; /* Index for the ID base elements ("Objects", "Materials", etc). */ @@ -109,108 +145,174 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( TreeElement *new_id_te = outliner_add_element( &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false); - build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te)); + builder.build_hierarchy_for_ID(*iter_id, *new_id_te); } FOREACH_MAIN_ID_END; + BKE_main_relations_free(bmain); + return tree; } -struct BuildHierarchyForeachIDCbData { - /* Don't allow copies, the sets below would need deep copying. */ - BuildHierarchyForeachIDCbData(const BuildHierarchyForeachIDCbData &) = delete; - - Main &bmain; - SpaceOutliner &space_outliner; - ID &override_root_id; - - /* The tree element to expand. Changes with every level of recursion. */ - TreeElementID *parent_te; - /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes - * with every level of recursion. */ - Set<ID *> parent_ids{}; - /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with - * every level of recursion. */ - Set<ID *> sibling_ids{}; -}; +void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id, + TreeElement &te_to_expand) +{ + HierarchyBuildData build_data{override_root_id}; + build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand); +} + +/* Helpers (defined below). */ +static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, + const ID &parent_id, + FunctionRef<void(ID &)> fn); +static bool id_is_in_override_hierarchy(const ID &id, + const ID &relationship_parent_id, + const ID &override_root_id); + +void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &parent_id, + HierarchyBuildData &build_data, + TreeElement &te_to_expand) +{ + /* In case this isn't added to the parents yet (does nothing if already there). */ + build_data.parent_ids.add(&parent_id); + + foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) { + if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) { + return; + } + + /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into + * itself. */ + if (build_data.parent_ids.lookup_key_default(&id, nullptr)) { + return; + } + + /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used + * multiple times by the same parent. */ + if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) { + return; + } + + TreeElement *new_te = outliner_add_element( + &space_outliner_, &te_to_expand.subtree, &id, &te_to_expand, TSE_SOME_ID, 0, false); + + build_data.sibling_ids.add(&id); + + /* Recurse into this ID. */ + HierarchyBuildData child_build_data{build_data.override_root_id_}; + child_build_data.parent_ids = build_data.parent_ids; + child_build_data.parent_ids.add(&id); + child_build_data.sibling_ids.reserve(10); + build_hierarchy_for_ID_recursive(id, child_build_data, *new_te); + }); +} -static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helpers for library override hierarchy building + * \{ */ + +/** + * Iterate over the IDs \a parent_id uses. E.g. the child collections and contained objects of a + * parent collection. Also does special handling for object parenting, so that: + * - When iterating over a child object, \a fn is executed for the parent instead. + * - When iterating over a parent object, \a fn is _additionally_ executed for all children. Given + * that the parent object isn't skipped, the caller has to ensure it's not added in the hierarchy + * twice. + * This allows us to build the hierarchy in the expected ("natural") order, where parent objects + * are actual parent elements in the hierarchy, even though in data, the relation goes the other + * way around (children point to or "use" the parent). + * + * Only handles regular object parenting, not cases like the "Child of" constraint. Other Outliner + * display modes don't show this as parent in the hierarchy either. + */ +static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, + const ID &parent_id, + FunctionRef<void(ID &)> fn) { - if (!*cb_data->id_pointer) { - return IDWALK_RET_NOP; + const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id)); + + /* Iterate over all IDs used by the parent ID (e.g. the child-collections of a collection). */ + for (MainIDRelationsEntryItem *to_id_entry = relations_of_id->to_ids; to_id_entry; + to_id_entry = to_id_entry->next) { + /* An ID pointed to (used) by the ID to recurse into. */ + ID &target_id = **to_id_entry->id_pointer.to; + + /* Don't walk up the hierarchy, e.g. ignore pointers to parent collections. */ + if (to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) { + continue; + } + + /* Special case for objects: Process the parent object instead of the child object. Below the + * parent will add the child objects then. */ + if (GS(target_id.name) == ID_OB) { + const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id); + if (potential_child_ob.parent) { + fn(potential_child_ob.parent->id); + continue; + } + } + + fn(target_id); + } + + /* If the ID is an object, find and iterate over any child objects. */ + if (GS(parent_id.name) == ID_OB) { + for (MainIDRelationsEntryItem *from_id_entry = relations_of_id->from_ids; from_id_entry; + from_id_entry = from_id_entry->next) { + ID &potential_child_id = *from_id_entry->id_pointer.from; + + if (GS(potential_child_id.name) != ID_OB) { + continue; + } + + Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id); + if (potential_child_ob.parent && &potential_child_ob.parent->id == &parent_id) { + fn(potential_child_id); + } + } } +} - BuildHierarchyForeachIDCbData &build_data = *reinterpret_cast<BuildHierarchyForeachIDCbData *>( - cb_data->user_data); - /* Note that this may be an embedded ID (see #real_override_id). */ - ID &id = **cb_data->id_pointer; +static bool id_is_in_override_hierarchy(const ID &id, + const ID &relationship_parent_id, + const ID &override_root_id) +{ /* If #id is an embedded ID, this will be set to the owner, which is a real ID and contains the * override data. So queries of override data should be done via this, but the actual tree * element we add is the embedded ID. */ const ID *real_override_id = &id; if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) { + /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no + * other ID directly uses the embedded one. Should be true, but the debug code adds some checks + * to validate this assumption. */ + real_override_id = &relationship_parent_id; + +#ifndef NDEBUG if (GS(id.name) == ID_KE) { - Key *key = (Key *)&id; - real_override_id = key->from; + const Key *key = (Key *)&id; + BLI_assert(real_override_id == key->from); } - else if (id.flag & LIB_EMBEDDED_DATA) { - /* TODO Needs double-checking if this handles all embedded IDs correctly. */ - real_override_id = cb_data->id_owner; + else { + BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0); } +#endif } if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) { - return IDWALK_RET_NOP; + return false; } /* Is this ID part of the same override hierarchy? */ - if (real_override_id->override_library->hierarchy_root != &build_data.override_root_id) { - return IDWALK_RET_NOP; - } - - /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into itself. - */ - if (build_data.parent_ids.lookup_key_default(&id, nullptr)) { - return IDWALK_RET_NOP; - } - - /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used multiple - * times by the same parent. */ - if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) { - return IDWALK_RET_NOP; + if (real_override_id->override_library->hierarchy_root != &override_root_id) { + return false; } - TreeElement *new_te = outliner_add_element(&build_data.space_outliner, - &build_data.parent_te->getLegacyElement().subtree, - &id, - &build_data.parent_te->getLegacyElement(), - TSE_SOME_ID, - 0, - false); - build_data.sibling_ids.add(&id); - - BuildHierarchyForeachIDCbData child_build_data{build_data.bmain, - build_data.space_outliner, - build_data.override_root_id, - tree_element_cast<TreeElementID>(new_te)}; - child_build_data.parent_ids = build_data.parent_ids; - child_build_data.parent_ids.add(&id); - child_build_data.sibling_ids.reserve(10); - BKE_library_foreach_ID_link( - &build_data.bmain, &id, build_hierarchy_foreach_ID_cb, &child_build_data, IDWALK_READONLY); - - return IDWALK_RET_NOP; + return true; } -void TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_ID(Main *bmain, - ID &override_root_id, - TreeElementID &te_id) const -{ - BuildHierarchyForeachIDCbData build_data{*bmain, space_outliner_, override_root_id, &te_id}; - build_data.parent_ids.add(&override_root_id); - - BKE_library_foreach_ID_link( - bmain, &te_id.get_ID(), build_hierarchy_foreach_ID_cb, &build_data, IDWALK_READONLY); -} +/** \} */ } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 0f6d6930530..dd6d58ee5a2 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -192,7 +192,7 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) Sequence *seq; Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_ensure(scene); - int timeline_frame = (int)CFRA; + int timeline_frame = (int)scene->r.cfra; int proximity = INT_MAX; if (!ed || !ed->seqbasep) { @@ -218,7 +218,7 @@ static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, i { Scene *scene = CTX_data_scene(C); - int timeline_frame = (int)CFRA; + int timeline_frame = (int)scene->r.cfra; /* Effect strips don't need a channel initialized from the mouse. */ if (!(flag & SEQPROP_NOCHAN) && RNA_struct_property_is_set(op->ptr, "channel") == 0) { diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c index 586cdad7e74..f6561cf07b9 100644 --- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c +++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c @@ -179,6 +179,7 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop) if (max_channel != -1) { RNA_int_set(drop->ptr, "channel", max_channel); } + SEQ_collection_free(strips); } } } @@ -233,7 +234,7 @@ static void update_overlay_strip_poistion_data(bContext *C, const int mval[2]) coords->is_intersecting = false; Sequence dummy_seq = { .machine = coords->channel, .start = coords->start_frame, .len = coords->strip_len}; - Editing *ed = SEQ_editing_get(scene); + Editing *ed = SEQ_editing_ensure(scene); for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) { coords->is_intersecting = SEQ_transform_test_overlap(scene, ed->seqbasep, &dummy_seq); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 4b9ff1e170e..eb2e4ef05e5 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -241,329 +241,296 @@ typedef struct WaveVizData { float pos[2]; float rms_pos; bool clip; - bool end; + bool draw_line; /* Draw triangle otherwise. */ + bool final_sample; /* There are no more samples. */ } WaveVizData; -static int get_section_len(WaveVizData *start, WaveVizData *end) +static bool seq_draw_waveforms_poll(const bContext *UNUSED(C), SpaceSeq *sseq, Sequence *seq) { - int len = 0; - while (start != end) { - len++; - if (start->end) { - return len; - } - start++; - } - return len; -} + const bool strip_is_valid = seq->type == SEQ_TYPE_SOUND_RAM && seq->sound != NULL; + const bool overlays_enabled = (sseq->flag & SEQ_SHOW_OVERLAY) != 0; + const bool ovelay_option = ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) != 0 || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM)); -static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms) -{ - int strip_len = get_section_len(iter, end); - if (strip_len > 1) { - GPU_blend(GPU_BLEND_ALPHA); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) != 0) { + return false; + } - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(prim_type, strip_len); + if (strip_is_valid && overlays_enabled && ovelay_option) { + return true; + } - while (iter != end) { - if (iter->clip) { - immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); - } - else if (use_rms) { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); - } - else { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); - } + return false; +} - if (use_rms) { - immVertex2f(pos, iter->pos[0], iter->rms_pos); - } - else { - immVertex2f(pos, iter->pos[0], iter->pos[1]); - } +static void waveform_job_start_if_needed(const bContext *C, Sequence *seq) +{ + bSound *sound = seq->sound; - if (iter->end) { - /* End of line. */ - iter++; - strip_len = get_section_len(iter, end); - if (strip_len != 0) { - immEnd(); - immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(prim_type, strip_len); - } - } - else { - iter++; - } + BLI_spin_lock(sound->spinlock); + if (!sound->waveform) { + /* Load the waveform data if it hasn't been loaded and cached already. */ + if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { + /* Prevent sounds from reloading. */ + sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; + BLI_spin_unlock(sound->spinlock); + sequencer_preview_add_sound(C, seq); + } + else { + BLI_spin_unlock(sound->spinlock); } - immEnd(); - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); } + BLI_spin_unlock(sound->spinlock); } -static float clamp_frame_coord_to_pixel(float frame_coord, - float pixel_frac, - float frames_per_pixel) +static size_t get_vertex_count(WaveVizData *waveform_data) { - float cur_pixel = (frame_coord / frames_per_pixel); - float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac; - if (cur_pixel > new_pixel) { - new_pixel += 1.0f; + bool draw_line = waveform_data->draw_line; + size_t length = 0; + + while (waveform_data->draw_line == draw_line && !waveform_data->final_sample) { + waveform_data++; + length++; } - return new_pixel * frames_per_pixel; + + return length; } -/** - * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. - * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). - */ -static void draw_seq_waveform_overlay(View2D *v2d, - const bContext *C, - SpaceSeq *sseq, - Scene *scene, - Sequence *seq, - float x1, - float y1, - float x2, - float y2, - float frames_per_pixel) +static size_t draw_waveform_segment(WaveVizData *waveform_data, bool use_rms) { - if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || - (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { - /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid - * flickering when moving around the strip. - * To do this we figure out the fractional offset in pixel space by checking where the - * window starts. - * We then append this pixel offset to our strip start coordinate to ensure we are aligned to - * the screen pixel grid. */ - float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel); - float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel); - - /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ - float x1_offset = max_ff(v2d->cur.xmin, x1_adj); - float x2_offset = min_ff(v2d->cur.xmax, x2); - - /* Calculate how long the strip that is in view is in pixels. */ - int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel); - - if (pix_strip_len < 2) { - return; - } + size_t vertices_done = 0; + size_t vertex_count = get_vertex_count(waveform_data); - bSound *sound = seq->sound; + /* Not enough data to draw. */ + if (vertex_count <= 2) { + return vertex_count; + } - BLI_spin_lock(sound->spinlock); - if (!sound->waveform) { - /* Load the waveform data if it hasn't been loaded and cached already. */ - if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { - /* Prevent sounds from reloading. */ - sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; - BLI_spin_unlock(sound->spinlock); - sequencer_preview_add_sound(C, seq); - } - else { - BLI_spin_unlock(sound->spinlock); - } - return; /* Nothing to draw. */ - } - BLI_spin_unlock(sound->spinlock); + GPU_blend(GPU_BLEND_ALPHA); + GPUVertFormat *format = immVertexFormat(); + GPUPrimType prim_type = waveform_data->draw_line ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP; + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, vertex_count); - SoundWaveform *waveform = sound->waveform; + while (vertices_done < vertex_count && !waveform_data->final_sample) { + /* Color. */ + if (waveform_data->clip) { + immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + } + else if (use_rms) { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); + } + else { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + } - /* Waveform could not be built. */ - if (waveform->length == 0) { - return; + /* Vertices. */ + if (use_rms) { + immVertex2f(pos, waveform_data->pos[0], waveform_data->rms_pos); + } + else { + immVertex2f(pos, waveform_data->pos[0], waveform_data->pos[1]); } - /* F-Curve lookup is quite expensive, so do this after precondition. */ - FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + vertices_done++; + waveform_data++; + } - WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2, - "tri_strip"); - WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len, - "line_strip"); + immEnd(); + immUnbindProgram(); - WaveVizData *tri_strip_iter = tri_strip_arr; - WaveVizData *line_strip_iter = line_strip_arr; + GPU_blend(GPU_BLEND_NONE); - /* The y coordinate for the middle of the strip. */ - float y_mid = (y1 + y2) / 2.0f; - /* The length from the middle of the strip to the top/bottom. */ - float y_scale = (y2 - y1) / 2.0f; - float volume = seq->volume; + return vertices_done; +} - /* Value to keep track if the previous item to be drawn was a line strip. */ - int8_t was_line_strip = -1; /* -1 == no previous value. */ +static void draw_waveform(WaveVizData *waveform_data, size_t wave_data_len) +{ + size_t items_done = 0; + while (items_done < wave_data_len) { + if (!waveform_data[items_done].draw_line) { /* Draw RMS. */ + draw_waveform_segment(&waveform_data[items_done], true); + } + items_done += draw_waveform_segment(&waveform_data[items_done], false); + } +} - float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; +static float align_frame_with_pixel(float frame_coord, float frames_per_pixel) +{ + return round_fl_to_int(frame_coord / frames_per_pixel) * frames_per_pixel; +} - /* How many samples do we have for each pixel? */ - float samples_per_pix = samples_per_frame * frames_per_pixel; +static void write_waveform_data(WaveVizData *waveform_data, + const vec2f pos, + const float rms, + const bool is_clipping, + const bool draw_line) +{ + waveform_data->pos[0] = pos.x; + waveform_data->pos[1] = pos.y; + waveform_data->clip = is_clipping; + waveform_data->rms_pos = rms; + waveform_data->draw_line = draw_line; +} - float strip_start_offset = seq->startofs + seq->anim_startofs; - float start_sample = 0; +static size_t waveform_append_sample(WaveVizData *waveform_data, + vec2f pos, + const float value_min, + const float value_max, + const float y_mid, + const float y_scale, + const float rms, + const bool is_clipping, + const bool is_line_strip) +{ + size_t data_written = 0; + pos.y = y_mid + value_min * y_scale; + float rms_value = y_mid + max_ff(-rms, value_min) * y_scale; + write_waveform_data(&waveform_data[0], pos, rms_value, is_clipping, is_line_strip); + data_written++; + + /* Use `value_max` as second vertex for triangle drawing. */ + if (!is_line_strip) { + pos.y = y_mid + value_max * y_scale; + rms_value = y_mid + min_ff(rms, value_max) * y_scale; + write_waveform_data(&waveform_data[1], pos, rms_value, is_clipping, is_line_strip); + data_written++; + } + return data_written; +} - if (strip_start_offset != 0) { - /* If start offset is not zero, we need to make sure that we pick the same start sample as if - * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering - * when changing start offset as the pixel alignment will not be the same for the drawn - * samples. */ - strip_start_offset = clamp_frame_coord_to_pixel( - x1 - strip_start_offset, pixel_frac, frames_per_pixel); - start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame; - } +/** + * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. + * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). + */ +static void draw_seq_waveform_overlay( + const bContext *C, ARegion *region, Sequence *seq, float x1, float y1, float x2, float y2) +{ + const View2D *v2d = ®ion->v2d; + Scene *scene = CTX_data_scene(C); - start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; - /* If we scrolled the start off-screen, then the start sample should be at the first visible - * sample. */ - start_sample += (x1_offset - x1_adj) * samples_per_frame; + const float frames_per_pixel = BLI_rctf_size_x(®ion->v2d.cur) / region->winx; + const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; + float samples_per_pixel = samples_per_frame * frames_per_pixel; - for (int i = 0; i < pix_strip_len; i++) { - float sample_offset = start_sample + i * samples_per_pix; - int p = sample_offset; + /* Align strip start with nearest pixel to prevent waveform flickering. */ + const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel); + /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ + const float frame_start = max_ff(v2d->cur.xmin, x1_aligned); + const float frame_end = min_ff(v2d->cur.xmax, x2); + const int pixels_to_draw = round_fl_to_int((frame_end - frame_start) / frames_per_pixel); - if (p < 0) { - continue; - } + if (pixels_to_draw < 2) { + return; /* Not much to draw, exit before running job. */ + } - if (p >= waveform->length) { - break; - } + waveform_job_start_if_needed(C, seq); - float value_min = waveform->data[p * 3]; - float value_max = waveform->data[p * 3 + 1]; - float rms = waveform->data[p * 3 + 2]; - - if (p + 1 < waveform->length) { - /* Use simple linear interpolation. */ - float f = sample_offset - p; - value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3]; - value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4]; - rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5]; - if (samples_per_pix > 1.0f) { - /* We need to sum up the values we skip over until the next step. */ - float next_pos = sample_offset + samples_per_pix; - int end_idx = next_pos; - - for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) { - value_min = min_ff(value_min, waveform->data[j * 3]); - value_max = max_ff(value_max, waveform->data[j * 3 + 1]); - rms = max_ff(rms, waveform->data[j * 3 + 2]); - } - } - } + SoundWaveform *waveform = seq->sound->waveform; + if (waveform == NULL || waveform->length == 0) { + return; /* Waveform was not built. */ + } - if (fcu && !BKE_fcurve_is_empty(fcu)) { - float evaltime = x1_offset + (i * frames_per_pixel); - volume = evaluate_fcurve(fcu, evaltime); - CLAMP_MIN(volume, 0.0f); - } + /* F-Curve lookup is quite expensive, so do this after precondition. */ + FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + WaveVizData *waveform_data = MEM_callocN(sizeof(WaveVizData) * pixels_to_draw * 3, __func__); + size_t wave_data_len = 0; - value_min *= volume; - value_max *= volume; - rms *= volume; + /* Offset must be also aligned, otherwise waveform flickers when moving left handle. */ + const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs, + frames_per_pixel); + float start_sample = strip_offset * samples_per_frame; + start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + /* Add off-screen part of strip to offset. */ + start_sample += (frame_start - x1_aligned) * samples_per_frame; - bool clipping = false; + for (int i = 0; i < pixels_to_draw; i++) { + float sample = start_sample + i * samples_per_pixel; + int sample_index = round_fl_to_int(sample); - if (value_max > 1 || value_min < -1) { - clipping = true; + if (sample_index < 0) { + continue; + } - CLAMP_MAX(value_max, 1.0f); - CLAMP_MIN(value_min, -1.0f); - } + if (sample_index >= waveform->length) { + break; + } - bool is_line_strip = (value_max - value_min < 0.05f); - - if (!ELEM(was_line_strip, -1, is_line_strip)) { - /* If the previously added strip type isn't the same as the current one, - * add transition areas so they transition smoothly between each other. */ - if (is_line_strip) { - /* This will be a line strip, end the tri strip. */ - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_min * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = tri_strip_iter->pos[1]; - tri_strip_iter->end = true; - - /* End of section. */ - tri_strip_iter++; - - /* Check if we are at the end. - * If so, skip one point line. */ - if (i + 1 == pix_strip_len) { - continue; - } - } - else { - /* This will be a tri strip. */ - line_strip_iter--; - tri_strip_iter->pos[0] = line_strip_iter->pos[0]; - tri_strip_iter->pos[1] = line_strip_iter->pos[1]; - tri_strip_iter->clip = line_strip_iter->clip; - tri_strip_iter->rms_pos = line_strip_iter->pos[1]; - tri_strip_iter++; - - /* Check if line had only one point. */ - line_strip_iter--; - if (line_strip_iter < line_strip_arr || line_strip_iter->end) { - /* Only one point, skip it. */ - line_strip_iter++; - } - else { - /* End of section. */ - line_strip_iter++; - line_strip_iter->end = true; - line_strip_iter++; - } + float value_min = waveform->data[sample_index * 3]; + float value_max = waveform->data[sample_index * 3 + 1]; + float rms = waveform->data[sample_index * 3 + 2]; + + if (sample_index + 1 < waveform->length) { + /* Use simple linear interpolation. */ + float f = sample - sample_index; + value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3]; + value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4]; + rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5]; + if (samples_per_pixel > 1.0f) { + /* We need to sum up the values we skip over until the next step. */ + float next_pos = sample + samples_per_pixel; + int end_idx = next_pos; + + for (int j = sample_index + 1; (j < waveform->length) && (j < end_idx); j++) { + value_min = min_ff(value_min, waveform->data[j * 3]); + value_max = max_ff(value_max, waveform->data[j * 3 + 1]); + rms = max_ff(rms, waveform->data[j * 3 + 2]); } } + } - was_line_strip = is_line_strip; - - if (is_line_strip) { - line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - line_strip_iter->pos[1] = y_mid + value_min * y_scale; - line_strip_iter->clip = clipping; - line_strip_iter++; - } - else { - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_min * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale; - tri_strip_iter++; - - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_max * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale; - tri_strip_iter++; - } + float volume = seq->volume; + if (fcu && !BKE_fcurve_is_empty(fcu)) { + float evaltime = frame_start + (i * frames_per_pixel); + volume = evaluate_fcurve(fcu, evaltime); + CLAMP_MIN(volume, 0.0f); } - WaveVizData *tri_strip_end = tri_strip_iter; - WaveVizData *line_strip_end = line_strip_iter; + value_min *= volume; + value_max *= volume; + rms *= volume; + + bool is_clipping = false; + + if (value_max > 1 || value_min < -1) { + is_clipping = true; - tri_strip_iter = tri_strip_arr; - line_strip_iter = line_strip_arr; + CLAMP_MAX(value_max, 1.0f); + CLAMP_MIN(value_min, -1.0f); + } - draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false); - draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false); - draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true); + bool is_line_strip = (value_max - value_min < 0.05f); + /* The y coordinate for the middle of the strip. */ + float y_mid = (y1 + y2) / 2.0f; + /* The length from the middle of the strip to the top/bottom. */ + float y_scale = (y2 - y1) / 2.0f; - MEM_freeN(tri_strip_arr); - MEM_freeN(line_strip_arr); + vec2f pos = {frame_start + i * frames_per_pixel, y_mid + value_min * y_scale}; + WaveVizData *new_data = &waveform_data[wave_data_len]; + wave_data_len += waveform_append_sample( + new_data, pos, value_min, value_max, y_mid, y_scale, rms, is_clipping, is_line_strip); } + + /* Terminate array, so `get_segment_length()` can know when to stop. */ + waveform_data[wave_data_len].final_sample = true; + draw_waveform(waveform_data, wave_data_len); + MEM_freeN(waveform_data); } +/* +static size_t *waveform_append(WaveVizData *waveform_data, + vec2f pos, + const float value_min, + const float value_max, + const float y_mid, + const float y_scale, + const float rms, + const bool is_clipping, + const bool is_line_strip) +*/ + static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, @@ -1430,18 +1397,9 @@ static void draw_seq_strip(const bContext *C, } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) && - (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) { - draw_seq_waveform_overlay(v2d, - C, - sseq, - scene, - seq, - x1, - y_threshold ? y1 + 0.05f : y1, - x2, - y_threshold ? text_margin_y : y2, - BLI_rctf_size_x(®ion->v2d.cur) / region->winx); + if (seq_draw_waveforms_poll(C, sseq, seq)) { + draw_seq_waveform_overlay( + C, region, seq, x1, y_threshold ? y1 + 0.05f : y1, x2, y_threshold ? text_margin_y : y2); } /* Draw locked state. */ if (SEQ_transform_is_locked(channels, seq)) { @@ -1762,8 +1720,7 @@ void sequencer_draw_maskedit(const bContext *C, Scene *scene, ARegion *region, S // ED_mask_get_size(C, &width, &height); //Scene *scene = CTX_data_scene(C); - width = (scene->r.size * scene->r.xsch) / 100; - height = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &width, &height); ED_mask_draw_region(mask, region, diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index d507c9ae6bc..cb95e9a75de 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -265,7 +265,7 @@ static int sequencer_gap_remove_exec(bContext *C, wmOperator *op) const bool do_all = RNA_boolean_get(op->ptr, "all"); const Editing *ed = SEQ_editing_get(scene); - SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all); + SEQ_edit_remove_gaps(scene, ed->seqbasep, scene->r.cfra, do_all); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -304,7 +304,7 @@ static int sequencer_gap_insert_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const int frames = RNA_int_get(op->ptr, "frames"); const Editing *ed = SEQ_editing_get(scene); - SEQ_transform_offset_after_frame(scene, ed->seqbasep, frames, CFRA); + SEQ_transform_offset_after_frame(scene, ed->seqbasep, frames, scene->r.cfra); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -423,7 +423,7 @@ static int sequencer_snap_invoke(bContext *C, wmOperator *op, const wmEvent *UNU int snap_frame; - snap_frame = CFRA; + snap_frame = scene->r.cfra; RNA_int_set(op->ptr, "frame", snap_frame); return sequencer_snap_exec(C, op); @@ -1465,7 +1465,7 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev View2D *v2d = UI_view2d_fromcontext(C); int split_side = RNA_enum_get(op->ptr, "side"); - int split_frame = CFRA; + int split_frame = scene->r.cfra; if (split_side == SEQ_SIDE_MOUSE) { if (ED_operator_sequencer_active(C) && v2d) { @@ -1703,8 +1703,10 @@ static int sequencer_delete_exec(bContext *C, wmOperator *op) static int sequencer_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + ListBase *markers = &scene->markers; - if (region->regiontype == RGN_TYPE_WINDOW) { + if (region->regiontype == RGN_TYPE_WINDOW && !BLI_listbase_is_empty(markers)) { /* Bounding box of 30 pixels is used for markers shortcuts, * prevent conflict with markers shortcuts here. */ @@ -2091,12 +2093,12 @@ static bool strip_jump_internal(Scene *scene, const bool do_center) { bool changed = false; - int timeline_frame = CFRA; + int timeline_frame = scene->r.cfra; int next_frame = SEQ_time_find_next_prev_edit( scene, timeline_frame, side, do_skip_mute, do_center, false); if (next_frame != timeline_frame) { - CFRA = next_frame; + scene->r.cfra = next_frame; changed = true; } @@ -3408,7 +3410,7 @@ static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op) for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) { - const int timeline_frame = CFRA; + const int timeline_frame = scene->r.cfra; StripElem *strip_elem = SEQ_render_give_stripelem(scene, seq, timeline_frame); if (strip_elem == NULL) { diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index cfdbeaf0658..4aaa3aeb2ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -589,8 +589,10 @@ static void sequencer_select_side_of_frame(const bContext *C, const float x = UI_view2d_region_to_view_x(v2d, mval[0]); LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { - if (((x < CFRA) && (SEQ_time_right_handle_frame_get(scene, seq_iter) <= CFRA)) || - ((x >= CFRA) && (SEQ_time_left_handle_frame_get(scene, seq_iter) >= CFRA))) { + if (((x < scene->r.cfra) && + (SEQ_time_right_handle_frame_get(scene, seq_iter) <= scene->r.cfra)) || + ((x >= scene->r.cfra) && + (SEQ_time_left_handle_frame_get(scene, seq_iter) >= scene->r.cfra))) { /* Select left or right. */ seq_iter->flag |= SELECT; recurs_sel_seq(seq_iter); @@ -603,8 +605,8 @@ static void sequencer_select_side_of_frame(const bContext *C, TimeMarker *tmarker; for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) { - if (((x < CFRA) && (tmarker->frame <= CFRA)) || - ((x >= CFRA) && (tmarker->frame >= CFRA))) { + if (((x < scene->r.cfra) && (tmarker->frame <= scene->r.cfra)) || + ((x >= scene->r.cfra) && (tmarker->frame >= scene->r.cfra))) { tmarker->flag |= SELECT; } else { @@ -1443,7 +1445,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) if (extend == false) { ED_sequencer_deselect_all(scene); } - const int timeline_frame = CFRA; + const int timeline_frame = scene->r.cfra; LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { bool test = false; switch (side) { diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 4d32c00109a..78fa8c379d9 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -12,6 +12,7 @@ #include "DNA_scene_types.h" #include "BKE_context.h" +#include "BKE_scene.h" #include "WM_api.h" #include "WM_types.h" @@ -174,8 +175,7 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) seq_reset_imageofs(sseq); - imgwidth = (scene->r.size * scene->r.xsch) / 100; - imgheight = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &imgwidth, &imgheight); /* Apply aspect, doesn't need to be that accurate. */ imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp)); @@ -227,11 +227,11 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op) float ratio = RNA_float_get(op->ptr, "ratio"); - float winx = (int)(rd->size * rd->xsch) / 100; - float winy = (int)(rd->size * rd->ysch) / 100; + int winx, winy; + BKE_render_resolution(rd, false, &winx, &winy); - float facx = BLI_rcti_size_x(&v2d->mask) / winx; - float facy = BLI_rcti_size_y(&v2d->mask) / winy; + float facx = BLI_rcti_size_x(&v2d->mask) / (float)winx; + float facy = BLI_rcti_size_y(&v2d->mask) / (float)winy; BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f)); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh index 76c7131e268..6a09d3f84c6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh @@ -27,9 +27,8 @@ class DataSource { * column. (This can be made a bit more generic in the future when necessary.) */ virtual void foreach_default_column_ids( - FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> /*fn*/) const { - UNUSED_VARS(fn); } /** @@ -37,9 +36,8 @@ class DataSource { * returned. */ virtual std::unique_ptr<ColumnValues> get_column_values( - const SpreadsheetColumnID &column_id) const + const SpreadsheetColumnID & /*column_id*/) const { - UNUSED_VARS(column_id); return {}; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index f5315b616d0..fdcf9798b7f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -1,8 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_index_mask_ops.hh" #include "BLI_virtual_array.hh" +#include "BKE_attribute.hh" #include "BKE_context.h" +#include "BKE_curves.hh" #include "BKE_editmesh.h" #include "BKE_geometry_fields.hh" #include "BKE_global.h" @@ -20,6 +23,7 @@ #include "DEG_depsgraph_query.h" +#include "ED_curves_sculpt.h" #include "ED_spreadsheet.h" #include "NOD_geometry_nodes_eval_log.hh" @@ -64,7 +68,12 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { - if (component_->attribute_domain_num(domain_) == 0) { + if (!component_->attributes().has_value()) { + return; + } + const bke::AttributeAccessor attributes = *component_->attributes(); + + if (attributes.domain_size(domain_) == 0) { return; } @@ -73,8 +82,9 @@ void GeometryDataSource::foreach_default_column_ids( } extra_columns_.foreach_default_column_ids(fn); - component_->attribute_foreach( - [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + + attributes.for_all( + [&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) { if (meta_data.domain != domain_) { return true; } @@ -113,7 +123,11 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { - const int domain_num = component_->attribute_domain_num(domain_); + if (!component_->attributes().has_value()) { + return {}; + } + const bke::AttributeAccessor attributes = *component_->attributes(); + const int domain_num = attributes.domain_size(domain_); if (domain_num == 0) { return {}; } @@ -199,7 +213,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( } } - bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); + bke::GAttributeReader attribute = attributes.lookup(column_id.name); if (!attribute) { return {}; } @@ -213,87 +227,112 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( int GeometryDataSource::tot_rows() const { - return component_->attribute_domain_num(domain_); + if (!component_->attributes().has_value()) { + return {}; + } + const bke::AttributeAccessor attributes = *component_->attributes(); + return attributes.domain_size(domain_); } -/** - * Only data sets corresponding to mesh objects in edit mode currently support selection filtering. - */ bool GeometryDataSource::has_selection_filter() const { Object *object_orig = DEG_get_original_object(object_eval_); - if (object_orig->type != OB_MESH) { - return false; - } - if (object_orig->mode != OB_MODE_EDIT) { - return false; - } - if (component_->type() != GEO_COMPONENT_TYPE_MESH) { - return false; - } - - return true; -} - -static IndexMask index_mask_from_bool_array(const VArray<bool> &selection, - Vector<int64_t> &indices) -{ - for (const int i : selection.index_range()) { - if (selection[i]) { - indices.append(i); + switch (component_->type()) { + case GEO_COMPONENT_TYPE_MESH: { + if (object_orig->type != OB_MESH) { + return false; + } + if (object_orig->mode != OB_MODE_EDIT) { + return false; + } + return true; } + case GEO_COMPONENT_TYPE_CURVE: { + if (object_orig->type != OB_CURVES) { + return false; + } + if (object_orig->mode != OB_MODE_SCULPT_CURVES) { + return false; + } + return true; + } + default: + return false; } - return IndexMask(indices); } IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) const { std::lock_guard lock{mutex_}; + const IndexMask full_range(this->tot_rows()); + + switch (component_->type()) { + case GEO_COMPONENT_TYPE_MESH: { + BLI_assert(object_eval_->type == OB_MESH); + BLI_assert(object_eval_->mode == OB_MODE_EDIT); + Object *object_orig = DEG_get_original_object(object_eval_); + const Mesh *mesh_eval = geometry_set_.get_mesh_for_read(); + const bke::AttributeAccessor attributes_eval = bke::mesh_attributes(*mesh_eval); + Mesh *mesh_orig = (Mesh *)object_orig->data; + BMesh *bm = mesh_orig->edit_mesh->bm; + BM_mesh_elem_table_ensure(bm, BM_VERT); + + const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); + if (orig_indices != nullptr) { + /* Use CD_ORIGINDEX layer if it exists. */ + VArray<bool> selection = attributes_eval.adapt_domain<bool>( + VArray<bool>::ForFunc(mesh_eval->totvert, + [bm, orig_indices](int vertex_index) -> bool { + const int i_orig = orig_indices[vertex_index]; + if (i_orig < 0) { + return false; + } + if (i_orig >= bm->totvert) { + return false; + } + const BMVert *vert = BM_vert_at_index(bm, i_orig); + return BM_elem_flag_test(vert, BM_ELEM_SELECT); + }), + ATTR_DOMAIN_POINT, + domain_); + return index_mask_ops::find_indices_from_virtual_array( + full_range, selection, 1024, indices); + } - BLI_assert(object_eval_->mode == OB_MODE_EDIT); - BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH); - Object *object_orig = DEG_get_original_object(object_eval_); - const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_); - const Mesh *mesh_eval = mesh_component->get_for_read(); - Mesh *mesh_orig = (Mesh *)object_orig->data; - BMesh *bm = mesh_orig->edit_mesh->bm; - BM_mesh_elem_table_ensure(bm, BM_VERT); - - const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); - if (orig_indices != nullptr) { - /* Use CD_ORIGINDEX layer if it exists. */ - VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>( - VArray<bool>::ForFunc(mesh_eval->totvert, - [bm, orig_indices](int vertex_index) -> bool { - const int i_orig = orig_indices[vertex_index]; - if (i_orig < 0) { - return false; - } - if (i_orig >= bm->totvert) { - return false; - } - BMVert *vert = bm->vtable[i_orig]; - return BM_elem_flag_test(vert, BM_ELEM_SELECT); - }), - ATTR_DOMAIN_POINT, - domain_); - return index_mask_from_bool_array(selection, indices); - } - - if (mesh_eval->totvert == bm->totvert) { - /* Use a simple heuristic to match original vertices to evaluated ones. */ - VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>( - VArray<bool>::ForFunc(mesh_eval->totvert, - [bm](int vertex_index) -> bool { - BMVert *vert = bm->vtable[vertex_index]; - return BM_elem_flag_test(vert, BM_ELEM_SELECT); - }), - ATTR_DOMAIN_POINT, - domain_); - return index_mask_from_bool_array(selection, indices); - } - - return IndexMask(mesh_eval->totvert); + if (mesh_eval->totvert == bm->totvert) { + /* Use a simple heuristic to match original vertices to evaluated ones. */ + VArray<bool> selection = attributes_eval.adapt_domain<bool>( + VArray<bool>::ForFunc(mesh_eval->totvert, + [bm](int vertex_index) -> bool { + const BMVert *vert = BM_vert_at_index(bm, vertex_index); + return BM_elem_flag_test(vert, BM_ELEM_SELECT); + }), + ATTR_DOMAIN_POINT, + domain_); + return index_mask_ops::find_indices_from_virtual_array( + full_range, selection, 2048, indices); + } + + return full_range; + } + case GEO_COMPONENT_TYPE_CURVE: { + BLI_assert(object_eval_->type == OB_CURVES); + BLI_assert(object_eval_->mode == OB_MODE_SCULPT_CURVES); + const CurveComponent &component = static_cast<const CurveComponent &>(*component_); + const Curves &curves_id = *component.get_for_read(); + switch (domain_) { + case ATTR_DOMAIN_POINT: + return sculpt_paint::retrieve_selected_points(curves_id, indices); + case ATTR_DOMAIN_CURVE: + return sculpt_paint::retrieve_selected_curves(curves_id, indices); + default: + BLI_assert_unreachable(); + } + return full_range; + } + default: + return full_range; + } } void VolumeDataSource::foreach_default_column_ids( @@ -398,10 +437,15 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread geometry_set.get_component_for_write<PointCloudComponent>(); pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly); } + else if (object_orig->type == OB_CURVES) { + const Curves &curves_id = *(const Curves *)object_orig->data; + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + curve_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly); + } } else { if (object_eval->mode == OB_MODE_EDIT && object_eval->type == OB_MESH) { - Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false); + Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval); if (mesh == nullptr) { return geometry_set; } @@ -472,18 +516,6 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, } } -static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval) -{ - SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); - if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) { - return (GeometryComponentType)sspreadsheet->geometry_component_type; - } - if (object_eval->type == OB_POINTCLOUD) { - return GEO_COMPONENT_TYPE_POINT_CLOUD; - } - return GEO_COMPONENT_TYPE_MESH; -} - class GeometryComponentCacheKey : public SpreadsheetCache::Key { public: /* Use the pointer to the geometry component as a key to detect when the geometry changed. */ @@ -527,9 +559,9 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, std::make_unique<GeometryComponentCacheKey>(component)); const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain; - const int domain_num = component.attribute_domain_num(domain); + const int domain_num = component.attribute_domain_size(domain); for (const auto item : fields_to_show.items()) { - StringRef name = item.key; + const StringRef name = item.key; const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ @@ -543,7 +575,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, return evaluated_array; }); - r_extra_columns.add(std::move(name), evaluated_array.as_span()); + r_extra_columns.add(name, evaluated_array.as_span()); } } @@ -551,9 +583,9 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain; - const GeometryComponentType component_type = get_display_component_type(C, object_eval); + const GeometryComponentType component_type = GeometryComponentType( + sspreadsheet->geometry_component_type); GeometrySet geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet, object_eval); - if (!geometry_set.has(component_type)) { return {}; } @@ -563,10 +595,10 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object add_fields_as_extra_columns(sspreadsheet, component, extra_columns); if (component_type == GEO_COMPONENT_TYPE_VOLUME) { - return std::make_unique<VolumeDataSource>(geometry_set); + return std::make_unique<VolumeDataSource>(std::move(geometry_set)); } return std::make_unique<GeometryDataSource>( - object_eval, geometry_set, component_type, domain, std::move(extra_columns)); + object_eval, std::move(geometry_set), component_type, domain, std::move(extra_columns)); } } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 04b4f6d8d06..71bc4768949 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -68,10 +68,6 @@ class GeometryDataSource : public DataSource { return object_eval_; } - /** - * Only data sets corresponding to mesh objects in edit mode currently support selection - * filtering. - */ bool has_selection_filter() const override; IndexMask apply_selection_filter(Vector<int64_t> &indices) const; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index ee22f4173ab..146b6091bde 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -145,7 +145,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) * to the right side of the number, which it didn't have with the button. */ char element_count[7]; BLI_str_format_decimal_unit(element_count, *count); - UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); + UI_but_hint_drawstr_set((uiBut *)this->view_item_button(), element_count); } } @@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const } if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) { - return component->attribute_domain_num(*domain_); + return component->attribute_domain_size(*domain_); } return 0; diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index f7d7f9c6238..4b3ee1c15c7 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -264,7 +264,7 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* White-space (all ws. has been converted to spaces). */ + /* White-space (all white-space has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } @@ -287,18 +287,19 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_ else if ((*str != '#') && text_check_delim(*str)) { *fmt = FMT_TYPE_SYMBOL; } - /* Identifiers and other text (no previous ws. or delims. so text continues) */ + /* Identifiers and other text (no previous white-space/delimiters so text continues). */ else if (prev == FMT_TYPE_DEFAULT) { str += BLI_str_utf8_size_safe(str) - 1; *fmt = FMT_TYPE_DEFAULT; } - /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */ + /* Not white-space, a digit, punctuation, or continuing text. + * Must be new, check for special words. */ else { - /* Keep aligned args for readability. */ + /* Keep aligned arguments for readability. */ /* clang-format off */ - /* Special vars(v) or built-in keywords(b) */ - /* keep in sync with 'txtfmt_osl_format_identifier()' */ + /* Special `vars(v)` or built-in `keywords(b)` */ + /* keep in sync with `txtfmt_osl_format_identifier()`. */ if ((i = txtfmt_lua_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_lua_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; } diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index 27dc1174c49..575eadeee66 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -285,7 +285,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* White-space (all ws. has been converted to spaces). */ + /* White-space (all white-space has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } @@ -298,18 +298,19 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_ else if ((*str != '#') && text_check_delim(*str)) { *fmt = FMT_TYPE_SYMBOL; } - /* Identifiers and other text (no previous ws. or delims. so text continues) */ + /* Identifiers and other text (no previous white-space or delimiters. so text continues). */ else if (prev == FMT_TYPE_DEFAULT) { str += BLI_str_utf8_size_safe(str) - 1; *fmt = FMT_TYPE_DEFAULT; } - /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */ + /* Not white-space, a digit, punctuation, or continuing text. + * Must be new, check for special words. */ else { - /* Keep aligned args for readability. */ + /* Keep aligned arguments for readability. */ /* clang-format off */ /* Special vars(v) or built-in keywords(b) */ - /* keep in sync with 'txtfmt_osl_format_identifier()' */ + /* keep in sync with `txtfmt_osl_format_identifier()`. */ if ((i = txtfmt_osl_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_osl_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_osl_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED; diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index 7c2f098f0fa..7c2c4829ad3 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -857,7 +857,7 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* White-space (all ws. has been converted to spaces). */ + /* White-space (all white-space has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } @@ -880,18 +880,19 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_ else if (text_check_delim(*str)) { *fmt = FMT_TYPE_SYMBOL; } - /* Identifiers and other text (no previous ws. or delims. so text continues) */ + /* Identifiers and other text (no previous white-space/delimiters so text continues). */ else if (prev == FMT_TYPE_DEFAULT) { str += BLI_str_utf8_size_safe(str) - 1; *fmt = FMT_TYPE_DEFAULT; } - /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */ + /* Not white-space, a digit, punctuation, or continuing text. + * Must be new, check for special words. */ else { - /* Keep aligned args for readability. */ + /* Keep aligned arguments for readability. */ /* clang-format off */ /* Special vars(v) or built-in keywords(b) */ - /* keep in sync with 'txtfmt_pov_format_identifier()' */ + /* keep in sync with `txtfmt_pov_format_identifier()`. */ if ((i = txtfmt_pov_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_pov_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_pov_find_reserved_keywords(str)) != -1) { prev = FMT_TYPE_RESERVED; diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 6844f08cca6..dda3b7089fe 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -435,7 +435,7 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* White-space (all ws. has been converted to spaces). */ + /* White-space (all white-space has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } @@ -458,18 +458,19 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool else if ((*str != '#') && text_check_delim(*str)) { *fmt = FMT_TYPE_SYMBOL; } - /* Identifiers and other text (no previous ws. or delims. so text continues) */ + /* Identifiers and other text (no previous white-space/delimiters so text continues). */ else if (prev == FMT_TYPE_DEFAULT) { str += BLI_str_utf8_size_safe(str) - 1; *fmt = FMT_TYPE_DEFAULT; } - /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */ + /* Not white-space, a digit, punctuation, or continuing text. + * Must be new, check for special words */ else { - /* Keep aligned args for readability. */ + /* Keep aligned arguments for readability. */ /* clang-format off */ /* Special vars(v) or built-in keywords(b) */ - /* keep in sync with 'txtfmt_ini_format_identifier()' */ + /* keep in sync with `txtfmt_ini_format_identifier()`. */ if ((i = txtfmt_ini_find_keyword(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_ini_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED; } diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index 47d0168195b..28f536ffa98 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -59,9 +59,9 @@ static int txtfmt_py_find_builtinfunc(const char *string) /* clang-format off */ if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len; @@ -425,7 +425,7 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n } *fmt = FMT_TYPE_STRING; } - /* White-space (all ws. has been converted to spaces). */ + /* White-space (all white-space has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } @@ -447,18 +447,19 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n else if ((*str != '@') && text_check_delim(*str)) { *fmt = FMT_TYPE_SYMBOL; } - /* Identifiers and other text (no previous ws. or delims. so text continues) */ + /* Identifiers and other text (no previous white-space/delimiters so text continues). */ else if (prev == FMT_TYPE_DEFAULT) { str += BLI_str_utf8_size_safe(str) - 1; *fmt = FMT_TYPE_DEFAULT; } - /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */ + /* Not white-space, a digit, punctuation, or continuing text. + * Must be new, check for special words. */ else { - /* Keep aligned args for readability. */ + /* Keep aligned arguments for readability. */ /* clang-format off */ /* Special vars(v) or built-in keywords(b) */ - /* keep in sync with 'txtfmt_py_format_identifier()' */ + /* keep in sync with `txtfmt_py_format_identifier()`. */ if ((i = txtfmt_py_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL; } else if ((i = txtfmt_py_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD; } else if ((i = txtfmt_py_find_decorator(str)) != -1) { prev = FMT_TYPE_DIRECTIVE; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 05d51cf6362..33219092d20 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3396,7 +3396,8 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm return OPERATOR_PASS_THROUGH; } - if (!(event->ascii >= '0' && event->ascii <= '9')) { + const char event_ascii = WM_event_utf8_to_ascii(event); + if (!(event_ascii >= '0' && event_ascii <= '9')) { return OPERATOR_PASS_THROUGH; } @@ -3406,7 +3407,7 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm } jump_to *= 10; - jump_to += (int)(event->ascii - '0'); + jump_to += (int)(event_ascii - '0'); txt_move_toline(text, jump_to - 1, 0); last_jump = time; @@ -3495,16 +3496,8 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) } char str[BLI_UTF8_MAX + 1]; - size_t len; - - if (event->utf8_buf[0]) { - len = BLI_str_utf8_size_safe(event->utf8_buf); - memcpy(str, event->utf8_buf, len); - } - else { - /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); - } + const size_t len = BLI_str_utf8_size_safe(event->utf8_buf); + memcpy(str, event->utf8_buf, len); str[len] = '\0'; RNA_string_set(op->ptr, "text", str); diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index e9a9e690e60..bc68de1dfce 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -155,6 +155,9 @@ static void topbar_header_listener(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_WORKSPACE: + ED_region_tag_redraw(region); + break; case NC_SPACE: if (wmn->data == ND_SPACE_INFO) { ED_region_tag_redraw(region); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 9ed2fec96db..a423a842019 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1300,6 +1300,10 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_WORKSPACE: + /* In case the region displays workspace settings. */ + ED_region_tag_redraw(region); + break; } } 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_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index b9e4c19295d..df5ff163cf2 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1294,7 +1294,7 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y static void draw_selected_name( Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset) { - const int cfra = CFRA; + const int cfra = scene->r.cfra; const char *msg_pin = " (Pinned)"; const char *msg_sep = " : "; @@ -2247,16 +2247,16 @@ void view3d_depths_rect_create(ARegion *region, rcti *rect, ViewDepths *r_d) static ViewDepths *view3d_depths_create(ARegion *region) { ViewDepths *d = MEM_callocN(sizeof(ViewDepths), "ViewDepths"); - d->w = region->winx; - d->h = region->winy; { GPUViewport *viewport = WM_draw_region_get_viewport(region); GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); uint32_t *int_depths = GPU_texture_read(depth_tx, GPU_DATA_UINT_24_8, 0); + d->w = GPU_texture_width(depth_tx); + d->h = GPU_texture_height(depth_tx); d->depths = (float *)int_depths; /* Convert in-place. */ - int pixel_count = GPU_texture_width(depth_tx) * GPU_texture_height(depth_tx); + int pixel_count = d->w * d->h; for (int i = 0; i < pixel_count; i++) { d->depths[i] = (int_depths[i] >> 8u) / (float)0xFFFFFF; } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 5fbdbef676c..d6ddd6d044e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -413,7 +413,9 @@ static void view3d_set_1_to_1_viewborder(Scene *scene, { RegionView3D *rv3d = region->regiondata; float size[2]; - int im_width = (scene->r.size * scene->r.xsch) / 100; + + int im_width, im_height; + BKE_render_resolution(&scene->r, false, &im_width, &im_height); ED_view3d_calc_camera_border_size(scene, depsgraph, region, v3d, rv3d, size); @@ -908,12 +910,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..62dc461e05c 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, @@ -522,7 +523,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER; } - gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); + gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW); BKE_gpencil_free_strokes(gpf); for (ruler_item = gzgroup_ruler_item_first_get(gzgroup); ruler_item; @@ -576,7 +577,7 @@ static bool view3d_ruler_from_gpencil(const bContext *C, wmGizmoGroup *gzgroup) gpl = view3d_ruler_layer_get(scene->gpd); if (gpl) { bGPDframe *gpf; - gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); + gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV); if (gpf) { bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 471231b5f27..f1e9ac22882 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -659,7 +659,7 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event) if (event->type == TIMER && event->customdata == walk->timer) { walk->redraw = true; } - else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + else if (ISMOUSE_MOTION(event->type)) { #ifdef USE_TABLET_SUPPORT if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) { diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 306394ce53d..85b1af8e55d 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -633,7 +633,7 @@ bool ED_view3d_camera_autokey(const Scene *scene, const bool do_translate) { if (autokeyframe_cfra_can_key(scene, id_key)) { - const float cfra = (float)CFRA; + const float cfra = (float)scene->r.cfra; ListBase dsources = {NULL, NULL}; /* add data-source override for the camera object */ @@ -1492,10 +1492,12 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain, depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) { ObjectTfmProtectedChannels obtfm; float obmat_new[4][4]; + bool is_ortho_camera = false; if ((camera_ob_eval->type == OB_CAMERA) && (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) { ((Camera *)camera_ob->data)->ortho_scale = scale; + is_ortho_camera = true; } copy_m4_m4(obmat_new, camera_ob_eval->obmat); @@ -1508,6 +1510,9 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain, /* notifiers */ DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM); + if (is_ortho_camera) { + DEG_id_tag_update_ex(bmain, camera_ob->data, ID_RECALC_PARAMETERS); + } return true; } diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 68c4f4e76ca..6984dcb18d4 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC transform_convert_mesh_edge.c transform_convert_mesh_skin.c transform_convert_mesh_uv.c + transform_convert_mesh_vert_cdata.c transform_convert_nla.c transform_convert_node.c transform_convert_object.c diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 960d9a25ca7..8dcbf07b776 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -525,7 +525,8 @@ static void viewRedrawPost(bContext *C, TransInfo *t) UVCALC_TRANSFORM_CORRECT_SLIDE : UVCALC_TRANSFORM_CORRECT; - if ((t->data_type == TC_MESH_VERTS) && (t->settings->uvcalc_flag & uvcalc_correct_flag)) { + if ((t->data_type == &TransConvertType_Mesh) && + (t->settings->uvcalc_flag & uvcalc_correct_flag)) { WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } @@ -847,7 +848,7 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type) return false; } - if (t->data_type == TC_SEQ_IMAGE_DATA) { + if (t->data_type == &TransConvertType_SequencerImage) { /* Setup the 2d msg string so it writes out the transform space. */ msg_2d = msg_3d; @@ -1327,7 +1328,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } - if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (t->redraw && !ISMOUSE_MOTION(event->type)) { WM_window_status_area_tag_redraw(CTX_wm_window(t->context)); } @@ -1577,7 +1578,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..fc59787e1ec 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -38,6 +38,7 @@ struct ReportList; struct Scene; struct ScrArea; struct SnapObjectContext; +struct TransConvertTypeInfo; struct TransDataContainer; struct TransInfo; struct TransSnap; @@ -204,36 +205,6 @@ typedef enum { HLP_TRACKBALL = 6, } eTHelpline; -typedef enum { - TC_NONE = 0, - TC_ACTION_DATA, - TC_POSE, - TC_ARMATURE_VERTS, - TC_CURSOR_IMAGE, - TC_CURSOR_SEQUENCER, - TC_CURSOR_VIEW3D, - TC_CURVE_VERTS, - TC_GRAPH_EDIT_DATA, - TC_GPENCIL, - TC_LATTICE_VERTS, - TC_MASKING_DATA, - TC_MBALL_VERTS, - TC_MESH_VERTS, - TC_MESH_EDGES, - TC_MESH_SKIN, - TC_MESH_UV, - TC_NLA_DATA, - TC_NODE_DATA, - TC_OBJECT, - TC_OBJECT_TEXSPACE, - TC_PAINT_CURVE_VERTS, - TC_PARTICLE_VERTS, - TC_SCULPT, - TC_SEQ_DATA, - TC_SEQ_IMAGE_DATA, - TC_TRACKING_DATA, -} eTConvertType; - /** \} */ /* -------------------------------------------------------------------- */ @@ -306,9 +277,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; @@ -463,6 +434,8 @@ typedef struct TransDataContainer { int data_len; /** Total number of transformed data_mirror. */ int data_mirror_len; + /** Total number of transformed gp-frames. */ + int data_gpf_len; struct Object *obedit; @@ -516,7 +489,7 @@ typedef struct TransInfo { int data_len_all; /** TODO: It should be a member of #TransDataContainer. */ - eTConvertType data_type; + struct TransConvertTypeInfo *data_type; /** Current context/options for transform. */ eTContext options; 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.c b/source/blender/editors/transform/transform_convert.c index d9b971c5478..d1c2af75274 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,132 +479,6 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ -/** - * Find the correction for the scaling factor when "Constrain to Bounds" is active. - * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. - * \param denominator: How far the AABB is from the origin of the scale. - * \param scale: Scale parameter to update. - */ -static void constrain_scale_to_boundary(const float numerator, - const float denominator, - float *scale) -{ - if (denominator == 0.0f) { - /* The origin of the scale is on the edge of the boundary. */ - if (numerator < 0.0f) { - /* Negative scale will wrap around and put us outside the boundary. */ - *scale = 0.0f; /* Hold at the boundary instead. */ - } - return; /* Nothing else we can do without more info. */ - } - - const float correction = numerator / denominator; - if (correction < 0.0f || !isfinite(correction)) { - /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ - return; - } - - if (denominator < 0.0f) { - /* Scale origin is outside boundary, only make scale bigger. */ - if (*scale < correction) { - *scale = correction; - } - return; - } - - /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ - if (*scale > correction) { - *scale = correction; - } -} - -bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) -{ - bool clipx = true, clipy = true; - float min[2], max[2]; - - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); - /* Stores the coordinates of the closest UDIM tile. - * Also acts as an offset to the tile from the origin of UV space. */ - float base_offset[2] = {0.0f, 0.0f}; - - /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); - if (nearest_tile_index != -1) { - nearest_tile_index -= 1001; - /* Getting coordinates of nearest tile from the tile index. */ - base_offset[0] = nearest_tile_index % 10; - base_offset[1] = nearest_tile_index / 10; - } - } - - min[0] = min[1] = FLT_MAX; - max[0] = max[1] = FLT_MIN; - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - - TransData *td; - int a; - - for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { - minmax_v2v2_v2(min, max, td->loc); - } - } - - if (resize) { - /* Assume no change is required. */ - float scalex = 1.0f; - float scaley = 1.0f; - - /* Update U against the left border. */ - constrain_scale_to_boundary( - t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); - /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ - constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], - max[0] - t->center_global[0], - &scalex); - - /* Do the same for the V co-ordinate, which is called `y`. */ - constrain_scale_to_boundary( - t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); - constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], - max[1] - t->center_global[1], - &scaley); - - clipx = (scalex != 1.0f); - clipy = (scaley != 1.0f); - vec[0] *= scalex; - vec[1] *= scaley; - } - else { - if (min[0] < base_offset[0]) { - vec[0] += base_offset[0] - min[0]; - } - else if (max[0] > base_offset[0] + t->aspect[0]) { - vec[0] -= max[0] - base_offset[0] - t->aspect[0]; - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1]) { - vec[1] += base_offset[1] - min[1]; - } - else if (max[1] > base_offset[1] + t->aspect[1]) { - vec[1] -= max[1] - base_offset[1] - t->aspect[1]; - } - else { - clipy = 0; - } - } - - return (clipx || clipy); -} - void clipUVData(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -769,7 +643,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand } else { /* Delete Keyframe */ - delete_fcurve_key(fcu, i, 0); + BKE_fcurve_delete_key(fcu, i); } /* Update count of how many we've deleted @@ -779,7 +653,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand } else { /* Always delete - Unselected keys don't matter */ - delete_fcurve_key(fcu, i, 0); + BKE_fcurve_delete_key(fcu, i); } /* Stop the RK search... we've found our match now */ @@ -902,62 +776,12 @@ void special_aftertrans_update(bContext *C, TransInfo *t) return; } - BLI_assert(CTX_data_main(t->context) == CTX_data_main(C)); - switch (t->data_type) { - case TC_ACTION_DATA: - special_aftertrans_update__actedit(C, t); - break; - case TC_POSE: - special_aftertrans_update__pose(C, t); - break; - case TC_GRAPH_EDIT_DATA: - special_aftertrans_update__graph(C, t); - break; - case TC_MASKING_DATA: - special_aftertrans_update__mask(C, t); - break; - case TC_MESH_VERTS: - case TC_MESH_EDGES: - special_aftertrans_update__mesh(C, t); - break; - case TC_NLA_DATA: - special_aftertrans_update__nla(C, t); - break; - case TC_NODE_DATA: - special_aftertrans_update__node(C, t); - break; - case TC_OBJECT: - special_aftertrans_update__object(C, t); - break; - case TC_SCULPT: - special_aftertrans_update__sculpt(C, t); - break; - case TC_SEQ_DATA: - special_aftertrans_update__sequencer(C, t); - break; - case TC_SEQ_IMAGE_DATA: - special_aftertrans_update__sequencer_image(C, t); - break; - case TC_TRACKING_DATA: - special_aftertrans_update__movieclip(C, t); - break; - case TC_ARMATURE_VERTS: - case TC_CURSOR_IMAGE: - case TC_CURSOR_SEQUENCER: - case TC_CURSOR_VIEW3D: - case TC_CURVE_VERTS: - case TC_GPENCIL: - case TC_LATTICE_VERTS: - case TC_MBALL_VERTS: - case TC_MESH_UV: - case TC_MESH_SKIN: - case TC_OBJECT_TEXSPACE: - case TC_PAINT_CURVE_VERTS: - case TC_PARTICLE_VERTS: - case TC_NONE: - default: - break; + if (!t->data_type || !t->data_type->special_aftertrans_update) { + return; } + + BLI_assert(CTX_data_main(t->context) == CTX_data_main(C)); + t->data_type->special_aftertrans_update(C, t); } int special_transform_moving(TransInfo *t) @@ -1018,54 +842,44 @@ static int countAndCleanTransDataContainer(TransInfo *t) static void init_proportional_edit(TransInfo *t) { - eTConvertType convert_type = t->data_type; - switch (convert_type) { - case TC_ACTION_DATA: - case TC_CURVE_VERTS: - case TC_GRAPH_EDIT_DATA: - case TC_GPENCIL: - case TC_LATTICE_VERTS: - case TC_MASKING_DATA: - case TC_MBALL_VERTS: - case TC_MESH_VERTS: - case TC_MESH_EDGES: - case TC_MESH_SKIN: - case TC_MESH_UV: - case TC_NODE_DATA: - case TC_OBJECT: - case TC_PARTICLE_VERTS: - break; - case TC_POSE: /* Disable PET, its not usable in pose mode yet T32444. */ - case TC_ARMATURE_VERTS: - case TC_CURSOR_IMAGE: - case TC_CURSOR_SEQUENCER: - case TC_CURSOR_VIEW3D: - case TC_NLA_DATA: - case TC_OBJECT_TEXSPACE: - case TC_PAINT_CURVE_VERTS: - case TC_SCULPT: - case TC_SEQ_DATA: - case TC_SEQ_IMAGE_DATA: - case TC_TRACKING_DATA: - case TC_NONE: - default: - t->options |= CTX_NO_PET; - t->flag &= ~T_PROP_EDIT_ALL; - return; + /* NOTE: PET is not usable in pose mode yet T32444. */ + if (!ELEM(t->data_type, + &TransConvertType_Action, + &TransConvertType_Curve, + &TransConvertType_Graph, + &TransConvertType_GPencil, + &TransConvertType_Lattice, + &TransConvertType_Mask, + &TransConvertType_MBall, + &TransConvertType_Mesh, + &TransConvertType_MeshEdge, + &TransConvertType_MeshSkin, + &TransConvertType_MeshUV, + &TransConvertType_MeshVertCData, + &TransConvertType_Node, + &TransConvertType_Object, + &TransConvertType_Particle)) { + /* Disable PET */ + t->options |= CTX_NO_PET; + t->flag &= ~T_PROP_EDIT_ALL; + return; } if (t->data_len_all && (t->flag & T_PROP_EDIT)) { - if (convert_type == TC_OBJECT) { + if (t->data_type == &TransConvertType_Object) { /* Selected objects are already first, no need to presort. */ } else { sort_trans_data_selected_first(t); } - if (ELEM(convert_type, TC_ACTION_DATA, TC_GRAPH_EDIT_DATA)) { + if (ELEM(t->data_type, &TransConvertType_Action, &TransConvertType_Graph)) { /* Distance has already been set. */ } - else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN)) { + else if (ELEM(t->data_type, + &TransConvertType_Mesh, + &TransConvertType_MeshSkin, + &TransConvertType_MeshVertCData)) { if (t->flag & T_PROP_CONNECTED) { /* Already calculated by transform_convert_mesh_connectivity_distance. */ } @@ -1073,10 +887,10 @@ static void init_proportional_edit(TransInfo *t) set_prop_dist(t, false); } } - else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) { + else if (t->data_type == &TransConvertType_MeshUV && t->flag & T_PROP_CONNECTED) { /* Already calculated by uv_set_connectivity_distance. */ } - else if (convert_type == TC_CURVE_VERTS) { + else if (t->data_type == &TransConvertType_Curve) { BLI_assert(t->obedit_type == OB_CURVES_LEGACY); set_prop_dist(t, false); } @@ -1099,44 +913,26 @@ static void init_TransDataContainers(TransInfo *t, Object **objects, uint objects_len) { - switch (t->data_type) { - case TC_POSE: - case TC_ARMATURE_VERTS: - case TC_CURVE_VERTS: - case TC_GPENCIL: - case TC_LATTICE_VERTS: - case TC_MBALL_VERTS: - case TC_MESH_VERTS: - case TC_MESH_EDGES: - case TC_MESH_SKIN: - case TC_MESH_UV: - break; - case TC_ACTION_DATA: - case TC_GRAPH_EDIT_DATA: - case TC_CURSOR_IMAGE: - case TC_CURSOR_SEQUENCER: - case TC_CURSOR_VIEW3D: - case TC_MASKING_DATA: - case TC_NLA_DATA: - case TC_NODE_DATA: - case TC_OBJECT: - case TC_OBJECT_TEXSPACE: - case TC_PAINT_CURVE_VERTS: - case TC_PARTICLE_VERTS: - case TC_SCULPT: - case TC_SEQ_DATA: - case TC_SEQ_IMAGE_DATA: - case TC_TRACKING_DATA: - case TC_NONE: - default: - /* Does not support Multi object editing. */ - return; + if (!ELEM(t->data_type, + &TransConvertType_Pose, + &TransConvertType_EditArmature, + &TransConvertType_Curve, + &TransConvertType_GPencil, + &TransConvertType_Lattice, + &TransConvertType_MBall, + &TransConvertType_Mesh, + &TransConvertType_MeshEdge, + &TransConvertType_MeshSkin, + &TransConvertType_MeshUV, + &TransConvertType_MeshVertCData)) { + /* Does not support Multi object editing. */ + return; } const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT; const short object_type = obact ? obact->type : -1; - if ((object_mode & OB_MODE_EDIT) || (t->data_type == TC_GPENCIL) || + if ((object_mode & OB_MODE_EDIT) || (t->data_type == &TransConvertType_GPencil) || ((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE))) { if (t->data_container) { MEM_freeN(t->data_container); @@ -1178,7 +974,7 @@ static void init_TransDataContainers(TransInfo *t, tc->poseobj = objects[i]; tc->use_local_mat = true; } - else if (t->data_type == TC_GPENCIL) { + else if (t->data_type == &TransConvertType_GPencil) { tc->use_local_mat = true; } @@ -1201,173 +997,131 @@ static void init_TransDataContainers(TransInfo *t, } } -static eTFlag flags_from_data_type(eTConvertType data_type) -{ - switch (data_type) { - case TC_ACTION_DATA: - case TC_GRAPH_EDIT_DATA: - case TC_MASKING_DATA: - case TC_NLA_DATA: - case TC_NODE_DATA: - case TC_PAINT_CURVE_VERTS: - case TC_SEQ_DATA: - case TC_SEQ_IMAGE_DATA: - case TC_TRACKING_DATA: - return T_POINTS | T_2D_EDIT; - case TC_ARMATURE_VERTS: - case TC_CURVE_VERTS: - case TC_GPENCIL: - case TC_LATTICE_VERTS: - case TC_MBALL_VERTS: - case TC_MESH_VERTS: - case TC_MESH_SKIN: - return T_EDIT | T_POINTS; - case TC_MESH_EDGES: - return T_EDIT; - case TC_MESH_UV: - return T_EDIT | T_POINTS | T_2D_EDIT; - case TC_CURSOR_IMAGE: - case TC_CURSOR_SEQUENCER: - return T_2D_EDIT; - case TC_PARTICLE_VERTS: - return T_POINTS; - case TC_POSE: - case TC_CURSOR_VIEW3D: - case TC_OBJECT: - case TC_OBJECT_TEXSPACE: - case TC_SCULPT: - case TC_NONE: - default: - break; - } - return 0; -} - -static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armature) +static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj_armature) { ViewLayer *view_layer = t->view_layer; Object *ob = OBACT(view_layer); - eTConvertType convert_type = TC_NONE; /* if tests must match recalcData for correct updates */ if (t->options & CTX_CURSOR) { if (t->spacetype == SPACE_IMAGE) { - convert_type = TC_CURSOR_IMAGE; - } - else if (t->spacetype == SPACE_SEQ) { - convert_type = TC_CURSOR_SEQUENCER; + return &TransConvertType_CursorImage; } - else { - convert_type = TC_CURSOR_VIEW3D; + + if (t->spacetype == SPACE_SEQ) { + return &TransConvertType_CursorSequencer; } + + return &TransConvertType_Cursor3D; } - else if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob && - (ob->mode == OB_MODE_SCULPT) && ob->sculpt) { - convert_type = TC_SCULPT; + if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob && + (ob->mode == OB_MODE_SCULPT) && ob->sculpt) { + return &TransConvertType_Sculpt; } - else if (t->options & CTX_TEXTURE_SPACE) { - convert_type = TC_OBJECT_TEXSPACE; + if (t->options & CTX_TEXTURE_SPACE) { + return &TransConvertType_ObjectTexSpace; } - else if (t->options & CTX_EDGE_DATA) { - convert_type = TC_MESH_EDGES; + if (t->options & CTX_EDGE_DATA) { + return &TransConvertType_MeshEdge; } - else if (t->options & CTX_GPENCIL_STROKES) { - convert_type = TC_GPENCIL; + if (t->options & CTX_GPENCIL_STROKES) { + return &TransConvertType_GPencil; } - else if (t->spacetype == SPACE_IMAGE) { + if (t->spacetype == SPACE_IMAGE) { if (t->options & CTX_MASK) { - convert_type = TC_MASKING_DATA; + return &TransConvertType_Mask; } - else if (t->options & CTX_PAINT_CURVE) { + if (t->options & CTX_PAINT_CURVE) { if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { - convert_type = TC_PAINT_CURVE_VERTS; + return &TransConvertType_PaintCurve; } } else if (t->obedit_type == OB_MESH) { - convert_type = TC_MESH_UV; + return &TransConvertType_MeshUV; } + return NULL; } - else if (t->spacetype == SPACE_ACTION) { - convert_type = TC_ACTION_DATA; + if (t->spacetype == SPACE_ACTION) { + return &TransConvertType_Action; } - else if (t->spacetype == SPACE_NLA) { - convert_type = TC_NLA_DATA; + if (t->spacetype == SPACE_NLA) { + return &TransConvertType_NLA; } - else if (t->spacetype == SPACE_SEQ) { + if (t->spacetype == SPACE_SEQ) { if (t->options & CTX_SEQUENCER_IMAGE) { - convert_type = TC_SEQ_IMAGE_DATA; - } - else { - convert_type = TC_SEQ_DATA; + return &TransConvertType_SequencerImage; } + return &TransConvertType_Sequencer; } - else if (t->spacetype == SPACE_GRAPH) { - convert_type = TC_GRAPH_EDIT_DATA; + if (t->spacetype == SPACE_GRAPH) { + return &TransConvertType_Graph; } - else if (t->spacetype == SPACE_NODE) { - convert_type = TC_NODE_DATA; + if (t->spacetype == SPACE_NODE) { + return &TransConvertType_Node; } - else if (t->spacetype == SPACE_CLIP) { + if (t->spacetype == SPACE_CLIP) { if (t->options & CTX_MOVIECLIP) { - convert_type = TC_TRACKING_DATA; + return &TransConvertType_Tracking; } - else if (t->options & CTX_MASK) { - convert_type = TC_MASKING_DATA; + if (t->options & CTX_MASK) { + return &TransConvertType_Mask; } + return NULL; } - else if (t->obedit_type != -1) { + if (t->obedit_type != -1) { if (t->obedit_type == OB_MESH) { if (t->mode == TFM_SKIN_RESIZE) { - convert_type = TC_MESH_SKIN; + return &TransConvertType_MeshSkin; } - else { - convert_type = TC_MESH_VERTS; + if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { + return &TransConvertType_MeshVertCData; } + return &TransConvertType_Mesh; } - else if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) { - convert_type = TC_CURVE_VERTS; + if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) { + return &TransConvertType_Curve; } - else if (t->obedit_type == OB_LATTICE) { - convert_type = TC_LATTICE_VERTS; + if (t->obedit_type == OB_LATTICE) { + return &TransConvertType_Lattice; } - else if (t->obedit_type == OB_MBALL) { - convert_type = TC_MBALL_VERTS; + if (t->obedit_type == OB_MBALL) { + return &TransConvertType_MBall; } - else if (t->obedit_type == OB_ARMATURE) { - convert_type = TC_ARMATURE_VERTS; + if (t->obedit_type == OB_ARMATURE) { + return &TransConvertType_EditArmature; } + return NULL; } - else if (ob && (ob->mode & OB_MODE_POSE)) { - convert_type = TC_POSE; + if (ob && (ob->mode & OB_MODE_POSE)) { + return &TransConvertType_Pose; } - else if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { + if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { Object *ob_armature = transform_object_deform_pose_armature_get(t, ob); if (ob_armature) { *r_obj_armature = ob_armature; - convert_type = TC_POSE; + return &TransConvertType_Pose; } + return NULL; } - else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && - PE_start_edit(PE_get_current(t->depsgraph, t->scene, ob))) { - convert_type = TC_PARTICLE_VERTS; + if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && + PE_start_edit(PE_get_current(t->depsgraph, t->scene, ob))) { + return &TransConvertType_Particle; } - else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { + if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { - convert_type = TC_PAINT_CURVE_VERTS; + return &TransConvertType_PaintCurve; } + return NULL; } - else if ((ob) && (ELEM(ob->mode, - OB_MODE_PAINT_GPENCIL, - OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL, - OB_MODE_VERTEX_GPENCIL))) { + if ((ob) && (ELEM(ob->mode, + OB_MODE_PAINT_GPENCIL, + OB_MODE_SCULPT_GPENCIL, + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL))) { /* In grease pencil all transformations must be canceled if not Object or Edit. */ + return NULL; } - else { - convert_type = TC_OBJECT; - } - - return convert_type; + return &TransConvertType_Object; } void createTransData(bContext *C, TransInfo *t) @@ -1376,7 +1130,14 @@ void createTransData(bContext *C, TransInfo *t) Object *ob_armature = NULL; t->data_type = convert_type_get(t, &ob_armature); - t->flag |= flags_from_data_type(t->data_type); + if (t->data_type == NULL) { + printf("edit type not implemented!\n"); + BLI_assert(t->data_len_all == -1); + t->data_len_all = 0; + return; + } + + t->flag |= t->data_type->flags; if (ob_armature) { init_TransDataContainers(t, ob_armature, &ob_armature, 1); @@ -1387,122 +1148,46 @@ void createTransData(bContext *C, TransInfo *t) init_TransDataContainers(t, ob, NULL, 0); } - switch (t->data_type) { - case TC_ACTION_DATA: - createTransActionData(C, t); - break; - case TC_POSE: - t->options |= CTX_POSE_BONE; + if (t->data_type == &TransConvertType_Object) { + t->options |= CTX_OBJECT; - /* XXX active-layer checking isn't done - * as that should probably be checked through context instead. */ - createTransPose(t); - break; - case TC_ARMATURE_VERTS: - createTransArmatureVerts(t); - break; - case TC_CURSOR_IMAGE: - createTransCursor_image(t); - break; - case TC_CURSOR_SEQUENCER: - createTransCursor_sequencer(t); - break; - case TC_CURSOR_VIEW3D: - createTransCursor_view3d(t); - break; - case TC_CURVE_VERTS: - createTransCurveVerts(t); - break; - case TC_GRAPH_EDIT_DATA: - createTransGraphEditData(C, t); - break; - case TC_GPENCIL: - createTransGPencil(C, t); - break; - case TC_LATTICE_VERTS: - createTransLatticeVerts(t); - break; - case TC_MASKING_DATA: - createTransMaskingData(C, t); - break; - case TC_MBALL_VERTS: - createTransMBallVerts(t); - break; - case TC_MESH_VERTS: - createTransEditVerts(t); - break; - case TC_MESH_EDGES: - createTransEdge(t); - break; - case TC_MESH_SKIN: - createTransMeshSkin(t); - break; - case TC_MESH_UV: - createTransUVs(C, t); - break; - case TC_NLA_DATA: - createTransNlaData(C, t); - break; - case TC_NODE_DATA: - createTransNodeData(t); - break; - case TC_OBJECT: - t->options |= CTX_OBJECT; - - /* Needed for correct Object.obmat after duplication, see: T62135. */ - BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context)); - - if ((t->settings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) { - t->options |= CTX_OBMODE_XFORM_OBDATA; - } - if ((t->settings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) { - t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN; - } - createTransObject(C, t); - /* Check if we're transforming the camera from the camera */ - if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) { - View3D *v3d = t->view; - RegionView3D *rv3d = t->region->regiondata; - if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) { - /* we could have a flag to easily check an object is being transformed */ - if (v3d->camera->id.tag & LIB_TAG_DOIT) { - t->options |= CTX_CAMERA; - } - } - else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) { + /* Needed for correct Object.obmat after duplication, see: T62135. */ + BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context)); + + if ((t->settings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) { + t->options |= CTX_OBMODE_XFORM_OBDATA; + } + if ((t->settings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) { + t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN; + } + TransConvertType_Object.createTransData(C, t); + /* Check if we're transforming the camera from the camera */ + if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) { + View3D *v3d = t->view; + RegionView3D *rv3d = t->region->regiondata; + if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) { + /* we could have a flag to easily check an object is being transformed */ + if (v3d->camera->id.tag & LIB_TAG_DOIT) { t->options |= CTX_CAMERA; } } - break; - case TC_OBJECT_TEXSPACE: - createTransTexspace(t); - break; - case TC_PAINT_CURVE_VERTS: - createTransPaintCurveVerts(C, t); - break; - case TC_PARTICLE_VERTS: - createTransParticleVerts(t); - break; - case TC_SCULPT: - createTransSculpt(C, t); - break; - case TC_SEQ_DATA: - t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */ - createTransSeqData(t); - break; - case TC_SEQ_IMAGE_DATA: + else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) { + t->options |= CTX_CAMERA; + } + } + } + else { + if (t->data_type == &TransConvertType_Pose) { + t->options |= CTX_POSE_BONE; + } + else if (t->data_type == &TransConvertType_Sequencer) { + /* Sequencer has no use for floating point transform. */ + t->num.flag |= NUM_NO_FRACTION; + } + else if (t->data_type == &TransConvertType_SequencerImage) { t->obedit_type = -1; - createTransSeqImageData(t); - break; - case TC_TRACKING_DATA: - createTransTrackingData(C, t); - break; - case TC_NONE: - default: - printf("edit type not implemented!\n"); - BLI_assert(t->data_len_all == -1); - t->data_len_all = 0; - return; + } + t->data_type->createTransData(C, t); } countAndCleanTransDataContainer(t); @@ -1706,89 +1391,10 @@ void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const fl void recalcData(TransInfo *t) { - switch (t->data_type) { - case TC_ACTION_DATA: - recalcData_actedit(t); - break; - case TC_POSE: - recalcData_pose(t); - break; - case TC_ARMATURE_VERTS: - recalcData_edit_armature(t); - break; - case TC_CURVE_VERTS: - recalcData_curve(t); - break; - case TC_CURSOR_IMAGE: - recalcData_cursor_image(t); - break; - case TC_CURSOR_SEQUENCER: - recalcData_cursor_sequencer(t); - break; - case TC_CURSOR_VIEW3D: - recalcData_cursor_view3d(t); - break; - case TC_GRAPH_EDIT_DATA: - recalcData_graphedit(t); - break; - case TC_GPENCIL: - recalcData_gpencil_strokes(t); - break; - case TC_MASKING_DATA: - recalcData_mask_common(t); - break; - case TC_MESH_VERTS: - recalcData_mesh(t); - break; - case TC_MESH_EDGES: - recalcData_mesh_edge(t); - break; - case TC_MESH_SKIN: - recalcData_mesh_skin(t); - break; - case TC_MESH_UV: - recalcData_uv(t); - break; - case TC_NLA_DATA: - recalcData_nla(t); - break; - case TC_NODE_DATA: - flushTransNodes(t); - break; - case TC_OBJECT: - recalcData_objects(t); - break; - case TC_OBJECT_TEXSPACE: - recalcData_texspace(t); - break; - case TC_PAINT_CURVE_VERTS: - flushTransPaintCurve(t); - break; - case TC_SCULPT: - recalcData_sculpt(t); - break; - case TC_SEQ_DATA: - recalcData_sequencer(t); - break; - case TC_SEQ_IMAGE_DATA: - recalcData_sequencer_image(t); - break; - case TC_TRACKING_DATA: - recalcData_tracking(t); - break; - case TC_MBALL_VERTS: - recalcData_mball(t); - break; - case TC_LATTICE_VERTS: - recalcData_lattice(t); - break; - case TC_PARTICLE_VERTS: - recalcData_particles(t); - break; - case TC_NONE: - default: - break; + if (!t->data_type || !t->data_type->recalcData) { + return; } + t->data_type->recalcData(t); } /** \} */ diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 037fbe26c77..f32bff6dcff 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -21,6 +21,25 @@ struct TransDataCurveHandleFlags; struct TransInfo; struct bContext; +typedef struct TransConvertTypeInfo { + int flags; /* eTFlag */ + + /** + * Allocate and initialize `t->data`. + */ + void (*createTransData)(bContext *C, TransInfo *t); + + /** + * Force recalculation of data during transformation. + */ + void (*recalcData)(TransInfo *t); + + /** + * Called when the operation is finished. + */ + void (*special_aftertrans_update)(bContext *C, TransInfo *t); +} TransConvertTypeInfo; + /* transform_convert.c */ /** @@ -34,7 +53,6 @@ int special_transform_moving(TransInfo *t); void special_aftertrans_update(struct bContext *C, TransInfo *t); void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); -bool clipUVTransform(TransInfo *t, float vec[2], bool resize); void clipUVData(TransInfo *t); void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, float y_fac); /** @@ -99,82 +117,53 @@ void animrecord_check_state(TransInfo *t, struct ID *id); /* transform_convert_action.c */ -void createTransActionData(bContext *C, TransInfo *t); -/* helper for recalcData() - for Action Editor transforms */ -void recalcData_actedit(TransInfo *t); -void special_aftertrans_update__actedit(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Action; /* transform_convert_armature.c */ +extern TransConvertTypeInfo TransConvertType_EditArmature; +extern TransConvertTypeInfo TransConvertType_Pose; + /** * Sets transform flags in the bones. * Returns total number of bones with #BONE_TRANSFORM. */ void transform_convert_pose_transflags_update(Object *ob, int mode, short around); -/** - * When objects array is NULL, use 't->data_container' as is. - */ -void createTransPose(TransInfo *t); -void createTransArmatureVerts(TransInfo *t); -void recalcData_edit_armature(TransInfo *t); -void recalcData_pose(TransInfo *t); -void special_aftertrans_update__pose(bContext *C, TransInfo *t); - /* transform_convert_cursor.c */ -void createTransCursor_image(TransInfo *t); -void createTransCursor_sequencer(TransInfo *t); -void createTransCursor_view3d(TransInfo *t); -void recalcData_cursor_image(TransInfo *t); -void recalcData_cursor_sequencer(TransInfo *t); -void recalcData_cursor_view3d(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_CursorImage; +extern TransConvertTypeInfo TransConvertType_CursorSequencer; +extern TransConvertTypeInfo TransConvertType_Cursor3D; /* transform_convert_curve.c */ -void createTransCurveVerts(TransInfo *t); -void recalcData_curve(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Curve; /* transform_convert_graph.c */ -/** - * It is important to note that this doesn't always act on the selection (like it's usually done), - * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a - * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the - * selected left or right handles accordingly. - * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve - * functions may need to be made aware of this. It's ugly that these act based on selection state - * anyway. - */ -void createTransGraphEditData(bContext *C, TransInfo *t); -/* helper for recalcData() - for Graph Editor transforms */ -void recalcData_graphedit(TransInfo *t); -void special_aftertrans_update__graph(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Graph; /* transform_convert_gpencil.c */ -void createTransGPencil(bContext *C, TransInfo *t); -/* force recalculation of triangles during transformation */ -void recalcData_gpencil_strokes(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_GPencil; /* transform_convert_lattice.c */ -void createTransLatticeVerts(TransInfo *t); -void recalcData_lattice(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Lattice; /* transform_convert_mask.c */ -void createTransMaskingData(bContext *C, TransInfo *t); -void recalcData_mask_common(TransInfo *t); -void special_aftertrans_update__mask(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Mask; /* transform_convert_mball.c */ -void createTransMBallVerts(TransInfo *t); -void recalcData_mball(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_MBall; /* transform_convert_mesh.c */ +extern TransConvertTypeInfo TransConvertType_Mesh; + struct TransIslandData { float (*center)[3]; float (*axismtx)[3][3]; @@ -233,84 +222,60 @@ void transform_convert_mesh_crazyspace_transdata_set(const float mtx[3][3], struct TransData *r_td); void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_crazyspace_data); -void createTransEditVerts(TransInfo *t); -void recalcData_mesh(TransInfo *t); void special_aftertrans_update__mesh(bContext *C, TransInfo *t); /* transform_convert_mesh_edge.c */ -void createTransEdge(TransInfo *t); -void recalcData_mesh_edge(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_MeshEdge; /* transform_convert_mesh_skin.c */ -void createTransMeshSkin(TransInfo *t); -void recalcData_mesh_skin(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_MeshSkin; /* transform_convert_mesh_uv.c */ -void createTransUVs(bContext *C, TransInfo *t); -/* helper for recalcData() - for Image Editor transforms */ -void recalcData_uv(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_MeshUV; + +/* transform_convert_mesh_vert_cdata.c */ + +extern TransConvertTypeInfo TransConvertType_MeshVertCData; /* transform_convert_nla.c */ -void createTransNlaData(bContext *C, TransInfo *t); -/* helper for recalcData() - for NLA Editor transforms */ -void recalcData_nla(TransInfo *t); -void special_aftertrans_update__nla(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_NLA; /* transform_convert_node.c */ -void createTransNodeData(TransInfo *t); -void flushTransNodes(TransInfo *t); -void special_aftertrans_update__node(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Node; /* transform_convert_object.c */ -void createTransObject(bContext *C, TransInfo *t); -/* helper for recalcData() - for object transforms, typically in the 3D view */ -void recalcData_objects(TransInfo *t); -void special_aftertrans_update__object(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Object; /* transform_convert_object_texspace.c */ -void createTransTexspace(TransInfo *t); -/* helper for recalcData() - for object transforms, typically in the 3D view */ -void recalcData_texspace(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_ObjectTexSpace; /* transform_convert_paintcurve.c */ -void createTransPaintCurveVerts(bContext *C, TransInfo *t); -void flushTransPaintCurve(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_PaintCurve; /* transform_convert_particle.c */ -void createTransParticleVerts(TransInfo *t); -void recalcData_particles(TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Particle; /* transform_convert_sculpt.c */ -void createTransSculpt(bContext *C, TransInfo *t); -void recalcData_sculpt(TransInfo *t); -void special_aftertrans_update__sculpt(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Sculpt; /* transform_convert_sequencer.c */ -void createTransSeqData(TransInfo *t); -/* helper for recalcData() - for sequencer transforms */ -void recalcData_sequencer(TransInfo *t); -void special_aftertrans_update__sequencer(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Sequencer; /* transform_convert_sequencer_image.c */ -void createTransSeqImageData(TransInfo *t); -void recalcData_sequencer_image(TransInfo *t); -void special_aftertrans_update__sequencer_image(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_SequencerImage; /* transform_convert_tracking.c */ -void createTransTrackingData(bContext *C, TransInfo *t); -/* helper for recalcData() - for Movie Clip transforms */ -void recalcData_tracking(TransInfo *t); -void special_aftertrans_update__movieclip(bContext *C, TransInfo *t); +extern TransConvertTypeInfo TransConvertType_Tracking; diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 71c245cd512..41635522d26 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -289,7 +289,7 @@ static int MaskLayerToTransData(TransData *td, return count; } -void createTransActionData(bContext *C, TransInfo *t) +static void createTransActionData(bContext *C, TransInfo *t) { Scene *scene = t->scene; TransData *td = NULL; @@ -311,6 +311,7 @@ void createTransActionData(bContext *C, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; int count = 0; + int gpf_count = 0; float cfra; float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->region->v2d.cur); @@ -320,17 +321,12 @@ void createTransActionData(bContext *C, TransInfo *t) } /* filter data */ - if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); - } - else { - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); - } + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* which side of the current frame should be allowed */ if (t->mode == TFM_TIME_EXTEND) { - t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA); + t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra); } else { /* normal transform - both sides of current frame are considered */ @@ -345,10 +341,10 @@ void createTransActionData(bContext *C, TransInfo *t) * higher scaling ratios, but is faster than converting all points) */ if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } else { - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; } if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { @@ -365,13 +361,16 @@ void createTransActionData(bContext *C, TransInfo *t) } if (adt_count > 0) { + if (ELEM(ale->type, ANIMTYPE_GPLAYER, ANIMTYPE_MASKLAYER)) { + gpf_count += adt_count; + } count += adt_count; ale->tag = true; } } /* stop if trying to build list if nothing selected */ - if (count == 0) { + if (count == 0 && gpf_count == 0) { /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); return; @@ -387,8 +386,9 @@ void createTransActionData(bContext *C, TransInfo *t) td = tc->data; td2d = tc->data_2d; - if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata"); + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) { + tc->data_gpf_len = gpf_count; + tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * gpf_count, "tGPFtransdata"); tc->custom.type.use_free = true; } @@ -399,7 +399,7 @@ void createTransActionData(bContext *C, TransInfo *t) continue; } - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; { AnimData *adt; @@ -447,10 +447,10 @@ void createTransActionData(bContext *C, TransInfo *t) adt = ANIM_nla_mapping_get(&ac, ale); if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } else { - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; } if (ale->type == ANIMTYPE_GPLAYER) { @@ -558,13 +558,14 @@ static void flushTransIntFrameActionData(TransInfo *t) TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); tGPFtransdata *tfd = tc->custom.type.data; - /* flush data! */ - for (int i = 0; i < tc->data_len; i++, tfd++) { + /* flush data! + * Expects data_gpf_len to be set in the data container. */ + for (int i = 0; i < tc->data_gpf_len; i++, tfd++) { *(tfd->sdata) = round_fl_to_int(tfd->val); } } -void recalcData_actedit(TransInfo *t) +static void recalcData_actedit(TransInfo *t) { ViewLayer *view_layer = t->view_layer; SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; @@ -589,7 +590,7 @@ void recalcData_actedit(TransInfo *t) ANIM_animdata_context_getdata(&ac); /* perform flush */ - if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { + if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK, ANIMCONT_DOPESHEET, ANIMCONT_TIMELINE)) { /* flush transform values back to actual coordinates */ flushTransIntFrameActionData(t); } @@ -735,7 +736,7 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) int filter; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(ac, &anim_data, filter, act, ANIMCONT_ACTION); /* loop through relevant data, removing keyframes as appropriate @@ -758,7 +759,7 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) ANIM_animdata_freelist(&anim_data); } -void special_aftertrans_update__actedit(bContext *C, TransInfo *t) +static void special_aftertrans_update__actedit(bContext *C, TransInfo *t) { SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; bAnimContext ac; @@ -776,32 +777,44 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t) if (ELEM(ac.datatype, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY, ANIMCONT_TIMELINE)) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); /* get channels to work on */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - /* these should all be F-Curves */ for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(&ac, ale); - FCurve *fcu = (FCurve *)ale->key_data; - - /* 3 cases here for curve cleanups: - * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done - * 2) canceled == 0 -> user confirmed the transform, - * so duplicates should be removed - * 3) canceled + duplicate -> user canceled the transform, - * but we made duplicates, so get rid of these - */ - if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ - ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); - } - else { - posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + switch (ale->datatype) { + case ALE_GPFRAME: + ale->id->tag &= ~LIB_TAG_DOIT; + posttrans_gpd_clean((bGPdata *)ale->id); + break; + + case ALE_FCURVE: { + AnimData *adt = ANIM_nla_mapping_get(&ac, ale); + FCurve *fcu = (FCurve *)ale->key_data; + + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done + * 2) canceled == 0 -> user confirmed the transform, + * so duplicates should be removed + * 3) canceled + duplicate -> user canceled the transform, + * but we made duplicates, so get rid of these + */ + if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); + posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); + } + else { + posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + } + } + break; } + + default: + BLI_assert_msg(false, "Keys cannot be transformed into this animation type."); } } @@ -847,15 +860,8 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t) LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ale->datatype == ALE_GPFRAME) { - ale->id->tag |= LIB_TAG_DOIT; - } - } - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - if (ale->datatype == ALE_GPFRAME) { - if (ale->id->tag & LIB_TAG_DOIT) { - ale->id->tag &= ~LIB_TAG_DOIT; - posttrans_gpd_clean((bGPdata *)ale->id); - } + ale->id->tag &= ~LIB_TAG_DOIT; + posttrans_gpd_clean((bGPdata *)ale->id); } } ANIM_animdata_freelist(&anim_data); @@ -878,15 +884,8 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t) LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ale->datatype == ALE_MASKLAY) { - ale->id->tag |= LIB_TAG_DOIT; - } - } - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - if (ale->datatype == ALE_MASKLAY) { - if (ale->id->tag & LIB_TAG_DOIT) { - ale->id->tag &= ~LIB_TAG_DOIT; - posttrans_mask_clean((Mask *)ale->id); - } + ale->id->tag &= ~LIB_TAG_DOIT; + posttrans_mask_clean((Mask *)ale->id); } } ANIM_animdata_freelist(&anim_data); @@ -927,3 +926,10 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Action = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransActionData, + /* recalcData */ recalcData_actedit, + /* special_aftertrans_update */ special_aftertrans_update__actedit, +}; diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index e1b25acb21e..97d9ab2964a 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -93,8 +93,8 @@ static void autokeyframe_pose( KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); ListBase nla_cache = {NULL, NULL}; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - (float)CFRA); + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( + depsgraph, (float)scene->r.cfra); eInsertKeyFlags flag = 0; /* flag is initialized from UserPref keyframing settings @@ -701,7 +701,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr td->con = pchan->constraints.first; } -void createTransPose(TransInfo *t) +static void createTransPose(bContext *UNUSED(C), TransInfo *t) { Main *bmain = CTX_data_main(t->context); @@ -879,7 +879,7 @@ void createTransPose(TransInfo *t) } } -void createTransArmatureVerts(TransInfo *t) +static void createTransArmatureVerts(bContext *UNUSED(C), TransInfo *t) { t->data_len_all = 0; @@ -1189,10 +1189,10 @@ static void restoreBones(TransDataContainer *tc) } } -void recalcData_edit_armature(TransInfo *t) +static void recalcData_edit_armature(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -1412,7 +1412,7 @@ static void restoreMirrorPoseBones(TransDataContainer *tc) } } -void recalcData_pose(TransInfo *t) +static void recalcData_pose(TransInfo *t) { if (t->mode == TFM_BONESIZE) { /* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be @@ -1684,7 +1684,7 @@ static void pose_grab_with_ik_clear(Main *bmain, Object *ob) } } -void special_aftertrans_update__pose(bContext *C, TransInfo *t) +static void special_aftertrans_update__pose(bContext *C, TransInfo *t) { Object *ob; @@ -1768,3 +1768,17 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_EditArmature = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransArmatureVerts, + /* recalcData */ recalcData_edit_armature, + /* special_aftertrans_update */ NULL, +}; + +TransConvertTypeInfo TransConvertType_Pose = { + /* flags */ 0, + /* createTransData */ createTransPose, + /* recalcData */ recalcData_pose, + /* special_aftertrans_update */ special_aftertrans_update__pose, +}; diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 5e6eee192f2..0bf6f06a9f2 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -82,13 +82,13 @@ static void recalcData_cursor_2D_impl(TransInfo *t) /** \name Image Cursor * \{ */ -void createTransCursor_image(TransInfo *t) +static void createTransCursor_image(bContext *UNUSED(C), TransInfo *t) { SpaceImage *sima = t->area->spacedata.first; createTransCursor_2D_impl(t, sima->cursor); } -void recalcData_cursor_image(TransInfo *t) +static void recalcData_cursor_image(TransInfo *t) { recalcData_cursor_2D_impl(t); } @@ -99,7 +99,7 @@ void recalcData_cursor_image(TransInfo *t) /** \name Sequencer Cursor * \{ */ -void createTransCursor_sequencer(TransInfo *t) +static void createTransCursor_sequencer(bContext *UNUSED(C), TransInfo *t) { SpaceSeq *sseq = t->area->spacedata.first; if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { @@ -108,7 +108,7 @@ void createTransCursor_sequencer(TransInfo *t) createTransCursor_2D_impl(t, sseq->cursor); } -void recalcData_cursor_sequencer(TransInfo *t) +static void recalcData_cursor_sequencer(TransInfo *t) { recalcData_cursor_2D_impl(t); } @@ -119,7 +119,7 @@ void recalcData_cursor_sequencer(TransInfo *t) /** \name View 3D Cursor * \{ */ -void createTransCursor_view3d(TransInfo *t) +static void createTransCursor_view3d(bContext *UNUSED(C), TransInfo *t) { TransData *td; @@ -178,9 +178,30 @@ void createTransCursor_view3d(TransInfo *t) td->ext->rotOrder = cursor->rotation_mode; } -void recalcData_cursor_view3d(TransInfo *t) +static void recalcData_cursor_view3d(TransInfo *t) { DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); } /** \} */ + +TransConvertTypeInfo TransConvertType_CursorImage = { + /* flags */ T_2D_EDIT, + /* createTransData */ createTransCursor_image, + /* recalcData */ recalcData_cursor_image, + /* special_aftertrans_update */ NULL, +}; + +TransConvertTypeInfo TransConvertType_CursorSequencer = { + /* flags */ T_2D_EDIT, + /* createTransData */ createTransCursor_sequencer, + /* recalcData */ recalcData_cursor_sequencer, + /* special_aftertrans_update */ NULL, +}; + +TransConvertTypeInfo TransConvertType_Cursor3D = { + /* flags */ 0, + /* createTransData */ createTransCursor_view3d, + /* recalcData */ recalcData_cursor_view3d, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index 51acfb2a788..404b1293208 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -62,7 +62,7 @@ static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const boo return flag; } -void createTransCurveVerts(TransInfo *t) +static void createTransCurveVerts(bContext *UNUSED(C), TransInfo *t) { #define SEL_F1 (1 << 0) @@ -415,10 +415,10 @@ void createTransCurveVerts(TransInfo *t) #undef SEL_F3 } -void recalcData_curve(TransInfo *t) +static void recalcData_curve(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -446,3 +446,10 @@ void recalcData_curve(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Curve = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransCurveVerts, + /* recalcData */ recalcData_curve, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index a88d42b7f30..0029eaefaba 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -672,7 +672,7 @@ static void createTransGPencil_strokes(bContext *C, } } -void createTransGPencil(bContext *C, TransInfo *t) +static void createTransGPencil(bContext *C, TransInfo *t) { if (t->data_container_len == 0) { return; @@ -685,7 +685,7 @@ void createTransGPencil(bContext *C, TransInfo *t) bGPdata *gpd = obact->data; BLI_assert(gpd != NULL); - const int cfra_scene = CFRA; + const int cfra_scene = scene->r.cfra; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); const bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != @@ -737,7 +737,7 @@ void createTransGPencil(bContext *C, TransInfo *t) } } -void recalcData_gpencil_strokes(TransInfo *t) +static void recalcData_gpencil_strokes(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); GHash *strokes = BLI_ghash_ptr_new(__func__); @@ -762,3 +762,10 @@ void recalcData_gpencil_strokes(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_GPencil = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransGPencil, + /* recalcData */ recalcData_gpencil_strokes, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 2039daee386..aca2439d5fb 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -199,7 +199,16 @@ static void graph_key_shortest_dist( } } -void createTransGraphEditData(bContext *C, TransInfo *t) +/** + * It is important to note that this doesn't always act on the selection (like it's usually done), + * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a + * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the + * selected left or right handles accordingly. + * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve + * functions may need to be made aware of this. It's ugly that these act based on selection state + * anyway. + */ +static void createTransGraphEditData(bContext *C, TransInfo *t) { SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; Scene *scene = t->scene; @@ -232,13 +241,14 @@ void createTransGraphEditData(bContext *C, TransInfo *t) anim_map_flag |= ANIM_get_normalization_flags(&ac); /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* which side of the current frame should be allowed */ /* XXX we still want this mode, but how to get this using standard transform too? */ if (t->mode == TFM_TIME_EXTEND) { - t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA); + t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra); } else { /* normal transform - both sides of current frame are considered */ @@ -263,10 +273,10 @@ void createTransGraphEditData(bContext *C, TransInfo *t) * higher scaling ratios, but is faster than converting all points) */ if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } else { - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; } for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { @@ -369,10 +379,10 @@ void createTransGraphEditData(bContext *C, TransInfo *t) * higher scaling ratios, but is faster than converting all points) */ if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } else { - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; } unit_scale = ANIM_unit_mapping_get_factor( @@ -560,10 +570,10 @@ void createTransGraphEditData(bContext *C, TransInfo *t) * higher scaling ratios, but is faster than converting all points) */ if (adt) { - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + cfra = BKE_nla_tweakedit_remap(adt, (float)scene->r.cfra, NLATIME_CONVERT_UNMAP); } else { - cfra = (float)CFRA; + cfra = (float)scene->r.cfra; } for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { @@ -885,7 +895,7 @@ static void remake_graph_transdata(TransInfo *t, ListBase *anim_data) } } -void recalcData_graphedit(TransInfo *t) +static void recalcData_graphedit(TransInfo *t) { SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; ViewLayer *view_layer = t->view_layer; @@ -915,7 +925,8 @@ void recalcData_graphedit(TransInfo *t) flushTransGraphData(t); /* get curves to check if a re-sort is needed */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* now test if there is a need to re-sort */ @@ -932,7 +943,7 @@ void recalcData_graphedit(TransInfo *t) dosort++; } else { - calchandles_fcurve_ex(fcu, BEZT_FLAG_TEMP_TAG); + BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_TEMP_TAG); } /* set refresh tags for objects using this animation, @@ -958,7 +969,7 @@ void recalcData_graphedit(TransInfo *t) /** \name Special After Transform Graph * \{ */ -void special_aftertrans_update__graph(bContext *C, TransInfo *t) +static void special_aftertrans_update__graph(bContext *C, TransInfo *t) { SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; bAnimContext ac; @@ -975,7 +986,8 @@ void special_aftertrans_update__graph(bContext *C, TransInfo *t) if (ac.datatype) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE | + ANIMFILTER_FCURVESONLY); /* get channels to work on */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1018,3 +1030,10 @@ void special_aftertrans_update__graph(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Graph = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransGraphEditData, + /* recalcData */ recalcData_graphedit, + /* special_aftertrans_update */ special_aftertrans_update__graph, +}; diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c index f02d4e94448..b77538dc249 100644 --- a/source/blender/editors/transform/transform_convert_lattice.c +++ b/source/blender/editors/transform/transform_convert_lattice.c @@ -25,7 +25,7 @@ /** \name Curve/Surfaces Transform Creation * \{ */ -void createTransLatticeVerts(TransInfo *t) +static void createTransLatticeVerts(bContext *UNUSED(C), TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -98,10 +98,10 @@ void createTransLatticeVerts(TransInfo *t) } } -void recalcData_lattice(TransInfo *t) +static void recalcData_lattice(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -114,3 +114,10 @@ void recalcData_lattice(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Lattice = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransLatticeVerts, + /* recalcData */ recalcData_lattice, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index b07d2bda0c5..2dca59a5da1 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -117,7 +117,7 @@ static void MaskPointToTransData(Scene *scene, const bool is_sel_any = MASKPOINT_ISSEL_ANY(point); float parent_matrix[3][3], parent_inverse_matrix[3][3]; - BKE_mask_point_parent_matrix_get(point, CFRA, parent_matrix); + BKE_mask_point_parent_matrix_get(point, scene->r.cfra, parent_matrix); invert_m3_m3(parent_inverse_matrix, parent_matrix); if (is_prop_edit || is_sel_point) { @@ -245,7 +245,7 @@ static void MaskPointToTransData(Scene *scene, } } -void createTransMaskingData(bContext *C, TransInfo *t) +static void createTransMaskingData(bContext *C, TransInfo *t) { Scene *scene = CTX_data_scene(C); Mask *mask = CTX_data_edit_mask(C); @@ -416,7 +416,7 @@ static void flushTransMasking(TransInfo *t) } } -void recalcData_mask_common(TransInfo *t) +static void recalcData_mask_common(TransInfo *t) { Mask *mask = CTX_data_edit_mask(t->context); @@ -431,7 +431,7 @@ void recalcData_mask_common(TransInfo *t) /** \name Special After Transform Mask * \{ */ -void special_aftertrans_update__mask(bContext *C, TransInfo *t) +static void special_aftertrans_update__mask(bContext *C, TransInfo *t) { Mask *mask = NULL; @@ -455,7 +455,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t) if (IS_AUTOKEY_ON(t->scene)) { Scene *scene = t->scene; - if (ED_mask_layer_shape_auto_key_select(mask, CFRA)) { + if (ED_mask_layer_shape_auto_key_select(mask, scene->r.cfra)) { WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); DEG_id_tag_update(&mask->id, 0); } @@ -463,3 +463,10 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Mask = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransMaskingData, + /* recalcData */ recalcData_mask_common, + /* special_aftertrans_update */ special_aftertrans_update__mask, +}; diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c index 7cba4f97886..7ae93524d0b 100644 --- a/source/blender/editors/transform/transform_convert_mball.c +++ b/source/blender/editors/transform/transform_convert_mball.c @@ -22,7 +22,7 @@ /** \name Meta Elements Transform Creation * \{ */ -void createTransMBallVerts(TransInfo *t) +static void createTransMBallVerts(bContext *UNUSED(C), TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { MetaBall *mb = (MetaBall *)tc->obedit->data; @@ -119,10 +119,10 @@ void createTransMBallVerts(TransInfo *t) /** \name Recalc Meta Ball * \{ */ -void recalcData_mball(TransInfo *t) +static void recalcData_mball(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->data_len) { @@ -132,3 +132,10 @@ void recalcData_mball(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_MBall = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransMBallVerts, + /* recalcData */ recalcData_mball, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index d4b12142162..b5a85f57b84 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1408,7 +1408,6 @@ static void VertsToTransData(TransInfo *t, TransDataExtension *tx, BMEditMesh *em, BMVert *eve, - float *bweight, const struct TransIslandData *island_data, const int island_index) { @@ -1449,17 +1448,13 @@ static void VertsToTransData(TransInfo *t, td->ext = NULL; td->val = NULL; td->extra = eve; - if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { - td->val = bweight; - td->ival = *bweight; - } - else if (t->mode == TFM_SHRINKFATTEN) { + if (t->mode == TFM_SHRINKFATTEN) { td->ext = tx; tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, no, BM_ELEM_SELECT); } } -void createTransEditVerts(TransInfo *t) +static void createTransEditVerts(bContext *UNUSED(C), TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransDataExtension *tx = NULL; @@ -1589,17 +1584,6 @@ void createTransEditVerts(TransInfo *t) "TransObData ext"); } - int cd_vert_bweight_offset = -1; - int cd_vert_crease_offset = -1; - if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); - cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - } - else if (t->mode == TFM_VERT_CREASE) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_CREASE); - cd_vert_crease_offset = CustomData_get_offset(&bm->vdata, CD_CREASE); - } - TransData *tob = tc->data; TransDataMirror *td_mirror = tc->data_mirror; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { @@ -1632,15 +1616,9 @@ void createTransEditVerts(TransInfo *t) td_mirror++; } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float *bweight = (cd_vert_bweight_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : - (cd_vert_crease_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_crease_offset) : - NULL; - /* Do not use the island center in case we are using islands * only to get axis for snap/rotate to normal... */ - VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index); + VertsToTransData(t, tob, tx, em, eve, &island_data, island_index); if (tx) { tx++; } @@ -1940,7 +1918,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; } @@ -2051,12 +2029,12 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) } } -void recalcData_mesh(TransInfo *t) +static 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) { @@ -2151,3 +2129,10 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Mesh = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransEditVerts, + /* recalcData */ recalcData_mesh, + /* special_aftertrans_update */ special_aftertrans_update__mesh, +}; diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c index 2d6c6a933d6..becf3c7ce5a 100644 --- a/source/blender/editors/transform/transform_convert_mesh_edge.c +++ b/source/blender/editors/transform/transform_convert_mesh_edge.c @@ -23,7 +23,7 @@ /** \name Edge (for crease) Transform Creation * \{ */ -void createTransEdge(TransInfo *t) +static void createTransEdge(bContext *UNUSED(C), TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -99,8 +99,8 @@ void createTransEdge(TransInfo *t) td->ext = NULL; fl_ptr = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_float_offset); - td->val = fl_ptr; - td->ival = *fl_ptr; + td->loc = fl_ptr; + td->iloc[0] = *fl_ptr; td++; } @@ -108,7 +108,7 @@ void createTransEdge(TransInfo *t) } } -void recalcData_mesh_edge(TransInfo *t) +static void recalcData_mesh_edge(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); @@ -116,3 +116,10 @@ void recalcData_mesh_edge(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_MeshEdge = { + /* flags */ T_EDIT, + /* createTransData */ createTransEdge, + /* recalcData */ recalcData_mesh_edge, + /* special_aftertrans_update */ special_aftertrans_update__mesh, +}; diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c index fdc2ba59035..376e559181e 100644 --- a/source/blender/editors/transform/transform_convert_mesh_skin.c +++ b/source/blender/editors/transform/transform_convert_mesh_skin.c @@ -66,7 +66,7 @@ static void tc_mesh_skin_transdata_create(TransDataBasic *td, td->extra = eve; } -void createTransMeshSkin(TransInfo *t) +static void createTransMeshSkin(bContext *UNUSED(C), TransInfo *t) { BLI_assert(t->mode == TFM_SKIN_RESIZE); FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -271,7 +271,7 @@ static void tc_mesh_skin_apply_to_mirror(TransInfo *t) } } -void recalcData_mesh_skin(TransInfo *t) +static void recalcData_mesh_skin(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; /* mirror modifier clipping? */ @@ -289,3 +289,10 @@ void recalcData_mesh_skin(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_MeshSkin = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransMeshSkin, + /* recalcData */ recalcData_mesh_skin, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index 18868643b6d..d95bc7b976f 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -234,11 +234,10 @@ static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float as MEM_freeN(dists_prev); } -void createTransUVs(bContext *C, TransInfo *t) +static void createTransUVs(bContext *C, TransInfo *t) { SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = t->scene; - ToolSettings *ts = CTX_data_tool_settings(C); const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; @@ -266,8 +265,7 @@ void createTransUVs(bContext *C, TransInfo *t) /* count */ if (is_island_center) { /* create element map with island information */ - const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; - elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true); + elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true); if (elementmap == NULL) { continue; } @@ -448,7 +446,7 @@ static void flushTransUVs(TransInfo *t) } } -void recalcData_uv(TransInfo *t) +static void recalcData_uv(TransInfo *t) { SpaceImage *sima = t->area->spacedata.first; @@ -465,3 +463,10 @@ void recalcData_uv(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_MeshUV = { + /* flags */ (T_EDIT | T_POINTS | T_2D_EDIT), + /* createTransData */ createTransUVs, + /* recalcData */ recalcData_uv, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c new file mode 100644 index 00000000000..f05688f3325 --- /dev/null +++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup edtransform + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_crazyspace.h" +#include "BKE_editmesh.h" +#include "BKE_modifier.h" +#include "BKE_scene.h" + +#include "ED_mesh.h" + +#include "DEG_depsgraph_query.h" + +#include "transform.h" +#include "transform_orientations.h" + +#include "transform_convert.h" + +/* -------------------------------------------------------------------- */ +/** \name Edit Mesh #CD_BWEIGHT and #CD_CREASE Transform Creation + * \{ */ + +static float *tc_mesh_cdata_transdata_center(const struct TransIslandData *island_data, + const int island_index, + BMVert *eve) +{ + if (island_data->center && island_index != -1) { + return island_data->center[island_index]; + } + return eve->co; +} + +static void tc_mesh_cdata_transdata_create(TransDataBasic *td, + BMVert *eve, + float *weight, + const struct TransIslandData *island_data, + const int island_index) +{ + BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); + + td->loc = weight; + td->iloc[0] = *weight; + + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + td->flag |= TD_SELECTED; + } + + copy_v3_v3(td->center, tc_mesh_cdata_transdata_center(island_data, island_index, eve)); + td->extra = eve; +} + +static void createTransMeshVertCData(bContext *UNUSED(C), TransInfo *t) +{ + BLI_assert(ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + Mesh *me = tc->obedit->data; + BMesh *bm = em->bm; + BMVert *eve; + BMIter iter; + float mtx[3][3], smtx[3][3]; + int a; + const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + + struct TransIslandData island_data = {NULL}; + struct TransMirrorData mirror_data = {NULL}; + struct TransMeshDataCrazySpace crazyspace_data = {NULL}; + + /* Support other objects using PET to adjust these, unless connected is enabled. */ + if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) { + continue; + } + + int cd_offset = -1; + if (t->mode == TFM_BWEIGHT) { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT); + cd_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + } + else { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_CREASE); + cd_offset = CustomData_get_offset(&bm->vdata, CD_CREASE); + } + + if (cd_offset == -1) { + continue; + } + + int data_len = 0; + if (prop_mode) { + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + data_len++; + } + } + } + else { + data_len = bm->totvertsel; + } + + if (data_len == 0) { + continue; + } + + const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); + if (is_island_center) { + /* In this specific case, near-by vertices will need to know + * the island of the nearest connected vertex. */ + const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) && + (t->around == V3D_AROUND_LOCAL_ORIGINS) && + (em->selectmode & SCE_SELECT_VERTEX)); + + const bool calc_island_center = false; + const bool calc_island_axismtx = false; + + transform_convert_mesh_islands_calc( + em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data); + } + + copy_m3_m4(mtx, tc->obedit->obmat); + /* we use a pseudo-inverse so that when one of the axes is scaled to 0, + * matrix inversion still works and we can still moving along the other */ + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + /* Original index of our connected vertex when connected distances are calculated. + * Optional, allocate if needed. */ + int *dists_index = NULL; + float *dists = NULL; + if (prop_mode & T_PROP_CONNECTED) { + dists = MEM_mallocN(bm->totvert * sizeof(float), __func__); + if (is_island_center) { + dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__); + } + transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index); + } + + /* Create TransDataMirror. */ + if (tc->use_mirror_axis_any) { + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + bool use_select = (t->flag & T_PROP_EDIT) == 0; + const bool mirror_axis[3] = { + tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z}; + transform_convert_mesh_mirrordata_calc( + em, use_select, use_topology, mirror_axis, &mirror_data); + + if (mirror_data.vert_map) { + tc->data_mirror_len = mirror_data.mirror_elem_len; + tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror), + __func__); + + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (mirror_data.vert_map[a].index != -1) { + data_len--; + } + } + } + } + } + + /* Detect CrazySpace [tm]. */ + transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data); + + /* Create TransData. */ + BLI_assert(data_len >= 1); + tc->data_len = data_len; + tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); + + TransData *td = tc->data; + TransDataMirror *td_mirror = tc->data_mirror; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + continue; + } + + int island_index = -1; + if (island_data.island_vert_map) { + const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; + island_index = island_data.island_vert_map[connected_index]; + } + + float *weight = BM_ELEM_CD_GET_VOID_P(eve, cd_offset); + if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { + tc_mesh_cdata_transdata_create( + (TransDataBasic *)td_mirror, eve, weight, &island_data, island_index); + + int elem_index = mirror_data.vert_map[a].index; + BMVert *v_src = BM_vert_at_index(bm, elem_index); + + td_mirror->flag |= mirror_data.vert_map[a].flag; + td_mirror->loc_src = BM_ELEM_CD_GET_VOID_P(v_src, cd_offset); + td_mirror++; + } + else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + tc_mesh_cdata_transdata_create( + (TransDataBasic *)td, eve, weight, &island_data, island_index); + + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + createSpaceNormal(td->axismtx, eve->no); + } + else { + /* Setting normals */ + copy_v3_v3(td->axismtx[2], eve->no); + td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] = + td->axismtx[1][1] = td->axismtx[1][2] = 0.0f; + } + + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { + td->dist = dists[a]; + } + else { + td->flag |= TD_NOTCONNECTED; + td->dist = FLT_MAX; + } + } + + /* CrazySpace */ + transform_convert_mesh_crazyspace_transdata_set( + mtx, + smtx, + crazyspace_data.defmats ? crazyspace_data.defmats[a] : NULL, + crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ? + crazyspace_data.quats[a] : + NULL, + td); + + td++; + } + } + + transform_convert_mesh_islanddata_free(&island_data); + transform_convert_mesh_mirrordata_free(&mirror_data); + transform_convert_mesh_crazyspace_free(&crazyspace_data); + if (dists) { + MEM_freeN(dists); + } + if (dists_index) { + MEM_freeN(dists_index); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Mesh Data + * \{ */ + +static void tc_mesh_cdata_apply_to_mirror(TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->use_mirror_axis_any) { + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + td_mirror->loc[0] = td_mirror->loc_src[0]; + } + } + } +} + +static void recalcData_mesh_cdata(TransInfo *t) +{ + bool is_canceling = t->state == TRANS_CANCEL; + /* mirror modifier clipping? */ + if (!is_canceling) { + if (!(t->flag & T_NO_MIRROR)) { + tc_mesh_cdata_apply_to_mirror(t); + } + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BKE_editmesh_looptri_and_normals_calc(em); + } +} + +/** \} */ + +TransConvertTypeInfo TransConvertType_MeshVertCData = { + /* flags */ (T_EDIT | T_POINTS), + /* createTransData */ createTransMeshVertCData, + /* recalcData */ recalcData_mesh_cdata, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 2fa8fcf65ba..cfa933d1600 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -59,7 +59,7 @@ typedef struct TransDataNla { /** \name NLA Transform Creation * \{ */ -void createTransNlaData(bContext *C, TransInfo *t) +static void createTransNlaData(bContext *C, TransInfo *t) { Scene *scene = t->scene; SpaceNla *snla = NULL; @@ -82,12 +82,13 @@ void createTransNlaData(bContext *C, TransInfo *t) snla = (SpaceNla *)ac.sl; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* which side of the current frame should be allowed */ if (t->mode == TFM_TIME_EXTEND) { - t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA); + t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra); } else { /* normal transform - both sides of current frame are considered */ @@ -108,10 +109,10 @@ void createTransNlaData(bContext *C, TransInfo *t) /* transition strips can't get directly transformed */ if (strip->type != NLASTRIP_TYPE_TRANSITION) { if (strip->flag & NLASTRIP_FLAG_SELECT) { - if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) { + if (FrameOnMouseSide(t->frame_side, strip->start, (float)scene->r.cfra)) { count++; } - if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) { + if (FrameOnMouseSide(t->frame_side, strip->end, (float)scene->r.cfra)) { count++; } } @@ -122,7 +123,7 @@ void createTransNlaData(bContext *C, TransInfo *t) /* stop if trying to build list if nothing selected */ if (count == 0) { /* clear temp metas that may have been created but aren't needed now - * because they fell on the wrong side of CFRA + * because they fell on the wrong side of scene->r.cfra */ for (ale = anim_data.first; ale; ale = ale->next) { NlaTrack *nlt = (NlaTrack *)ale->data; @@ -181,12 +182,12 @@ void createTransNlaData(bContext *C, TransInfo *t) tdn->h2[0] = strip->end; tdn->h2[1] = yval; - center[0] = (float)CFRA; + center[0] = (float)scene->r.cfra; center[1] = yval; center[2] = 0.0f; /* set td's based on which handles are applicable */ - if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) { + if (FrameOnMouseSide(t->frame_side, strip->start, (float)scene->r.cfra)) { /* just set tdn to assume that it only has one handle for now */ tdn->handle = -1; @@ -206,7 +207,7 @@ void createTransNlaData(bContext *C, TransInfo *t) td->extra = tdn; td++; } - if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) { + if (FrameOnMouseSide(t->frame_side, strip->end, (float)scene->r.cfra)) { /* if tdn is already holding the start handle, * then we're doing both, otherwise, only end */ tdn->handle = (tdn->handle) ? 2 : 1; @@ -248,7 +249,7 @@ void createTransNlaData(bContext *C, TransInfo *t) ANIM_animdata_freelist(&anim_data); } -void recalcData_nla(TransInfo *t) +static void recalcData_nla(TransInfo *t) { SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; @@ -463,7 +464,7 @@ void recalcData_nla(TransInfo *t) /** \name Special After Transform NLA * \{ */ -void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) +static void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) { bAnimContext ac; @@ -475,7 +476,7 @@ void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) if (ac.datatype) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_FCURVESONLY); /* get channels to work on */ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -505,3 +506,10 @@ void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) } /** \} */ + +TransConvertTypeInfo TransConvertType_NLA = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransNlaData, + /* recalcData */ recalcData_nla, + /* special_aftertrans_update */ special_aftertrans_update__nla, +}; diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index 8281052c314..e18f75b71ae 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -89,7 +89,7 @@ static bool is_node_parent_select(bNode *node) return false; } -void createTransNodeData(TransInfo *t) +static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) { const float dpi_fac = UI_DPI_FAC; SpaceNode *snode = t->area->spacedata.first; @@ -150,7 +150,7 @@ void createTransNodeData(TransInfo *t) /** \name Node Transform Creation * \{ */ -void flushTransNodes(TransInfo *t) +static void flushTransNodes(TransInfo *t) { const float dpi_fac = UI_DPI_FAC; @@ -220,7 +220,7 @@ void flushTransNodes(TransInfo *t) /** \name Special After Transform Node * \{ */ -void special_aftertrans_update__node(bContext *C, TransInfo *t) +static void special_aftertrans_update__node(bContext *C, TransInfo *t) { struct Main *bmain = CTX_data_main(C); const bool canceled = (t->state == TRANS_CANCEL); @@ -249,3 +249,10 @@ void special_aftertrans_update__node(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Node = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransNodeData, + /* recalcData */ flushTransNodes, + /* special_aftertrans_update */ special_aftertrans_update__node, +}; diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 5879a65eb4b..be5f59c61c0 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -480,7 +480,7 @@ static void clear_trans_object_base_flags(TransInfo *t) } } -void createTransObject(bContext *C, TransInfo *t) +static void createTransObject(bContext *C, TransInfo *t) { Main *bmain = CTX_data_main(C); TransData *td = NULL; @@ -732,8 +732,8 @@ static void autokeyframe_object( KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); ListBase dsources = {NULL, NULL}; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - (float)CFRA); + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( + depsgraph, (float)scene->r.cfra); eInsertKeyFlags flag = 0; /* Get flags used for inserting keyframes. */ @@ -860,12 +860,12 @@ static bool motionpath_need_update_object(Scene *scene, Object *ob) /** \name Recalc Data object * \{ */ -void recalcData_objects(TransInfo *t) +static void recalcData_objects(TransInfo *t) { bool motionpath_update = false; if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -918,7 +918,7 @@ void recalcData_objects(TransInfo *t) /** \name Special After Transform Object * \{ */ -void special_aftertrans_update__object(bContext *C, TransInfo *t) +static void special_aftertrans_update__object(bContext *C, TransInfo *t) { BLI_assert(t->options & CTX_OBJECT); @@ -991,3 +991,10 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Object = { + /* flags */ 0, + /* createTransData */ createTransObject, + /* recalcData */ recalcData_objects, + /* special_aftertrans_update */ special_aftertrans_update__object, +}; diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c index 1f58ec80f02..39bf22a9af9 100644 --- a/source/blender/editors/transform/transform_convert_object_texspace.c +++ b/source/blender/editors/transform/transform_convert_object_texspace.c @@ -29,7 +29,7 @@ * * \{ */ -void createTransTexspace(TransInfo *t) +static void createTransTexspace(bContext *UNUSED(C), TransInfo *t) { ViewLayer *view_layer = t->view_layer; TransData *td; @@ -86,11 +86,11 @@ void createTransTexspace(TransInfo *t) /** \name Recalc Data object * \{ */ -void recalcData_texspace(TransInfo *t) +static void recalcData_texspace(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -106,3 +106,10 @@ void recalcData_texspace(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_ObjectTexSpace = { + /* flags */ 0, + /* createTransData */ createTransTexspace, + /* recalcData */ recalcData_texspace, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_paintcurve.c b/source/blender/editors/transform/transform_convert_paintcurve.c index b2566016496..61b1cb9493b 100644 --- a/source/blender/editors/transform/transform_convert_paintcurve.c +++ b/source/blender/editors/transform/transform_convert_paintcurve.c @@ -108,7 +108,7 @@ static void PaintCurvePointToTransData(PaintCurvePoint *pcp, } } -void createTransPaintCurveVerts(bContext *C, TransInfo *t) +static void createTransPaintCurveVerts(bContext *C, TransInfo *t) { Paint *paint = BKE_paint_get_active_from_context(C); PaintCurve *pc; @@ -188,7 +188,7 @@ void createTransPaintCurveVerts(bContext *C, TransInfo *t) /** \name Paint Curve Transform Flush * \{ */ -void flushTransPaintCurve(TransInfo *t) +static void flushTransPaintCurve(TransInfo *t) { int i; @@ -204,3 +204,10 @@ void flushTransPaintCurve(TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_PaintCurve = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransPaintCurveVerts, + /* recalcData */ flushTransPaintCurve, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c index d7b0f378c8a..41f37b34af0 100644 --- a/source/blender/editors/transform/transform_convert_particle.c +++ b/source/blender/editors/transform/transform_convert_particle.c @@ -28,7 +28,7 @@ /** \name Particle Edit Transform Creation * \{ */ -void createTransParticleVerts(TransInfo *t) +static void createTransParticleVerts(bContext *UNUSED(C), TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -235,12 +235,19 @@ static void flushTransParticles(TransInfo *t) /** \name Recalc Transform Particles Data * \{ */ -void recalcData_particles(TransInfo *t) +static void recalcData_particles(TransInfo *t) { if (t->state != TRANS_CANCEL) { - applyProject(t); + applySnappingIndividual(t); } flushTransParticles(t); } /** \} */ + +TransConvertTypeInfo TransConvertType_Particle = { + /* flags */ T_POINTS, + /* createTransData */ createTransParticleVerts, + /* recalcData */ recalcData_particles, + /* special_aftertrans_update */ NULL, +}; diff --git a/source/blender/editors/transform/transform_convert_sculpt.c b/source/blender/editors/transform/transform_convert_sculpt.c index 5bf6bfa8644..95958b816ab 100644 --- a/source/blender/editors/transform/transform_convert_sculpt.c +++ b/source/blender/editors/transform/transform_convert_sculpt.c @@ -23,7 +23,7 @@ /** \name Sculpt Transform Creation * \{ */ -void createTransSculpt(bContext *C, TransInfo *t) +static void createTransSculpt(bContext *C, TransInfo *t) { TransData *td; @@ -94,13 +94,13 @@ void createTransSculpt(bContext *C, TransInfo *t) /** \name Recalc Data object * \{ */ -void recalcData_sculpt(TransInfo *t) +static void recalcData_sculpt(TransInfo *t) { Object *ob = OBACT(t->view_layer); ED_sculpt_update_modal_transform(t->context, ob); } -void special_aftertrans_update__sculpt(bContext *C, TransInfo *t) +static void special_aftertrans_update__sculpt(bContext *C, TransInfo *t) { Scene *scene = t->scene; if (!BKE_id_is_editable(CTX_data_main(C), &scene->id)) { @@ -114,3 +114,10 @@ void special_aftertrans_update__sculpt(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Sculpt = { + /* flags */ 0, + /* createTransData */ createTransSculpt, + /* recalcData */ recalcData_sculpt, + /* special_aftertrans_update */ special_aftertrans_update__sculpt, +}; diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 849641fd320..eefc9d0cc2a 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -90,7 +90,7 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag) if (t->mode == TFM_TIME_EXTEND) { /* *** Extend Transform *** */ - int cfra = CFRA; + int cfra = scene->r.cfra; int left = SEQ_time_left_handle_frame_get(scene, seq); int right = SEQ_time_right_handle_frame_get(scene, seq); @@ -465,7 +465,7 @@ static SeqCollection *query_time_dependent_strips_strips(TransInfo *t) return dependent; } -void createTransSeqData(TransInfo *t) +static void createTransSeqData(bContext *UNUSED(C), TransInfo *t) { Scene *scene = t->scene; Editing *ed = SEQ_editing_get(t->scene); @@ -489,7 +489,7 @@ void createTransSeqData(TransInfo *t) } tc->custom.type.free_cb = freeSeqData; - t->frame_side = transform_convert_frame_side_dir_get(t, (float)CFRA); + t->frame_side = transform_convert_frame_side_dir_get(t, (float)scene->r.cfra); count = SeqTransCount(t, ed->seqbasep); @@ -659,7 +659,7 @@ static void flushTransSeq(TransInfo *t) SEQ_collection_free(transformed_strips); } -void recalcData_sequencer(TransInfo *t) +static void recalcData_sequencer(TransInfo *t) { TransData *td; int a; @@ -689,7 +689,7 @@ void recalcData_sequencer(TransInfo *t) /** \name Special After Transform Sequencer * \{ */ -void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t) +static void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t) { if (t->state == TRANS_CANCEL) { return; @@ -734,3 +734,10 @@ void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2]) } /** \} */ + +TransConvertTypeInfo TransConvertType_Sequencer = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransSeqData, + /* recalcData */ recalcData_sequencer, + /* special_aftertrans_update */ special_aftertrans_update__sequencer, +}; diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index 46606d5814f..3d0c3ddaa4c 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -105,7 +105,7 @@ static void freeSeqData(TransInfo *UNUSED(t), MEM_freeN(td->extra); } -void createTransSeqImageData(TransInfo *t) +static void createTransSeqImageData(bContext *UNUSED(C), TransInfo *t) { Editing *ed = SEQ_editing_get(t->scene); const SpaceSeq *sseq = t->area->spacedata.first; @@ -173,25 +173,25 @@ static bool autokeyframe_sequencer_image(bContext *C, bool changed = false; if (do_rot) { prop = RNA_struct_find_property(&ptr, "rotation"); - changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false); } if (do_loc) { prop = RNA_struct_find_property(&ptr, "offset_x"); - changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false); prop = RNA_struct_find_property(&ptr, "offset_y"); - changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false); } if (do_scale) { prop = RNA_struct_find_property(&ptr, "scale_x"); - changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false); prop = RNA_struct_find_property(&ptr, "scale_y"); - changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, CFRA, false); + changed |= ED_autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, false); } return changed; } -void recalcData_sequencer_image(TransInfo *t) +static void recalcData_sequencer_image(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); TransData *td = NULL; @@ -247,7 +247,7 @@ void recalcData_sequencer_image(TransInfo *t) } } -void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t) +static void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); @@ -271,3 +271,10 @@ void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo * } } } + +TransConvertTypeInfo TransConvertType_SequencerImage = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransSeqImageData, + /* recalcData */ recalcData_sequencer_image, + /* special_aftertrans_update */ special_aftertrans_update__sequencer_image, +}; diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index d447cd71a40..c0c660289a5 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -518,7 +518,7 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t) } } -void createTransTrackingData(bContext *C, TransInfo *t) +static void createTransTrackingData(bContext *C, TransInfo *t) { ARegion *region = CTX_wm_region(C); SpaceClip *sc = CTX_wm_space_clip(C); @@ -694,7 +694,7 @@ static void flushTransTracking(TransInfo *t) } } -void recalcData_tracking(TransInfo *t) +static void recalcData_tracking(TransInfo *t) { SpaceClip *sc = t->area->spacedata.first; @@ -747,7 +747,7 @@ void recalcData_tracking(TransInfo *t) /** \name Special After Transform Tracking * \{ */ -void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) +static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) { SpaceClip *sc = t->area->spacedata.first; MovieClip *clip = ED_space_clip_get_clip(sc); @@ -790,3 +790,10 @@ void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) } /** \} */ + +TransConvertTypeInfo TransConvertType_Tracking = { + /* flags */ (T_POINTS | T_2D_EDIT), + /* createTransData */ createTransTrackingData, + /* recalcData */ recalcData_tracking, + /* special_aftertrans_update */ special_aftertrans_update__movieclip, +}; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index e45cac36736..56a7d045dfd 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -555,7 +555,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else { /* Release confirms preference should not affect node editor (T69288, T70504). */ - if (ISMOUSE(t->launch_event) && + if (ISMOUSE_BUTTON(t->launch_event) && ((U.flag & USER_RELEASECONFIRM) || (t->spacetype == SPACE_NODE))) { /* Global "release confirm" on mouse bindings */ t->flag |= T_RELEASE_CONFIRM; diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 83f1bd35f81..d8da7a11d28 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -292,6 +292,9 @@ void constraintTransLim(const TransInfo *t, TransData *td) continue; } + /* Initialize the custom space for use in calculating the matrices. */ + BKE_constraint_custom_object_space_init(&cob, con); + /* get constraint targets if needed */ BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime); @@ -549,18 +552,14 @@ void ElementRotation_ex(const TransInfo *t, mul_m3_m3m3(totmat, mat, td->mtx); mul_m3_m3m3(smat, td->smtx, totmat); - /* apply gpencil falloff */ + /* Apply gpencil falloff. */ if (t->options & CTX_GPENCIL_STROKES) { bGPDstroke *gps = (bGPDstroke *)td->extra; - float sx = smat[0][0]; - float sy = smat[1][1]; - float sz = smat[2][2]; - - mul_m3_fl(smat, gps->runtime.multi_frame_falloff); - /* fix scale */ - smat[0][0] = sx; - smat[1][1] = sy; - smat[2][2] = sz; + if (gps->runtime.multi_frame_falloff != 1.0f) { + float ident_mat[3][3]; + unit_m3(ident_mat); + interp_m3_m3m3(smat, ident_mat, smat, gps->runtime.multi_frame_falloff); + } } sub_v3_v3v3(vec, td->iloc, center); @@ -945,7 +944,11 @@ void ElementResize(const TransInfo *t, if (td->ext && td->ext->size) { float fsize[3]; - if (ELEM(t->data_type, TC_SCULPT, TC_OBJECT, TC_OBJECT_TEXSPACE, TC_POSE)) { + if (ELEM(t->data_type, + &TransConvertType_Sculpt, + &TransConvertType_Object, + &TransConvertType_ObjectTexSpace, + &TransConvertType_Pose)) { float obsizemat[3][3]; /* Reorient the size mat to fit the oriented object. */ mul_m3_m3m3(obsizemat, tmat, td->axismtx); @@ -1205,7 +1208,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) break; } - if (t->data_type == TC_MESH_VERTS) { + if (t->data_type == &TransConvertType_Mesh) { /* Init Custom Data correction. * Ideally this should be called when creating the TransData. */ transform_convert_mesh_customdatacorrect_init(t); diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index aa4d608de04..f7f9e14b8ac 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -64,10 +64,8 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); - if (*td->val <= 0.0f) { - *td->val = 0.001f; - } + *td->val = interpf(*td->val, td->ival, td->factor); + CLAMP_MIN(*td->val, 0.0f); } } } @@ -93,10 +91,6 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; -#ifdef USE_NUM_NO_ZERO - t->num.val_flag[0] |= NUM_NO_ZERO; -#endif - t->flag |= T_NO_CONSTRAINT; } diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c index 987d8396907..e96e74b596c 100644 --- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c +++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c @@ -44,16 +44,11 @@ static void transdata_elem_bevel_weight(const TransInfo *UNUSED(t), TransData *td, const float weight) { - if (td->val == NULL) { + if (td->loc == NULL) { return; } - *td->val = td->ival + weight * td->factor; - if (*td->val < 0.0f) { - *td->val = 0.0f; - } - if (*td->val > 1.0f) { - *td->val = 1.0f; - } + *td->loc = td->iloc[0] + weight * td->factor; + CLAMP(*td->loc, 0.0f, 1.0f); } static void transdata_elem_bevel_weight_fn(void *__restrict iter_data_v, diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c index f1acc2a4c9a..1a3ccf30387 100644 --- a/source/blender/editors/transform/transform_mode_edge_crease.c +++ b/source/blender/editors/transform/transform_mode_edge_crease.c @@ -44,17 +44,12 @@ static void transdata_elem_crease(const TransInfo *UNUSED(t), TransData *td, const float crease) { - if (td->val == NULL) { + if (td->loc == NULL) { return; } - *td->val = td->ival + crease * td->factor; - if (*td->val < 0.0f) { - *td->val = 0.0f; - } - if (*td->val > 1.0f) { - *td->val = 1.0f; - } + *td->loc = td->iloc[0] + crease * td->factor; + CLAMP(*td->loc, 0.0f, 1.0f); } static void transdata_elem_crease_fn(void *__restrict iter_data_v, 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_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index 83dce17d104..8b9431b65ea 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -74,7 +74,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); CLAMP(*td->val, 0.0f, 1.0f); } } diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 796d5c7ae9c..d8ec7d4ff50 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -74,7 +74,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); if (*td->val <= 0.0f) { *td->val = 0.001f; } diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 19a3deade63..e2ccf61796b 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -90,7 +90,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) } /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); if (*td->val <= 0.0f) { *td->val = 0.001f; } diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index ffae651e4aa..1ccda96fecb 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -11,6 +11,7 @@ #include "BLI_task.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_unit.h" #include "ED_screen.h" @@ -84,6 +85,103 @@ static void ApplySnapResize(TransInfo *t, float vec[3]) } } +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + +static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) +{ + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset); + } + + /* Assume no change is required. */ + float scale = 1.0f; + + /* Are we scaling U and V together, or just one axis? */ + const bool adjust_u = !(t->con.mode & CON_AXIS1); + const bool adjust_v = !(t->con.mode & CON_AXIS0); + const bool use_local_center = transdata_check_local_center(t, t->around); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) { + + /* Get scale origin. */ + const float *scale_origin = use_local_center ? td->center : t->center_global; + + /* Alias td->loc as min and max just in case we need to optimize later. */ + const float *min = td->loc; + const float *max = td->loc; + + if (adjust_u) { + /* Update U against the left border. */ + constrain_scale_to_boundary( + scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale); + + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary( + base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale); + } + + /* Do the same for the V co-ordinate. */ + if (adjust_v) { + constrain_scale_to_boundary( + scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale); + + constrain_scale_to_boundary( + base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale); + } + } + } + vec[0] *= scale; + vec[1] *= scale; + return scale != 1.0f; +} + static void applyResize(TransInfo *t, const int UNUSED(mval[2])) { float mat[3][3]; @@ -105,7 +203,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); @@ -157,7 +255,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) } /* Evil hack - redo resize if clipping needed. */ - if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values_final, 1)) { + if (t->flag & T_CLIP_UV && clip_uv_transform_resize(t, t->values_final)) { size_to_mat3(mat, t->values_final); if (t->con.mode & CON_APPLY) { 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_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 4130f6dc034..1474bc4591a 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -59,7 +59,7 @@ static void applyTimeScaleValue(TransInfo *t, float value) * (this is only valid when not in NLA) */ AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - float startx = CFRA; + float startx = scene->r.cfra; float fac = value; /* take proportional editing into account */ @@ -107,7 +107,7 @@ void initTimeScale(TransInfo *t) t->mode = TFM_TIME_SCALE; t->transform = applyTimeScale; - /* recalculate center2d to use CFRA and mouse Y, since that's + /* recalculate center2d to use scene->r.cfra and mouse Y, since that's * what is used in time scale */ if ((t->flag & T_OVERRIDE_CENTER) == 0) { t->center_global[0] = t->scene->r.cfra; diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 3c6b6ea4117..04a41814b53 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -16,6 +16,7 @@ #include "BLI_task.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_report.h" #include "BKE_unit.h" @@ -434,6 +435,54 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) custom_data->prev.rotate_mode = rotate_mode; } +static bool clip_uv_transform_translation(TransInfo *t, float vec[2]) +{ + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset); + } + + float min[2], max[2]; + min[0] = min[1] = FLT_MAX; + max[0] = max[1] = -FLT_MAX; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) { + minmax_v2v2_v2(min, max, td->loc); + } + } + + bool result = false; + if (min[0] < base_offset[0]) { + vec[0] += base_offset[0] - min[0]; + result = true; + } + else if (max[0] > base_offset[0] + t->aspect[0]) { + vec[0] -= max[0] - base_offset[0] - t->aspect[0]; + result = true; + } + + if (min[1] < base_offset[1]) { + vec[1] += base_offset[1] - min[1]; + result = true; + } + else if (max[1] > base_offset[1] + t->aspect[1]) { + vec[1] -= max[1] - base_offset[1] - t->aspect[1]; + result = true; + } + + return result; +} + static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) { char str[UI_MAX_DRAW_STR]; @@ -470,7 +519,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) { @@ -498,7 +547,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) applyTranslationValue(t, global_dir); /* evil hack - redo translation if clipping needed */ - if (t->flag & T_CLIP_UV && clipUVTransform(t, global_dir, 0)) { + if (t->flag & T_CLIP_UV && clip_uv_transform_translation(t, global_dir)) { applyTranslationValue(t, global_dir); /* In proportional edit it can happen that */ 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_ops.c b/source/blender/editors/transform/transform_ops.c index a64eff8f981..7c94241f3e3 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -566,6 +566,17 @@ static bool transform_poll_property(const bContext *UNUSED(C), } } + /* Snapping. */ + { + PropertyRNA *prop_snap = RNA_struct_find_property(op->ptr, "snap"); + if (prop_snap && (prop_snap != prop) && + (RNA_property_boolean_get(op->ptr, prop_snap) == false)) { + if (STRPREFIX(prop_id, "snap") || STRPREFIX(prop_id, "use_snap")) { + return false; + } + } + } + return true; } @@ -644,28 +655,63 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) } if (flags & P_SNAP) { - prop = RNA_def_boolean(ot->srna, "snap", 0, "Use Snapping Options", ""); + prop = RNA_def_boolean(ot->srna, "snap", false, "Use Snapping Options", ""); RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_enum(ot->srna, + "snap_elements", + rna_enum_snap_element_items, + SCE_SNAP_MODE_INCREMENT, + "Snap to Elements", + ""); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + + RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", ""); + if (flags & P_GEO_SNAP) { - /* 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). Use "Source snap point" and "Point on - * source that will snap to target" for name and description, respectively. */ - prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Target", ""); + /* 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). Use "Source snap point" and "Point on source that will snap to + * target" for name and description, respectively. */ + prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* Target selection. */ + prop = RNA_def_boolean(ot->srna, "use_snap_self", true, "Target: Include Active", ""); RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "use_snap_edit", true, "Target: Include Edit", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "use_snap_nonedit", true, "Target: Include Non-Edited", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean( + ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* Face Nearest options */ + prop = RNA_def_boolean( + ot->srna, "use_snap_to_same_target", false, "Snap to Same Target", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_int( + ot->srna, "snap_face_nearest_steps", 1, 1, 32767, "Face Nearest Steps", "", 1, 32767); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_float_vector( ot->srna, "snap_point", 3, NULL, -FLT_MAX, FLT_MAX, "Point", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); if (flags & P_ALIGN_SNAP) { - prop = RNA_def_boolean(ot->srna, "snap_align", 0, "Align with Point Normal", ""); + prop = RNA_def_boolean(ot->srna, "snap_align", false, "Align with Point Normal", ""); RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_float_vector( ot->srna, "snap_normal", 3, NULL, -FLT_MAX, FLT_MAX, "Normal", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); } } + else { + prop = RNA_def_boolean( + ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + } } if (flags & P_GPENCIL_EDIT) { 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); 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..479214ee2d3 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<ThreadMutex *>(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 attributes of potential target. */ + 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); + /* Get attributes of state. */ + 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 (is_in_object_mode) { + /* Handle target selection options that make sense for object mode. */ + if ((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && is_selected) { + /* What is selectable or not is part of the object and depends on the mode. */ + return false; + } } - - if (snap_select == SCE_SNAP_TARGET_NOT_SELECTED) { - if (is_in_object_mode) { - return !((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)); + else { + /* Handle target selection options that make sense for edit/pose mode. */ + if ((snap_target_select & SCE_SNAP_TARGET_NOT_ACTIVE) && is_active) { + return false; + } + if ((snap_target_select & SCE_SNAP_TARGET_NOT_EDITED) && is_edited && !is_active) { + /* Base is edited, but not active. */ + return false; + } + if ((snap_target_select & SCE_SNAP_TARGET_NOT_NONEDITED) && !is_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_ONLY_SELECTABLE) && !is_selectable) { + 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<ThreadMutex *>(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). @@ -1150,6 +1186,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<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 + * =================================== + * + * 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; } @@ -2268,7 +2623,7 @@ static eSnapMode snapCamera(const SnapObjectContext *sctx, if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { BKE_tracking_camera_get_reconstructed_interpolate( - tracking, tracking_object, CFRA, reconstructed_camera_mat); + tracking, tracking_object, scene->r.cfra, reconstructed_camera_mat); invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); } @@ -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/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index 7972410ad67..06d9bb05206 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -28,6 +28,7 @@ #include "SEQ_time.h" #include "transform.h" +#include "transform_convert.h" #include "transform_snap.h" typedef struct TransSeqSnapData { @@ -191,7 +192,7 @@ static void seq_snap_target_points_build(Scene *scene, int i = 0; if (snap_mode & SEQ_SNAP_TO_CURRENT_FRAME) { - snap_data->target_snap_points[i] = CFRA; + snap_data->target_snap_points[i] = scene->r.cfra; i++; } @@ -244,7 +245,7 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t) TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) { - if (t->data_type == TC_SEQ_IMAGE_DATA) { + if (t->data_type == &TransConvertType_SequencerImage) { return NULL; } @@ -375,7 +376,7 @@ bool ED_transform_snap_sequencer_to_closest_strip_calc(Scene *scene, t.scene = scene; t.region = region; t.values[0] = 0; - t.data_type = TC_SEQ_DATA; + t.data_type = &TransConvertType_Sequencer; t.tsnap.mode = SEQ_tool_settings_snap_mode_get(scene); *r_snap_distance = transform_snap_sequencer_to_closest_strip_ex(&t, frame_1, frame_2); diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 5c2a3374aa1..cdfe40c7d35 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC ../include/ED_uvedit.h ../include/ED_view3d.h ../include/ED_view3d_offscreen.h + ../include/UI_abstract_view.hh ../include/UI_grid_view.hh ../include/UI_icons.h ../include/UI_interface.h diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index cf6f26652a7..fc5fb9f9c28 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -291,7 +291,7 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e Scene *scene = CTX_data_scene(C); SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C); ARegion *region = CTX_wm_region(C); - ImBuf *ibuf = sequencer_ibuf_get(bmain, region, depsgraph, scene, sseq, CFRA, 0, NULL); + ImBuf *ibuf = sequencer_ibuf_get(bmain, region, depsgraph, scene, sseq, scene->r.cfra, 0, NULL); ImageSampleInfo *info = op->customdata; float fx, fy; diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index be6ac6e13e6..60cbc2a2df6 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -311,6 +311,7 @@ static bool editstr_is_simple_numinput(const char ascii) bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) { const char *utf8_buf = NULL; + const char event_ascii = WM_event_utf8_to_ascii(event); char ascii[2] = {'\0', '\0'}; bool updated = false; short idx = n->idx, idx_max = n->idx_max; @@ -321,8 +322,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) if (U.flag & USER_FLAG_NUMINPUT_ADVANCED) #endif { - if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') && - strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) { + if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event_ascii != '\0') && + strchr("01234567890@%^&*-+/{}()[]<>.|", event_ascii)) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDITED; n->flag |= NUM_EDIT_FULL; @@ -333,7 +334,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) #ifdef USE_FAKE_EDIT /* XXX Hack around keyboards without direct access to '=' nor '*'... */ - if (ELEM(event->ascii, '=', '*')) { + if (ELEM(event_ascii, '=', '*')) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDIT_FULL; n->val_flag[idx] |= NUM_EDITED; @@ -357,7 +358,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) else { /* might be a char too... */ utf8_buf = event->utf8_buf; - ascii[0] = event->ascii; + ascii[0] = event_ascii; } break; case EVT_BACKSPACEKEY: @@ -523,9 +524,9 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) break; } - if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) { + if (!updated && !utf8_buf && event->utf8_buf[0]) { utf8_buf = event->utf8_buf; - ascii[0] = event->ascii; + ascii[0] = event_ascii; } /* Up to this point, if we have a ctrl modifier, skip. diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index e1752ae5a29..9a31fd6469d 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -256,16 +256,12 @@ bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const floa * Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number. */ static float uv_nearest_image_tile_distance(const Image *image, - float coords[2], + const float coords[2], float nearest_tile_co[2]) { - int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords); - if (nearest_image_tile_index == -1) { - nearest_image_tile_index = 1001; + if (BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co) == -1) { + zero_v2(nearest_tile_co); } - - nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10; - nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10; /* Add 0.5 to get tile center coordinates. */ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; add_v2_fl(nearest_tile_center_co, 0.5f); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 0b5d6592426..74a9989f550 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -372,6 +372,198 @@ typedef enum eUVWeldAlign { UV_WELD, } eUVWeldAlign; +static bool uvedit_uv_align_weld(Scene *scene, + BMesh *bm, + const eUVWeldAlign tool, + const float cent[2]) +{ + bool changed = false; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (ELEM(tool, UV_ALIGN_X, UV_WELD)) { + if (luv->uv[0] != cent[0]) { + luv->uv[0] = cent[0]; + changed = true; + } + } + if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) { + if (luv->uv[1] != cent[1]) { + luv->uv[1] = cent[1]; + changed = true; + } + } + } + } + return changed; +} + +/** Bitwise-or together, then choose #MLoopUV with highest value. */ +typedef enum eUVEndPointPrecedence { + UVEP_INVALID = 0, + UVEP_SELECTED = (1 << 0), + UVEP_PINNED = (1 << 1), /* i.e. Pinned verts are preferred to selected. */ +} eUVEndPointPrecedence; + +static eUVEndPointPrecedence uvedit_line_update_get_precedence(const MLoopUV *luv) +{ + eUVEndPointPrecedence precedence = UVEP_SELECTED; + if (luv->flag & MLOOPUV_PINNED) { + precedence |= UVEP_PINNED; + } + return precedence; +} + +/** + * Helper to find two endpoints (`a` and `b`) which have higher precedence, and are far apart. + * Note that is only a heuristic and won't always find the best two endpoints. + */ +static bool uvedit_line_update_endpoint(const MLoopUV *luv, + float uv_a[2], + eUVEndPointPrecedence *prec_a, + float uv_b[2], + eUVEndPointPrecedence *prec_b) +{ + eUVEndPointPrecedence flags = uvedit_line_update_get_precedence(luv); + + float len_sq_a = len_squared_v2v2(uv_a, luv->uv); + float len_sq_b = len_squared_v2v2(uv_b, luv->uv); + + /* Caching the value of `len_sq_ab` is unlikely to be faster than recalculating. + * Profile before optimizing. */ + float len_sq_ab = len_squared_v2v2(uv_a, uv_b); + + if ((*prec_a < flags && 0.0f < len_sq_b) || (*prec_a == flags && len_sq_ab < len_sq_b)) { + *prec_a = flags; + copy_v2_v2(uv_a, luv->uv); + return true; + } + + if ((*prec_b < flags && 0.0f < len_sq_a) || (*prec_b == flags && len_sq_ab < len_sq_a)) { + *prec_b = flags; + copy_v2_v2(uv_b, luv->uv); + return true; + } + + return false; +} + +/** + * Find two end extreme points to specify a line, then straighten `len` elements + * by moving UVs on the X-axis, Y-axis, or the closest point on the line segment. + */ +static bool uvedit_uv_straighten_elements(const UvElement *element, + const int len, + const int cd_loop_uv_offset, + const eUVWeldAlign tool) +{ + float uv_start[2]; + float uv_end[2]; + eUVEndPointPrecedence prec_start = UVEP_INVALID; + eUVEndPointPrecedence prec_end = UVEP_INVALID; + + /* Find start and end of line. */ + for (int i = 0; i < 10; i++) { /* Heuristic to prevent infinite loop. */ + bool update = false; + for (int j = 0; j < len; j++) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(element[j].l, cd_loop_uv_offset); + update |= uvedit_line_update_endpoint(luv, uv_start, &prec_start, uv_end, &prec_end); + } + if (!update) { + break; + } + } + + if (prec_start == UVEP_INVALID || prec_end == UVEP_INVALID) { + return false; /* Unable to find two endpoints. */ + } + + float a = 0.0f; /* Similar to "slope". */ + eUVWeldAlign tool_local = tool; + + if (tool_local == UV_STRAIGHTEN_X) { + if (uv_start[1] == uv_end[1]) { + /* Caution, different behavior outside line segment. */ + tool_local = UV_STRAIGHTEN; + } + else { + a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]); + } + } + else if (tool_local == UV_STRAIGHTEN_Y) { + if (uv_start[0] == uv_end[0]) { + /* Caution, different behavior outside line segment. */ + tool_local = UV_STRAIGHTEN; + } + else { + a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]); + } + } + + bool changed = false; + for (int j = 0; j < len; j++) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(element[j].l, cd_loop_uv_offset); + /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis: + * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1 + * Maybe this should be a BLI func? Or is it already existing? + * Could use interp_v2_v2v2, but not sure it's worth it here. */ + if (tool_local == UV_STRAIGHTEN_X) { + luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0]; + } + else if (tool_local == UV_STRAIGHTEN_Y) { + luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1]; + } + else { + closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end); + } + changed = true; /* TODO: Did the UV actually move? */ + } + return changed; +} + +/** + * Group selected UVs into islands, then apply uvedit_uv_straighten_elements to each island. + */ +static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + if (cd_loop_uv_offset == -1) { + return false; + } + + UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true); + if (element_map == NULL) { + return false; + } + + bool changed = false; + + /* Loop backwards to simplify logic. */ + int j1 = element_map->totalUVs; + for (int i = element_map->totalIslands - 1; i >= 0; --i) { + int j0 = element_map->islandIndices[i]; + changed |= uvedit_uv_straighten_elements( + element_map->buf + j0, j1 - j0, cd_loop_uv_offset, tool); + j1 = j0; + } + + BM_uv_element_map_free(element_map); + return changed; +} + static void uv_weld_align(bContext *C, eUVWeldAlign tool) { Scene *scene = CTX_data_scene(C); @@ -429,194 +621,12 @@ static void uv_weld_align(bContext *C, eUVWeldAlign tool) continue; } - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - if (ELEM(tool, UV_ALIGN_X, UV_WELD)) { - BMIter iter, liter; - BMFace *efa; - BMLoop *l; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, efa)) { - continue; - } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[0] = cent[0]; - changed = true; - } - } - } - } - - if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) { - BMIter iter, liter; - BMFace *efa; - BMLoop *l; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, efa)) { - continue; - } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[1] = cent[1]; - changed = true; - } - } - } + if (ELEM(tool, UV_ALIGN_AUTO, UV_ALIGN_X, UV_ALIGN_Y, UV_WELD)) { + changed |= uvedit_uv_align_weld(scene, em->bm, tool, cent); } if (ELEM(tool, UV_STRAIGHTEN, UV_STRAIGHTEN_X, UV_STRAIGHTEN_Y)) { - BMEdge *eed; - BMLoop *l; - BMVert *eve; - BMVert *eve_start; - BMIter iter, liter, eiter; - - /* clear tag */ - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - - /* tag verts with a selected UV */ - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) { - if (!uvedit_face_visible_test(scene, l->f)) { - continue; - } - - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - BM_elem_flag_enable(eve, BM_ELEM_TAG); - break; - } - } - } - - /* flush vertex tags to edges */ - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set( - eed, - BM_ELEM_TAG, - (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) && BM_elem_flag_test(eed->v2, BM_ELEM_TAG))); - } - - /* find a vertex with only one tagged edge */ - eve_start = NULL; - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - int tot_eed_tag = 0; - BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(eed, BM_ELEM_TAG)) { - tot_eed_tag++; - } - } - - if (tot_eed_tag == 1) { - eve_start = eve; - break; - } - } - - if (eve_start) { - BMVert **eve_line = NULL; - BMVert *eve_next = NULL; - BLI_array_declare(eve_line); - int i; - - eve = eve_start; - - /* walk over edges, building an array of verts in a line */ - while (eve) { - BLI_array_append(eve_line, eve); - /* don't touch again */ - BM_elem_flag_disable(eve, BM_ELEM_TAG); - - eve_next = NULL; - - /* find next eve */ - BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(eed, BM_ELEM_TAG)) { - BMVert *eve_other = BM_edge_other_vert(eed, eve); - if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) { - /* this is a tagged vert we didn't walk over yet, step onto it */ - eve_next = eve_other; - break; - } - } - } - - eve = eve_next; - } - - /* now we have all verts, make into a line */ - if (BLI_array_len(eve_line) > 2) { - - /* we know the returns from these must be valid */ - const float *uv_start = uvedit_first_selected_uv_from_vertex( - scene, eve_line[0], cd_loop_uv_offset); - const float *uv_end = uvedit_first_selected_uv_from_vertex( - scene, eve_line[BLI_array_len(eve_line) - 1], cd_loop_uv_offset); - /* For UV_STRAIGHTEN_X & UV_STRAIGHTEN_Y modes */ - float a = 0.0f; - eUVWeldAlign tool_local = tool; - - if (tool_local == UV_STRAIGHTEN_X) { - if (uv_start[1] == uv_end[1]) { - tool_local = UV_STRAIGHTEN; - } - else { - a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]); - } - } - else if (tool_local == UV_STRAIGHTEN_Y) { - if (uv_start[0] == uv_end[0]) { - tool_local = UV_STRAIGHTEN; - } - else { - a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]); - } - } - - /* go over all verts except for endpoints */ - for (i = 0; i < BLI_array_len(eve_line); i++) { - BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) { - if (!uvedit_face_visible_test(scene, l->f)) { - continue; - } - - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis: - * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1 - * Maybe this should be a BLI func? Or is it already existing? - * Could use interp_v2_v2v2, but not sure it's worth it here. */ - if (tool_local == UV_STRAIGHTEN_X) { - luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0]; - } - else if (tool_local == UV_STRAIGHTEN_Y) { - luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1]; - } - else { - closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end); - } - changed = true; - } - } - } - } - else { - /* error - not a line, needs 3+ points. */ - } - - if (eve_line) { - MEM_freeN(eve_line); - } - } - else { - /* error - can't find an endpoint. */ - } + changed |= uvedit_uv_straighten(scene, em->bm, tool); } if (changed) { @@ -1026,6 +1036,12 @@ static bool uv_snap_cursor_to_selection(Scene *scene, return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around); } +static void uv_snap_cursor_to_origin(float uvco[2]) +{ + uvco[0] = 0; + uvco[1] = 0; +} + static int uv_snap_cursor_exec(bContext *C, wmOperator *op) { SpaceImage *sima = CTX_wm_space_image(C); @@ -1048,6 +1064,10 @@ static int uv_snap_cursor_exec(bContext *C, wmOperator *op) MEM_freeN(objects); break; } + case 2: + uv_snap_cursor_to_origin(sima->cursor); + changed = true; + break; } if (!changed) { @@ -1064,6 +1084,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot) static const EnumPropertyItem target_items[] = { {0, "PIXELS", 0, "Pixels", ""}, {1, "SELECTED", 0, "Selected", ""}, + {2, "ORIGIN", 0, "Origin", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c index 7c6960a634a..31a1b60167e 100644 --- a/source/blender/editors/uvedit/uvedit_path.c +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -56,75 +56,6 @@ #include "bmesh_tools.h" /* -------------------------------------------------------------------- */ -/** \name Local Utilities - * \{ */ - -/** - * Support edge-path using vert-path calculation code. - * - * Cheat! Pick 2 closest loops and do vertex path, - * in practices only obscure/contrived cases will make give noticeably worse behavior. - * - * While the code below is a bit awkward, it's significantly less overhead than - * adding full edge selection which is nearly the same as vertex path in the case of UV's. - * - * \param use_nearest: When false use the post distant pair of loops, - * use when filling a region as we want both verts from each edge to be included in the region. - */ -static void bm_loop_calc_vert_pair_from_edge_pair(const bool use_nearest, - const int cd_loop_uv_offset, - const float aspect_y, - BMElem **ele_src_p, - BMElem **ele_dst_p, - BMElem **r_ele_dst_final) -{ - BMLoop *l_src = (BMLoop *)*ele_src_p; - BMLoop *l_dst = (BMLoop *)*ele_dst_p; - - const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset); - const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset); - const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset); - const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset); - - const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y}; - const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y}; - const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y}; - const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y}; - - struct { - int src_index; - int dst_index; - float len_sq; - } tests[4] = { - {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)}, - {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)}, - {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)}, - {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)}, - }; - int i_best = 0; - for (int i = 1; i < ARRAY_SIZE(tests); i++) { - if (use_nearest) { - if (tests[i].len_sq < tests[i_best].len_sq) { - i_best = i; - } - } - else { - if (tests[i].len_sq > tests[i_best].len_sq) { - i_best = i; - } - } - } - - *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src); - *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst); - - /* Ensure the edge is selected, not just the vertices up until we hit it. */ - *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Path Select Struct & Properties * \{ */ @@ -180,12 +111,12 @@ static void path_select_params_from_op(wmOperator *op, struct PathSelectParams * * \{ */ /* callbacks */ -static bool looptag_filter_cb(BMLoop *l, void *user_data_v) +static bool verttag_filter_cb(BMLoop *l, void *user_data_v) { struct UserData_UV *user_data = user_data_v; return uvedit_face_visible_test(user_data->scene, l->f); } -static bool looptag_test_cb(BMLoop *l, void *user_data_v) +static bool verttag_test_cb(BMLoop *l, void *user_data_v) { /* All connected loops are selected or we return false. */ struct UserData_UV *user_data = user_data_v; @@ -195,7 +126,7 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v) BMIter iter; BMLoop *l_iter; BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) { - if (looptag_filter_cb(l_iter, user_data)) { + if (verttag_filter_cb(l_iter, user_data)) { const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); if (equals_v2v2(luv->uv, luv_iter->uv)) { if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) { @@ -206,7 +137,7 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v) } return true; } -static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v) +static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v) { struct UserData_UV *user_data = user_data_v; const Scene *scene = user_data->scene; @@ -216,7 +147,7 @@ static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v) BMIter iter; BMLoop *l_iter; BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) { - if (looptag_filter_cb(l_iter, user_data)) { + if (verttag_filter_cb(l_iter, user_data)) { MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); if (equals_v2v2(luv->uv, luv_iter->uv)) { uvedit_uv_select_set(scene, em, l_iter, val, false, cd_loop_uv_offset); @@ -233,42 +164,10 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, const float aspect_y, const int cd_loop_uv_offset) { - const char uv_selectmode = ED_uvedit_select_mode_get(scene); - /* TODO(@sidd017): Implement logic to calculate shortest path for UV edges, since we now support - * proper edge selection for UVs (D12028). - * Till then continue using vertex path to fake shortest path calculation for edges. */ - const bool use_fake_edge_select = (uv_selectmode & UV_SELECT_EDGE); BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; int flush = 0; - /* Variables to use when `use_fake_edge_select` is set. */ - struct { - BMLoop *l_dst_activate; - BMLoop *l_dst_add_to_path; - } fake_edge_select = {NULL}; - - if (use_fake_edge_select) { - fake_edge_select.l_dst_activate = l_dst; - - /* Use most distant when doing region selection. - * without this we get dangling edges outside the region. */ - bool use_neaerst = (op_params->use_fill == false); - BMElem *ele_src = (BMElem *)l_src; - BMElem *ele_dst = (BMElem *)l_dst; - BMElem *ele_dst_final = NULL; - bm_loop_calc_vert_pair_from_edge_pair( - use_neaerst, cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final); - - if (op_params->use_fill == false) { - /* Always activate the item under the cursor. */ - fake_edge_select.l_dst_add_to_path = (BMLoop *)ele_dst_final; - } - - l_src = (BMLoop *)ele_src; - l_dst = (BMLoop *)ele_dst; - } - struct UserData_UV user_data = { .scene = scene, .em = em, @@ -291,33 +190,23 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, (BMElem *)l_src, (BMElem *)l_dst, params.cd_loop_uv_offset, - looptag_filter_cb, + verttag_filter_cb, &user_data); } else { is_path_ordered = true; - path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, ¶ms, looptag_filter_cb, &user_data); + path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, ¶ms, verttag_filter_cb, &user_data); } } BMLoop *l_dst_last = l_dst; if (path) { - if (use_fake_edge_select) { - if ((fake_edge_select.l_dst_add_to_path != NULL) && - (BLI_linklist_index(path, fake_edge_select.l_dst_add_to_path) == -1)) { - /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */ - LinkNode *path_last = BLI_linklist_find_last(path); - BLI_linklist_insert_after(&path_last, fake_edge_select.l_dst_add_to_path); - BLI_assert(BLI_linklist_find_last(path)->link == fake_edge_select.l_dst_add_to_path); - } - } - /* toggle the flag */ bool all_set = true; LinkNode *node = path; do { - if (!looptag_test_cb((BMLoop *)node->link, &user_data)) { + if (!verttag_test_cb((BMLoop *)node->link, &user_data)) { all_set = false; break; } @@ -328,7 +217,7 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, do { if ((is_path_ordered == false) || WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) { - looptag_set_cb((BMLoop *)node->link, !all_set, &user_data); + verttag_set_cb((BMLoop *)node->link, !all_set, &user_data); if (is_path_ordered) { l_dst_last = node->link; } @@ -339,23 +228,133 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, flush = all_set ? -1 : 1; } else { - const bool is_act = !looptag_test_cb(l_dst, &user_data); - looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ + const bool is_act = !verttag_test_cb(l_dst, &user_data); + verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ } if (op_params->track_active) { - /* Fake edge selection. */ - if (use_fake_edge_select) { - BMLoop *l_dst_activate = fake_edge_select.l_dst_activate; - /* TODO(campbell): Search for an active loop attached to 'l_dst'. - * when `BLI_linklist_index(path, l_dst_activate) == -1` - * In practice this rarely happens though. */ - ED_uvedit_active_edge_loop_set(bm, l_dst_activate); + ED_uvedit_active_vert_loop_set(bm, l_dst_last); + } + return flush; +} + +/* -------------------------------------------------------------------- */ +/** \name UV Edge Path + * \{ */ + +/* callbacks */ +static bool edgetag_filter_cb(BMLoop *l, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + return uvedit_face_visible_test(user_data->scene, l->f); +} +static bool edgetag_test_cb(BMLoop *l, void *user_data_v) +{ + /* All connected loops (UV) are selected or we return false. */ + struct UserData_UV *user_data = user_data_v; + const Scene *scene = user_data->scene; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + BMIter iter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) { + if (edgetag_filter_cb(l_iter, user_data)) { + if (BM_loop_uv_share_edge_check(l, l_iter, cd_loop_uv_offset)) { + if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) { + return false; + } + } + } + } + return true; +} +static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + const Scene *scene = user_data->scene; + BMEditMesh *em = user_data->em; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + uvedit_edge_select_set_with_sticky(scene, em, l, val, false, cd_loop_uv_offset); +} + +static int mouse_mesh_uv_shortest_path_edge(Scene *scene, + Object *obedit, + const struct PathSelectParams *op_params, + BMLoop *l_src, + BMLoop *l_dst, + const float aspect_y, + const int cd_loop_uv_offset) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + int flush = 0; + + struct UserData_UV user_data = { + .scene = scene, + .em = em, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + + const struct BMCalcPathUVParams params = { + .use_topology_distance = op_params->use_topology_distance, + .use_step_face = op_params->use_face_step, + .aspect_y = aspect_y, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + + LinkNode *path = NULL; + bool is_path_ordered = false; + + if (l_src != l_dst) { + if (op_params->use_fill) { + path = BM_mesh_calc_path_uv_region_edge(bm, + (BMElem *)l_src, + (BMElem *)l_dst, + params.cd_loop_uv_offset, + edgetag_filter_cb, + &user_data); } else { - ED_uvedit_active_vert_loop_set(bm, l_dst_last); + is_path_ordered = true; + path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, ¶ms, edgetag_filter_cb, &user_data); } } + + BMLoop *l_dst_last = l_dst; + + if (path) { + /* toggle the flag */ + bool all_set = true; + LinkNode *node = path; + do { + if (!edgetag_test_cb((BMLoop *)node->link, &user_data)) { + all_set = false; + break; + } + } while ((node = node->next)); + + int depth = -1; + node = path; + do { + if ((is_path_ordered == false) || + WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) { + edgetag_set_cb((BMLoop *)node->link, !all_set, &user_data); + if (is_path_ordered) { + l_dst_last = node->link; + } + } + } while ((void)depth++, (node = node->next)); + + BLI_linklist_free(path, NULL); + flush = all_set ? -1 : 1; + } + else { + const bool is_act = !edgetag_test_cb(l_dst, &user_data); + edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ + } + + if (op_params->track_active) { + ED_uvedit_active_edge_loop_set(bm, l_dst_last); + } return flush; } @@ -514,13 +513,24 @@ static bool uv_shortest_path_pick_ex(Scene *scene, ok = true; } else if (ele_src->head.htype == BM_LOOP) { - flush = mouse_mesh_uv_shortest_path_vert(scene, - obedit, - op_params, - (BMLoop *)ele_src, - (BMLoop *)ele_dst, - aspect_y, - cd_loop_uv_offset); + if (uv_selectmode & UV_SELECT_EDGE) { + flush = mouse_mesh_uv_shortest_path_edge(scene, + obedit, + op_params, + (BMLoop *)ele_src, + (BMLoop *)ele_dst, + aspect_y, + cd_loop_uv_offset); + } + else { + flush = mouse_mesh_uv_shortest_path_vert(scene, + obedit, + op_params, + (BMLoop *)ele_src, + (BMLoop *)ele_dst, + aspect_y, + cd_loop_uv_offset); + } ok = true; } @@ -529,24 +539,9 @@ static bool uv_shortest_path_pick_ex(Scene *scene, const bool select = (flush == 1); BMEditMesh *em = BKE_editmesh_from_object(obedit); if (ts->uv_flag & UV_SYNC_SELECTION) { - if (uv_selectmode & UV_SELECT_EDGE) { - /* Special case as we don't use true edge selection, - * flush the selection from the vertices. */ - BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX, BM_SELECT_LEN_FLUSH_RECALC_ALL); - } ED_uvedit_select_sync_flush(scene->toolsettings, em, select); } else { - if (uv_selectmode & UV_SELECT_EDGE) { - /* TODO(@sidd017): Remove this case when adding proper uv edge support for this operator. - * In the meantime, this case helps ensures proper UV selection states for edge mode. */ - if (select) { - uvedit_select_flush(scene, em); - } - else { - uvedit_deselect_flush(scene, em); - } - } ED_uvedit_selectmode_flush(scene, em); } } diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index db834f6a0fd..d59dcb4f4ed 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3582,6 +3582,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) } } else if (use_edge && !pinned) { + bool do_second_pass = true; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -3596,11 +3597,35 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) uvedit_edge_select_set_with_sticky( scene, em, l_prev, select, false, cd_loop_uv_offset); changed = true; + do_second_pass = false; } l_prev = l; luv_prev = luv; } } + /* Do a second pass if no complete edges could be selected. + * This matches wire-frame edit-mesh selection in the 3D view. */ + if (do_second_pass) { + /* Second pass to check if edges partially overlap with the selection area (box). */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (BLI_rctf_isect_segment(&rectf, luv_prev->uv, luv->uv)) { + uvedit_edge_select_set_with_sticky( + scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; + } + l_prev = l; + luv_prev = luv; + } + } + } } else { /* other selection modes */ @@ -3920,6 +3945,24 @@ static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region, return false; } +static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, + const rcti *clip_rect, + const int mcoords[][2], + const int mcoords_len, + const float co_test_a[2], + const float co_test_b[2]) +{ + int co_screen_a[2], co_screen_b[2]; + if (UI_view2d_view_to_region_segment_clip( + ®ion->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && + BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && + BLI_lasso_is_edge_inside( + mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) { + return true; + } + return false; +} + static bool do_lasso_select_mesh_uv(bContext *C, const int mcoords[][2], const int mcoords_len, @@ -3988,6 +4031,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, } } else if (use_edge) { + bool do_second_pass = true; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -4004,12 +4048,37 @@ static bool do_lasso_select_mesh_uv(bContext *C, region, &rect, mcoords, mcoords_len, luv_prev->uv)) { uvedit_edge_select_set_with_sticky( scene, em, l_prev, select, false, cd_loop_uv_offset); + do_second_pass = false; changed = true; } l_prev = l; luv_prev = luv; } } + /* Do a second pass if no complete edges could be selected. + * This matches wire-frame edit-mesh selection in the 3D view. */ + if (do_second_pass) { + /* Second pass to check if edges partially overlap with the selection area (lasso). */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (do_lasso_select_mesh_uv_is_edge_inside( + region, &rect, mcoords, mcoords_len, luv->uv, luv_prev->uv)) { + uvedit_edge_select_set_with_sticky( + scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; + } + l_prev = l; + luv_prev = luv; + } + } + } } else { /* Vert Selection. */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); @@ -5154,7 +5223,7 @@ static void uv_isolate_selected_islands(const Scene *scene, BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0); BMFace *efa; BMIter iter, liter; - UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, true, false, false, true); + UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true); if (elementmap == NULL) { return; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 55e44607f6f..579674930a6 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1902,15 +1902,7 @@ static StitchState *stitch_init(bContext *C, * for stitch this isn't useful behavior, see T86924. */ const int selectmode_orig = scene->toolsettings->selectmode; scene->toolsettings->selectmode = SCE_SELECT_VERTEX; - - /* in uv synch selection, all uv's are visible */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true); - } - else { - state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true); - } - + state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true); scene->toolsettings->selectmode = selectmode_orig; if (!state->element_map) { diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index ae81aaffeb2..2c7ad012dd2 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) /** \name Average UV Islands Scale Operator * \{ */ -static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) +static int average_islands_scale_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1215,8 +1215,12 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + /* RNA props */ + const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv"); + const bool shear = RNA_boolean_get(op->ptr, "shear"); + ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options); - GEO_uv_parametrizer_average(handle, false); + GEO_uv_parametrizer_average(handle, false, scale_uv, shear); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot) /* api callbacks */ ot->exec = average_islands_scale_exec; ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently"); + RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands"); } /** \} */ @@ -1845,7 +1853,7 @@ static void uvedit_unwrap(const Scene *scene, result_info ? &result_info->count_failed : NULL); GEO_uv_parametrizer_lscm_end(handle); - GEO_uv_parametrizer_average(handle, true); + GEO_uv_parametrizer_average(handle, true, false, false); GEO_uv_parametrizer_flush(handle); @@ -1907,7 +1915,7 @@ static int unwrap_exec(bContext *C, wmOperator *op) UnwrapOptions options = { .topology_from_uvs = false, - .only_selected_faces = false, + .only_selected_faces = true, .only_selected_uvs = false, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), @@ -1918,7 +1926,6 @@ static int unwrap_exec(bContext *C, wmOperator *op) if (CTX_wm_space_image(C)) { /* Inside the UV Editor, only unwrap selected UVs. */ options.only_selected_uvs = true; - options.only_selected_faces = true; options.pin_unselected = true; } |