diff options
Diffstat (limited to 'source/blender/editors/space_view3d/view3d_view.c')
-rw-r--r-- | source/blender/editors/space_view3d/view3d_view.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index f8f2e9635e4..a7098ae255d 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -43,6 +43,7 @@ #include "BKE_context.h" #include "BKE_object.h" #include "BKE_global.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -60,6 +61,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_object.h" #include "ED_screen.h" #include "DRW_engine.h" @@ -1106,6 +1108,312 @@ finally: /** \} */ /* -------------------------------------------------------------------- */ +/** \name Local View Operators + * \{ */ + +static unsigned int free_localbit(Main *bmain) +{ + ScrArea *sa; + bScreen *sc; + + unsigned short local_view_bits = 0; + + /* sometimes we loose a localview: when an area is closed */ + /* check all areas: which localviews are in use? */ + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl = sa->spacedata.first; + for (; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *) sl; + if (v3d->localvd) { + local_view_bits |= v3d->local_view_uuid; + } + } + } + } + } + + for (int i = 0; i < 16; i++) { + if ((local_view_bits & (1 << i)) == 0) { + return (1 << i); + } + } + + return 0; +} + +static bool view3d_localview_init( + const Depsgraph *depsgraph, + wmWindowManager *wm, + wmWindow *win, + Main *bmain, + ViewLayer *view_layer, + ScrArea *sa, + const int smooth_viewtx, + ReportList *reports) +{ + View3D *v3d = sa->spacedata.first; + Base *base; + float min[3], max[3], box[3], mid[3]; + float size = 0.0f; + unsigned int local_view_bit; + bool ok = false; + + if (v3d->localvd) { + return ok; + } + + INIT_MINMAX(min, max); + + local_view_bit = free_localbit(bmain); + + if (local_view_bit == 0) { + /* TODO(dfelinto): We can kick one of the other 3D views out of local view + specially if it is not being used. */ + BKE_report(reports, RPT_ERROR, "No more than 16 local views"); + ok = false; + } + else { + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + if (obedit) { + FOREACH_BASE_IN_EDIT_MODE_BEGIN(view_layer, v3d, base_iter) { + BKE_object_minmax(base_iter->object, min, max, false); + base_iter->local_view_bits |= local_view_bit; + ok = true; + } FOREACH_BASE_IN_EDIT_MODE_END; + } + else { + for (base = FIRSTBASE(view_layer); base; base = base->next) { + if (TESTBASE(v3d, base)) { + BKE_object_minmax(base->object, min, max, false); + base->local_view_bits |= local_view_bit; + /* Technically we should leave for Depsgraph to handle this. + But it is harmless to do it here, and it seems to be necessary. */ + base->object->base_local_view_bits = base->local_view_bits; + ok = true; + } + } + } + + sub_v3_v3v3(box, max, min); + size = max_fff(box[0], box[1], box[2]); + } + + if (ok == true) { + ARegion *ar; + + v3d->localvd = MEM_mallocN(sizeof(View3D), "localview"); + + memcpy(v3d->localvd, v3d, sizeof(View3D)); + + mid_v3_v3v3(mid, min, max); + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = ar->regiondata; + bool ok_dist = true; + + /* New view values. */ + Object *camera_old = NULL; + float dist_new, ofs_new[3]; + + rv3d->localvd = MEM_mallocN(sizeof(RegionView3D), "localview region"); + memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D)); + + negate_v3_v3(ofs_new, mid); + + if (rv3d->persp == RV3D_CAMOB) { + rv3d->persp = RV3D_PERSP; + camera_old = v3d->camera; + } + + if (rv3d->persp == RV3D_ORTHO) { + if (size < 0.0001f) { + ok_dist = false; + } + } + + if (ok_dist) { + dist_new = ED_view3d_radius_to_dist(v3d, ar, depsgraph, rv3d->persp, true, (size / 2) * VIEW3D_MARGIN); + + if (rv3d->persp == RV3D_PERSP) { + /* Don't zoom closer than the near clipping plane. */ + dist_new = max_ff(dist_new, v3d->near * 1.5f); + } + } + + ED_view3d_smooth_view_ex( + depsgraph, + wm, win, sa, v3d, ar, smooth_viewtx, + &(const V3D_SmoothParams) { + .camera_old = camera_old, + .ofs = ofs_new, .quat = rv3d->viewquat, + .dist = ok_dist ? &dist_new : NULL, .lens = &v3d->lens}); + } + } + + v3d->local_view_uuid = local_view_bit; + } + + DEG_on_visible_update(bmain, false); + return ok; +} + +static void restore_localviewdata( + const Depsgraph *depsgraph, + wmWindowManager *wm, + wmWindow *win, + Main *bmain, + ScrArea *sa, + const int smooth_viewtx) +{ + const bool free = true; + ARegion *ar; + View3D *v3d = sa->spacedata.first; + Object *camera_old, *camera_new; + + if (v3d->localvd == NULL) return; + + camera_old = v3d->camera; + camera_new = v3d->localvd->camera; + + v3d->local_view_uuid = 0; + v3d->camera = v3d->localvd->camera; + + if (free) { + MEM_freeN(v3d->localvd); + v3d->localvd = NULL; + } + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = ar->regiondata; + + if (rv3d->localvd) { + Object *camera_old_rv3d, *camera_new_rv3d; + + camera_old_rv3d = (rv3d->persp == RV3D_CAMOB) ? camera_old : NULL; + camera_new_rv3d = (rv3d->localvd->persp == RV3D_CAMOB) ? camera_new : NULL; + + rv3d->view = rv3d->localvd->view; + rv3d->persp = rv3d->localvd->persp; + rv3d->camzoom = rv3d->localvd->camzoom; + + ED_view3d_smooth_view_ex( + depsgraph, + wm, win, sa, + v3d, ar, smooth_viewtx, + &(const V3D_SmoothParams) { + .camera_old = camera_old_rv3d, .camera = camera_new_rv3d, + .ofs = rv3d->localvd->ofs, .quat = rv3d->localvd->viewquat, + .dist = &rv3d->localvd->dist}); + + if (free) { + MEM_freeN(rv3d->localvd); + rv3d->localvd = NULL; + } + } + + ED_view3d_shade_update(bmain, v3d, sa); + } + } +} + +static bool view3d_localview_exit( + const Depsgraph *depsgraph, + wmWindowManager *wm, + wmWindow *win, + Main *bmain, + ViewLayer *view_layer, + ScrArea *sa, + const int smooth_viewtx) +{ + View3D *v3d = sa->spacedata.first; + struct Base *base; + unsigned int local_view_bit; + + if (v3d->localvd) { + + local_view_bit = v3d->local_view_uuid; + + restore_localviewdata(depsgraph, wm, win, bmain, sa, smooth_viewtx); + + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + for (base = FIRSTBASE(view_layer); base; base = base->next) { + if (base->local_view_bits & local_view_bit) { + base->local_view_bits &= ~local_view_bit; + if (base->object != obedit) { + ED_object_base_select(base, BA_SELECT); + } + } + } + + DEG_on_visible_update(bmain, false); + + return true; + } + else { + return false; + } +} + +static int localview_exec(bContext *C, wmOperator *op) +{ + const Depsgraph *depsgraph = CTX_data_depsgraph(C); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = CTX_wm_view3d(C); + bool changed; + + if (v3d->localvd) { + changed = view3d_localview_exit(depsgraph, wm, win, bmain, view_layer, sa, smooth_viewtx); + } + else { + changed = view3d_localview_init(depsgraph, wm, win, bmain, view_layer, sa, smooth_viewtx, op->reports); + } + + if (changed) { + DEG_id_type_tag(bmain, ID_OB); + ED_area_tag_redraw(sa); + + /* Unselected objects become selected when exiting. */ + if (v3d->localvd == NULL) { + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + } + else { + DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE); + } + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void VIEW3D_OT_localview(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Local View"; + ot->description = "Toggle display of selected object(s) separately and centered in view"; + ot->idname = "VIEW3D_OT_localview"; + + /* api callbacks */ + ot->exec = localview_exec; + ot->flag = OPTYPE_UNDO; /* localview changes object layer bitflags */ + + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name View Layer Utilities * \{ */ |