diff options
Diffstat (limited to 'source/blender/editors')
25 files changed, 1068 insertions, 649 deletions
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index e3c64b523b1..25f1b282f14 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -328,7 +328,7 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) /* add pose to poselib */ marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker"); - BLI_strncpy(marker->name, "Pose", sizeof(marker->name)); + BLI_snprintf(marker->name, sizeof(marker->name), "F%d Pose", (int)ak->cfra); marker->frame = (int)ak->cfra; marker->flag = -1; 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 6eaae49912c..8066adf55ce 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -84,7 +84,6 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex( struct SnapObjectContext *sctx, - const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, /* return args */ @@ -98,7 +97,6 @@ bool ED_transform_snap_object_project_ray( bool ED_transform_snap_object_project_ray_all( SnapObjectContext *sctx, - const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float ray_depth, bool sort, diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 534bd4278ca..5ed94474726 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2099,9 +2099,11 @@ static void ui_update_color_picker_buts_rgb(uiBlock *block, ColorPicker *cpicker continue; if (bt->rnaprop) { - ui_but_v3_set(bt, rgb); + /* original button that created the color picker already does undo + * push, so disable it on RNA buttons in the color picker block */ + UI_but_flag_disable(bt, UI_BUT_UNDO); } else if (STREQ(bt->str, "Hex: ")) { float rgb_gamma[3]; diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 636b7e4e9ce..cf16cc9f50d 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -380,6 +380,17 @@ uiButStore *UI_butstore_create(uiBlock *block) void UI_butstore_free(uiBlock *block, uiButStore *bs_handle) { + /* Workaround for button store being moved into new block, + * which then can't use the previous buttons state ('ui_but_update_from_old_block' fails to find a match), + * keeping the active button in the old block holding a reference to the button-state in the new block: see T49034. + * + * Ideally we would manage moving the 'uiButStore', keeping a correct state. + * All things considered this is the most straightforward fix - Campbell. + */ + if (block != bs_handle->block && bs_handle->block != NULL) { + block = bs_handle->block; + } + BLI_freelistN(&bs_handle->items); BLI_remlink(&block->butstore, bs_handle); diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index fb20d9f3caa..ca4ab30a08d 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -31,6 +31,9 @@ # include "BLI_winstuff.h" #endif +#include <string.h> +#include <errno.h> + #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" @@ -417,9 +420,20 @@ static int get_sequence_len(char *filename, int *ofs) } char path[FILE_MAX]; + BLI_path_abs(filename, G.main->name); BLI_split_dir_part(filename, path, FILE_MAX); + if (path[0] == '\0') { + /* The filename had no path, so just use the blend file path. */ + BLI_split_dir_part(G.main->name, path, FILE_MAX); + } + DIR *dir = opendir(path); + if (dir == NULL) { + fprintf(stderr, "Error opening directory '%s': %s\n", + path, errno ? strerror(errno) : "unknown error"); + return -1; + } const char *ext = ".abc"; const char *basename = BLI_path_basename(filename); @@ -523,6 +537,10 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) if (is_sequence) { sequence_len = get_sequence_len(filename, &offset); + if (sequence_len < 0) { + BKE_report(op->reports, RPT_ERROR, "Unable to determine ABC sequence length"); + return OPERATOR_CANCELLED; + } } bool ok = ABC_import(C, filename, scale, is_sequence, set_frame_range, diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c index af6f55d7a64..a5e90ebbe7a 100644 --- a/source/blender/editors/io/io_cache.c +++ b/source/blender/editors/io/io_cache.c @@ -97,22 +97,24 @@ static int cachefile_open_exec(bContext *C, wmOperator *op) BLI_strncpy(cache_file->filepath, filename, FILE_MAX); BKE_cachefile_reload(bmain, cache_file); - /* hook into UI */ - PropertyPointerRNA *pprop = op->customdata; - - if (pprop->prop) { - /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ - id_us_min(&cache_file->id); - - PointerRNA idptr; - RNA_id_pointer_create(&cache_file->id, &idptr); - RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr); - RNA_property_update(C, &pprop->ptr, pprop->prop); + /* Will be set when running invoke, not exec directly. */ + if (op->customdata != NULL) { + /* hook into UI */ + PropertyPointerRNA *pprop = op->customdata; + if (pprop->prop) { + /* when creating new ID blocks, use is already 1, but RNA + * pointer se also increases user, so this compensates it */ + id_us_min(&cache_file->id); + + PointerRNA idptr; + RNA_id_pointer_create(&cache_file->id, &idptr); + RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr); + RNA_property_update(C, &pprop->ptr, pprop->prop); + } + + MEM_freeN(op->customdata); } - MEM_freeN(op->customdata); - return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index a6de1b284b7..68bd8ff27b1 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -446,6 +446,9 @@ BMVert *EDBM_vert_find_nearest_ex( unsigned int index; BMVert *eve; + /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */ + ED_view3d_backbuf_validate(vc); + index = ED_view3d_backbuf_sample_rect( vc, vc->mval, dist_px, bm_wireoffs, 0xFFFFFF, &dist_test); eve = index ? BM_vert_at_index_find_or_table(bm, index - 1) : NULL; @@ -630,7 +633,8 @@ BMEdge *EDBM_edge_find_nearest_ex( float dist_test = 0.0f; unsigned int index; BMEdge *eed; - + + /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */ ED_view3d_backbuf_validate(vc); index = ED_view3d_backbuf_sample_rect(vc, vc->mval, dist_px, bm_solidoffs, bm_wireoffs, &dist_test); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index a901560079a..b5a9c4e9e5b 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1464,8 +1464,6 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, } if (ob_dst->parent) { - invert_m4_m4(ob_dst->parentinv, dob->mat); - /* note, this may be the parent of other objects, but it should * still work out ok */ BKE_object_apply_mat4(ob_dst, dob->mat, false, true); @@ -1486,7 +1484,6 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, ob_dst->partype = PAROBJECT; /* similer to the code above, see comments */ - invert_m4_m4(ob_dst->parentinv, dob->mat); BKE_object_apply_mat4(ob_dst, dob->mat, false, true); DAG_id_tag_update(&ob_dst->id, OB_RECALC_OB); } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index fa18b82507e..35d772afae7 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -1180,7 +1180,7 @@ void ED_preview_icon_render(Main *bmain, Scene *scene, ID *id, unsigned int *rec ip.bmain = bmain; ip.scene = scene; - ip.owner = id; + ip.owner = BKE_previewimg_id_ensure(id); ip.id = id; icon_preview_add_size(&ip, rect, sizex, sizey); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 18f02aff482..f469686b0b2 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1228,6 +1228,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) winrct.ymax = winsize_y - 1; /* header size depends on DPI, let's verify */ + WM_window_set_dpi(win); screen_refresh_headersizes(); screen_test_scale(win->screen, winsize_x, winsize_y); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index d0f1cc99b8d..b43581170d0 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -3712,8 +3712,12 @@ static void project_paint_prepare_all_faces( } /* don't allow using the same inage for painting and stencilling */ - if (slot->ima == ps->stencil_ima) + if (slot->ima == ps->stencil_ima) { + /* While this shouldn't be used, face-winding reads all polys. + * It's less trouble to set all faces to valid UV's, avoiding NULL checks all over. */ + ps->dm_mloopuv[lt->poly] = mloopuv_base; continue; + } tpage = slot->ima; } diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 72de7e5c81c..1d67ac620b0 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -470,7 +470,7 @@ void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts) } else { /* set one user as active based on active index */ - if (ct->index == BLI_listbase_count_ex(&ct->users, ct->index + 1)) + if (ct->index >= BLI_listbase_count_ex(&ct->users, ct->index + 1)) ct->index = 0; ct->user = BLI_findlink(&ct->users, ct->index); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 501a5a35899..a0d67626940 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1098,6 +1098,7 @@ static void image_open_cancel(bContext *UNUSED(C), wmOperator *op) static void image_sequence_get_frame_ranges(PointerRNA *ptr, ListBase *frames_all) { char dir[FILE_MAXDIR]; + const bool do_frame_range = RNA_boolean_get(ptr, "use_sequence_detection"); ImageFrameRange *frame_range = NULL; RNA_string_get(ptr, "directory", dir); @@ -1113,7 +1114,8 @@ static void image_sequence_get_frame_ranges(PointerRNA *ptr, ListBase *frames_al frame->framenr = BLI_stringdec(filename, head, tail, &digits); /* still in the same sequence */ - if ((frame_range != NULL) && + if (do_frame_range && + (frame_range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) && (STREQLEN(base_tail, tail, FILE_MAX))) { @@ -1325,7 +1327,11 @@ static int image_open_exec(bContext *C, wmOperator *op) iuser->frames = frame_seq_len; iuser->sfra = 1; iuser->framenr = 1; - iuser->offset = frame_ofs - 1; + if (ima->source == IMA_SRC_MOVIE) { + iuser->offset = 0; + } else { + iuser->offset = frame_ofs - 1; + } iuser->fie_ima = 2; iuser->scene = scene; BKE_image_init_imageuser(ima, iuser); @@ -1448,6 +1454,9 @@ void IMAGE_OT_open(wmOperatorType *ot) ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_boolean(ot->srna, "use_sequence_detection", true, "Detect Sequences", + "Automatically detect animated sequences in selected images (based on file names)"); } /******************** Match movie length operator ********************/ diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 0e427623840..b87a0de23b9 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -162,7 +162,6 @@ static int pack_all_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); packAll(bmain, op->reports, true); - G.fileflags |= G_AUTOPACK; return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 7e05c76b35b..18cc2a015e6 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -707,7 +707,12 @@ static eOLDrawState tree_element_active_pose( { Object *ob = (Object *)tselem->id; Base *base = BKE_scene_base_find(scene, ob); - + + if (base == NULL) { + /* Armature not instantiated in current scene (e.g. inside an appended group...). */ + return OL_DRAWSEL_NONE; + } + if (set != OL_SETSEL_NONE) { if (scene->obedit) ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO); diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index ec46c5df9a0..09a49f201d4 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1080,6 +1080,12 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i PointerRNA pptr, propptr, *ptr = (PointerRNA *)idv; PropertyRNA *prop, *iterprop; PropertyType proptype; + + /* Don't display arrays larger, weak but index is stored as a short, + * also the outliner isn't intended for editing such large data-sets. */ + BLI_STATIC_ASSERT(sizeof(te->index) == 2, "Index is no longer short!"); + const int tot_limit = SHRT_MAX; + int a, tot; /* we do lazy build, for speed and to avoid infinite recusion */ @@ -1101,6 +1107,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i iterprop = RNA_struct_iterator_property(ptr->type); tot = RNA_property_collection_length(ptr, iterprop); + CLAMP_MAX(tot, tot_limit); /* auto open these cases */ if (!parent || (RNA_property_type(parent->directdata)) == PROP_POINTER) @@ -1147,6 +1154,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i } else if (proptype == PROP_COLLECTION) { tot = RNA_property_collection_length(ptr, prop); + CLAMP_MAX(tot, tot_limit); if (TSELEM_OPEN(tselem, soops)) { for (a = 0; a < tot; a++) { @@ -1159,6 +1167,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i } else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { tot = RNA_property_array_length(ptr, prop); + CLAMP_MAX(tot, tot_limit); if (TSELEM_OPEN(tselem, soops)) { for (a = 0; a < tot; a++) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 3ae4a233353..e63569ac386 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3512,7 +3512,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) bool proxy_50 = RNA_boolean_get(op->ptr, "proxy_50"); bool proxy_75 = RNA_boolean_get(op->ptr, "proxy_75"); bool proxy_100 = RNA_boolean_get(op->ptr, "proxy_100"); - bool override = RNA_boolean_get(op->ptr, "override"); + bool overwrite = RNA_boolean_get(op->ptr, "overwrite"); bool turnon = true; if (ed == NULL || !(proxy_25 || proxy_50 || proxy_75 || proxy_100)) { @@ -3548,7 +3548,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) else seq->strip->proxy->build_size_flags &= ~SEQ_PROXY_IMAGE_SIZE_100; - if (!override) + if (!overwrite) seq->strip->proxy->build_flags |= SEQ_PROXY_SKIP_EXISTING; else seq->strip->proxy->build_flags &= ~SEQ_PROXY_SKIP_EXISTING; @@ -3580,7 +3580,7 @@ void SEQUENCER_OT_enable_proxies(wmOperatorType *ot) RNA_def_boolean(ot->srna, "proxy_50", false, "50%", ""); RNA_def_boolean(ot->srna, "proxy_75", false, "75%", ""); RNA_def_boolean(ot->srna, "proxy_100", false, "100%", ""); - RNA_def_boolean(ot->srna, "override", false, "Override", ""); + RNA_def_boolean(ot->srna, "overwrite", false, "Overwrite", ""); } /* change ops */ diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index 314b7bf3335..5208013b6fe 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -1903,6 +1903,11 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, } } + /* custom bone may draw outline double-width */ + if (arm->flag & ARM_POSEMODE) { + glLineWidth(1.0f); + } + /* draw custom bone shapes as wireframes */ if (!(arm->flag & ARM_NO_CUSTOM) && (draw_wire || (dt <= OB_WIRE)) ) @@ -1968,11 +1973,6 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, index = -1; } } - - /* custom bone may draw outline double-width */ - if (arm->flag & ARM_POSEMODE) { - glLineWidth(1.0f); - } /* wire draw over solid only in posemode */ if ((dt <= OB_WIRE) || (arm->flag & ARM_POSEMODE) || ELEM(arm->drawtype, ARM_LINE, ARM_WIRE)) { diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 884bc6b83ba..73fd77db477 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -7347,7 +7347,7 @@ static void draw_object_wire_color(Scene *scene, Base *base, unsigned char r_ob_ theme_id = TH_GROUP_ACTIVE; if (scene->basact != base) { - theme_shade = -16; + theme_shade = -32; } } else { @@ -8378,9 +8378,13 @@ static void bbs_mesh_solid_verts(Scene *scene, Object *ob) DM_update_materials(dm, ob); - dm->drawMappedFaces(dm, bbs_mesh_solid_hide2__setDrawOpts, GPU_object_material_bind, NULL, me, DM_DRAW_SKIP_HIDDEN); + /* Only draw faces to mask out verts, we don't want their selection ID's. */ + const int G_f_orig = G.f; + G.f &= ~G_BACKBUFSEL; - GPU_object_material_unbind(); + dm->drawMappedFaces(dm, bbs_mesh_solid_hide2__setDrawOpts, NULL, NULL, me, DM_DRAW_SKIP_HIDDEN); + + G.f |= (G_f_orig & G_BACKBUFSEL); bbs_obmode_mesh_verts(ob, dm, 1); bm_vertoffs = me->totvert + 1; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 4686ff0523e..ca6e2267218 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -4067,13 +4067,15 @@ static void initTrackball(TransInfo *t) static void applyTrackballValue(TransInfo *t, const float axis1[3], const float axis2[3], float angles[2]) { TransData *td = t->data; - float mat[3][3], smat[3][3], totmat[3][3]; + float mat[3][3]; + float axis[3]; + float angle; int i; - axis_angle_normalized_to_mat3(smat, axis1, angles[0]); - axis_angle_normalized_to_mat3(totmat, axis2, angles[1]); - - mul_m3_m3m3(mat, smat, totmat); + mul_v3_v3fl(axis, axis1, angles[0]); + madd_v3_v3fl(axis, axis2, angles[1]); + angle = normalize_v3(axis); + axis_angle_normalized_to_mat3(mat, axis, angle); for (i = 0; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) @@ -4083,10 +4085,7 @@ static void applyTrackballValue(TransInfo *t, const float axis1[3], const float continue; if (t->flag & T_PROP_EDIT) { - axis_angle_normalized_to_mat3(smat, axis1, td->factor * angles[0]); - axis_angle_normalized_to_mat3(totmat, axis2, td->factor * angles[1]); - - mul_m3_m3m3(mat, smat, totmat); + axis_angle_normalized_to_mat3(mat, axis, td->factor * angle); } ElementRotation(t, td, mat, t->around); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index ba474e93b70..4429d19613a 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -771,34 +771,43 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) /* -------- Auto-IK ---------- */ /* adjust pose-channel's auto-ik chainlen */ -static void pchan_autoik_adjust(bPoseChannel *pchan, short chainlen) +static bool pchan_autoik_adjust(bPoseChannel *pchan, short chainlen) { bConstraint *con; + bool changed = false; /* don't bother to search if no valid constraints */ - if ((pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_TARGET)) == 0) - return; + if ((pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_TARGET)) == 0) { + return changed; + } /* check if pchan has ik-constraint */ for (con = pchan->constraints.first; con; con = con->next) { if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) { bKinematicConstraint *data = con->data; - + /* only accept if a temporary one (for auto-ik) */ if (data->flag & CONSTRAINT_IK_TEMP) { /* chainlen is new chainlen, but is limited by maximum chainlen */ - if ((chainlen == 0) || (chainlen > data->max_rootbone)) + const int old_rootbone = data->rootbone; + if ((chainlen == 0) || (chainlen > data->max_rootbone)) { data->rootbone = data->max_rootbone; - else + } + else { data->rootbone = chainlen; + } + changed |= (data->rootbone != old_rootbone); } } } + + return changed; } /* change the chain-length of auto-ik */ void transform_autoik_update(TransInfo *t, short mode) { + const short old_len = t->settings->autoik_chainlen; short *chainlen = &t->settings->autoik_chainlen; bPoseChannel *pchan; @@ -812,13 +821,29 @@ void transform_autoik_update(TransInfo *t, short mode) if (*chainlen > 0) (*chainlen)--; } + /* IK length did not change, skip any updates. */ + if (old_len == *chainlen) { + return; + } + /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */ if (ELEM(NULL, t->poseobj, t->poseobj->pose)) return; /* apply to all pose-channels */ + bool changed = false; for (pchan = t->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) { - pchan_autoik_adjust(pchan, *chainlen); + changed |= pchan_autoik_adjust(pchan, *chainlen); + } + +#ifdef WITH_LEGACY_DEPSGRAPH + if (!DEG_depsgraph_use_legacy()) +#endif + { + if (changed) { + /* TODO(sergey): Consider doing partial update only. */ + DAG_relations_tag_update(G.main); + } } } @@ -1500,6 +1525,48 @@ static TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struc return hdata; } +/** + * For the purpose of transform code we need to behave as if handles are selected, + * even when they aren't (see special case below). + */ +static int bezt_select_to_transform_triple_flag( + const BezTriple *bezt, const bool hide_handles) +{ + int flag = 0; + + if (hide_handles) { + if (bezt->f2 & SELECT) { + flag = (1 << 0) | (1 << 1) | (1 << 2); + } + } + else { + flag = ( + ((bezt->f1 & SELECT) ? (1 << 0) : 0) | + ((bezt->f2 & SELECT) ? (1 << 1) : 0) | + ((bezt->f3 & SELECT) ? (1 << 2) : 0) + ); + } + + /* Special case for auto & aligned handles: + * When a center point is being moved without the handles, + * leaving the handles stationary makes no sense and only causes strange behavior, + * where one handle is arbitrarily anchored, the other one is aligned and lengthened + * based on where the center point is moved. Also a bug when cancelling, see: T52007. + * + * A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'. + * However that doesn't resolve odd behavior, so best transform the handles in this case. + */ + if ((flag != ((1 << 0) | (1 << 1) | (1 << 2))) && (flag & (1 << 1))) { + if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && + ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) + { + flag = (1 << 0) | (1 << 1) | (1 << 2); + } + } + + return flag; +} + static void createTransCurveVerts(TransInfo *t) { Curve *cu = t->obedit->data; @@ -1517,22 +1584,22 @@ static void createTransCurveVerts(TransInfo *t) /* to be sure */ if (cu->editnurb == NULL) return; +#define SEL_F1 (1 << 0) +#define SEL_F2 (1 << 1) +#define SEL_F3 (1 << 2) + /* count total of vertices, check identical as in 2nd loop for making transdata! */ nurbs = BKE_curve_editNurbs_get(cu); for (nu = nurbs->first; nu; nu = nu->next) { if (nu->type == CU_BEZIER) { for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) { if (bezt->hide == 0) { - if (hide_handles) { - if (bezt->f2 & SELECT) countsel += 3; - if (is_prop_edit) count += 3; - } - else { - if (bezt->f1 & SELECT) countsel++; - if (bezt->f2 & SELECT) countsel++; - if (bezt->f3 & SELECT) countsel++; - if (is_prop_edit) count += 3; - } + const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles); + if (bezt_tx & SEL_F1) { countsel++; } + if (bezt_tx & SEL_F2) { countsel++; } + if (bezt_tx & SEL_F3) { countsel++; } + if (is_prop_edit) count += 3; + } } } @@ -1583,10 +1650,10 @@ static void createTransCurveVerts(TransInfo *t) } } - if (is_prop_edit || - ((bezt->f2 & SELECT) && hide_handles) || - ((bezt->f1 & SELECT) && hide_handles == 0)) - { + /* Elements that will be transform (not always a match to selection). */ + const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles); + + if (is_prop_edit || bezt_tx & SEL_F1) { copy_v3_v3(td->iloc, bezt->vec[0]); td->loc = bezt->vec[0]; copy_v3_v3(td->center, bezt->vec[(hide_handles || @@ -1617,7 +1684,7 @@ static void createTransCurveVerts(TransInfo *t) } /* This is the Curve Point, the other two are handles */ - if (is_prop_edit || (bezt->f2 & SELECT)) { + if (is_prop_edit || bezt_tx & SEL_F2) { copy_v3_v3(td->iloc, bezt->vec[1]); td->loc = bezt->vec[1]; copy_v3_v3(td->center, td->loc); @@ -1643,7 +1710,7 @@ static void createTransCurveVerts(TransInfo *t) copy_m3_m3(td->axismtx, axismtx); } - if ((bezt->f1 & SELECT) == 0 && (bezt->f3 & SELECT) == 0) + if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) /* If the middle is selected but the sides arnt, this is needed */ if (hdata == NULL) { /* if the handle was not saved by the previous handle */ hdata = initTransDataCurveHandles(td, bezt); @@ -1653,10 +1720,7 @@ static void createTransCurveVerts(TransInfo *t) count++; tail++; } - if (is_prop_edit || - ((bezt->f2 & SELECT) && hide_handles) || - ((bezt->f3 & SELECT) && hide_handles == 0)) - { + if (is_prop_edit || bezt_tx & SEL_F3) { copy_v3_v3(td->iloc, bezt->vec[2]); td->loc = bezt->vec[2]; copy_v3_v3(td->center, bezt->vec[(hide_handles || @@ -1711,6 +1775,26 @@ static void createTransCurveVerts(TransInfo *t) for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { if (bp->hide == 0) { if (is_prop_edit || (bp->f1 & SELECT)) { + float axismtx[3][3]; + + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + if (nu->pntsv == 1) { + float normal[3], plane[3]; + + BKE_nurb_bpoint_calc_normal(nu, bp, normal); + BKE_nurb_bpoint_calc_plane(nu, bp, plane); + + if (createSpaceNormalTangent(axismtx, normal, plane)) { + /* pass */ + } + else { + normalize_v3(normal); + axis_dominant_v3_to_m3(axismtx, normal); + invert_m3(axismtx); + } + } + } + copy_v3_v3(td->iloc, bp->vec); td->loc = bp->vec; copy_v3_v3(td->center, td->loc); @@ -1729,6 +1813,11 @@ static void createTransCurveVerts(TransInfo *t) copy_m3_m3(td->smtx, smtx); copy_m3_m3(td->mtx, mtx); + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + if (nu->pntsv == 1) { + copy_m3_m3(td->axismtx, axismtx); + } + } td++; count++; @@ -1744,6 +1833,10 @@ static void createTransCurveVerts(TransInfo *t) calc_distanceCurveVerts(head, tail - 1); } } + +#undef SEL_F1 +#undef SEL_F2 +#undef SEL_F3 } /* ********************* lattice *************** */ @@ -5349,7 +5442,8 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) } /* update object's loc/rot to get current rigid body transform */ mat4_to_loc_rot_size(ob->loc, rot, scale, ob->obmat); - BKE_object_mat3_to_rot(ob, rot, false); + sub_v3_v3(ob->loc, ob->dloc); + BKE_object_mat3_to_rot(ob, rot, false); /* drot is already corrected here */ } } diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 23158495b44..54959304d72 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -817,15 +817,21 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { Curve *cu = obedit->data; Nurb *nu = NULL; - BezTriple *bezt = NULL; int a; ListBase *nurbs = BKE_curve_editNurbs_get(cu); - if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, (void *)&bezt)) { + void *vert_act = NULL; + if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, &vert_act)) { if (nu->type == CU_BEZIER) { + BezTriple *bezt = vert_act; BKE_nurb_bezt_calc_normal(nu, bezt, normal); BKE_nurb_bezt_calc_plane(nu, bezt, plane); } + else { + BPoint *bp = vert_act; + BKE_nurb_bpoint_calc_normal(nu, bp, normal); + BKE_nurb_bpoint_calc_plane(nu, bp, plane); + } } else { const bool use_handle = (cu->drawflag & CU_HIDE_HANDLES) == 0; @@ -833,7 +839,7 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3 for (nu = nurbs->first; nu; nu = nu->next) { /* only bezier has a normal */ if (nu->type == CU_BEZIER) { - bezt = nu->bezt; + BezTriple *bezt = nu->bezt; a = nu->pntsu; while (a--) { short flag = 0; @@ -885,6 +891,36 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3 bezt++; } } + else if (nu->bp && (nu->pntsv == 1)) { + BPoint *bp = nu->bp; + a = nu->pntsu; + while (a--) { + if (bp->f1 & SELECT) { + float tvec[3]; + + BPoint *bp_prev = BKE_nurb_bpoint_get_prev(nu, bp); + BPoint *bp_next = BKE_nurb_bpoint_get_next(nu, bp); + + const bool is_prev_sel = bp_prev && (bp_prev->f1 & SELECT); + const bool is_next_sel = bp_next && (bp_next->f1 & SELECT); + if (is_prev_sel == false && is_next_sel == false) { + /* Isolated, add based on surrounding */ + BKE_nurb_bpoint_calc_normal(nu, bp, tvec); + add_v3_v3(normal, tvec); + } + else if (is_next_sel) { + /* A segment, add the edge normal */ + sub_v3_v3v3(tvec, bp->vec, bp_next->vec ); + normalize_v3(tvec); + add_v3_v3(normal, tvec); + } + + BKE_nurb_bpoint_calc_plane(nu, bp, tvec); + add_v3_v3(plane, tvec); + } + bp++; + } + } } } diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index aecd24d4e40..85833f54da6 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -130,18 +130,138 @@ struct SnapObjectContext { }; -static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); +/** \} */ /* -------------------------------------------------------------------- */ -/** \name Support for storing all depths, not just the first (raycast 'all') +/** Common utilities +* \{ */ + + +#define ITER_SNAP_OBJECTS(use_obedit, ob, obmat, sctx, snap_select, obedit, CODE) \ + Base *base_act = sctx->scene->basact;\ + /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA\ + * which makes the loop skip it, even the derived mesh will never change\ + *\ + * To solve that problem, we do it first as an exception.\ + * */\ + if (base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT) {\ + use_obedit = false;\ + ob = base_act->object;\ + obmat = ob->obmat;\ + CODE\ + }\ + for (Base *base = sctx->scene->base.first; base != NULL; base = base->next) {\ + if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) &&\ + (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 &&\ + !((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL))) ||\ + (snap_select == SNAP_NOT_ACTIVE && base == base_act)))\ + {\ + Object *obj = base->object;\ + if (ob->transflag & OB_DUPLI) {\ + DupliObject *dupli_ob;\ + ListBase *lb = object_duplilist(sctx->bmain->eval_ctx, sctx->scene, obj);\ + for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {\ + use_obedit = obedit && dupli_ob->ob->data == obedit->data;;\ + ob = use_obedit ? obedit : dupli_ob->ob;\ + obmat = dupli_ob->mat;\ + CODE\ + }\ + free_object_duplilist(lb);\ + }\ + use_obedit = obedit && ob->data == obedit->data;\ + ob = use_obedit ? obedit : obj;\ + obmat = ob->obmat;\ + CODE\ + }\ + }\ + + +/** + * Generates a struct with the immutable parameters that will be used on all objects. * - * This uses a list of #SnapObjectHitDepth structs. + * \param snap_to: Element to snap, Vertice, Edge or Face. + * \param view_proj: ORTHO or PERSP. + * Currently only works one at a time, but can eventually operate as flag. * - * \{ */ + * \param mval: Mouse coords. + * (When NULL, ray-casting is handled without any projection matrix correction.) + * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min. + * \param ray_start: ray_origin moved for the start clipping plane (clip_min). + * \param ray_direction: Unit length direction of the ray. + * \param depth_range: distances of clipe plane min and clip plane max; + */ +static void snap_data_set( + SnapData *snapdata, + const ARegion *ar, const unsigned short snap_to, const enum eViewProj view_proj, + const float mval[2], const float ray_origin[3], const float ray_start[3], + const float ray_direction[3], const float depth_range[2]) +{ + copy_m4_m4(snapdata->pmat, ((RegionView3D *)ar->regiondata)->persmat); + snapdata->win_half[0] = ar->winx / 2; + snapdata->win_half[1] = ar->winy / 2; + copy_v2_v2(snapdata->mval, mval); + snapdata->snap_to = snap_to; + copy_v3_v3(snapdata->ray_origin, ray_origin); + copy_v3_v3(snapdata->ray_start, ray_start); + copy_v3_v3(snapdata->ray_dir, ray_direction); + snapdata->view_proj = view_proj; + copy_v2_v2(snapdata->depth_range, depth_range); +} + + +MINLINE float depth_get(const float co[3], const float ray_start[3], const float ray_dir[3]) +{ + float dvec[3]; + sub_v3_v3v3(dvec, co, ray_start); + return dot_v3v3(dvec, ray_dir); +} + + +static bool walk_parent_bvhroot_cb(const BVHTreeAxisRange *bounds, void *userdata) +{ + BVHTreeRay *ray = userdata; + const float bbmin[3] = {bounds[0].min, bounds[1].min, bounds[2].min}; + const float bbmax[3] = {bounds[0].max, bounds[1].max, bounds[2].max}; + if (!isect_ray_aabb_v3_simple(ray->origin, ray->direction, bbmin, bbmax, &ray->radius, NULL)) { + ray->radius = -1; + } + return false; +} + + +static bool isect_ray_bvhroot_v3(struct BVHTree *tree, const float ray_start[3], const float ray_dir[3], float *depth) +{ + BVHTreeRay ray; + copy_v3_v3(ray.origin, ray_start); + copy_v3_v3(ray.direction, ray_dir); + + BLI_bvhtree_walk_dfs(tree, walk_parent_bvhroot_cb, NULL, NULL, &ray); + + if (ray.radius > 0) { + *depth = ray.radius; + return true; + } + else { + return false; + } +} + + +static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Ray Cast Funcs +* \{ */ + +/* Store all ray-hits + * Support for storing all depths, not just the first (raycast 'all') */ -/* Store all ray-hits */ struct RayCastAll_Data { void *bvhdata; @@ -162,6 +282,7 @@ struct RayCastAll_Data { bool retval; }; + static struct SnapObjectHitDepth *hit_depth_create( const float depth, const float co[3], const float no[3], int index, Object *ob, const float obmat[4][4], unsigned int ob_uuid) @@ -229,57 +350,493 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH } } -/** \} */ +static bool raycastDerivedMesh( + SnapObjectContext *sctx, + const float ray_orig[3], const float ray_start[3], const float ray_dir[3], const float depth_range[2], + Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index, + /* read/write args */ + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + ListBase *r_hit_list) +{ + bool retval = false; -/* -------------------------------------------------------------------- */ + if (dm->getNumPolys(dm) == 0) { + return retval; + } + + float imat[4][4]; + float timat[3][3]; /* transpose inverse matrix for normals */ + float ray_start_local[3], ray_normal_local[3]; + float local_scale, local_depth, len_diff = 0.0f; + + invert_m4_m4(imat, obmat); + transpose_m3_m4(timat, imat); + + copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_dir); + + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + + /* local scale in normal direction */ + local_scale = normalize_v3(ray_normal_local); + local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } + + /* Test BoundBox */ + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ + if (!isect_ray_aabb_v3_simple( + ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], &len_diff, NULL)) + { + return retval; + } + } + + SnapObjectData_Mesh *sod = NULL; + BVHTreeFromMesh *treedata; + + void **sod_p; + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + sod = *sod_p; + } + else { + sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); + sod->sd.type = SNAP_MESH; + } + + if (sod->bvh_trees[2] == NULL) { + sod->bvh_trees[2] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); + } + + treedata = sod->bvh_trees[2]; + + if (treedata) { + /* the tree is owned by the DM and may have been freed since we last used! */ + if (treedata->tree) { + if (treedata->cached && !bvhcache_has_tree(dm->bvhCache, treedata->tree)) { + free_bvhtree_from_mesh(treedata); + } + else { + if (!treedata->vert_allocated) { + treedata->vert = DM_get_vert_array(dm, &treedata->vert_allocated); + } + if (!treedata->loop_allocated) { + treedata->loop = DM_get_loop_array(dm, &treedata->loop_allocated); + } + if (!treedata->looptri_allocated) { + if (!sod->poly_allocated) { + sod->mpoly = DM_get_poly_array(dm, &sod->poly_allocated); + } + treedata->looptri = DM_get_looptri_array( + dm, treedata->vert, + sod->mpoly, dm->getNumPolys(dm), + treedata->loop, dm->getNumLoops(dm), + &treedata->looptri_allocated); + } + } + } + + if (treedata->tree == NULL) { + bvhtree_from_mesh_looptri(treedata, dm, 0.0f, 4, 6); + + if (treedata->tree == NULL) { + return retval; + } + } + } + else { + return retval; + } + + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already + * been *inside* boundbox, leading to snap failures (see T38409). + * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + */ + if (len_diff == 0.0f) { /* do_ray_start_correction */ + /* We *need* a reasonably valid len_diff in this case. + * Get the distance to bvhtree root */ + if (!isect_ray_bvhroot_v3(treedata->tree, ray_start_local, ray_normal_local, &len_diff)) + { + return retval; + } + } + /* You need to make sure that ray_start is really far away, + * because even in the Orthografic view, in some cases, + * the ray can start inside the object (see T50486) */ + if (len_diff > 400.0f) { + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_orig); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with + * very far away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, + len_diff + depth_range[0] * local_scale); + local_depth -= len_diff; + } + else { + len_diff = 0.0f; + } + if (r_hit_list) { + struct RayCastAll_Data data; + + data.bvhdata = treedata; + data.raycast_callback = treedata->raycast_callback; + data.obmat = obmat; + data.timat = timat; + data.len_diff = len_diff; + data.local_scale = local_scale; + data.ob = ob; + data.ob_uuid = ob_index; + data.hit_list = r_hit_list; + data.retval = retval; + + BLI_bvhtree_ray_cast_all( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); + + retval = data.retval; + } + else { + BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; + + if (BLI_bvhtree_ray_cast( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata->raycast_callback, treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + if (hit.dist <= *ray_depth) { + *ray_depth = hit.dist; + copy_v3_v3(r_loc, hit.co); + + /* back to worldspace */ + mul_m4_v3(obmat, r_loc); + + if (r_no) { + copy_v3_v3(r_no, hit.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + + retval = true; + + if (r_index) { + *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]); + } + } + } + } + + return retval; +} + +static bool raycastEditMesh( + SnapObjectContext *sctx, + const float ray_orig[3], const float ray_start[3], const float ray_dir[3], const float depth_range[2], + Object *ob, BMEditMesh *em, float obmat[4][4], const unsigned int ob_index, + /* read/write args */ + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + ListBase *r_hit_list) +{ + bool retval = false; + if (em->bm->totface == 0) { + return retval; + } + + SnapObjectData_EditMesh *sod = NULL; + + BVHTreeFromEditMesh *treedata; + + void **sod_p; + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + sod = *sod_p; + } + else { + sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); + sod->sd.type = SNAP_EDIT_MESH; + } + + if (sod->bvh_trees[2] == NULL) { + sod->bvh_trees[2] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); + } + treedata = sod->bvh_trees[2]; + + if (treedata) { + if (treedata->tree == NULL) { + BLI_bitmap *elem_mask = NULL; + int looptri_num_active = -1; + + if (sctx->callbacks.edit_mesh.test_face_fn) { + elem_mask = BLI_BITMAP_NEW(em->tottri, __func__); + looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( + em->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, NULL); + + if (elem_mask) { + MEM_freeN(elem_mask); + } + } + if (treedata->tree == NULL) { + return retval; + } + } + else { + return retval; + } + + float imat[4][4]; + float timat[3][3]; /* transpose inverse matrix for normals */ + float ray_normal_local[3], ray_start_local[3], len_diff = 0.0f; + + invert_m4_m4(imat, obmat); + transpose_m3_m4(timat, imat); + + copy_v3_v3(ray_normal_local, ray_dir); + mul_mat3_m4_v3(imat, ray_normal_local); + + copy_v3_v3(ray_start_local, ray_start); + mul_m4_v3(imat, ray_start_local); + + /* local scale in normal direction */ + float local_scale = normalize_v3(ray_normal_local); + float local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } + + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start + * may already been *inside* boundbox, leading to snap failures (see T38409). + * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + */ + if (sctx->use_v3d && !((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp) { /* do_ray_start_correction */ + /* We *need* a reasonably valid len_diff in this case. + * Get the distance to bvhtree root */ + if (!isect_ray_bvhroot_v3(treedata->tree, ray_start_local, ray_normal_local, &len_diff)) + { + return retval; + } + /* You need to make sure that ray_start is really far away, + * because even in the Orthografic view, in some cases, + * the ray can start inside the object (see T50486) */ + if (len_diff > 400.0f) { + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_orig); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with + * very far away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, + len_diff + depth_range[0] * local_scale); + local_depth -= len_diff; + } + else len_diff = 0.0f; + } + if (r_hit_list) { + struct RayCastAll_Data data; + + data.bvhdata = treedata; + data.raycast_callback = treedata->raycast_callback; + data.obmat = obmat; + data.timat = timat; + data.len_diff = len_diff; + data.local_scale = local_scale; + data.ob = ob; + data.ob_uuid = ob_index; + data.hit_list = r_hit_list; + data.retval = retval; + + BLI_bvhtree_ray_cast_all( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); + + retval = data.retval; + } + else { + BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; + + if (BLI_bvhtree_ray_cast( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata->raycast_callback, treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + if (hit.dist <= *ray_depth) { + *ray_depth = hit.dist; + copy_v3_v3(r_loc, hit.co); + + /* back to worldspace */ + mul_m4_v3(obmat, r_loc); + + if (r_no) { + copy_v3_v3(r_no, hit.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + + retval = true; + + if (r_index) { + *r_index = hit.index; + } + } + } + } + + return retval; +} -/** Common utilities - * \{ */ /** - * Generates a struct with the immutable parameters that will be used on all objects. - * - * \param snap_to: Element to snap, Vertice, Edge or Face. - * \param view_proj: ORTHO or PERSP. - * Currently only works one at a time, but can eventually operate as flag. + * \param use_obedit: Uses the coordinates of BMesh (if any) to do the snapping; * - * \param mval: Mouse coords. - * (When NULL, ray-casting is handled without any projection matrix correction.) - * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min. - * \param ray_start: ray_origin moved for the start clipping plane (clip_min). - * \param ray_direction: Unit length direction of the ray. - * \param depth_range: distances of clipe plane min and clip plane max; + * \note Duplicate args here are documented at #snapObjectsRay */ -static void snap_data_set( - SnapData *snapdata, - const ARegion *ar, const unsigned short snap_to, const enum eViewProj view_proj, - const float mval[2], const float ray_origin[3], const float ray_start[3], - const float ray_direction[3], const float depth_range[2]) +static bool raycastObj( + SnapObjectContext *sctx, + const float ray_orig[3], const float ray_start[3], const float ray_dir[3], const float depth_range[2], + Object *ob, float obmat[4][4], const unsigned int ob_index, + bool use_obedit, + /* read/write args */ + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4], + ListBase *r_hit_list) { - if (ar) { - copy_m4_m4(snapdata->pmat, ((RegionView3D *)ar->regiondata)->persmat); - snapdata->win_half[0] = ar->winx / 2; - snapdata->win_half[1] = ar->winy / 2; + bool retval = false; + + if (ob->type == OB_MESH) { + BMEditMesh *em; + + if (use_obedit) { + em = BKE_editmesh_from_object(ob); + retval = raycastEditMesh( + sctx, + ray_orig, ray_start, ray_dir, depth_range, + ob, em, obmat, ob_index, + ray_depth, r_loc, r_no, r_index, r_hit_list); + } + else { + /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. + * still set the 'em' to NULL, since we only want the 'dm'. */ + DerivedMesh *dm; + em = BKE_editmesh_from_object(ob); + if (em) { + editbmesh_get_derived_cage_and_final(sctx->scene, ob, em, CD_MASK_BAREMESH, &dm); + } + else { + dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); + } + retval = raycastDerivedMesh( + sctx, + ray_orig, ray_start, ray_dir, depth_range, + ob, dm, obmat, ob_index, + ray_depth, r_loc, r_no, r_index, r_hit_list); + + dm->release(dm); + } } - if (mval) { - copy_v2_v2(snapdata->mval, mval); + + if (retval) { + if (r_ob) { + *r_ob = ob; + copy_m4_m4(r_obmat, obmat); + } } - snapdata->snap_to = snap_to; - copy_v3_v3(snapdata->ray_origin, ray_origin); - copy_v3_v3(snapdata->ray_start, ray_start); - copy_v3_v3(snapdata->ray_dir, ray_direction); - snapdata->view_proj = view_proj; - copy_v2_v2(snapdata->depth_range, depth_range); + + return retval; } -MINLINE float depth_get(const float co[3], const float ray_start[3], const float ray_dir[3]) + +/** + * Main RayCast Function + * ====================== + * + * Walks through all objects in the scene to find the `hit` on object surface. + * + * \param sctx: Snap context to store data. + * \param snapdata: struct generated in `set_snapdata`. + * \param snap_select : from enum SnapSelect. + * \param use_object_edit_cage : Uses the coordinates of BMesh(if any) to do the snapping. + * \param obj_list: List with objects to snap (created in `create_object_list`). + * + * Read/Write Args + * --------------- + * + * \param ray_depth: maximum depth allowed for r_co, elements deeper than this value will be ignored. + * + * Output Args + * ----------- + * + * \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 when using ``snap_to == SCE_SNAP_MODE_FACE``). + * \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). + * + */ +static bool raycastObjects( + SnapObjectContext *sctx, + const float ray_orig[3], const float ray_start[3], const float ray_dir[3], const float depth_range[2], + const SnapSelect snap_select, const bool use_object_edit_cage, + /* read/write args */ + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4], + ListBase *r_hit_list) { - float dvec[3]; - sub_v3_v3v3(dvec, co, ray_start); - return dot_v3v3(dvec, ray_dir); + bool retval = false; + bool use_obedit; + + unsigned int ob_index = 0; + + Object *ob, *obedit; + float (*obmat)[4]; + + obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; + ITER_SNAP_OBJECTS(use_obedit, ob, obmat, sctx, snap_select, obedit, + retval |= raycastObj( + sctx, + ray_orig, ray_start, ray_dir, depth_range, + ob, obmat, ob_index++, use_obedit, + ray_depth, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); + ) + + return retval; } + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** Snap Nearest utilities + * \{ */ + static void copy_dm_vert_no(const int index, float r_no[3], const BVHTreeFromMesh *data) { const MVert *vert = data->vert + index; @@ -558,15 +1115,12 @@ static float dist_squared_to_projected_aabb( vb2d[0] *= data->win_half[0]; vb2d[1] *= data->win_half[1]; - //float dvec[2], edge[2], rdist; - //sub_v2_v2v2(dvec, data->mval, va2d); - //sub_v2_v2v2(edge, vb2d, va2d); - float rdist; - short dvec[2] = {data->mval[0] - va2d[0], data->mval[1] - va2d[1]}; - short edge[2] = {vb2d[0] - va2d[0], vb2d[1] - va2d[1]}; - float lambda = dvec[0] * edge[0] + dvec[1] * edge[1]; + float dvec[2], edge[2], lambda, rdist; + sub_v2_v2v2(dvec, data->mval, va2d); + sub_v2_v2v2(edge, vb2d, va2d); + lambda = dot_v2v2(dvec, edge); if (lambda != 0.0f) { - lambda /= edge[0] * edge[0] + edge[1] * edge[1]; + lambda /= len_squared_v2(edge); if (lambda <= 0.0f) { rdist = len_squared_v2v2(data->mval, va2d); r_axis_closest[main_axis] = true; @@ -603,18 +1157,6 @@ static float dist_squared_to_projected_aabb_simple( return dist_squared_to_projected_aabb(&data, bbmin, bbmax, dummy); } -static float dist_aabb_to_plane( - const float bbmin[3], const float bbmax[3], - const float plane_co[3], const float plane_no[3]) -{ - const float local_bvmin[3] = { - (plane_no[0] < 0) ? bbmax[0] : bbmin[0], - (plane_no[1] < 0) ? bbmax[1] : bbmin[1], - (plane_no[2] < 0) ? bbmax[2] : bbmin[2], - }; - return depth_get(local_bvmin, plane_co, plane_no); -} - /** \} */ @@ -1053,21 +1595,15 @@ static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) static bool snapDerivedMesh( SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index, + Object *ob, DerivedMesh *dm, float obmat[4][4], /* read/write args */ float *ray_depth, float *dist_px, /* return args */ - float r_loc[3], float r_no[3], int *r_index, - ListBase *r_hit_list) + float r_loc[3], float r_no[3]) { bool retval = false; - if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { - if (dm->getNumPolys(dm) == 0) { - return retval; - } - } - else if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { + if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { if (dm->getNumEdges(dm) == 0) { return retval; } @@ -1078,38 +1614,27 @@ static bool snapDerivedMesh( } } - bool need_ray_start_correction_init = - (snapdata->snap_to == SCE_SNAP_MODE_FACE) && - (snapdata->view_proj == VIEW_PROJ_ORTHO); - float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ - float ray_start_local[3], ray_normal_local[3]; - float local_scale, local_depth, len_diff; + float ray_normal_local[3]; + float local_scale; invert_m4_m4(imat, obmat); transpose_m3_m4(timat, imat); - copy_v3_v3(ray_start_local, snapdata->ray_start); copy_v3_v3(ray_normal_local, snapdata->ray_dir); - mul_m4_v3(imat, ray_start_local); mul_mat3_m4_v3(imat, ray_normal_local); /* local scale in normal direction */ local_scale = normalize_v3(ray_normal_local); - local_depth = *ray_depth; - if (local_depth != BVH_RAYCAST_DIST_MAX) { - local_depth *= local_scale; - } float lpmat[4][4]; float ray_org_local[3]; float ray_min_dist; - if (ELEM(snapdata->snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - ray_min_dist = snapdata->depth_range[0] * local_scale; - } + + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + ray_min_dist = snapdata->depth_range[0] * local_scale; copy_v3_v3(ray_org_local, snapdata->ray_origin); mul_m4_v3(imat, ray_org_local); @@ -1118,26 +1643,12 @@ static bool snapDerivedMesh( BoundBox *bb = BKE_object_boundbox_get(ob); if (bb) { /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */ - if (ELEM(snapdata->snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { - float dist_px_sq = dist_squared_to_projected_aabb_simple( - lpmat, snapdata->win_half, ray_min_dist, snapdata->mval, - ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]); - if (dist_px_sq > SQUARE(*dist_px)) { - return retval; - } - } - else { - /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ - if (!isect_ray_aabb_v3_simple( - ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], NULL, NULL)) - { - return retval; - } + float dist_px_sq = dist_squared_to_projected_aabb_simple( + lpmat, snapdata->win_half, ray_min_dist, snapdata->mval, + ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]); + if (dist_px_sq > SQUARE(*dist_px)) { + return retval; } - /* was local_depth, see: T47838 */ - len_diff = dist_aabb_to_plane(bb->vec[0], bb->vec[6], ray_start_local, ray_normal_local); - if (len_diff < 0) len_diff = 0.0f; - need_ray_start_correction_init = false; } SnapObjectData_Mesh *sod = NULL; @@ -1154,9 +1665,6 @@ static bool snapDerivedMesh( int tree_index = -1; switch (snapdata->snap_to) { - case SCE_SNAP_MODE_FACE: - tree_index = 2; - break; case SCE_SNAP_MODE_EDGE: tree_index = 1; break; @@ -1180,179 +1688,71 @@ static bool snapDerivedMesh( treedata->vert = DM_get_vert_array(dm, &treedata->vert_allocated); } if ((tree_index == 1) && !treedata->edge_allocated) { - treedata->edge = DM_get_edge_array(dm, &treedata->vert_allocated); - } - if (tree_index == 2) { - if (!treedata->loop_allocated) { - treedata->loop = DM_get_loop_array(dm, &treedata->loop_allocated); - } - if (!treedata->looptri_allocated) { - if (!sod->poly_allocated) { - sod->mpoly = DM_get_poly_array(dm, &sod->poly_allocated); - } - treedata->looptri = DM_get_looptri_array( - dm, treedata->vert, - sod->mpoly, dm->getNumPolys(dm), - treedata->loop, dm->getNumLoops(dm), - &treedata->looptri_allocated); - } + treedata->edge = DM_get_edge_array(dm, &treedata->edge_allocated); } } } } - if (treedata && treedata->tree == NULL) { - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_FACE: - bvhtree_from_mesh_looptri(treedata, dm, 0.0f, 4, 6); - break; - case SCE_SNAP_MODE_EDGE: - bvhtree_from_mesh_edges(treedata, dm, 0.0f, 2, 6); - break; - case SCE_SNAP_MODE_VERTEX: - bvhtree_from_mesh_verts(treedata, dm, 0.0f, 2, 6); - break; + if (treedata) { + if (treedata->tree == NULL) { + switch (snapdata->snap_to) { + case SCE_SNAP_MODE_EDGE: + bvhtree_from_mesh_edges(treedata, dm, 0.0f, 2, 6); + break; + case SCE_SNAP_MODE_VERTEX: + bvhtree_from_mesh_verts(treedata, dm, 0.0f, 2, 6); + break; + } + } + if (treedata->tree == NULL) { + return retval; } } - if (!treedata || !treedata->tree) { + else { return retval; } - if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { - /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already - * been *inside* boundbox, leading to snap failures (see T38409). - * Note also ar might be null (see T38435), in this case we assume ray_start is ok! - */ - if (snapdata->view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ - if (need_ray_start_correction_init) { - /* We *need* a reasonably valid len_diff in this case. - * Use BHVTree to find the closest face from ray_start_local. - */ - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); - if (nearest.index != -1) { - float dvec[3]; - sub_v3_v3v3(dvec, nearest.co, ray_start_local); - len_diff = dot_v3v3(dvec, ray_normal_local); - } - } - /* You need to make sure that ray_start is really far away, - * because even in the Orthografic view, in some cases, - * the ray can start inside the object (see T50486) */ - if (len_diff > 400.0f) { - /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with - * very far away ray_start values (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl( - ray_start_local, ray_org_local, ray_normal_local, - len_diff + snapdata->depth_range[0] * local_scale); - local_depth -= len_diff; - } - else len_diff = 0.0f; - } - else { - len_diff = 0.0f; - } - if (r_hit_list) { - struct RayCastAll_Data data; - - data.bvhdata = treedata; - data.raycast_callback = treedata->raycast_callback; - data.obmat = obmat; - data.timat = timat; - data.len_diff = len_diff; - data.local_scale = local_scale; - data.ob = ob; - data.ob_uuid = ob_index; - data.hit_list = r_hit_list; - data.retval = retval; - - BLI_bvhtree_ray_cast_all( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - *ray_depth, raycast_all_cb, &data); - - retval = data.retval; - } - else { - BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; + /* Warning: the depth_max is currently being used only in perspective view. + * It is not correct to limit the maximum depth for elements obtained with nearest + * since this limitation depends on the normal and the size of the occlusion face. + * And more... ray_depth is being confused with Z-depth here... (varies only the precision) */ + const float ray_depth_max_global = *ray_depth + snapdata->depth_range[0]; - if (BLI_bvhtree_ray_cast( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - &hit, treedata->raycast_callback, treedata) != -1) - { - hit.dist += len_diff; - hit.dist /= local_scale; - if (hit.dist <= *ray_depth) { - *ray_depth = hit.dist; - copy_v3_v3(r_loc, hit.co); - - /* back to worldspace */ - mul_m4_v3(obmat, r_loc); - - if (r_no) { - copy_v3_v3(r_no, hit.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } + Nearest2dUserData neasrest2d = { + .dist_px_sq = SQUARE(*dist_px), + .r_axis_closest = {1.0f, 1.0f, 1.0f}, + .depth_range = {snapdata->depth_range[0], ray_depth_max_global}, + .userdata = treedata, + .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_dm_edge_verts, + .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_dm_vert_no, + .index = -1}; - retval = true; + dist_squared_to_projected_aabb_precalc( + &neasrest2d.data_precalc, lpmat, + snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, + ray_min_dist, snapdata->mval, ray_org_local, ray_normal_local); - if (r_index) { - *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]); - } - } - } - } - } - /* SCE_SNAP_MODE_VERTEX or SCE_SNAP_MODE_EDGE */ - else { + BVHTree_WalkLeafCallback cb_walk_leaf = + (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - /* Warning: the depth_max is currently being used only in perspective view. - * It is not correct to limit the maximum depth for elements obtained with nearest - * since this limitation depends on the normal and the size of the occlusion face. - * And more... ray_depth is being confused with Z-depth here... (varies only the precision) */ - const float ray_depth_max_global = *ray_depth + snapdata->depth_range[0]; - - Nearest2dUserData neasrest2d = { - .dist_px_sq = SQUARE(*dist_px), - .r_axis_closest = {1.0f, 1.0f, 1.0f}, - .depth_range = {snapdata->depth_range[0], ray_depth_max_global}, - .userdata = treedata, - .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_dm_edge_verts, - .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_dm_vert_no, - .index = -1}; - - dist_squared_to_projected_aabb_precalc( - &neasrest2d.data_precalc, lpmat, - snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, - ray_min_dist, snapdata->mval, ray_org_local, ray_normal_local); - - BVHTree_WalkLeafCallback cb_walk_leaf = - (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? - cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - - BLI_bvhtree_walk_dfs( - treedata->tree, - cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); - - if (neasrest2d.index != -1) { - copy_v3_v3(r_loc, neasrest2d.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, neasrest2d.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - *dist_px = sqrtf(neasrest2d.dist_px_sq); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); - retval = true; + if (neasrest2d.index != -1) { + copy_v3_v3(r_loc, neasrest2d.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, neasrest2d.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); } + *dist_px = sqrtf(neasrest2d.dist_px_sq); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); + + retval = true; } return retval; @@ -1360,20 +1760,14 @@ static bool snapDerivedMesh( static bool snapEditMesh( SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, BMEditMesh *em, float obmat[4][4], const unsigned int ob_index, + Object *ob, BMEditMesh *em, float obmat[4][4], /* read/write args */ float *ray_depth, float *dist_px, /* return args */ - float r_loc[3], float r_no[3], int *r_index, - ListBase *r_hit_list) + float r_loc[3], float r_no[3]) { bool retval = false; - if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { - if (em->bm->totface == 0) { - return retval; - } - } if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { if (em->bm->totedge == 0) { return retval; @@ -1398,14 +1792,10 @@ static bool snapEditMesh( /* local scale in normal direction */ float local_scale = normalize_v3(ray_normal_local); - float local_depth = *ray_depth; - if (local_depth != BVH_RAYCAST_DIST_MAX) { - local_depth *= local_scale; - } SnapObjectData_EditMesh *sod = NULL; - BVHTreeFromEditMesh *treedata = NULL; + BVHTreeFromEditMesh *treedata; void **sod_p; if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { @@ -1418,9 +1808,6 @@ static bool snapEditMesh( int tree_index = -1; switch (snapdata->snap_to) { - case SCE_SNAP_MODE_FACE: - tree_index = 2; - break; case SCE_SNAP_MODE_EDGE: tree_index = 1; break; @@ -1435,197 +1822,90 @@ static bool snapEditMesh( treedata = sod->bvh_trees[tree_index]; } - if (treedata && treedata->tree == NULL) { - BLI_bitmap *elem_mask = NULL; - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_FACE: - { - int looptri_num_active = -1; - if (sctx->callbacks.edit_mesh.test_face_fn) { - elem_mask = BLI_BITMAP_NEW(em->tottri, __func__); - looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( - em->bm, elem_mask, - sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data); + if (treedata) { + if (treedata->tree == NULL) { + BLI_bitmap *elem_mask = NULL; + switch (snapdata->snap_to) { + case SCE_SNAP_MODE_EDGE: + { + int edges_num_active = -1; + if (sctx->callbacks.edit_mesh.test_edge_fn) { + elem_mask = BLI_BITMAP_NEW(em->bm->totedge, __func__); + edges_num_active = BM_iter_mesh_bitmap_from_filter( + BM_EDGES_OF_MESH, em->bm, elem_mask, + (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_edge_fn, + sctx->callbacks.edit_mesh.user_data); + } + bvhtree_from_editmesh_edges_ex(treedata, em, elem_mask, edges_num_active, 0.0f, 2, 6); + break; } - bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6, NULL); - break; - } - case SCE_SNAP_MODE_EDGE: - { - int edges_num_active = -1; - if (sctx->callbacks.edit_mesh.test_edge_fn) { - elem_mask = BLI_BITMAP_NEW(em->bm->totedge, __func__); - edges_num_active = BM_iter_mesh_bitmap_from_filter( - BM_EDGES_OF_MESH, em->bm, elem_mask, - (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_edge_fn, - sctx->callbacks.edit_mesh.user_data); + case SCE_SNAP_MODE_VERTEX: + { + int verts_num_active = -1; + if (sctx->callbacks.edit_mesh.test_vert_fn) { + elem_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); + verts_num_active = BM_iter_mesh_bitmap_from_filter( + BM_VERTS_OF_MESH, em->bm, elem_mask, + (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn, + sctx->callbacks.edit_mesh.user_data); + } + bvhtree_from_editmesh_verts_ex(treedata, em, elem_mask, verts_num_active, 0.0f, 2, 6); + break; } - bvhtree_from_editmesh_edges_ex(treedata, em, elem_mask, edges_num_active, 0.0f, 2, 6); - break; } - case SCE_SNAP_MODE_VERTEX: - { - int verts_num_active = -1; - if (sctx->callbacks.edit_mesh.test_vert_fn) { - elem_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); - verts_num_active = BM_iter_mesh_bitmap_from_filter( - BM_VERTS_OF_MESH, em->bm, elem_mask, - (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn, - sctx->callbacks.edit_mesh.user_data); - } - bvhtree_from_editmesh_verts_ex(treedata, em, elem_mask, verts_num_active, 0.0f, 2, 6); - break; + if (elem_mask) { + MEM_freeN(elem_mask); } } - if (elem_mask) { - MEM_freeN(elem_mask); + if (treedata->tree == NULL) { + return retval; } } - - if (!treedata || !treedata->tree) { + else { return retval; } - if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { - float ray_start_local[3]; - copy_v3_v3(ray_start_local, snapdata->ray_start); - mul_m4_v3(imat, ray_start_local); - - /* Only use closer ray_start in case of ortho view! In perspective one, ray_start - * may already been *inside* boundbox, leading to snap failures (see T38409). - * Note also ar might be null (see T38435), in this case we assume ray_start is ok! - */ - float len_diff = 0.0f; - if (snapdata->view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ - /* We *need* a reasonably valid len_diff in this case. - * Use BHVTree to find the closest face from ray_start_local. - */ - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - if (BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1) - { - float dvec[3]; - sub_v3_v3v3(dvec, nearest.co, ray_start_local); - len_diff = dot_v3v3(dvec, ray_normal_local); - /* You need to make sure that ray_start is really far away, - * because even in the Orthografic view, in some cases, - * the ray can start inside the object (see T50486) */ - if (len_diff > 400.0f) { - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, snapdata->ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, - * to avoid precision issues with very far away ray_start values - * (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl( - ray_start_local, ray_org_local, ray_normal_local, - len_diff + snapdata->depth_range[0] * local_scale); - local_depth -= len_diff; - } - else len_diff = 0.0f; - } - } - if (r_hit_list) { - struct RayCastAll_Data data; - - data.bvhdata = treedata; - data.raycast_callback = treedata->raycast_callback; - data.obmat = obmat; - data.timat = timat; - data.len_diff = len_diff; - data.local_scale = local_scale; - data.ob = ob; - data.ob_uuid = ob_index; - data.hit_list = r_hit_list; - data.retval = retval; - - BLI_bvhtree_ray_cast_all( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - *ray_depth, raycast_all_cb, &data); - - retval = data.retval; - } - else { - BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; + float ray_org_local[3]; + copy_v3_v3(ray_org_local, snapdata->ray_origin); + mul_m4_v3(imat, ray_org_local); - if (BLI_bvhtree_ray_cast( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - &hit, treedata->raycast_callback, treedata) != -1) - { - hit.dist += len_diff; - hit.dist /= local_scale; - if (hit.dist <= *ray_depth) { - *ray_depth = hit.dist; - copy_v3_v3(r_loc, hit.co); - - /* back to worldspace */ - mul_m4_v3(obmat, r_loc); - - if (r_no) { - copy_v3_v3(r_no, hit.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } + Nearest2dUserData neasrest2d = { + .dist_px_sq = SQUARE(*dist_px), + .r_axis_closest = {1.0f, 1.0f, 1.0f}, + .depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]}, + .userdata = treedata, + .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_bedge_verts, + .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_bvert_no, + .index = -1}; - retval = true; + float lpmat[4][4]; + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + dist_squared_to_projected_aabb_precalc( + &neasrest2d.data_precalc, lpmat, + snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, + (snapdata->depth_range[0] * local_scale), snapdata->mval, + ray_org_local, ray_normal_local); - if (r_index) { - *r_index = hit.index; - } - } - } - } - } - else { - float ray_org_local[3]; - copy_v3_v3(ray_org_local, snapdata->ray_origin); - mul_m4_v3(imat, ray_org_local); + BVHTree_WalkLeafCallback cb_walk_leaf = + (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - Nearest2dUserData neasrest2d = { - .dist_px_sq = SQUARE(*dist_px), - .r_axis_closest = {1.0f, 1.0f, 1.0f}, - .depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]}, - .userdata = treedata, - .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_bedge_verts, - .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_bvert_no, - .index = -1}; - - float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - dist_squared_to_projected_aabb_precalc( - &neasrest2d.data_precalc, lpmat, - snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, - (snapdata->depth_range[0] * local_scale), snapdata->mval, - ray_org_local, ray_normal_local); - - BVHTree_WalkLeafCallback cb_walk_leaf = - (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? - cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - - BLI_bvhtree_walk_dfs( - treedata->tree, - cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); - - if (neasrest2d.index != -1) { - copy_v3_v3(r_loc, neasrest2d.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, neasrest2d.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - *dist_px = sqrtf(neasrest2d.dist_px_sq); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); - retval = true; + if (neasrest2d.index != -1) { + copy_v3_v3(r_loc, neasrest2d.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, neasrest2d.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); } + *dist_px = sqrtf(neasrest2d.dist_px_sq); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); + + retval = true; } return retval; @@ -1638,14 +1918,13 @@ static bool snapEditMesh( */ static bool snapObject( SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, float obmat[4][4], const unsigned int ob_index, + Object *ob, float obmat[4][4], bool use_obedit, /* read/write args */ float *ray_depth, float *dist_px, /* return args */ - float r_loc[3], float r_no[3], int *r_index, - Object **r_ob, float r_obmat[4][4], - ListBase *r_hit_list) + float r_loc[3], float r_no[3], + Object **r_ob, float r_obmat[4][4]) { bool retval = false; @@ -1655,10 +1934,9 @@ static bool snapObject( if (use_obedit) { em = BKE_editmesh_from_object(ob); retval = snapEditMesh( - sctx, snapdata, ob, em, obmat, ob_index, + sctx, snapdata, ob, em, obmat, ray_depth, dist_px, - r_loc, r_no, r_index, - r_hit_list); + r_loc, r_no); } else { /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. @@ -1672,10 +1950,9 @@ static bool snapObject( dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); } retval = snapDerivedMesh( - sctx, snapdata, ob, dm, obmat, ob_index, + sctx, snapdata, ob, dm, obmat, ray_depth, dist_px, - r_loc, r_no, - r_index, r_hit_list); + r_loc, r_no); dm->release(dm); } @@ -1720,6 +1997,7 @@ static bool snapObject( return retval; } + /** * Main Snapping Function * ====================== @@ -1728,8 +2006,8 @@ static bool snapObject( * * \param sctx: Snap context to store data. * \param snapdata: struct generated in `get_snapdata`. - * \param snap_select: from enum SnapSelect. - * \param use_object_edit_cage: Uses the coordinates of BMesh (if any) to do the snapping. + * \param snap_select : from enum SnapSelect. + * \param use_object_edit_cage : Uses the coordinates of BMesh(if any) to do the snapping. * * Read/Write Args * --------------- @@ -1746,87 +2024,30 @@ static bool snapObject( * (currently only set to the polygon index when when using ``snap_to == SCE_SNAP_MODE_FACE``). * \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). * */ static bool snapObjectsRay( SnapObjectContext *sctx, SnapData *snapdata, - const SnapSelect snap_select, - const bool use_object_edit_cage, + const SnapSelect snap_select, const bool use_object_edit_cage, /* read/write args */ float *ray_depth, float *dist_px, /* return args */ - float r_loc[3], float r_no[3], int *r_index, - Object **r_ob, float r_obmat[4][4], - ListBase *r_hit_list) + float r_loc[3], float r_no[3], + Object **r_ob, float r_obmat[4][4]) { bool retval = false; + bool use_obedit; - unsigned int ob_index = 0; - Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; - - /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA - * which makes the loop skip it, even the derived mesh will never change - * - * To solve that problem, we do it first as an exception. - * */ - Base *base_act = sctx->scene->basact; - if (base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT) { - Object *ob = base_act->object; + Object *ob, *obedit; + float (*obmat)[4]; + obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; + ITER_SNAP_OBJECTS(use_obedit, ob, obmat, sctx, snap_select, obedit, retval |= snapObject( - sctx, snapdata, ob, ob->obmat, ob_index++, false, + sctx, snapdata, ob, obmat, use_obedit, ray_depth, dist_px, - r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); - } - - bool ignore_object_selected = false, ignore_object_active = false; - switch (snap_select) { - case SNAP_ALL: - break; - case SNAP_NOT_SELECTED: - ignore_object_selected = true; - break; - case SNAP_NOT_ACTIVE: - ignore_object_active = true; - break; - } - for (Base *base = sctx->scene->base.first; base != NULL; base = base->next) { - if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) && - (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 && - - !((ignore_object_selected && (base->flag & (SELECT | BA_WAS_SEL))) || - (ignore_object_active && base == base_act))) - { - Object *ob = base->object; - - if (ob->transflag & OB_DUPLI) { - DupliObject *dupli_ob; - ListBase *lb = object_duplilist(sctx->bmain->eval_ctx, sctx->scene, ob); - - for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - bool use_obedit_dupli = (obedit && dupli_ob->ob->data == obedit->data); - Object *dupli_snap = (use_obedit_dupli) ? obedit : dupli_ob->ob; - - retval |= snapObject( - sctx, snapdata, dupli_snap, dupli_ob->mat, - ob_index++, use_obedit_dupli, - ray_depth, dist_px, - r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); - } - - free_object_duplilist(lb); - } - - bool use_obedit = (obedit != NULL) && (ob->data == obedit->data); - Object *ob_snap = use_obedit ? obedit : ob; - - retval |= snapObject( - sctx, snapdata, ob_snap, ob->obmat, ob_index++, use_obedit, - ray_depth, dist_px, - r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); - } - } + r_loc, r_no, r_ob, r_obmat); + ) return retval; } @@ -1922,7 +2143,6 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex( SnapObjectContext *sctx, - const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, @@ -1931,16 +2151,11 @@ bool ED_transform_snap_object_project_ray_ex( { const float depth_range[2] = {0.0f, FLT_MAX}; - SnapData snapdata; - snap_data_set( - &snapdata, sctx->v3d_data.ar, snap_to, VIEW_PROJ_NONE, - NULL, ray_start, ray_start, ray_normal, depth_range); - - return snapObjectsRay( - sctx, &snapdata, + return raycastObjects( + sctx, + ray_start, ray_start, ray_normal, depth_range, params->snap_select, params->use_object_edit_cage, - ray_depth, NULL, - r_loc, r_no, r_index, r_ob, r_obmat, NULL); + ray_depth, r_loc, r_no, r_index, r_ob, r_obmat, NULL); } /** @@ -1952,7 +2167,6 @@ bool ED_transform_snap_object_project_ray_ex( */ bool ED_transform_snap_object_project_ray_all( SnapObjectContext *sctx, - const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float ray_depth, bool sort, @@ -1967,15 +2181,11 @@ bool ED_transform_snap_object_project_ray_all( float ray_depth_prev = ray_depth; #endif - SnapData snapdata; - snap_data_set(&snapdata, sctx->v3d_data.ar, snap_to, VIEW_PROJ_NONE, NULL, - ray_start, ray_start, ray_normal, depth_range); - - bool retval = snapObjectsRay( - sctx, &snapdata, + bool retval = raycastObjects( + sctx, + ray_start, ray_start, ray_normal, depth_range, params->snap_select, params->use_object_edit_cage, - &ray_depth, NULL, - NULL, NULL, NULL, NULL, NULL, + &ray_depth, NULL, NULL, NULL, NULL, NULL, r_hit_list); /* meant to be readonly for 'all' hits, ensure it is */ @@ -2008,7 +2218,6 @@ static bool transform_snap_context_project_ray_impl( /* try snap edge, then face if it fails */ ret = ED_transform_snap_object_project_ray_ex( sctx, - SCE_SNAP_MODE_FACE, params, ray_start, ray_normal, ray_depth, r_co, r_no, NULL, @@ -2154,16 +2363,24 @@ bool ED_transform_snap_object_project_view3d_ex( ray_depth = &ray_depth_fallback; } - SnapData snapdata; - const enum eViewProj view_proj = ((RegionView3D *)ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; - snap_data_set(&snapdata, ar, snap_to, view_proj, mval, - ray_origin, ray_start, ray_normal, depth_range); + if (snap_to == SCE_SNAP_MODE_FACE) { + return raycastObjects( + sctx, + ray_origin, ray_start, ray_normal, depth_range, + params->snap_select, params->use_object_edit_cage, + ray_depth, r_loc, r_no, r_index, NULL, NULL, NULL); + } + else { + SnapData snapdata; + const enum eViewProj view_proj = ((RegionView3D *)ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; + snap_data_set(&snapdata, ar, snap_to, view_proj, mval, + ray_origin, ray_start, ray_normal, depth_range); - return snapObjectsRay( - sctx, &snapdata, - params->snap_select, params->use_object_edit_cage, - ray_depth, dist_px, - r_loc, r_no, r_index, NULL, NULL, NULL); + return snapObjectsRay( + sctx, &snapdata, + params->snap_select, params->use_object_edit_cage, + ray_depth, dist_px, r_loc, r_no, NULL, NULL); + } } bool ED_transform_snap_object_project_view3d( @@ -2204,7 +2421,6 @@ bool ED_transform_snap_object_project_all_view3d_ex( return ED_transform_snap_object_project_ray_all( sctx, - SCE_SNAP_MODE_FACE, params, ray_start, ray_normal, ray_depth, sort, r_hit_list); diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index 4e021d4833e..419c15bf83f 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -334,6 +334,12 @@ static int ed_undo_redo_exec(bContext *C, wmOperator *UNUSED(op)) return ret ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +static int ed_undo_redo_poll(bContext *C) +{ + wmOperator *last_op = WM_operator_last_redo(C); + return last_op && ED_operator_screenactive(C) && + WM_operator_check_ui_enabled(C, last_op->type->name); +} /* ********************** */ @@ -385,7 +391,7 @@ void ED_OT_undo_redo(wmOperatorType *ot) /* api callbacks */ ot->exec = ed_undo_redo_exec; - ot->poll = ED_operator_screenactive; + ot->poll = ed_undo_redo_poll; } /* ui callbacks should call this rather than calling WM_operator_repeat() themselves */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 7334ce882e1..5125bb54dd4 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1162,8 +1162,13 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */ - /* use winding so we don't consider overlapping islands as connected, see T44320 */ - vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, true); + /* Note, we had 'use winding' so we don't consider overlapping islands as connected, see T44320 + * this made *every* projection split the island into front/back islands. + * Keep 'use_winding' to false, see: T50970. + * + * Better solve this by having a delimit option for select-linked operator, + * keeping island-select working as is. */ + vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, false); if (vmap == NULL) return; |