1152 lines
36 KiB
C++
1152 lines
36 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup spview3d
|
|
*/
|
|
|
|
#include "DNA_curve_types.h"
|
|
#include "DNA_gpencil_legacy_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math_geom.h"
|
|
#include "BLI_math_matrix.h"
|
|
#include "BLI_math_rotation.h"
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_rect.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BKE_armature.h"
|
|
#include "BKE_context.h"
|
|
#include "BKE_gpencil_geom_legacy.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_object.hh"
|
|
#include "BKE_paint.hh"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_screen.hh"
|
|
#include "BKE_vfont.h"
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
#include "ED_mesh.hh"
|
|
#include "ED_particle.hh"
|
|
#include "ED_screen.hh"
|
|
#include "ED_transform.hh"
|
|
|
|
#include "WM_api.hh"
|
|
#include "WM_message.hh"
|
|
|
|
#include "RNA_access.hh"
|
|
#include "RNA_define.hh"
|
|
|
|
#include "UI_resources.hh"
|
|
|
|
#include "view3d_intern.h"
|
|
|
|
#include "view3d_navigate.hh" /* own include */
|
|
|
|
/* Prototypes. */
|
|
static const ViewOpsType *view3d_navigation_type_from_idname(const char *idname);
|
|
|
|
static eViewOpsFlag viewops_flag_from_prefs()
|
|
{
|
|
const bool use_select = (U.uiflag & USER_ORBIT_SELECTION) != 0;
|
|
const bool use_depth = (U.uiflag & USER_DEPTH_NAVIGATE) != 0;
|
|
const bool use_zoom_to_mouse = (U.uiflag & USER_ZOOM_TO_MOUSEPOS) != 0;
|
|
|
|
/**
|
|
* If the mode requires it, always set the #VIEWOPS_FLAG_PERSP_ENSURE.
|
|
* The function `ED_view3d_persp_ensure` already handles the checking of the preferences.
|
|
* And even with the option disabled, in some modes, it is still necessary to exit the camera
|
|
* view.
|
|
*
|
|
* \code{.c}
|
|
* const bool use_auto_persp = (U.uiflag & USER_AUTOPERSP) != 0;
|
|
* if (use_auto_persp) {
|
|
* flag |= VIEWOPS_FLAG_PERSP_ENSURE;
|
|
* }
|
|
* \endcode
|
|
*/
|
|
enum eViewOpsFlag flag = VIEWOPS_FLAG_INIT_ZFAC | VIEWOPS_FLAG_PERSP_ENSURE;
|
|
|
|
if (use_select) {
|
|
flag |= VIEWOPS_FLAG_ORBIT_SELECT;
|
|
}
|
|
if (use_depth) {
|
|
flag |= VIEWOPS_FLAG_DEPTH_NAVIGATE;
|
|
}
|
|
if (use_zoom_to_mouse) {
|
|
flag |= VIEWOPS_FLAG_ZOOM_TO_MOUSE;
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name ViewOpsData definition
|
|
* \{ */
|
|
|
|
void ViewOpsData::init_context(bContext *C)
|
|
{
|
|
/* Store data. */
|
|
this->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
|
this->scene = CTX_data_scene(C);
|
|
this->area = CTX_wm_area(C);
|
|
this->region = CTX_wm_region(C);
|
|
this->v3d = static_cast<View3D *>(this->area->spacedata.first);
|
|
this->rv3d = static_cast<RegionView3D *>(this->region->regiondata);
|
|
}
|
|
|
|
void ViewOpsData::state_backup()
|
|
{
|
|
copy_v3_v3(this->init.ofs, rv3d->ofs);
|
|
copy_v3_v3(this->init.ofs_lock, rv3d->ofs_lock);
|
|
this->init.camdx = rv3d->camdx;
|
|
this->init.camdy = rv3d->camdy;
|
|
this->init.camzoom = rv3d->camzoom;
|
|
this->init.dist = rv3d->dist;
|
|
copy_qt_qt(this->init.quat, rv3d->viewquat);
|
|
|
|
this->init.persp = rv3d->persp;
|
|
this->init.view = rv3d->view;
|
|
this->init.view_axis_roll = rv3d->view_axis_roll;
|
|
}
|
|
|
|
void ViewOpsData::state_restore()
|
|
{
|
|
/* DOLLY, MOVE, ROTATE and ZOOM. */
|
|
{
|
|
/* For Move this only changes when offset is not locked. */
|
|
/* For Rotate this only changes when rotating around objects or last-brush. */
|
|
/* For Zoom this only changes when zooming to mouse position. */
|
|
/* Note this does not remove auto-keys on locked cameras. */
|
|
copy_v3_v3(this->rv3d->ofs, this->init.ofs);
|
|
}
|
|
|
|
/* MOVE and ZOOM. */
|
|
{
|
|
/* For Move this only changes when offset is not locked. */
|
|
/* For Zoom this only changes when zooming to mouse position in camera view. */
|
|
this->rv3d->camdx = this->init.camdx;
|
|
this->rv3d->camdy = this->init.camdy;
|
|
}
|
|
|
|
/* MOVE. */
|
|
{
|
|
if ((this->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(this->v3d, this->rv3d)) {
|
|
// this->rv3d->camdx = this->init.camdx;
|
|
// this->rv3d->camdy = this->init.camdy;
|
|
}
|
|
else if (ED_view3d_offset_lock_check(this->v3d, this->rv3d)) {
|
|
copy_v2_v2(this->rv3d->ofs_lock, this->init.ofs_lock);
|
|
}
|
|
else {
|
|
// copy_v3_v3(vod->rv3d->ofs, vod->init.ofs);
|
|
if (RV3D_LOCK_FLAGS(this->rv3d) & RV3D_BOXVIEW) {
|
|
view3d_boxview_sync(this->area, this->region);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ZOOM. */
|
|
{
|
|
this->rv3d->camzoom = this->init.camzoom;
|
|
}
|
|
|
|
/* ROTATE and ZOOM. */
|
|
{
|
|
/* For Rotate this only changes when orbiting from a camera view.
|
|
* In this case the `dist` is calculated based on the camera relative to the `ofs`. */
|
|
|
|
/* Note this does not remove auto-keys on locked cameras. */
|
|
this->rv3d->dist = this->init.dist;
|
|
}
|
|
|
|
/* ROLL and ROTATE. */
|
|
{
|
|
/* Note this does not remove auto-keys on locked cameras. */
|
|
copy_qt_qt(this->rv3d->viewquat, this->init.quat);
|
|
}
|
|
|
|
/* ROTATE. */
|
|
{
|
|
this->rv3d->persp = this->init.persp;
|
|
this->rv3d->view = this->init.view;
|
|
this->rv3d->view_axis_roll = this->init.view_axis_roll;
|
|
}
|
|
|
|
/* NOTE: there is no need to restore "last" values (as set by #ED_view3d_lastview_store). */
|
|
|
|
ED_view3d_camera_lock_sync(this->depsgraph, this->v3d, this->rv3d);
|
|
}
|
|
|
|
static eViewOpsFlag navigate_pivot_get(bContext *C,
|
|
Depsgraph *depsgraph,
|
|
ARegion *region,
|
|
View3D *v3d,
|
|
const wmEvent *event,
|
|
eViewOpsFlag viewops_flag,
|
|
const float dyn_ofs_override[3],
|
|
float r_pivot[3])
|
|
{
|
|
if ((viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT) && view3d_orbit_calc_center(C, r_pivot)) {
|
|
return VIEWOPS_FLAG_ORBIT_SELECT;
|
|
}
|
|
|
|
wmWindow *win = CTX_wm_window(C);
|
|
|
|
if (!(viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE)) {
|
|
ED_view3d_autodist_last_clear(win);
|
|
|
|
/* Uses the `lastofs` in #view3d_orbit_calc_center. */
|
|
BLI_assert(viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT);
|
|
return VIEWOPS_FLAG_ORBIT_SELECT;
|
|
}
|
|
|
|
if (dyn_ofs_override) {
|
|
ED_view3d_win_to_3d_int(v3d, region, dyn_ofs_override, event->mval, r_pivot);
|
|
return VIEWOPS_FLAG_DEPTH_NAVIGATE;
|
|
}
|
|
|
|
const bool use_depth_last = ED_view3d_autodist_last_check(win, event);
|
|
|
|
if (use_depth_last) {
|
|
ED_view3d_autodist_last_get(win, r_pivot);
|
|
}
|
|
else {
|
|
float fallback_depth_pt[3];
|
|
negate_v3_v3(fallback_depth_pt, static_cast<RegionView3D *>(region->regiondata)->ofs);
|
|
|
|
const bool is_set = ED_view3d_autodist(
|
|
depsgraph, region, v3d, event->mval, r_pivot, true, fallback_depth_pt);
|
|
|
|
ED_view3d_autodist_last_set(win, event, r_pivot, is_set);
|
|
}
|
|
|
|
return VIEWOPS_FLAG_DEPTH_NAVIGATE;
|
|
}
|
|
|
|
void ViewOpsData::init_navigation(bContext *C,
|
|
const wmEvent *event,
|
|
const ViewOpsType *nav_type,
|
|
const float dyn_ofs_override[3],
|
|
const bool use_cursor_init)
|
|
{
|
|
this->nav_type = nav_type;
|
|
eViewOpsFlag viewops_flag = nav_type->flag & viewops_flag_from_prefs();
|
|
|
|
if (!use_cursor_init) {
|
|
viewops_flag &= ~(VIEWOPS_FLAG_DEPTH_NAVIGATE | VIEWOPS_FLAG_ZOOM_TO_MOUSE);
|
|
}
|
|
|
|
bool calc_rv3d_dist = true;
|
|
#ifdef WITH_INPUT_NDOF
|
|
if (ELEM(nav_type,
|
|
&ViewOpsType_ndof_orbit,
|
|
&ViewOpsType_ndof_orbit_zoom,
|
|
&ViewOpsType_ndof_pan,
|
|
&ViewOpsType_ndof_all))
|
|
{
|
|
calc_rv3d_dist = false;
|
|
}
|
|
#endif
|
|
|
|
/* Set the view from the camera, if view locking is enabled.
|
|
* we may want to make this optional but for now its needed always. */
|
|
ED_view3d_camera_lock_init_ex(depsgraph, v3d, rv3d, calc_rv3d_dist);
|
|
|
|
this->state_backup();
|
|
|
|
if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) {
|
|
if (ED_view3d_persp_ensure(depsgraph, this->v3d, this->region)) {
|
|
/* If we're switching from camera view to the perspective one,
|
|
* need to tag viewport update, so camera view and borders are properly updated. */
|
|
ED_region_tag_redraw(this->region);
|
|
}
|
|
}
|
|
|
|
if (viewops_flag & (VIEWOPS_FLAG_DEPTH_NAVIGATE | VIEWOPS_FLAG_ORBIT_SELECT)) {
|
|
float pivot_new[3];
|
|
eViewOpsFlag pivot_type = navigate_pivot_get(
|
|
C, depsgraph, region, v3d, event, viewops_flag, dyn_ofs_override, pivot_new);
|
|
|
|
viewops_flag &= ~(VIEWOPS_FLAG_DEPTH_NAVIGATE | VIEWOPS_FLAG_ORBIT_SELECT);
|
|
viewops_flag |= pivot_type;
|
|
|
|
negate_v3_v3(this->dyn_ofs, pivot_new);
|
|
this->use_dyn_ofs = true;
|
|
|
|
if (!(nav_type->flag & VIEWOPS_FLAG_ORBIT_SELECT)) {
|
|
/* Calculate new #RegionView3D::ofs and #RegionView3D::dist. */
|
|
|
|
if (rv3d->is_persp) {
|
|
float my_origin[3]; /* Original #RegionView3D.ofs. */
|
|
float my_pivot[3]; /* View pivot. */
|
|
float dvec[3];
|
|
|
|
/* locals for dist correction */
|
|
float mat[3][3];
|
|
float upvec[3];
|
|
|
|
negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
|
|
|
|
/* Set the dist value to be the distance from this 3d point this means you'll
|
|
* always be able to zoom into it and panning won't go bad when dist was zero. */
|
|
|
|
/* remove dist value */
|
|
upvec[0] = upvec[1] = 0;
|
|
upvec[2] = rv3d->dist;
|
|
copy_m3_m4(mat, rv3d->viewinv);
|
|
|
|
mul_m3_v3(mat, upvec);
|
|
add_v3_v3v3(my_pivot, my_origin, upvec);
|
|
|
|
/* find a new ofs value that is along the view axis
|
|
* (rather than the mouse location) */
|
|
closest_to_line_v3(dvec, pivot_new, my_pivot, my_origin);
|
|
|
|
negate_v3_v3(rv3d->ofs, dvec);
|
|
rv3d->dist = len_v3v3(my_pivot, dvec);
|
|
}
|
|
else {
|
|
const float mval_region_mid[2] = {float(region->winx) / 2.0f, float(region->winy) / 2.0f};
|
|
ED_view3d_win_to_3d(v3d, region, pivot_new, mval_region_mid, rv3d->ofs);
|
|
negate_v3(rv3d->ofs);
|
|
}
|
|
|
|
/* XXX: The initial state captured by #ViewOpsData::state_backup is being modified here.
|
|
* This causes the state when canceling a navigation operation to not be fully restored. */
|
|
this->init.dist = rv3d->dist;
|
|
copy_v3_v3(this->init.ofs, rv3d->ofs);
|
|
}
|
|
}
|
|
|
|
if (viewops_flag & VIEWOPS_FLAG_INIT_ZFAC) {
|
|
float tvec[3];
|
|
negate_v3_v3(tvec, rv3d->ofs);
|
|
this->init.zfac = ED_view3d_calc_zfac(rv3d, tvec);
|
|
}
|
|
|
|
this->init.persp_with_auto_persp_applied = rv3d->persp;
|
|
|
|
if (event) {
|
|
this->init.event_type = event->type;
|
|
copy_v2_v2_int(this->init.event_xy, event->xy);
|
|
copy_v2_v2_int(this->prev.event_xy, event->xy);
|
|
|
|
if (use_cursor_init) {
|
|
zero_v2_int(this->init.event_xy_offset);
|
|
}
|
|
else {
|
|
/* Simulate the event starting in the middle of the region. */
|
|
this->init.event_xy_offset[0] = BLI_rcti_cent_x(&this->region->winrct) - event->xy[0];
|
|
this->init.event_xy_offset[1] = BLI_rcti_cent_y(&this->region->winrct) - event->xy[1];
|
|
}
|
|
|
|
/* For dolly */
|
|
const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
|
|
ED_view3d_win_to_vector(region, mval, this->init.mousevec);
|
|
|
|
{
|
|
int event_xy_offset[2];
|
|
add_v2_v2v2_int(event_xy_offset, event->xy, this->init.event_xy_offset);
|
|
|
|
/* For rotation with trackball rotation. */
|
|
calctrackballvec(®ion->winrct, event_xy_offset, this->init.trackvec);
|
|
}
|
|
}
|
|
|
|
copy_qt_qt(this->curr.viewquat, rv3d->viewquat);
|
|
|
|
this->reverse = 1.0f;
|
|
if (rv3d->persmat[2][1] < 0.0f) {
|
|
this->reverse = -1.0f;
|
|
}
|
|
|
|
this->viewops_flag = viewops_flag;
|
|
|
|
/* Default. */
|
|
this->use_dyn_ofs_ortho_correction = false;
|
|
|
|
rv3d->rflag |= RV3D_NAVIGATING;
|
|
}
|
|
|
|
void ViewOpsData::end_navigation(bContext *C)
|
|
{
|
|
this->rv3d->rflag &= ~RV3D_NAVIGATING;
|
|
|
|
if (this->timer) {
|
|
WM_event_timer_remove(CTX_wm_manager(C), this->timer->win, this->timer);
|
|
}
|
|
|
|
MEM_SAFE_FREE(this->init.dial);
|
|
|
|
/* Need to redraw because drawing code uses RV3D_NAVIGATING to draw
|
|
* faster while navigation operator runs. */
|
|
ED_region_tag_redraw(this->region);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic Operator Callback Utils
|
|
* \{ */
|
|
|
|
/* Used for navigation utility in operators. */
|
|
struct ViewOpsData_Utility : ViewOpsData {
|
|
/* To track only the navigation #wmKeyMapItem items and allow changes to them, an internal
|
|
* #wmKeyMap is created with their copy. */
|
|
ListBase keymap_items;
|
|
|
|
/* Used by #ED_view3d_navigation_do. */
|
|
bool is_modal_event;
|
|
|
|
ViewOpsData_Utility(bContext *C, const wmKeyMapItem *kmi_merge = nullptr)
|
|
: ViewOpsData(), keymap_items(), is_modal_event(false)
|
|
{
|
|
this->init_context(C);
|
|
|
|
wmKeyMap *keymap = WM_keymap_find_all(
|
|
CTX_wm_manager(C), "3D View", SPACE_VIEW3D, RGN_TYPE_WINDOW);
|
|
|
|
WM_keyconfig_update_suppress_begin();
|
|
|
|
wmKeyMap keymap_tmp = {};
|
|
|
|
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
|
|
if (!STRPREFIX(kmi->idname, "VIEW3D")) {
|
|
continue;
|
|
}
|
|
if (kmi->flag & KMI_INACTIVE) {
|
|
continue;
|
|
}
|
|
if (view3d_navigation_type_from_idname(kmi->idname) == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
wmKeyMapItem *kmi_cpy = WM_keymap_add_item_copy(&keymap_tmp, kmi);
|
|
if (kmi_merge) {
|
|
if (kmi_merge->shift == 1 || ELEM(kmi_merge->type, EVT_RIGHTSHIFTKEY, EVT_LEFTSHIFTKEY)) {
|
|
kmi_cpy->shift = 1;
|
|
}
|
|
if (kmi_merge->ctrl == 1 || ELEM(kmi_merge->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
|
|
kmi_cpy->ctrl = 1;
|
|
}
|
|
if (kmi_merge->alt == 1 || ELEM(kmi_merge->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) {
|
|
kmi_cpy->alt = 1;
|
|
}
|
|
if (kmi_merge->oskey == 1 || ELEM(kmi_merge->type, EVT_OSKEY)) {
|
|
kmi_cpy->oskey = 1;
|
|
}
|
|
if (!ELEM(kmi_merge->type,
|
|
EVT_LEFTCTRLKEY,
|
|
EVT_LEFTALTKEY,
|
|
EVT_RIGHTALTKEY,
|
|
EVT_RIGHTCTRLKEY,
|
|
EVT_RIGHTSHIFTKEY,
|
|
EVT_LEFTSHIFTKEY,
|
|
EVT_OSKEY))
|
|
{
|
|
kmi_cpy->keymodifier |= kmi_merge->type;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Weak, but only the keymap items from the #wmKeyMap struct are needed here. */
|
|
this->keymap_items = keymap_tmp.items;
|
|
|
|
WM_keyconfig_update_suppress_end();
|
|
}
|
|
|
|
~ViewOpsData_Utility()
|
|
{
|
|
/* Weak, but rebuild the struct #wmKeyMap to clear the keymap items. */
|
|
WM_keyconfig_update_suppress_begin();
|
|
|
|
wmKeyMap keymap_tmp = {};
|
|
keymap_tmp.items = this->keymap_items;
|
|
WM_keymap_clear(&keymap_tmp);
|
|
|
|
WM_keyconfig_update_suppress_end();
|
|
}
|
|
|
|
#ifdef WITH_CXX_GUARDEDALLOC
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("ViewOpsData_Utility")
|
|
#endif
|
|
};
|
|
|
|
static bool view3d_navigation_poll_impl(bContext *C, const char viewlock)
|
|
{
|
|
if (!ED_operator_region_view3d_active(C)) {
|
|
return false;
|
|
}
|
|
|
|
const RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
|
return !(RV3D_LOCK_FLAGS(rv3d) & viewlock);
|
|
}
|
|
|
|
static eV3D_OpEvent view3d_navigate_event(ViewOpsData *vod, const wmEvent *event)
|
|
{
|
|
if (event->type == EVT_MODAL_MAP) {
|
|
switch (event->val) {
|
|
case VIEW_MODAL_CANCEL:
|
|
return VIEW_CANCEL;
|
|
case VIEW_MODAL_CONFIRM:
|
|
return VIEW_CONFIRM;
|
|
case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
|
|
vod->axis_snap = true;
|
|
return VIEW_APPLY;
|
|
case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
|
|
vod->rv3d->persp = vod->init.persp_with_auto_persp_applied;
|
|
vod->axis_snap = false;
|
|
return VIEW_APPLY;
|
|
case VIEWROT_MODAL_SWITCH_ZOOM:
|
|
case VIEWROT_MODAL_SWITCH_MOVE:
|
|
case VIEWROT_MODAL_SWITCH_ROTATE: {
|
|
const ViewOpsType *nav_type_new = (event->val == VIEWROT_MODAL_SWITCH_ZOOM) ?
|
|
&ViewOpsType_zoom :
|
|
(event->val == VIEWROT_MODAL_SWITCH_MOVE) ?
|
|
&ViewOpsType_move :
|
|
&ViewOpsType_rotate;
|
|
if (nav_type_new == vod->nav_type) {
|
|
break;
|
|
}
|
|
vod->nav_type = nav_type_new;
|
|
return VIEW_APPLY;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (event->type == TIMER && event->customdata == vod->timer) {
|
|
/* Zoom uses timer for continuous zoom. */
|
|
return VIEW_APPLY;
|
|
}
|
|
if (event->type == MOUSEMOVE) {
|
|
return VIEW_APPLY;
|
|
}
|
|
if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
|
|
return VIEW_CONFIRM;
|
|
}
|
|
if (event->type == EVT_ESCKEY && event->val == KM_PRESS) {
|
|
return VIEW_CANCEL;
|
|
}
|
|
}
|
|
|
|
return VIEW_PASS;
|
|
}
|
|
|
|
static int view3d_navigation_invoke_generic(bContext *C,
|
|
ViewOpsData *vod,
|
|
const wmEvent *event,
|
|
PointerRNA *ptr,
|
|
const ViewOpsType *nav_type,
|
|
const float dyn_ofs_override[3])
|
|
{
|
|
if (!nav_type->init_fn) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
bool use_cursor_init = false;
|
|
if (PropertyRNA *prop = RNA_struct_find_property(ptr, "use_cursor_init")) {
|
|
use_cursor_init = RNA_property_boolean_get(ptr, prop);
|
|
}
|
|
|
|
vod->init_navigation(C, event, nav_type, dyn_ofs_override, use_cursor_init);
|
|
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
|
|
|
|
return nav_type->init_fn(C, vod, event, ptr);
|
|
}
|
|
|
|
int view3d_navigate_invoke_impl(bContext *C,
|
|
wmOperator *op,
|
|
const wmEvent *event,
|
|
const ViewOpsType *nav_type)
|
|
{
|
|
ViewOpsData *vod = new ViewOpsData();
|
|
vod->init_context(C);
|
|
int ret = view3d_navigation_invoke_generic(C, vod, event, op->ptr, nav_type, nullptr);
|
|
op->customdata = (void *)vod;
|
|
|
|
if (ret == OPERATOR_RUNNING_MODAL) {
|
|
WM_event_add_modal_handler(C, op);
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
viewops_data_free(C, vod);
|
|
op->customdata = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic Callbacks
|
|
* \{ */
|
|
|
|
bool view3d_location_poll(bContext *C)
|
|
{
|
|
return view3d_navigation_poll_impl(C, RV3D_LOCK_LOCATION);
|
|
}
|
|
|
|
bool view3d_rotation_poll(bContext *C)
|
|
{
|
|
return view3d_navigation_poll_impl(C, RV3D_LOCK_ROTATION);
|
|
}
|
|
|
|
bool view3d_zoom_or_dolly_poll(bContext *C)
|
|
{
|
|
return view3d_navigation_poll_impl(C, RV3D_LOCK_ZOOM_AND_DOLLY);
|
|
}
|
|
|
|
int view3d_navigate_modal_fn(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
ViewOpsData *vod = static_cast<ViewOpsData *>(op->customdata);
|
|
|
|
const ViewOpsType *nav_type_prev = vod->nav_type;
|
|
const eV3D_OpEvent event_code = view3d_navigate_event(vod, event);
|
|
if (nav_type_prev != vod->nav_type) {
|
|
wmOperatorType *ot_new = WM_operatortype_find(vod->nav_type->idname, false);
|
|
WM_operator_type_set(op, ot_new);
|
|
vod->end_navigation(C);
|
|
return view3d_navigation_invoke_generic(C, vod, event, op->ptr, vod->nav_type, nullptr);
|
|
}
|
|
|
|
int ret = vod->nav_type->apply_fn(C, vod, event_code, event->xy);
|
|
|
|
if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
|
|
if (ret & OPERATOR_FINISHED) {
|
|
ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
|
|
}
|
|
viewops_data_free(C, vod);
|
|
op->customdata = nullptr;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void view3d_navigate_cancel_fn(bContext *C, wmOperator *op)
|
|
{
|
|
viewops_data_free(C, static_cast<ViewOpsData *>(op->customdata));
|
|
op->customdata = nullptr;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic View Operator Properties
|
|
* \{ */
|
|
|
|
void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPropFlag flag)
|
|
{
|
|
if (flag & V3D_OP_PROP_MOUSE_CO) {
|
|
PropertyRNA *prop;
|
|
prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Region Position X", "", 0, INT_MAX);
|
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
|
prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Region Position Y", "", 0, INT_MAX);
|
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
|
}
|
|
if (flag & V3D_OP_PROP_DELTA) {
|
|
RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
|
|
}
|
|
if (flag & V3D_OP_PROP_USE_ALL_REGIONS) {
|
|
PropertyRNA *prop;
|
|
prop = RNA_def_boolean(
|
|
ot->srna, "use_all_regions", false, "All Regions", "View selected for all regions");
|
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
|
}
|
|
if (flag & V3D_OP_PROP_USE_MOUSE_INIT) {
|
|
WM_operator_properties_use_cursor_init(ot);
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic View Operator Custom-Data
|
|
* \{ */
|
|
|
|
void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3])
|
|
{
|
|
const float radius = V3D_OP_TRACKBALLSIZE;
|
|
const float t = radius / float(M_SQRT2);
|
|
const float size[2] = {float(BLI_rcti_size_x(rect)), float(BLI_rcti_size_y(rect))};
|
|
/* Aspect correct so dragging in a non-square view doesn't squash the direction.
|
|
* So diagonal motion rotates the same direction the cursor is moving. */
|
|
const float size_min = min_ff(size[0], size[1]);
|
|
const float aspect[2] = {size_min / size[0], size_min / size[1]};
|
|
|
|
/* Normalize x and y. */
|
|
r_dir[0] = (event_xy[0] - BLI_rcti_cent_x(rect)) / ((size[0] * aspect[0]) / 2.0);
|
|
r_dir[1] = (event_xy[1] - BLI_rcti_cent_y(rect)) / ((size[1] * aspect[1]) / 2.0);
|
|
const float d = len_v2(r_dir);
|
|
if (d < t) {
|
|
/* Inside sphere. */
|
|
r_dir[2] = sqrtf(square_f(radius) - square_f(d));
|
|
}
|
|
else {
|
|
/* On hyperbola. */
|
|
r_dir[2] = square_f(t) / d;
|
|
}
|
|
}
|
|
|
|
void view3d_orbit_apply_dyn_ofs(float r_ofs[3],
|
|
const float ofs_old[3],
|
|
const float viewquat_old[4],
|
|
const float viewquat_new[4],
|
|
const float dyn_ofs[3])
|
|
{
|
|
float q[4];
|
|
invert_qt_qt_normalized(q, viewquat_old);
|
|
mul_qt_qtqt(q, q, viewquat_new);
|
|
|
|
invert_qt_normalized(q);
|
|
|
|
sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
|
|
mul_qt_v3(q, r_ofs);
|
|
add_v3_v3(r_ofs, dyn_ofs);
|
|
}
|
|
|
|
static void view3d_orbit_apply_dyn_ofs_ortho_correction(float ofs[3],
|
|
const float viewquat_old[4],
|
|
const float viewquat_new[4],
|
|
const float dyn_ofs[3])
|
|
{
|
|
/* NOTE(@ideasman42): While orbiting in orthographic mode the "depth" of the offset
|
|
* (position along the views Z-axis) is only noticeable when the view contents is clipped.
|
|
* The likelihood of clipping depends on the clipping range & size of the scene.
|
|
* In practice some users might not run into this, however using dynamic-offset in
|
|
* orthographic views can cause the depth of the offset to drift while navigating the view,
|
|
* causing unexpected clipping that seems like a bug from the user perspective, see: #104385.
|
|
*
|
|
* Imagine a camera is focused on a distant object. Now imagine a closer object in front of
|
|
* the camera is used as a pivot, the camera is rotated to view it from the side (~90d rotation).
|
|
* The outcome is the camera is now focused on a distant region to the left/right.
|
|
* The new focal point is unlikely to point to anything useful (unless by accident).
|
|
* Instead of a focal point - the `rv3d->ofs` is being manipulated in this case.
|
|
*
|
|
* Resolve by moving #RegionView3D::ofs so it is depth-aligned to `dyn_ofs`,
|
|
* this is interpolated by the amount of rotation so minor rotations don't cause
|
|
* the view-clipping to suddenly jump.
|
|
*
|
|
* Perspective Views
|
|
* =================
|
|
*
|
|
* This logic could also be applied to perspective views because the issue of the `ofs`
|
|
* being a location which isn't useful exists there too, however the problem where this location
|
|
* impacts the clipping does *not* exist, as the clipping range starts from the view-point
|
|
* (`ofs` + `dist` along the view Z-axis) unlike orthographic views which center around `ofs`.
|
|
* Nevertheless there will be cases when having `ofs` and a large `dist` pointing nowhere doesn't
|
|
* give ideal behavior (zooming may jump in larger than expected steps and panning the view may
|
|
* move too much in relation to nearby objects - for e.g.). So it's worth investigating but
|
|
* should be done with extra care as changing `ofs` in perspective view also requires changing
|
|
* the `dist` which could cause unexpected results if the calculated `dist` happens to be small.
|
|
* So disable this workaround in perspective view unless there are clear benefits to enabling. */
|
|
|
|
float q_inv[4];
|
|
|
|
float view_z_init[3] = {0.0f, 0.0f, 1.0f};
|
|
invert_qt_qt_normalized(q_inv, viewquat_old);
|
|
mul_qt_v3(q_inv, view_z_init);
|
|
|
|
float view_z_curr[3] = {0.0f, 0.0f, 1.0f};
|
|
invert_qt_qt_normalized(q_inv, viewquat_new);
|
|
mul_qt_v3(q_inv, view_z_curr);
|
|
|
|
const float angle_cos = max_ff(0.0f, dot_v3v3(view_z_init, view_z_curr));
|
|
/* 1.0 or more means no rotation, there is nothing to do in that case. */
|
|
if (LIKELY(angle_cos < 1.0f)) {
|
|
const float dot_ofs_curr = dot_v3v3(view_z_curr, ofs);
|
|
const float dot_ofs_next = dot_v3v3(view_z_curr, dyn_ofs);
|
|
const float ofs_delta = dot_ofs_next - dot_ofs_curr;
|
|
if (LIKELY(ofs_delta != 0.0f)) {
|
|
/* Calculate a factor where 0.0 represents no rotation and 1.0 represents 90d or more.
|
|
* NOTE: Without applying the factor, the distances immediately changes
|
|
* (useful for testing), but not good for the users experience as minor rotations
|
|
* should not immediately adjust the depth. */
|
|
const float factor = acosf(angle_cos) / M_PI_2;
|
|
madd_v3_v3fl(ofs, view_z_curr, ofs_delta * factor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
|
|
{
|
|
if (vod->use_dyn_ofs) {
|
|
RegionView3D *rv3d = vod->rv3d;
|
|
view3d_orbit_apply_dyn_ofs(
|
|
rv3d->ofs, vod->init.ofs, vod->init.quat, viewquat_new, vod->dyn_ofs);
|
|
|
|
if (vod->use_dyn_ofs_ortho_correction) {
|
|
view3d_orbit_apply_dyn_ofs_ortho_correction(
|
|
rv3d->ofs, vod->init.quat, viewquat_new, vod->dyn_ofs);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
|
|
{
|
|
static float lastofs[3] = {0, 0, 0};
|
|
bool is_set = false;
|
|
|
|
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
|
Scene *scene = CTX_data_scene(C);
|
|
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
|
ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
BKE_view_layer_synced_ensure(scene_eval, view_layer_eval);
|
|
Object *ob_act_eval = BKE_view_layer_active_object_get(view_layer_eval);
|
|
Object *ob_act = DEG_get_original_object(ob_act_eval);
|
|
|
|
if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
|
|
/* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
|
|
((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
|
|
{
|
|
BKE_paint_stroke_get_average(scene, ob_act_eval, lastofs);
|
|
is_set = true;
|
|
}
|
|
else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
|
|
Curve *cu = static_cast<Curve *>(ob_act_eval->data);
|
|
EditFont *ef = cu->editfont;
|
|
|
|
zero_v3(lastofs);
|
|
for (int i = 0; i < 4; i++) {
|
|
add_v2_v2(lastofs, ef->textcurs[i]);
|
|
}
|
|
mul_v2_fl(lastofs, 1.0f / 4.0f);
|
|
|
|
mul_m4_v3(ob_act_eval->object_to_world, lastofs);
|
|
|
|
is_set = true;
|
|
}
|
|
else if (ob_act == nullptr || ob_act->mode == OB_MODE_OBJECT) {
|
|
/* object mode use boundbox centers */
|
|
uint tot = 0;
|
|
float select_center[3];
|
|
|
|
zero_v3(select_center);
|
|
LISTBASE_FOREACH (Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
|
|
if (BASE_SELECTED(v3d, base_eval)) {
|
|
/* use the boundbox if we can */
|
|
Object *ob_eval = base_eval->object;
|
|
|
|
if (ob_eval->runtime.bb && !(ob_eval->runtime.bb->flag & BOUNDBOX_DIRTY)) {
|
|
float cent[3];
|
|
|
|
BKE_boundbox_calc_center_aabb(ob_eval->runtime.bb, cent);
|
|
|
|
mul_m4_v3(ob_eval->object_to_world, cent);
|
|
add_v3_v3(select_center, cent);
|
|
}
|
|
else {
|
|
add_v3_v3(select_center, ob_eval->object_to_world[3]);
|
|
}
|
|
tot++;
|
|
}
|
|
}
|
|
if (tot) {
|
|
mul_v3_fl(select_center, 1.0f / float(tot));
|
|
copy_v3_v3(lastofs, select_center);
|
|
is_set = true;
|
|
}
|
|
}
|
|
else {
|
|
/* If there's no selection, `lastofs` is unmodified and last value since static. */
|
|
is_set = ED_transform_calc_pivot_pos(C, V3D_AROUND_CENTER_MEDIAN, lastofs);
|
|
}
|
|
|
|
copy_v3_v3(r_dyn_ofs, lastofs);
|
|
|
|
return is_set;
|
|
}
|
|
|
|
ViewOpsData *viewops_data_create(bContext *C,
|
|
const wmEvent *event,
|
|
const ViewOpsType *nav_type,
|
|
const bool use_cursor_init)
|
|
{
|
|
ViewOpsData *vod = new ViewOpsData();
|
|
vod->init_context(C);
|
|
vod->init_navigation(C, event, nav_type, nullptr, use_cursor_init);
|
|
return vod;
|
|
}
|
|
|
|
void viewops_data_free(bContext *C, ViewOpsData *vod)
|
|
{
|
|
if (!vod) {
|
|
return;
|
|
}
|
|
vod->end_navigation(C);
|
|
delete vod;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic View Operator Utilities
|
|
* \{ */
|
|
|
|
/**
|
|
* \param align_to_quat: When not nullptr, set the axis relative to this rotation.
|
|
*/
|
|
void axis_set_view(bContext *C,
|
|
View3D *v3d,
|
|
ARegion *region,
|
|
const float quat_[4],
|
|
char view,
|
|
char view_axis_roll,
|
|
int perspo,
|
|
const float *align_to_quat,
|
|
const int smooth_viewtx)
|
|
{
|
|
/* no nullptr check is needed, poll checks */
|
|
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
|
|
|
float quat[4];
|
|
const short orig_persp = rv3d->persp;
|
|
|
|
normalize_qt_qt(quat, quat_);
|
|
|
|
if (align_to_quat) {
|
|
mul_qt_qtqt(quat, quat, align_to_quat);
|
|
rv3d->view = view = RV3D_VIEW_USER;
|
|
rv3d->view_axis_roll = RV3D_VIEW_AXIS_ROLL_0;
|
|
}
|
|
|
|
if (align_to_quat == nullptr) {
|
|
rv3d->view = view;
|
|
rv3d->view_axis_roll = view_axis_roll;
|
|
}
|
|
|
|
if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) {
|
|
ED_region_tag_redraw(region);
|
|
return;
|
|
}
|
|
|
|
if (U.uiflag & USER_AUTOPERSP) {
|
|
rv3d->persp = RV3D_VIEW_IS_AXIS(view) ? RV3D_ORTHO : perspo;
|
|
}
|
|
else if (rv3d->persp == RV3D_CAMOB) {
|
|
rv3d->persp = perspo;
|
|
}
|
|
|
|
if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
|
|
/* to camera */
|
|
V3D_SmoothParams sview = {nullptr};
|
|
sview.camera_old = v3d->camera;
|
|
sview.ofs = rv3d->ofs;
|
|
sview.quat = quat;
|
|
/* No undo because this switches to/from camera. */
|
|
sview.undo_str = nullptr;
|
|
|
|
ED_view3d_smooth_view(C, v3d, region, smooth_viewtx, &sview);
|
|
}
|
|
else if (orig_persp == RV3D_CAMOB && v3d->camera) {
|
|
/* from camera */
|
|
float ofs[3], dist;
|
|
|
|
copy_v3_v3(ofs, rv3d->ofs);
|
|
dist = rv3d->dist;
|
|
|
|
/* so we animate _from_ the camera location */
|
|
Object *camera_eval = DEG_get_evaluated_object(CTX_data_ensure_evaluated_depsgraph(C),
|
|
v3d->camera);
|
|
ED_view3d_from_object(camera_eval, rv3d->ofs, nullptr, &rv3d->dist, nullptr);
|
|
|
|
V3D_SmoothParams sview = {nullptr};
|
|
sview.camera_old = camera_eval;
|
|
sview.ofs = ofs;
|
|
sview.quat = quat;
|
|
sview.dist = &dist;
|
|
/* No undo because this switches to/from camera. */
|
|
sview.undo_str = nullptr;
|
|
|
|
ED_view3d_smooth_view(C, v3d, region, smooth_viewtx, &sview);
|
|
}
|
|
else {
|
|
/* rotate around selection */
|
|
const float *dyn_ofs_pt = nullptr;
|
|
float dyn_ofs[3];
|
|
|
|
if (U.uiflag & USER_ORBIT_SELECTION) {
|
|
if (view3d_orbit_calc_center(C, dyn_ofs)) {
|
|
negate_v3(dyn_ofs);
|
|
dyn_ofs_pt = dyn_ofs;
|
|
}
|
|
}
|
|
|
|
/* no camera involved */
|
|
V3D_SmoothParams sview = {nullptr};
|
|
sview.quat = quat;
|
|
sview.dyn_ofs = dyn_ofs_pt;
|
|
/* No undo because this switches to/from camera. */
|
|
sview.undo_str = nullptr;
|
|
|
|
ED_view3d_smooth_view(C, v3d, region, smooth_viewtx, &sview);
|
|
}
|
|
}
|
|
|
|
void viewmove_apply(ViewOpsData *vod, int x, int y)
|
|
{
|
|
const float event_ofs[2] = {
|
|
float(vod->prev.event_xy[0] - x),
|
|
float(vod->prev.event_xy[1] - y),
|
|
};
|
|
|
|
if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
|
|
ED_view3d_camera_view_pan(vod->region, event_ofs);
|
|
}
|
|
else if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
|
|
vod->rv3d->ofs_lock[0] -= (event_ofs[0] * 2.0f) / float(vod->region->winx);
|
|
vod->rv3d->ofs_lock[1] -= (event_ofs[1] * 2.0f) / float(vod->region->winy);
|
|
}
|
|
else {
|
|
float dvec[3];
|
|
|
|
ED_view3d_win_to_delta(vod->region, event_ofs, vod->init.zfac, dvec);
|
|
|
|
sub_v3_v3(vod->rv3d->ofs, dvec);
|
|
|
|
if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
|
|
view3d_boxview_sync(vod->area, vod->region);
|
|
}
|
|
}
|
|
|
|
vod->prev.event_xy[0] = x;
|
|
vod->prev.event_xy[1] = y;
|
|
|
|
ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
|
|
|
|
ED_region_tag_redraw(vod->region);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Navigation Utilities
|
|
* \{ */
|
|
|
|
/* Detect the navigation operation, by the name of the navigation operator (obtained by
|
|
* `wmKeyMapItem::idname`) */
|
|
static const ViewOpsType *view3d_navigation_type_from_idname(const char *idname)
|
|
{
|
|
const blender::Array<const ViewOpsType *> nav_types = {
|
|
&ViewOpsType_zoom,
|
|
&ViewOpsType_rotate,
|
|
&ViewOpsType_move,
|
|
&ViewOpsType_pan,
|
|
// &ViewOpsType_orbit,
|
|
// &ViewOpsType_roll,
|
|
// &ViewOpsType_dolly,
|
|
#ifdef WITH_INPUT_NDOF
|
|
&ViewOpsType_ndof_orbit,
|
|
&ViewOpsType_ndof_orbit_zoom,
|
|
&ViewOpsType_ndof_pan,
|
|
&ViewOpsType_ndof_all,
|
|
#endif
|
|
};
|
|
|
|
const char *op_name = idname + sizeof("VIEW3D_OT_");
|
|
for (const ViewOpsType *nav_type : nav_types) {
|
|
if (STREQ(op_name, nav_type->idname + sizeof("VIEW3D_OT_"))) {
|
|
return nav_type;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/* Unlike `viewops_data_create`, `ED_view3d_navigation_init` creates a navigation context along
|
|
* with an array of `wmKeyMapItem`s used for navigation. */
|
|
ViewOpsData *ED_view3d_navigation_init(bContext *C, const wmKeyMapItem *kmi_merge)
|
|
{
|
|
if (!CTX_wm_region_view3d(C)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return new ViewOpsData_Utility(C, kmi_merge);
|
|
}
|
|
|
|
bool ED_view3d_navigation_do(bContext *C,
|
|
ViewOpsData *vod,
|
|
const wmEvent *event,
|
|
const float depth_loc_override[3])
|
|
{
|
|
if (!vod) {
|
|
return false;
|
|
}
|
|
|
|
wmEvent event_tmp;
|
|
if (event->type == EVT_MODAL_MAP) {
|
|
/* Workaround to use the original event values. */
|
|
event_tmp = *event;
|
|
event_tmp.type = event->prev_type;
|
|
event_tmp.val = event->prev_val;
|
|
event = &event_tmp;
|
|
}
|
|
|
|
int op_return = OPERATOR_CANCELLED;
|
|
|
|
ViewOpsData_Utility *vod_intern = static_cast<ViewOpsData_Utility *>(vod);
|
|
if (vod_intern->is_modal_event) {
|
|
const eV3D_OpEvent event_code = view3d_navigate_event(vod, event);
|
|
op_return = vod->nav_type->apply_fn(C, vod, event_code, event->xy);
|
|
if (op_return != OPERATOR_RUNNING_MODAL) {
|
|
vod->end_navigation(C);
|
|
vod_intern->is_modal_event = false;
|
|
}
|
|
}
|
|
else {
|
|
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &vod_intern->keymap_items) {
|
|
if (!WM_event_match(event, kmi)) {
|
|
continue;
|
|
}
|
|
|
|
const ViewOpsType *nav_type = view3d_navigation_type_from_idname(kmi->idname);
|
|
if (nav_type->poll_fn && !nav_type->poll_fn(C)) {
|
|
break;
|
|
}
|
|
|
|
op_return = view3d_navigation_invoke_generic(
|
|
C, vod, event, kmi->ptr, nav_type, depth_loc_override);
|
|
|
|
if (op_return == OPERATOR_RUNNING_MODAL) {
|
|
vod_intern->is_modal_event = true;
|
|
}
|
|
else {
|
|
vod->end_navigation(C);
|
|
/* Postpone the navigation confirmation to the next call.
|
|
* This avoids constant updating of the transform operation for example. */
|
|
vod->rv3d->rflag |= RV3D_NAVIGATING;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (op_return != OPERATOR_CANCELLED) {
|
|
/* Although #ED_view3d_update_viewmat is already called when redrawing the 3D View, do it here
|
|
* as well, so the updated matrix values can be accessed by the operator. */
|
|
ED_view3d_update_viewmat(
|
|
vod->depsgraph, vod->scene, vod->v3d, vod->region, nullptr, nullptr, nullptr, false);
|
|
|
|
return true;
|
|
}
|
|
else if (vod->rv3d->rflag & RV3D_NAVIGATING) {
|
|
/* Add a fake confirmation. */
|
|
vod->rv3d->rflag &= ~RV3D_NAVIGATING;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ED_view3d_navigation_free(bContext *C, ViewOpsData *vod)
|
|
{
|
|
ViewOpsData_Utility *vod_intern = static_cast<ViewOpsData_Utility *>(vod);
|
|
vod_intern->end_navigation(C);
|
|
delete vod_intern;
|
|
}
|
|
|
|
/** \} */
|