Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_view3d/view3d_navigate_smoothview.c')
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_smoothview.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
new file mode 100644
index 00000000000..aeffb520b0a
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
@@ -0,0 +1,406 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "DNA_camera_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+
+#include "ED_screen.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Smooth View Operator & Utilities
+ *
+ * Use for view transitions to have smooth (animated) transitions.
+ * \{ */
+
+/* This operator is one of the 'timer refresh' ones like animation playback */
+
+struct SmoothView3DState {
+ float dist;
+ float lens;
+ float quat[4];
+ float ofs[3];
+};
+
+struct SmoothView3DStore {
+ /* Source. */
+ struct SmoothView3DState src; /* source */
+ struct SmoothView3DState dst; /* destination */
+ struct SmoothView3DState org; /* original */
+
+ bool to_camera;
+
+ bool use_dyn_ofs;
+ float dyn_ofs[3];
+
+ /* When smooth-view is enabled, store the 'rv3d->view' here,
+ * assign back when the view motion is completed. */
+ char org_view;
+
+ double time_allowed;
+};
+
+static void view3d_smooth_view_state_backup(struct SmoothView3DState *sms_state,
+ const View3D *v3d,
+ const RegionView3D *rv3d)
+{
+ copy_v3_v3(sms_state->ofs, rv3d->ofs);
+ copy_qt_qt(sms_state->quat, rv3d->viewquat);
+ sms_state->dist = rv3d->dist;
+ sms_state->lens = v3d->lens;
+}
+
+static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms_state,
+ View3D *v3d,
+ RegionView3D *rv3d)
+{
+ copy_v3_v3(rv3d->ofs, sms_state->ofs);
+ copy_qt_qt(rv3d->viewquat, sms_state->quat);
+ rv3d->dist = sms_state->dist;
+ v3d->lens = sms_state->lens;
+}
+
+/* will start timer if appropriate */
+void ED_view3d_smooth_view_ex(
+ /* avoid passing in the context */
+ const Depsgraph *depsgraph,
+ wmWindowManager *wm,
+ wmWindow *win,
+ ScrArea *area,
+ View3D *v3d,
+ ARegion *region,
+ const int smooth_viewtx,
+ const V3D_SmoothParams *sview)
+{
+ RegionView3D *rv3d = region->regiondata;
+ struct SmoothView3DStore sms = {{0}};
+
+ /* initialize sms */
+ view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d);
+ view3d_smooth_view_state_backup(&sms.src, v3d, rv3d);
+ /* If smooth-view runs multiple times. */
+ if (rv3d->sms == NULL) {
+ view3d_smooth_view_state_backup(&sms.org, v3d, rv3d);
+ }
+ else {
+ sms.org = rv3d->sms->org;
+ }
+ sms.org_view = rv3d->view;
+
+ /* sms.to_camera = false; */ /* initialized to zero anyway */
+
+ /* note on camera locking, this is a little confusing but works ok.
+ * we may be changing the view 'as if' there is no active camera, but in fact
+ * there is an active camera which is locked to the view.
+ *
+ * In the case where smooth view is moving _to_ a camera we don't want that
+ * camera to be moved or changed, so only when the camera is not being set should
+ * we allow camera option locking to initialize the view settings from the camera.
+ */
+ if (sview->camera == NULL && sview->camera_old == NULL) {
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
+
+ /* store the options we want to end with */
+ if (sview->ofs) {
+ copy_v3_v3(sms.dst.ofs, sview->ofs);
+ }
+ if (sview->quat) {
+ copy_qt_qt(sms.dst.quat, sview->quat);
+ }
+ if (sview->dist) {
+ sms.dst.dist = *sview->dist;
+ }
+ if (sview->lens) {
+ sms.dst.lens = *sview->lens;
+ }
+
+ if (sview->dyn_ofs) {
+ BLI_assert(sview->ofs == NULL);
+ BLI_assert(sview->quat != NULL);
+
+ copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs);
+ sms.use_dyn_ofs = true;
+
+ /* calculate the final destination offset */
+ view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs);
+ }
+
+ if (sview->camera) {
+ Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
+ if (sview->ofs != NULL) {
+ sms.dst.dist = ED_view3d_offset_distance(
+ ob_camera_eval->obmat, sview->ofs, VIEW3D_DIST_FALLBACK);
+ }
+ ED_view3d_from_object(ob_camera_eval, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens);
+ sms.to_camera = true; /* restore view3d values in end */
+ }
+
+ if ((sview->camera_old == sview->camera) && /* Camera. */
+ (sms.dst.dist == rv3d->dist) && /* Distance. */
+ (sms.dst.lens == v3d->lens) && /* Lens. */
+ equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */
+ equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */
+ ) {
+ /* Early return if nothing changed. */
+ return;
+ }
+
+ /* Skip smooth viewing for external render engine draw. */
+ if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) {
+
+ /* original values */
+ if (sview->camera_old) {
+ Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old);
+ if (sview->ofs != NULL) {
+ sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f);
+ }
+ ED_view3d_from_object(
+ ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens);
+ }
+ /* grid draw as floor */
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
+ /* use existing if exists, means multiple calls to smooth view
+ * won't lose the original 'view' setting */
+ rv3d->view = RV3D_VIEW_USER;
+ }
+
+ sms.time_allowed = (double)smooth_viewtx / 1000.0;
+
+ /* If this is view rotation only we can decrease the time allowed by the angle between quats
+ * this means small rotations won't lag. */
+ if (sview->quat && !sview->ofs && !sview->dist) {
+ /* scale the time allowed by the rotation */
+ /* 180deg == 1.0 */
+ sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) /
+ M_PI;
+ }
+
+ /* ensure it shows correct */
+ if (sms.to_camera) {
+ /* use ortho if we move from an ortho view to an ortho camera */
+ Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
+ rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) &&
+ (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ?
+ RV3D_ORTHO :
+ RV3D_PERSP);
+ }
+
+ rv3d->rflag |= RV3D_NAVIGATING;
+
+ /* not essential but in some cases the caller will tag the area for redraw, and in that
+ * case we can get a flicker of the 'org' user view but we want to see 'src' */
+ view3d_smooth_view_state_restore(&sms.src, v3d, rv3d);
+
+ /* keep track of running timer! */
+ if (rv3d->sms == NULL) {
+ rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d");
+ }
+ *rv3d->sms = sms;
+ if (rv3d->smooth_timer) {
+ WM_event_remove_timer(wm, win, rv3d->smooth_timer);
+ }
+ /* #TIMER1 is hard-coded in key-map. */
+ rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0);
+ }
+ else {
+ /* Animation is disabled, apply immediately. */
+ if (sms.to_camera == false) {
+ copy_v3_v3(rv3d->ofs, sms.dst.ofs);
+ copy_qt_qt(rv3d->viewquat, sms.dst.quat);
+ rv3d->dist = sms.dst.dist;
+ v3d->lens = sms.dst.lens;
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_copy(area, region);
+ }
+
+ ED_region_tag_redraw(region);
+
+ WM_event_add_mousemove(win);
+ }
+}
+
+void ED_view3d_smooth_view(bContext *C,
+ View3D *v3d,
+ ARegion *region,
+ const int smooth_viewtx,
+ const struct V3D_SmoothParams *sview)
+{
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ ScrArea *area = CTX_wm_area(C);
+
+ ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview);
+}
+
+/* only meant for timer usage */
+static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ RegionView3D *rv3d = region->regiondata;
+ struct SmoothView3DStore *sms = rv3d->sms;
+ float step, step_inv;
+
+ if (sms->time_allowed != 0.0) {
+ step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
+ }
+ else {
+ step = 1.0f;
+ }
+
+ /* end timer */
+ if (step >= 1.0f) {
+ wmWindow *win = CTX_wm_window(C);
+
+ /* if we went to camera, store the original */
+ if (sms->to_camera) {
+ rv3d->persp = RV3D_CAMOB;
+ view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
+ }
+ else {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+
+ view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
+ }
+
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
+ rv3d->view = sms->org_view;
+ }
+
+ MEM_freeN(rv3d->sms);
+ rv3d->sms = NULL;
+
+ WM_event_remove_timer(wm, win, rv3d->smooth_timer);
+ rv3d->smooth_timer = NULL;
+ rv3d->rflag &= ~RV3D_NAVIGATING;
+
+ /* Event handling won't know if a UI item has been moved under the pointer. */
+ WM_event_add_mousemove(win);
+ }
+ else {
+ /* ease in/out */
+ step = (3.0f * step * step - 2.0f * step * step * step);
+
+ step_inv = 1.0f - step;
+
+ interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step);
+
+ if (sms->use_dyn_ofs) {
+ view3d_orbit_apply_dyn_ofs(
+ rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
+ }
+ else {
+ interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step);
+ }
+
+ rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv;
+ v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv;
+
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ if (ED_screen_animation_playing(wm)) {
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
+ }
+ }
+
+ if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) {
+ view3d_boxview_copy(CTX_wm_area(C), region);
+ }
+
+ /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636,
+ * when switching camera in quad-view the other ortho views would zoom & reset.
+ *
+ * For now only redraw all regions when smooth-view finishes.
+ */
+ if (step >= 1.0f) {
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ }
+ else {
+ ED_region_tag_redraw(region);
+ }
+}
+
+static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ View3D *v3d = CTX_wm_view3d(C);
+ ARegion *region = CTX_wm_region(C);
+ RegionView3D *rv3d = region->regiondata;
+
+ /* escape if not our timer */
+ if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ view3d_smoothview_apply(C, v3d, region, true);
+
+ return OPERATOR_FINISHED;
+}
+
+void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
+{
+ RegionView3D *rv3d = region->regiondata;
+
+ if (rv3d && rv3d->sms) {
+ rv3d->sms->time_allowed = 0.0; /* force finishing */
+ view3d_smoothview_apply(C, v3d, region, false);
+
+ /* force update of view matrix so tools that run immediately after
+ * can use them without redrawing first */
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false);
+ }
+}
+
+void VIEW3D_OT_smoothview(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Smooth View";
+ ot->idname = "VIEW3D_OT_smoothview";
+
+ /* api callbacks */
+ ot->invoke = view3d_smoothview_invoke;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL;
+
+ ot->poll = ED_operator_view3d_active;
+}
+
+/** \} */